1 /***************************************************************************
2  * SANE - Scanner Access Now Easy.
3 
4    dc25.c
5 
6    This file (C) 1998 Peter Fales
7 
8    This file is part of the SANE package.
9 
10    This program is free software; you can redistribute it and/or
11    modify it under the terms of the GNU General Public License as
12    published by the Free Software Foundation; either version 2 of the
13    License, or (at your option) any later version.
14 
15    This program is distributed in the hope that it will be useful, but
16    WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    General Public License for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <https://www.gnu.org/licenses/>.
22 
23    As a special exception, the authors of SANE give permission for
24    additional uses of the libraries contained in this release of SANE.
25 
26    The exception is that, if you link a SANE library with other files
27    to produce an executable, this does not by itself cause the
28    resulting executable to be covered by the GNU General Public
29    License.  Your use of that executable is in no way restricted on
30    account of linking the SANE library code into it.
31 
32    This exception does not, however, invalidate any other reasons why
33    the executable file might be covered by the GNU General Public
34    License.
35 
36    If you submit changes to SANE to the maintainers to be included in
37    a subsequent release, you agree by submitting the changes that
38    those changes may be distributed with this exception intact.
39 
40    If you write modifications of your own for SANE, it is your choice
41    whether to permit this exception to apply to your modifications.
42    If you do not wish that, delete this exception notice.
43 
44  ***************************************************************************
45 
46    This file implements a SANE backend for the Kodak DC-25 (and
47    probably the DC-20) digital cameras.  THIS IS EXTREMELY ALPHA CODE!
48    USE AT YOUR OWN RISK!!
49 
50    (feedback to:  dc25-devel@fales-lorenz.net)
51 
52    This backend is based heavily on the dc20ctrl package by Ugo
53    Paternostro <paterno@dsi.unifi.it>.  I've attached his header below:
54 
55  ***************************************************************************
56 
57  *	Copyright (C) 1998 Ugo Paternostro <paterno@dsi.unifi.it>
58  *
59  *	This file is part of the dc20ctrl package. The complete package can be
60  *	downloaded from:
61  *	    http://aguirre.dsi.unifi.it/~paterno/binaries/dc20ctrl.tar.gz
62  *
63  *	This package is derived from the dc20 package, built by Karl Hakimian
64  *	<hakimian@aha.com> that you can find it at ftp.eecs.wsu.edu in the
65  *	/pub/hakimian directory. The complete URL is:
66  *	    ftp://ftp.eecs.wsu.edu/pub/hakimian/dc20.tar.gz
67  *
68  *	This package also includes a slightly modified version of the Comet to ppm
69  *	conversion routine written by YOSHIDA Hideki <hideki@yk.rim.or.jp>
70  *
71  *	This program is free software; you can redistribute it and/or modify
72  *	it under the terms of the GNU General Public License as published
73  *	the Free Software Foundation; either version 2 of the License, or
74  *	(at your option) any later version.
75  *
76  *	This program is distributed in the hope that it will be useful,
77  *	but WITHOUT ANY WARRANTY; without even the implied warranty of
78  *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
79  *	GNU General Public License for more details.
80  *
81  *	You should have received a copy of the GNU General Public License
82  *	along with this program; if not, write to the Free Software
83  *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
84  *
85 
86  ***************************************************************************/
87 
88 #include "../include/sane/config.h"
89 
90 #include <stdlib.h>
91 #include <string.h>
92 #include <stdio.h>
93 #include <unistd.h>
94 #include <fcntl.h>
95 #include <limits.h>
96 
97 #include "../include/sane/sane.h"
98 #include "../include/sane/sanei.h"
99 #include "../include/sane/saneopts.h"
100 
101 #define BACKEND_NAME	dc25
102 #include "../include/sane/sanei_backend.h"
103 
104 #include "dc25.h"
105 
106 #ifndef PATH_MAX
107 # define PATH_MAX	1024
108 #endif
109 
110 #define MAGIC			(void *)0xab730324
111 #define DC25_CONFIG_FILE 	"dc25.conf"
112 #define THUMBSIZE  ( (CameraInfo.model == 0x25 ) ? 14400 : 5120 )
113 
114 static SANE_Bool is_open = 0;
115 
116 static SANE_Byte dc25_opt_image_number = 1;	/* Image to load */
117 static SANE_Bool dc25_opt_thumbnails;	/* Load thumbnails */
118 static SANE_Bool dc25_opt_snap;	/* Take new picture */
119 static SANE_Bool dc25_opt_lowres;	/* Use low resoluiton */
120 #define DC25_OPT_CONTRAST_DEFAULT 1.6
121 						/* Contrast enhancement */
122 static SANE_Fixed dc25_opt_contrast = SANE_FIX (DC25_OPT_CONTRAST_DEFAULT);
123 #define DC25_OPT_GAMMA_DEFAULT 4.5
124 						/* Gamma correction (10x) */
125 static SANE_Fixed dc25_opt_gamma = SANE_FIX (DC25_OPT_GAMMA_DEFAULT);
126 static SANE_Bool dc25_opt_erase;	/* Erase all after download */
127 static SANE_Bool dc25_opt_erase_one;	/* Erase one after download */
128 static SANE_Bool dumpinquiry;
129 
130 static SANE_Int info_flags;
131 
132 static int tfd;			/* Camera File Descriptor */
133 static char tty_name[PATH_MAX];
134 #define DEF_TTY_NAME "/dev/ttyS0"
135 
136 static speed_t tty_baud = DEFAULT_TTY_BAUD;
137 #define TMPFILE_PATTERN "/tmp/dc25XXXXXX";
138 
139 static Dc20Info *dc20_info;
140 static Dc20Info CameraInfo;
141 
142 static SANE_Byte contrast_table[256];
143 
144 static struct pixmap *pp;
145 
146 static const SANE_Range contrast_range = {
147   0 << SANE_FIXED_SCALE_SHIFT,	/* minimum */
148   3 << SANE_FIXED_SCALE_SHIFT,	/* maximum */
149   16384				/* quantization ~ 0.025 */
150 };
151 
152 static const SANE_Range gamma_range = {
153   0 << SANE_FIXED_SCALE_SHIFT,	/* minimum */
154   10 << SANE_FIXED_SCALE_SHIFT,	/* maximum */
155   16384				/* quantization ~ 0.025 */
156 };
157 
158 static SANE_Range image_range = {
159   0,
160   14,
161   0
162 };
163 
164 static SANE_Option_Descriptor sod[] = {
165   {
166    SANE_NAME_NUM_OPTIONS,
167    SANE_TITLE_NUM_OPTIONS,
168    SANE_DESC_NUM_OPTIONS,
169    SANE_TYPE_INT,
170    SANE_UNIT_NONE,
171    sizeof (SANE_Word),
172    SANE_CAP_SOFT_DETECT,
173    SANE_CONSTRAINT_NONE,
174    {NULL}
175    }
176   ,
177 
178 #define D25_OPT_IMAGE_SELECTION 1
179   {
180    "",
181    "Image Selection",
182    "Selection of the image to load.",
183    SANE_TYPE_GROUP,
184    SANE_UNIT_NONE,
185    0,
186    0,
187    SANE_CONSTRAINT_NONE,
188    {NULL}
189    }
190   ,
191 
192 #define DC25_OPT_IMAGE_NUMBER 2
193   {
194    "image",
195    "Image Number",
196    "Select Image Number to load from camera",
197    SANE_TYPE_INT,
198    SANE_UNIT_NONE,
199    4,
200    SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
201    SANE_CONSTRAINT_RANGE,
202    {(SANE_String_Const *) & image_range}	/* this is ANSI conformant! */
203    }
204   ,
205 
206 #define DC25_OPT_THUMBS 3
207   {
208    "thumbs",
209    "Load Thumbnail",
210    "Load the image as thumbnail.",
211    SANE_TYPE_BOOL,
212    SANE_UNIT_NONE,
213    sizeof (SANE_Word),
214    SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
215    SANE_CONSTRAINT_NONE,
216    {NULL}
217    }
218   ,
219 #define DC25_OPT_SNAP 4
220   {
221    "snap",
222    "Snap new picture",
223    "Take new picture and download it",
224    SANE_TYPE_BOOL,
225    SANE_UNIT_NONE,
226    sizeof (SANE_Word),
227    SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED,
228    SANE_CONSTRAINT_NONE,
229    {NULL}
230    }
231   ,
232 #define DC25_OPT_LOWRES 5
233   {
234    "lowres",
235    "Low Resolution",
236    "New pictures taken in low resolution",
237    SANE_TYPE_BOOL,
238    SANE_UNIT_NONE,
239    sizeof (SANE_Word),
240    SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE |
241    SANE_CAP_ADVANCED,
242    SANE_CONSTRAINT_NONE,
243    {NULL}
244    }
245   ,
246 
247 #define DC25_OPT_ERASE 6
248   {
249    "erase",
250    "Erase",
251    "Erase all pictures after downloading",
252    SANE_TYPE_BOOL,
253    SANE_UNIT_NONE,
254    sizeof (SANE_Word),
255    SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
256    SANE_CONSTRAINT_NONE,
257    {NULL}
258    }
259   ,
260 
261 #define DC25_OPT_ERASE_ONE 7
262   {
263    "erase-one",
264    "Erase One",
265    "Erase downloaded picture after downloading",
266    SANE_TYPE_BOOL,
267    SANE_UNIT_NONE,
268    sizeof (SANE_Word),
269    SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
270    SANE_CONSTRAINT_NONE,
271    {NULL}
272    }
273   ,
274 
275 #define DC25_OPT_ENHANCE 8
276   {
277    "",
278    "Image Parameters",
279    "Modifications to image parameters",
280    SANE_TYPE_GROUP,
281    SANE_UNIT_NONE,
282    0,
283    0,
284    SANE_CONSTRAINT_NONE,
285    {NULL}
286    }
287   ,
288 
289 #define DC25_OPT_CONTRAST 9
290   {
291    "contrast",
292    "Contrast Adjustment",
293    "Values > 1 enhance contrast",
294    SANE_TYPE_FIXED,
295    SANE_UNIT_NONE,
296    sizeof (SANE_Word),
297    SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
298    SANE_CONSTRAINT_RANGE,
299    {(const SANE_String_Const *) &contrast_range}	/* this is ANSI conformant! */
300    },
301 
302 #define DC25_OPT_GAMMA 10
303   {
304    "gamma",
305    "Gamma Adjustment",
306    "Larger values make image darker",
307    SANE_TYPE_FIXED,
308    SANE_UNIT_NONE,
309    sizeof (SANE_Word),
310    SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
311    SANE_CONSTRAINT_RANGE,
312    {(const SANE_String_Const *) &gamma_range}	/* this is ANSI conformant! */
313    },
314 
315 #define DC25_OPT_DEFAULT 11
316   {
317    "default-enhancements",
318    "Defaults",
319    "Set default values for enhancement controls (i.e. contrast).",
320    SANE_TYPE_BUTTON,
321    SANE_UNIT_NONE,
322    0,
323    SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
324    SANE_CONSTRAINT_NONE,
325    {NULL}
326    }
327 };
328 
329 static SANE_Parameters parms = {
330   SANE_FRAME_RGB,
331   1,
332   500,				/* Number of bytes returned per scan line: */
333   500,				/* Number of pixels per scan line.  */
334   373,				/* Number of lines for the current scan.  */
335   8,				/* Number of bits per sample. */
336 };
337 
338 
339 
340 
341 static unsigned char init_pck[] = INIT_PCK;
342 
343 /*
344  * List of speeds to try to establish connection with the camera.
345  * Check 9600 first, as it's the speed the camera comes up in, then
346  * 115200, as that is the one most likely to be configured from a
347  * previous run
348  */
349 static struct pkt_speed speeds[] = { {B9600, {0x96, 0x00}},
350 #ifdef B115200
351 {B115200, {0x11, 0x52}},
352 #endif
353 #ifdef B57600
354 {B57600, {0x57, 0x60}},
355 #endif
356 {B38400, {0x38, 0x40}},
357 {B19200, {0x19, 0x20}},
358 };
359 #define NUM_OF_SPEEDS	((int)(sizeof(speeds) / sizeof(struct pkt_speed)))
360 
361 static struct termios tty_orig;
362 
363 static int
send_pck(int fd, unsigned char *pck)364 send_pck (int fd, unsigned char *pck)
365 {
366   int n;
367   unsigned char r;
368 
369   /*
370    * Not quite sure why we need this, but the program works a whole
371    * lot better (at least on the DC25)  with this short delay.
372    */
373 #ifdef HAVE_USLEEP
374   usleep (10);
375 #else
376   sleep (1);
377 #endif
378   if (write (fd, (char *) pck, 8) != 8)
379     {
380       DBG (2, "send_pck: error: write returned -1\n");
381       return -1;
382     }
383 
384   if ((n = read (fd, (char *) &r, 1)) != 1)
385     {
386       DBG (2, "send_pck: error: read returned -1\n");
387       return -1;
388     }
389 
390   return (r == 0xd1) ? 0 : -1;
391 }
392 
393 static int
init_dc20(char *device, speed_t speed)394 init_dc20 (char *device, speed_t speed)
395 {
396   struct termios tty_new;
397   int speed_index;
398 
399   DBG (1, "DC-20/25 Backend 05/07/01\n");
400 
401   for (speed_index = 0; speed_index < NUM_OF_SPEEDS; speed_index++)
402     {
403       if (speeds[speed_index].baud == speed)
404 	{
405 	  init_pck[2] = speeds[speed_index].pkt_code[0];
406 	  init_pck[3] = speeds[speed_index].pkt_code[1];
407 	  break;
408 	}
409     }
410 
411   if (init_pck[2] == 0)
412     {
413       DBG (2, "unsupported baud rate.\n");
414       return -1;
415     }
416 
417   /*
418      Open device file.
419    */
420   if ((tfd = open (device, O_RDWR)) == -1)
421     {
422       DBG (2, "init_dc20: error: could not open %s for read/write\n", device);
423       return -1;
424     }
425   /*
426      Save old device information to restore when we are done.
427    */
428   if (tcgetattr (tfd, &tty_orig) == -1)
429     {
430       DBG (2, "init_dc20: error: could not get attributes\n");
431       return -1;
432     }
433 
434   memcpy ((char *) &tty_new, (char *) &tty_orig, sizeof (struct termios));
435   /*
436      We need the device to be raw. 8 bits even parity on 9600 baud to start.
437    */
438 #ifdef HAVE_CFMAKERAW
439   cfmakeraw (&tty_new);
440 #else
441   tty_new.c_lflag &= ~(ICANON | ECHO | ISIG);
442 #endif
443   tty_new.c_oflag &= ~CSTOPB;
444   tty_new.c_cflag |= PARENB;
445   tty_new.c_cflag &= ~PARODD;
446   tty_new.c_cc[VMIN] = 0;
447   tty_new.c_cc[VTIME] = 50;
448   cfsetospeed (&tty_new, B9600);
449   cfsetispeed (&tty_new, B9600);
450 
451   if (tcsetattr (tfd, TCSANOW, &tty_new) == -1)
452     {
453       DBG (2, "init_dc20: error: could not set attributes\n");
454       return -1;
455     }
456 
457   if (send_pck (tfd, init_pck) == -1)
458     {
459       /*
460        *      The camera always powers up at 9600, so we try
461        *      that first.  However, it may be already set to
462        *      a different speed.  Try the entries in the table:
463        */
464 
465       for (speed_index = NUM_OF_SPEEDS - 1; speed_index > 0; speed_index--)
466 	{
467 	  DBG (3, "init_dc20: changing speed to %d\n",
468 	       (int) speeds[speed_index].baud);
469 
470 	  cfsetospeed (&tty_new, speeds[speed_index].baud);
471 	  cfsetispeed (&tty_new, speeds[speed_index].baud);
472 
473 	  if (tcsetattr (tfd, TCSANOW, &tty_new) == -1)
474 	    {
475 	      DBG (2, "init_dc20: error: could not set attributes\n");
476 	      return -1;
477 	    }
478 	  if (send_pck (tfd, init_pck) != -1)
479 	    break;
480 	}
481 
482       if (speed_index == 0)
483 	{
484 	  tcsetattr (tfd, TCSANOW, &tty_orig);
485 	  DBG (2, "init_dc20: error: no suitable baud rate\n");
486 	  return -1;
487 	}
488     }
489   /*
490      Set speed to requested speed. Also, make a long timeout (we need this for
491      erase and shoot operations)
492    */
493   tty_new.c_cc[VTIME] = 150;
494   cfsetospeed (&tty_new, speed);
495   cfsetispeed (&tty_new, speed);
496 
497   if (tcsetattr (tfd, TCSANOW, &tty_new) == -1)
498     {
499       DBG (2, "init_dc20: error: could not set attributes\n");
500       return -1;
501     }
502 
503   return tfd;
504 }
505 
506 static void
close_dc20(int fd)507 close_dc20 (int fd)
508 {
509   DBG (127, "close_dc20() called\n");
510   /*
511    *      Put the camera back to 9600 baud
512    */
513 
514   init_pck[2] = speeds[0].pkt_code[0];
515   init_pck[3] = speeds[0].pkt_code[1];
516   if (send_pck (fd, init_pck) == -1)
517     {
518       DBG (4, "close_dc20: error: could not set attributes\n");
519     }
520 
521   /*
522      Restore original device settings.
523    */
524   if (tcsetattr (fd, TCSANOW, &tty_orig) == -1)
525     {
526       DBG (4, "close_dc20: error: could not set attributes\n");
527     }
528 
529   if (close (fd) == -1)
530     {
531       DBG (4, "close_dc20: error: could not close device\n");
532     }
533 }
534 
535 static unsigned char info_pck[] = INFO_PCK;
536 
537 static Dc20Info *
get_info(int fd)538 get_info (int fd)
539 {
540   unsigned char buf[256];
541 
542   if (send_pck (fd, info_pck) == -1)
543     {
544       DBG (2, "get_info: error: send_pck returned -1\n");
545       return NULL;
546     }
547 
548   DBG (9, "get_info: read info packet\n");
549 
550   if (read_data (fd, buf, 256) == -1)
551     {
552       DBG (2, "get_info: error: read_data returned -1\n");
553       return NULL;
554     }
555 
556   if (end_of_data (fd) == -1)
557     {
558       DBG (2, "get_info: error: end_of_data returned -1\n");
559       return NULL;
560     }
561 
562   CameraInfo.model = buf[1];
563   CameraInfo.ver_major = buf[2];
564   CameraInfo.ver_minor = buf[3];
565   CameraInfo.pic_taken = buf[8] << 8 | buf[9];
566   if (CameraInfo.model == 0x25)
567     {
568 
569       /* Not sure where the previous line came from.  All the
570        * information I have says that even on the DC20 the number of
571        * standard res pics is in byte 17 and the number of high res pics
572        * is in byte 19.  This is definitely true on my DC25.
573        */
574       CameraInfo.pic_taken = buf[17] + buf[19];
575     }
576 
577   image_range.max = CameraInfo.pic_taken;
578   image_range.min = CameraInfo.pic_taken ? 1 : 0;
579 
580   CameraInfo.pic_left = buf[10] << 8 | buf[11];
581 
582   if (CameraInfo.model == 0x25)
583     {
584       /* Not sure where the previous line came from.  All the
585        * information I have says that even on the DC20 the number of
586        * standard res pics left is in byte 23 and the number of high res
587        * pics left is in byte 21.  It seems to me that the conservative
588        * approach is to report the number of high res pics left.
589        */
590       CameraInfo.pic_left = buf[21];
591     }
592   CameraInfo.flags.low_res = buf[23];
593 
594   if (CameraInfo.model == 0x25)
595     {
596       /* Not sure where the previous line came from.  All the
597        * information I have says that even on the DC20 the low_res
598        * byte is 11.
599        */
600       CameraInfo.flags.low_res = buf[11];
601     }
602   CameraInfo.flags.low_batt = buf[29];
603 
604   return &CameraInfo;
605 }
606 
607 static int
read_data(int fd, unsigned char *buf, int sz)608 read_data (int fd, unsigned char *buf, int sz)
609 {
610   unsigned char ccsum;
611   unsigned char rcsum;
612   unsigned char c;
613   int retries = 0;
614   int n;
615   int r = 0;
616   int i;
617 
618   while (retries++ < 5)
619     {
620 
621       /*
622        * If this is not the first time through, then it must be
623        * a retry - signal the camera that we didn't like what
624        * we got.  In either case, start filling the packet
625        */
626       if (retries != 1)
627 	{
628 
629 	  DBG (2, "Attempt retry %d\n", retries);
630 	  c = 0xe3;
631 	  if (write (fd, (char *) &c, 1) != 1)
632 	    {
633 	      DBG (2, "read_data: error: write ack\n");
634 	      return -1;
635 	    }
636 
637 	}
638 
639       for (n = 0; n < sz && (r = read (fd, (char *) &buf[n], sz - n)) > 0;
640 	   n += r)
641 	;
642 
643       if (r <= 0)
644 	{
645 	  DBG (2, "read_data: error: read returned -1\n");
646 	  continue;
647 	}
648 
649       if (n < sz || read (fd, &rcsum, 1) != 1)
650 	{
651 	  DBG (2, "read_data: error: buffer underrun or no checksum\n");
652 	  continue;
653 	}
654 
655       for (i = 0, ccsum = 0; i < n; i++)
656 	ccsum ^= buf[i];
657 
658       if (ccsum != rcsum)
659 	{
660 	  DBG (2, "read_data: error: bad checksum (%02x != %02x)\n", rcsum,
661 	       ccsum);
662 	  continue;
663 	}
664 
665       /* If we got this far, then the packet is OK */
666       break;
667     }
668 
669   c = 0xd2;
670 
671   if (write (fd, (char *) &c, 1) != 1)
672     {
673       DBG (2, "read_data: error: write ack\n");
674       return -1;
675     }
676 
677   return 0;
678 }
679 
680 static int
end_of_data(int fd)681 end_of_data (int fd)
682 {
683   char c;
684 
685   if (read (fd, &c, 1) != 1)
686     {
687       DBG (2, "end_of_data: error: read returned -1\n");
688       return -1;
689     }
690 
691   if (c != 0)
692     {
693       DBG (2, "end_of_data: error: bad EOD from camera (%02x)\n",
694 	   (unsigned) c);
695       return -1;
696     }
697 
698   return 0;
699 }
700 
701 #include <math.h>
702 
703 #define BIDIM_ARRAY(name, x, y, width)	(name[((x) + ((y) * (width)))])
704 
705 /*
706  *	These definitions depend on the resolution of the image
707  */
708 
709 #define MY_LOW_RIGHT_MARGIN 6
710 
711 /*
712  *	These definitions are constant with resolution
713  */
714 
715 #define MY_LEFT_MARGIN 2
716 
717 #define NET_COLUMNS (columns - MY_LEFT_MARGIN - right_margin)
718 #define NET_LINES   (HEIGHT - TOP_MARGIN - BOTTOM_MARGIN)
719 #define NET_PIXELS  (NET_COLUMNS * NET_LINES)
720 
721 
722 #define SCALE 64
723 #define SMAX (256 * SCALE - 1)
724 #define HORIZONTAL_INTERPOLATIONS 3
725 #define HISTOGRAM_STEPS 4096
726 
727 #define RFACTOR 0.64
728 #define GFACTOR 0.58
729 #define BFACTOR 1.00
730 #define RINTENSITY 0.476
731 #define GINTENSITY 0.299
732 #define BINTENSITY 0.175
733 
734 #define SATURATION 1.0
735 #define NORM_PERCENTAGE 3
736 
737 static int columns = HIGH_WIDTH,
738   right_margin = HIGH_RIGHT_MARGIN, camera_header_size = HIGH_CAMERA_HEADER;
739 static int low_i = -1, high_i = -1, norm_percentage = NORM_PERCENTAGE;
740 static float saturation = SATURATION,
741   rfactor = RFACTOR, gfactor = GFACTOR, bfactor = BFACTOR;
742 
743 static void
set_initial_interpolation(const unsigned char ccd[], short horizontal_interpolation[])744 set_initial_interpolation (const unsigned char ccd[],
745 			   short horizontal_interpolation[])
746 {
747   int column, line;
748   for (line = 0; line < HEIGHT; line++)
749     {
750       BIDIM_ARRAY (horizontal_interpolation, MY_LEFT_MARGIN, line, columns) =
751 	BIDIM_ARRAY (ccd, MY_LEFT_MARGIN + 1, line, columns) * SCALE;
752       BIDIM_ARRAY (horizontal_interpolation, columns - right_margin - 1, line,
753 		   columns) =
754 	BIDIM_ARRAY (ccd, columns - right_margin - 2, line, columns) * SCALE;
755       for (column = MY_LEFT_MARGIN + 1; column < columns - right_margin - 1;
756 	   column++)
757 	{
758 	  BIDIM_ARRAY (horizontal_interpolation, column, line, columns) =
759 	    (BIDIM_ARRAY (ccd, column - 1, line, columns) +
760 	     BIDIM_ARRAY (ccd, column + 1, line, columns)) * (SCALE / 2);
761 	}
762     }
763 }
764 
765 static void
interpolate_horizontally(const unsigned char ccd[], short horizontal_interpolation[])766 interpolate_horizontally (const unsigned char ccd[],
767 			  short horizontal_interpolation[])
768 {
769   int column, line, i, initial_column;
770   for (line = TOP_MARGIN - 1; line < HEIGHT - BOTTOM_MARGIN + 1; line++)
771     {
772       for (i = 0; i < HORIZONTAL_INTERPOLATIONS; i++)
773 	{
774 	  for (initial_column = MY_LEFT_MARGIN + 1;
775 	       initial_column <= MY_LEFT_MARGIN + 2; initial_column++)
776 	    {
777 	      for (column = initial_column;
778 		   column < columns - right_margin - 1; column += 2)
779 		{
780 		  BIDIM_ARRAY (horizontal_interpolation, column, line,
781 			       columns) =
782 		    ((float) BIDIM_ARRAY (ccd, column - 1, line, columns) /
783 		     BIDIM_ARRAY (horizontal_interpolation, column - 1, line,
784 				  columns) + (float) BIDIM_ARRAY (ccd,
785 								  column + 1,
786 								  line,
787 								  columns) /
788 		     BIDIM_ARRAY (horizontal_interpolation, column + 1, line,
789 				  columns)) * BIDIM_ARRAY (ccd, column, line,
790 							   columns) * (SCALE *
791 								       SCALE /
792 								       2) +
793 		    0.5;
794 		}
795 	    }
796 	}
797     }
798 }
799 
800 static void
interpolate_vertically(const unsigned char ccd[], const short horizontal_interpolation[], short red[], short green[], short blue[])801 interpolate_vertically (const unsigned char ccd[],
802 			const short horizontal_interpolation[],
803 			short red[], short green[], short blue[])
804 {
805   int column, line;
806   for (line = TOP_MARGIN; line < HEIGHT - BOTTOM_MARGIN; line++)
807     {
808       for (column = MY_LEFT_MARGIN; column < columns - right_margin; column++)
809 	{
810 	  int r2gb, g2b, rg2, rgb2, r, g, b;
811 	  int this_ccd = BIDIM_ARRAY (ccd, column, line, columns) * SCALE;
812 	  int up_ccd = BIDIM_ARRAY (ccd, column, line - 1, columns) * SCALE;
813 	  int down_ccd = BIDIM_ARRAY (ccd, column, line + 1, columns) * SCALE;
814 	  int this_horizontal_interpolation =
815 	    BIDIM_ARRAY (horizontal_interpolation, column, line, columns);
816 	  int this_intensity = this_ccd + this_horizontal_interpolation;
817 	  int up_intensity =
818 	    BIDIM_ARRAY (horizontal_interpolation, column, line - 1,
819 			 columns) + up_ccd;
820 	  int down_intensity =
821 	    BIDIM_ARRAY (horizontal_interpolation, column, line + 1,
822 			 columns) + down_ccd;
823 	  int this_vertical_interpolation;
824 
825 	  /*
826 	   * PSF: I don't understand all this code, but I've found pictures
827 	   * where up_intensity or down_intensity are zero, resulting in a
828 	   * divide by zero error.  It looks like this only happens when
829 	   * up_ccd or down_ccd are also zero, so we just set the intensity
830 	   * value to non-zero to prevent the error.
831 	   */
832 	  if (down_ccd == 0)
833 	    DBG (10, "down_ccd==0 at %d,%d\n", line, column);
834 	  if (up_ccd == 0)
835 	    DBG (10, "up_ccd==0 at %d,%d\n", line, column);
836 	  if (down_intensity == 0)
837 	    {
838 	      DBG (9, "Found down_intensity==0 at %d,%d down_ccd=%d\n", line,
839 		   column, down_ccd);
840 	      down_intensity = 1;
841 	    }
842 	  if (up_intensity == 0)
843 	    {
844 	      DBG (9, "Found up_intensity==0 at %d,%d up_ccd=%d\n", line,
845 		   column, up_ccd);
846 	      up_intensity = 1;
847 	    }
848 
849 	  if (line == TOP_MARGIN)
850 	    {
851 	      this_vertical_interpolation =
852 		(float) down_ccd / down_intensity * this_intensity + 0.5;
853 	    }
854 	  else if (line == HEIGHT - BOTTOM_MARGIN - 1)
855 	    {
856 	      this_vertical_interpolation =
857 		(float) up_ccd / up_intensity * this_intensity + 0.5;
858 	    }
859 	  else
860 	    {
861 	      this_vertical_interpolation =
862 		((float) up_ccd / up_intensity +
863 		 (float) down_ccd / down_intensity) * this_intensity / 2.0 +
864 		0.5;
865 	    }
866 	  if (line & 1)
867 	    {
868 	      if (column & 1)
869 		{
870 		  r2gb = this_ccd;
871 		  g2b = this_horizontal_interpolation;
872 		  rg2 = this_vertical_interpolation;
873 		  r = (2 * (r2gb - g2b) + rg2) / 5;
874 		  g = (rg2 - r) / 2;
875 		  b = g2b - 2 * g;
876 		}
877 	      else
878 		{
879 		  g2b = this_ccd;
880 		  r2gb = this_horizontal_interpolation;
881 		  rgb2 = this_vertical_interpolation;
882 		  r = (3 * r2gb - g2b - rgb2) / 5;
883 		  g = 2 * r - r2gb + g2b;
884 		  b = g2b - 2 * g;
885 		}
886 	    }
887 	  else
888 	    {
889 	      if (column & 1)
890 		{
891 		  rg2 = this_ccd;
892 		  rgb2 = this_horizontal_interpolation;
893 		  r2gb = this_vertical_interpolation;
894 		  b = (3 * rgb2 - r2gb - rg2) / 5;
895 		  g = (rgb2 - r2gb + rg2 - b) / 2;
896 		  r = rg2 - 2 * g;
897 		}
898 	      else
899 		{
900 		  rgb2 = this_ccd;
901 		  rg2 = this_horizontal_interpolation;
902 		  g2b = this_vertical_interpolation;
903 		  b = (g2b - 2 * (rg2 - rgb2)) / 5;
904 		  g = (g2b - b) / 2;
905 		  r = rg2 - 2 * g;
906 		}
907 	    }
908 	  if (r < 0)
909 	    r = 0;
910 	  if (g < 0)
911 	    g = 0;
912 	  if (b < 0)
913 	    b = 0;
914 	  BIDIM_ARRAY (red, column, line, columns) = r;
915 	  BIDIM_ARRAY (green, column, line, columns) = g;
916 	  BIDIM_ARRAY (blue, column, line, columns) = b;
917 	}
918     }
919 }
920 
921 static void
adjust_color_and_saturation(short red[], short green[], short blue[])922 adjust_color_and_saturation (short red[], short green[], short blue[])
923 {
924   int line, column;
925   int r_min = SMAX, g_min = SMAX, b_min = SMAX;
926   int r_max = 0, g_max = 0, b_max = 0;
927   float sqr_saturation = sqrt (saturation);
928   for (line = TOP_MARGIN; line < HEIGHT - BOTTOM_MARGIN; line++)
929     {
930       for (column = MY_LEFT_MARGIN; column < columns - right_margin; column++)
931 	{
932 	  float r = BIDIM_ARRAY (red, column, line, columns) * rfactor;
933 	  float g = BIDIM_ARRAY (green, column, line, columns) * gfactor;
934 	  float b = BIDIM_ARRAY (blue, column, line, columns) * bfactor;
935 	  if (saturation != 1.0)
936 	    {
937 	      float *min, *mid, *max, new_intensity;
938 	      float intensity =
939 		r * RINTENSITY + g * GINTENSITY + b * BINTENSITY;
940 	      if (r > g)
941 		{
942 		  if (r > b)
943 		    {
944 		      max = &r;
945 		      if (g > b)
946 			{
947 			  min = &b;
948 			  mid = &g;
949 			}
950 		      else
951 			{
952 			  min = &g;
953 			  mid = &b;
954 			}
955 		    }
956 		  else
957 		    {
958 		      min = &g;
959 		      mid = &r;
960 		      max = &b;
961 		    }
962 		}
963 	      else
964 		{
965 		  if (g > b)
966 		    {
967 		      max = &g;
968 		      if (r > b)
969 			{
970 			  min = &b;
971 			  mid = &r;
972 			}
973 		      else
974 			{
975 			  min = &r;
976 			  mid = &b;
977 			}
978 		    }
979 		  else
980 		    {
981 		      min = &r;
982 		      mid = &g;
983 		      max = &b;
984 		    }
985 		}
986 	      *mid = *min + sqr_saturation * (*mid - *min);
987 	      *max = *min + saturation * (*max - *min);
988 	      new_intensity =
989 		r * RINTENSITY + g * GINTENSITY + b * BINTENSITY;
990 	      r *= intensity / new_intensity;
991 	      g *= intensity / new_intensity;
992 	      b *= intensity / new_intensity;
993 	    }
994 	  r += 0.5;
995 	  g += 0.5;
996 	  b += 0.5;
997 	  if (r_min > r)
998 	    r_min = r;
999 	  if (g_min > g)
1000 	    g_min = g;
1001 	  if (b_min > b)
1002 	    b_min = b;
1003 	  if (r_max < r)
1004 	    r_max = r;
1005 	  if (g_max < g)
1006 	    g_max = g;
1007 	  if (b_max < b)
1008 	    b_max = b;
1009 	  BIDIM_ARRAY (red, column, line, columns) = r;
1010 	  BIDIM_ARRAY (green, column, line, columns) = g;
1011 	  BIDIM_ARRAY (blue, column, line, columns) = b;
1012 	}
1013     }
1014 }
1015 
1016 static int
min3(int x, int y, int z)1017 min3 (int x, int y, int z)
1018 {
1019   return (x < y ? (x < z ? x : z) : (y < z ? y : z));
1020 }
1021 
1022 static int
max3(int x, int y, int z)1023 max3 (int x, int y, int z)
1024 {
1025   return (x > y ? (x > z ? x : z) : (y > z ? y : z));
1026 }
1027 
1028 static void
determine_limits(const short red[], const short green[], const short blue[], int *low_i_ptr, int *high_i_ptr)1029 determine_limits (const short red[],
1030 		  const short green[],
1031 		  const short blue[], int *low_i_ptr, int *high_i_ptr)
1032 {
1033   unsigned int histogram[HISTOGRAM_STEPS + 1];
1034   int column, line, i, s;
1035   int low_i = *low_i_ptr, high_i = *high_i_ptr;
1036   int max_i = 0;
1037   for (line = TOP_MARGIN; line < HEIGHT - BOTTOM_MARGIN; line++)
1038     {
1039       for (column = MY_LEFT_MARGIN; column < columns - right_margin; column++)
1040 	{
1041 	  i = max3 (BIDIM_ARRAY (red, column, line, columns),
1042 		    BIDIM_ARRAY (green, column, line, columns),
1043 		    BIDIM_ARRAY (blue, column, line, columns));
1044 	  if (i > max_i)
1045 	    max_i = i;
1046 	}
1047     }
1048   if (low_i == -1)
1049     {
1050       for (i = 0; i <= HISTOGRAM_STEPS; i++)
1051 	histogram[i] = 0;
1052       for (line = TOP_MARGIN; line < HEIGHT - BOTTOM_MARGIN; line++)
1053 	{
1054 	  for (column = MY_LEFT_MARGIN; column < columns - right_margin;
1055 	       column++)
1056 	    {
1057 	      i = min3 (BIDIM_ARRAY (red, column, line, columns),
1058 			BIDIM_ARRAY (green, column, line, columns),
1059 			BIDIM_ARRAY (blue, column, line, columns));
1060 	      histogram[i * HISTOGRAM_STEPS / max_i]++;
1061 	    }
1062 	}
1063       for (low_i = 0, s = 0;
1064 	   low_i <= HISTOGRAM_STEPS && s < NET_PIXELS * norm_percentage / 100;
1065 	   low_i++)
1066 	{
1067 	  s += histogram[low_i];
1068 	}
1069       low_i = (low_i * max_i + HISTOGRAM_STEPS / 2) / HISTOGRAM_STEPS;
1070       *low_i_ptr = low_i;
1071     }
1072   if (high_i == -1)
1073     {
1074       for (i = 0; i <= HISTOGRAM_STEPS; i++)
1075 	histogram[i] = 0;
1076       for (line = TOP_MARGIN; line < HEIGHT - BOTTOM_MARGIN; line++)
1077 	{
1078 	  for (column = MY_LEFT_MARGIN; column < columns - right_margin;
1079 	       column++)
1080 	    {
1081 	      i = max3 (BIDIM_ARRAY (red, column, line, columns),
1082 			BIDIM_ARRAY (green, column, line, columns),
1083 			BIDIM_ARRAY (blue, column, line, columns));
1084 	      histogram[i * HISTOGRAM_STEPS / max_i]++;
1085 	    }
1086 	}
1087       for (high_i = HISTOGRAM_STEPS, s = 0;
1088 	   high_i >= 0 && s < NET_PIXELS * norm_percentage / 100; high_i--)
1089 	{
1090 	  s += histogram[high_i];
1091 	}
1092       high_i = (high_i * max_i + HISTOGRAM_STEPS / 2) / HISTOGRAM_STEPS;
1093       *high_i_ptr = high_i;
1094     }
1095 /*
1096 if (verbose) printf ("%s: determine_limits: low_i = %d, high_i = %d\n", __progname, low_i, high_i);
1097 */
1098 }
1099 
1100 /*
1101  * The original dc20ctrl program used a default gamma of 0.35, but I thought
1102  * 0.45 looks better.  In addition, since xscanimage seems to always force
1103  * a resolution of 0.1, I multiply everything by 10 and make the default
1104  * 4.5.
1105  */
1106 
1107 static unsigned char *
make_gamma_table(int range)1108 make_gamma_table (int range)
1109 {
1110   int i;
1111   double factor =
1112     pow (256.0, 1.0 / (SANE_UNFIX (dc25_opt_gamma) / 10.0)) / range;
1113   unsigned char *gamma_table;
1114   if ((gamma_table = malloc (range * sizeof (unsigned char))) == NULL)
1115     {
1116       DBG (1, "make_gamma_table: can't allocate memory for gamma table\n");
1117       return NULL;
1118     }
1119   for (i = 0; i < range; i++)
1120     {
1121       int g =
1122 	pow ((double) i * factor, (SANE_UNFIX (dc25_opt_gamma) / 10.0)) + 0.5;
1123 /*
1124 		if (verbose) fprintf (stderr, "%s: make_gamma_table: gamma[%4d] = %3d\n", __progname, i, g);
1125 */
1126       if (g > 255)
1127 	g = 255;
1128       gamma_table[i] = g;
1129     }
1130   return gamma_table;
1131 }
1132 
1133 static int
lookup_gamma_table(int i, int low_i, int high_i, const unsigned char gamma_table[])1134 lookup_gamma_table (int i, int low_i, int high_i,
1135 		    const unsigned char gamma_table[])
1136 {
1137   if (i <= low_i)
1138     return 0;
1139   if (i >= high_i)
1140     return 255;
1141   return gamma_table[i - low_i];
1142 }
1143 
1144 static int
output_rgb(const short red[], const short green[], const short blue[], int low_i, int high_i, struct pixmap *pp)1145 output_rgb (const short red[],
1146 	    const short green[],
1147 	    const short blue[], int low_i, int high_i, struct pixmap *pp)
1148 {
1149   int r_min = 255, g_min = 255, b_min = 255;
1150   int r_max = 0, g_max = 0, b_max = 0;
1151   int column, line;
1152   unsigned char *gamma_table = make_gamma_table (high_i - low_i);
1153 
1154   if (gamma_table == NULL)
1155     {
1156       DBG (10, "output_rgb: error: cannot make gamma table\n");
1157       return -1;
1158     }
1159 
1160   for (line = TOP_MARGIN; line < HEIGHT - BOTTOM_MARGIN; line++)
1161     {
1162       for (column = MY_LEFT_MARGIN; column < columns - right_margin; column++)
1163 	{
1164 	  int r =
1165 	    lookup_gamma_table (BIDIM_ARRAY (red, column, line, columns),
1166 				low_i, high_i, gamma_table);
1167 	  int g =
1168 	    lookup_gamma_table (BIDIM_ARRAY (green, column, line, columns),
1169 				low_i, high_i, gamma_table);
1170 	  int b =
1171 	    lookup_gamma_table (BIDIM_ARRAY (blue, column, line, columns),
1172 				low_i, high_i, gamma_table);
1173 	  if (r > 255)
1174 	    r = 255;
1175 	  else if (r < 0)
1176 	    r = 0;
1177 	  if (g > 255)
1178 	    g = 255;
1179 	  else if (g < 0)
1180 	    g = 0;
1181 	  if (b > 255)
1182 	    b = 255;
1183 	  else if (b < 0)
1184 	    b = 0;
1185 	  set_pixel_rgb (pp, column - MY_LEFT_MARGIN, line - TOP_MARGIN, r, g,
1186 			 b);
1187 	  if (r_min > r)
1188 	    r_min = r;
1189 	  if (g_min > g)
1190 	    g_min = g;
1191 	  if (b_min > b)
1192 	    b_min = b;
1193 	  if (r_max < r)
1194 	    r_max = r;
1195 	  if (g_max < g)
1196 	    g_max = g;
1197 	  if (b_max < b)
1198 	    b_max = b;
1199 	}
1200     }
1201   free (gamma_table);
1202   return 0;
1203 }
1204 
1205 static int
comet_to_pixmap(unsigned char *pic, struct pixmap *pp)1206 comet_to_pixmap (unsigned char *pic, struct pixmap *pp)
1207 {
1208   unsigned char *ccd;
1209   short *horizontal_interpolation, *red, *green, *blue;
1210   int retval = 0;
1211 
1212   if (pic == NULL)
1213     {
1214       DBG (1, "cmttoppm: error: no input image\n");
1215       return -1;
1216     }
1217 
1218   if (pic[4] == 0x01)
1219     {
1220       /* Low resolution mode */
1221       columns = LOW_WIDTH;
1222       right_margin = MY_LOW_RIGHT_MARGIN;
1223       camera_header_size = LOW_CAMERA_HEADER;
1224     }
1225   else
1226     {
1227       /* High resolution mode */
1228       columns = HIGH_WIDTH;
1229       right_margin = HIGH_RIGHT_MARGIN;
1230       camera_header_size = HIGH_CAMERA_HEADER;
1231     }
1232   ccd = pic + camera_header_size;
1233 
1234   if ((horizontal_interpolation =
1235        malloc (sizeof (short) * HEIGHT * columns)) == NULL)
1236     {
1237       DBG (1,
1238 	   "cmttoppm: error: not enough memory for horizontal_interpolation\n");
1239       return -1;
1240     }
1241 
1242 
1243   if ((red = malloc (sizeof (short) * HEIGHT * columns)) == NULL)
1244     {
1245       DBG (1, "error: not enough memory for red\n");
1246       return -1;
1247     }
1248 
1249   if ((green = malloc (sizeof (short) * HEIGHT * columns)) == NULL)
1250     {
1251       DBG (1, "error: not enough memory for green\n");
1252       return -1;
1253     }
1254 
1255   if ((blue = malloc (sizeof (short) * HEIGHT * columns)) == NULL)
1256     {
1257       DBG (1, "error: not enough memory for blue\n");
1258       return -1;
1259     }
1260 
1261   /* Decode raw CCD data to RGB */
1262   set_initial_interpolation (ccd, horizontal_interpolation);
1263   interpolate_horizontally (ccd, horizontal_interpolation);
1264   interpolate_vertically (ccd, horizontal_interpolation, red, green, blue);
1265 
1266   adjust_color_and_saturation (red, green, blue);
1267 
1268   /* Determine lower and upper limit using histogram */
1269   if (low_i == -1 || high_i == -1)
1270     {
1271       determine_limits (red, green, blue, &low_i, &high_i);
1272     }
1273 
1274   /* Output pixmap structure */
1275   retval = output_rgb (red, green, blue, low_i, high_i, pp);
1276 
1277   return retval;
1278 }
1279 
1280 static int
convert_pic(char *base_name, int format)1281 convert_pic (char *base_name, int format)
1282 {
1283   FILE *ifp;
1284   unsigned char pic[MAX_IMAGE_SIZE];
1285   int res, image_width, net_width, components;
1286   struct pixmap *pp2;
1287 
1288   DBG (127, "convert_pic() called\n");
1289 
1290   /*
1291    *      Read the image in memory
1292    */
1293 
1294   if ((ifp = fopen (base_name, "rb")) == NULL)
1295     {
1296       DBG (10, "convert_pic: error: cannot open %s for reading\n", base_name);
1297       return -1;
1298     }
1299 
1300   if (fread (pic, COMET_HEADER_SIZE, 1, ifp) != 1)
1301     {
1302       DBG (10, "convert_pic: error: cannot read COMET header\n");
1303       fclose (ifp);
1304       return -1;
1305     }
1306 
1307   if (strncmp ((char *) pic, COMET_MAGIC, sizeof (COMET_MAGIC)) != 0)
1308     {
1309       DBG (10, "convert_pic: error: file %s is not in COMET format\n",
1310 	   base_name);
1311       fclose (ifp);
1312       return -1;
1313     }
1314 
1315   if (fread (pic, LOW_CAMERA_HEADER, 1, ifp) != 1)
1316     {
1317       DBG (10, "convert_pic: error: cannot read camera header\n");
1318       fclose (ifp);
1319       return -1;
1320     }
1321 
1322   res = pic[4];
1323   if (res == 0)
1324     {
1325       /*
1326        *      We just read a LOW_CAMERA_HEADER block, so resync with the
1327        *      HIGH_CAMERA_HEADER length by reading once more one of this.
1328        */
1329       if (fread (pic + LOW_CAMERA_HEADER, LOW_CAMERA_HEADER, 1, ifp) != 1)
1330 	{
1331 	  DBG (10,
1332 	       "convert_pic: error: cannot resync with high resolution header\n");
1333 	  fclose (ifp);
1334 	  return -1;
1335 	}
1336     }
1337 
1338   if (fread (pic + CAMERA_HEADER (res), WIDTH (res), HEIGHT, ifp) != HEIGHT)
1339     {
1340       DBG (9, "convert_pic: error: cannot read picture\n");
1341       fclose (ifp);
1342       return -1;
1343     }
1344 
1345   fclose (ifp);
1346 
1347   /*
1348    *      Setup image size with resolution
1349    */
1350 
1351   image_width = WIDTH (res);
1352   net_width = image_width - LEFT_MARGIN - RIGHT_MARGIN (res);
1353   components = (format & SAVE_24BITS) ? 3 : 1;
1354 
1355   /*
1356    *      Convert the image to 24 bits
1357    */
1358 
1359   if ((pp =
1360        alloc_pixmap (net_width - 1, HEIGHT - BOTTOM_MARGIN - 1,
1361 		     components)) == NULL)
1362     {
1363       DBG (1, "convert_pic: error: alloc_pixmap\n");
1364       return -1;
1365     }
1366 
1367   comet_to_pixmap (pic, pp);
1368 
1369   if (format & SAVE_ADJASPECT)
1370     {
1371       /*
1372        *      Stretch image
1373        */
1374 
1375       if (res)
1376 	pp2 = alloc_pixmap (320, HEIGHT - BOTTOM_MARGIN - 1, components);
1377       else
1378 	pp2 = alloc_pixmap (net_width - 1, 373, components);
1379 
1380       if (pp2 == NULL)
1381 	{
1382 	  DBG (2, "convert_pic: error: alloc_pixmap\n");
1383 	  free_pixmap (pp);
1384 	  return -1;
1385 	}
1386 
1387       if (res)
1388 	zoom_x (pp, pp2);
1389       else
1390 	zoom_y (pp, pp2);
1391 
1392       free_pixmap (pp);
1393       pp = pp2;
1394       pp2 = NULL;
1395 
1396     }
1397 
1398   return 0;
1399 }
1400 
1401 #define PGM_EXT		"pgm"
1402 #define PPM_EXT		"ppm"
1403 
1404 #define RED		0.30
1405 #define GREEN		0.59
1406 #define BLUE		0.11
1407 
1408 #define RED_OFFSET	0
1409 #define GREEN_OFFSET	1
1410 #define BLUE_OFFSET	2
1411 
1412 #define GET_COMP(pp, x, y, c)	(pp->planes[((x) + (y)*pp->width)*pp->components + (c)])
1413 
1414 #define GET_R(pp, x, y)	(GET_COMP(pp, x, y, RED_OFFSET))
1415 #define GET_G(pp, x, y)	(GET_COMP(pp, x, y, GREEN_OFFSET))
1416 #define GET_B(pp, x, y)	(GET_COMP(pp, x, y, BLUE_OFFSET))
1417 
1418 static struct pixmap *
alloc_pixmap(int x, int y, int d)1419 alloc_pixmap (int x, int y, int d)
1420 {
1421   struct pixmap *result = NULL;
1422 
1423   if (d == 1 || d == 3)
1424     {
1425       if (x > 0)
1426 	{
1427 	  if (y > 0)
1428 	    {
1429 	      if ((result = malloc (sizeof (struct pixmap))) != NULL)
1430 		{
1431 		  result->width = x;
1432 		  result->height = y;
1433 		  result->components = d;
1434 		  if (!(result->planes = malloc (x * y * d)))
1435 		    {
1436 		      DBG (10,
1437 			   "alloc_pixmap: error: not enough memory for bitplanes\n");
1438 		      free (result);
1439 		      result = NULL;
1440 		    }
1441 		}
1442 	      else
1443 		DBG (10,
1444 		     "alloc_pixmap: error: not enough memory for pixmap\n");
1445 	    }
1446 	  else
1447 	    DBG (10, "alloc_pixmap: error: y is out of range\n");
1448 	}
1449       else
1450 	DBG (10, "alloc_pixmap: error: x is out of range\n");
1451     }
1452   else
1453     DBG (10, "alloc_pixmap: error: cannot handle %d components\n", d);
1454 
1455   return result;
1456 }
1457 
1458 static void
free_pixmap(struct pixmap *p)1459 free_pixmap (struct pixmap *p)
1460 {
1461   if (p)
1462     {
1463       free (p->planes);
1464       free (p);
1465     }
1466 }
1467 
1468 static int
set_pixel_rgb(struct pixmap *p, int x, int y, unsigned char r, unsigned char g, unsigned char b)1469 set_pixel_rgb (struct pixmap *p, int x, int y, unsigned char r,
1470 	       unsigned char g, unsigned char b)
1471 {
1472   int result = 0;
1473 
1474   if (p)
1475     {
1476       if (x >= 0 && x < p->width)
1477 	{
1478 	  if (y >= 0 && y < p->height)
1479 	    {
1480 	      if (p->components == 1)
1481 		{
1482 		  GET_R (p, x, y) = RED * r + GREEN * g + BLUE * b;
1483 		}
1484 	      else
1485 		{
1486 		  GET_R (p, x, y) = r;
1487 		  GET_G (p, x, y) = g;
1488 		  GET_B (p, x, y) = b;
1489 		}
1490 	    }
1491 	  else
1492 	    {
1493 	      DBG (10, "set_pixel_rgb: error: y out of range\n");
1494 	      result = -1;
1495 	    }
1496 	}
1497       else
1498 	{
1499 	  DBG (10, "set_pixel_rgb: error: x out of range\n");
1500 	  result = -1;
1501 	}
1502     }
1503 
1504   return result;
1505 }
1506 
1507 static int
zoom_x(struct pixmap *source, struct pixmap *dest)1508 zoom_x (struct pixmap *source, struct pixmap *dest)
1509 {
1510   int result = 0, dest_col, row, component, src_index;
1511   float ratio, src_ptr, delta;
1512   unsigned char src_component;
1513 
1514   if (source && dest)
1515     {
1516       /*
1517        *      We could think of resizing a pixmap and changing the number of
1518        *      components at the same time. Maybe this will be implemented later.
1519        */
1520       if (source->height == dest->height
1521 	  && source->components == dest->components)
1522 	{
1523 	  if (source->width < dest->width)
1524 	    {
1525 	      ratio = ((float) source->width / (float) dest->width);
1526 
1527 	      for (src_ptr = 0, dest_col = 0; dest_col < dest->width;
1528 		   src_ptr += ratio, dest_col++)
1529 		{
1530 		  /*
1531 		   *      dest[dest_col] = source[(int)src_ptr] +
1532 		   *        (source[((int)src_ptr) + 1] - source[(int)src_ptr])
1533 		   *        * (src_ptr - (int)src_ptr);
1534 		   */
1535 		  src_index = (int) src_ptr;
1536 		  delta = src_ptr - src_index;
1537 
1538 		  for (row = 0; row < source->height; row++)
1539 		    {
1540 		      for (component = 0; component < source->components;
1541 			   component++)
1542 			{
1543 			  src_component =
1544 			    GET_COMP (source, src_index, row, component);
1545 
1546 			  GET_COMP (dest, dest_col, row, component) =
1547 			    src_component +
1548 			    (GET_COMP (source, src_index + 1, row,
1549 				       component) - src_component) * delta;
1550 			}
1551 		    }
1552 		}
1553 	    }
1554 	  else
1555 	    {
1556 	      DBG (10, "zoom_x: error: can only zoom out\n");
1557 	      result = -1;
1558 	    }
1559 	}
1560       else
1561 	{
1562 	  DBG (10, "zoom_x: error: incompatible pixmaps\n");
1563 	  result = -1;
1564 	}
1565     }
1566 
1567   return result;
1568 }
1569 
1570 static int
zoom_y(struct pixmap *source, struct pixmap *dest)1571 zoom_y (struct pixmap *source, struct pixmap *dest)
1572 {
1573   int result = 0, dest_row, column, component, src_index;
1574   float ratio, src_ptr, delta;
1575   unsigned char src_component;
1576 
1577   if (source && dest)
1578     {
1579       /*
1580        *      We could think of resizing a pixmap and changing the number of
1581        *      components at the same time. Maybe this will be implemented later.
1582        */
1583       if (source->width == dest->width
1584 	  && source->components == dest->components)
1585 	{
1586 	  if (source->height < dest->height)
1587 	    {
1588 	      ratio = ((float) source->height / (float) dest->height);
1589 
1590 	      for (src_ptr = 0, dest_row = 0; dest_row < dest->height;
1591 		   src_ptr += ratio, dest_row++)
1592 		{
1593 		  /*
1594 		   *      dest[dest_row] = source[(int)src_ptr] +
1595 		   *        (source[((int)src_ptr) + 1] - source[(int)src_ptr])
1596 		   *        * (src_ptr - (int)src_ptr);
1597 		   */
1598 		  src_index = (int) src_ptr;
1599 		  delta = src_ptr - src_index;
1600 
1601 		  for (column = 0; column < source->width; column++)
1602 		    {
1603 		      for (component = 0; component < source->components;
1604 			   component++)
1605 			{
1606 			  src_component =
1607 			    GET_COMP (source, column, src_index, component);
1608 
1609 			  GET_COMP (dest, column, dest_row, component) =
1610 			    src_component +
1611 			    (GET_COMP (source, column, src_index + 1,
1612 				       component) - src_component) * delta;
1613 			}
1614 		    }
1615 		}
1616 	    }
1617 	  else
1618 	    {
1619 	      DBG (10, "zoom_y: error: can only zoom out\n");
1620 	      result = -1;
1621 	    }
1622 	}
1623       else
1624 	{
1625 	  DBG (10, "zoom_y: error: incompatible pixmaps\n");
1626 	  result = -1;
1627 	}
1628     }
1629 
1630   return result;
1631 }
1632 
1633 static unsigned char shoot_pck[] = SHOOT_PCK;
1634 
1635 static int
shoot(int fd)1636 shoot (int fd)
1637 {
1638   struct termios tty_temp, tty_old;
1639   int result = 0;
1640 
1641   DBG (127, "shoot() called\n");
1642 
1643   if (write (fd, (char *) shoot_pck, 8) != 8)
1644     {
1645       DBG (3, "shoot: error: write error\n");
1646       return -1;
1647     }
1648 
1649   if (CameraInfo.model != 0x25)
1650     {
1651       /*
1652        *      WARNING: now we set the serial port to 9600 baud!
1653        */
1654 
1655       if (tcgetattr (fd, &tty_old) == -1)
1656 	{
1657 	  DBG (3, "shoot: error: could not get attributes\n");
1658 	  return -1;
1659 	}
1660 
1661       memcpy ((char *) &tty_temp, (char *) &tty_old, sizeof (struct termios));
1662 
1663       cfsetispeed (&tty_temp, B9600);
1664       cfsetospeed (&tty_temp, B9600);
1665 
1666       /*
1667        * Apparently there is a bug in the DC20 where the response to
1668        * the shoot request is always at 9600.  The DC25 does not have
1669        * this bug, so we skip this block.
1670        */
1671       if (tcsetattr (fd, TCSANOW, &tty_temp) == -1)
1672 	{
1673 	  DBG (3, "shoot: error: could not set attributes\n");
1674 	  return -1;
1675 	}
1676     }
1677 
1678   if (read (fd, (char *) &result, 1) != 1)
1679     {
1680       DBG (3, "shoot: error: read returned -1\n");
1681       result = -1;
1682     }
1683   else
1684     {
1685       result = (result == 0xD1) ? 0 : -1;
1686     }
1687 
1688   if (CameraInfo.model != 0x25)
1689     {
1690       /*
1691        * We reset the serial to its original speed.
1692        * We can skip this on the DC25 also.
1693        */
1694       if (tcsetattr (fd, TCSANOW, &tty_old) == -1)
1695 	{
1696 	  DBG (3, "shoot: error: could not reset attributes\n");
1697 	  result = -1;
1698 	}
1699     }
1700 
1701   if (result == 0)
1702     {
1703       if (CameraInfo.model == 0x25)
1704 	{
1705 	  /*
1706 	   * If we don't put this in, the next read will time out
1707 	   * and return failure.  Does the DC-20 need it too?
1708 	   */
1709 	  sleep (3);
1710 	}
1711       if (end_of_data (fd) == -1)
1712 	{
1713 	  DBG (3, "shoot: error: end_of_data returned -1\n");
1714 	  result = -1;
1715 	}
1716     }
1717 
1718   return result;
1719 }
1720 
1721 
1722 static unsigned char erase_pck[] = ERASE_PCK;
1723 
1724 static int
erase(int fd)1725 erase (int fd)
1726 {
1727   int count = 0;
1728 
1729   DBG (127, "erase() called for image %d\n", dc25_opt_image_number);
1730   erase_pck[3] = dc25_opt_image_number;
1731   if (dc25_opt_erase)
1732     {
1733       erase_pck[3] = 0;
1734     }
1735 
1736   if (send_pck (fd, erase_pck) == -1)
1737     {
1738       DBG (3, "erase: error: send_pck returned -1\n");
1739       return -1;
1740     }
1741 
1742   if (CameraInfo.model == 0x25)
1743     {
1744       /*
1745        * This block may really apply to the DC20 also, but since I
1746        * don't have one, it's hard to say for sure.  On the DC25, erase
1747        * takes long enough that the read may timeout without returning
1748        * any data before the erase is complete.   We let this happen
1749        * up to 4 times, then give up.
1750        */
1751       while (count < 4)
1752 	{
1753 	  if (end_of_data (fd) == -1)
1754 	    {
1755 	      count++;
1756 	    }
1757 	  else
1758 	    {
1759 	      break;
1760 	    }
1761 	}
1762       if (count == 4)
1763 	{
1764 	  DBG (3, "erase: error: end_of_data returned -1\n");
1765 	  return -1;
1766 	}
1767     }
1768   else
1769     {				/* Assume DC-20 */
1770 
1771       if (end_of_data (fd) == -1)
1772 	{
1773 	  DBG (3, "erase: error: end_of_data returned -1\n");
1774 	  return -1;
1775 	}
1776     }
1777 
1778   return 0;
1779 }
1780 
1781 static unsigned char res_pck[] = RES_PCK;
1782 
1783 static int
change_res(int fd, unsigned char res)1784 change_res (int fd, unsigned char res)
1785 {
1786   DBG (127, "change_res called\n");
1787   if (res != 0 && res != 1)
1788     {
1789       DBG (3, "change_res: error: unsupported resolution\n");
1790       return -1;
1791     }
1792 
1793   res_pck[2] = res;
1794 
1795   if (send_pck (fd, res_pck) == -1)
1796     {
1797       DBG (4, "change_res: error: send_pck returned -1\n");
1798     }
1799 
1800   if (end_of_data (fd) == -1)
1801     {
1802       DBG (4, "change_res: error: end_of_data returned -1\n");
1803     }
1804   return 0;
1805 }
1806 
1807 SANE_Status
sane_init(SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize)1808 sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize)
1809 {
1810   char dev_name[PATH_MAX], *p;
1811   size_t len;
1812   FILE *fp;
1813   int baud;
1814 
1815   strcpy (tty_name, DEF_TTY_NAME);
1816 
1817   DBG_INIT ();
1818 
1819   if (version_code)
1820     *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0);
1821 
1822   fp = sanei_config_open (DC25_CONFIG_FILE);
1823 
1824   DBG (127, "sane_init()\n");
1825 
1826   if (!fp)
1827     {
1828       /* default to /dev/ttyS0 instead of insisting on config file */
1829       DBG (1, "sane_init:  missing config file '%s'\n", DC25_CONFIG_FILE);
1830     }
1831   else
1832     {
1833       while (sanei_config_read (dev_name, sizeof (dev_name), fp))
1834 	{
1835 	  dev_name[sizeof (dev_name) - 1] = '\0';
1836 	  DBG (20, "sane_init:  config- %s", dev_name);
1837 
1838 	  if (dev_name[0] == '#')
1839 	    continue;		/* ignore line comments */
1840 	  len = strlen (dev_name);
1841 	  if (!len)
1842 	    continue;		/* ignore empty lines */
1843 	  if (strncmp (dev_name, "port=", 5) == 0)
1844 	    {
1845 	      p = strchr (dev_name, '/');
1846 	      if (p)
1847 		{
1848 		  strcpy (tty_name, p);
1849 		}
1850 	      DBG (20, "Config file port=%s\n", tty_name);
1851 	    }
1852 	  else if (strncmp (dev_name, "baud=", 5) == 0)
1853 	    {
1854 	      baud = atoi (&dev_name[5]);
1855 	      switch (baud)
1856 		{
1857 		case 9600:
1858 		  tty_baud = B9600;
1859 		  break;
1860 		case 19200:
1861 		  tty_baud = B19200;
1862 		  break;
1863 		case 38400:
1864 		  tty_baud = B38400;
1865 		  break;
1866 #ifdef B57600
1867 		case 57600:
1868 		  tty_baud = B57600;
1869 		  break;
1870 #endif
1871 #ifdef B115200
1872 		case 115200:
1873 		  tty_baud = B115200;
1874 		  break;
1875 #endif
1876 		default:
1877 		  DBG (20, "Unknown baud=%d\n", baud);
1878 		  tty_baud = DEFAULT_TTY_BAUD;
1879 		  break;
1880 		}
1881 	      DBG (20, "Config file baud=%lu\n", (u_long) tty_baud);
1882 	    }
1883 	  else if (strcmp (dev_name, "dumpinquiry") == 0)
1884 	    {
1885 	      dumpinquiry = SANE_TRUE;
1886 	    }
1887 	}
1888       fclose (fp);
1889     }
1890 
1891   if ((tfd = init_dc20 (tty_name, tty_baud)) == -1)
1892     {
1893       return SANE_STATUS_INVAL;
1894     }
1895 
1896   if ((dc20_info = get_info (tfd)) == NULL)
1897     {
1898       DBG (2, "error: could not get info\n");
1899       close_dc20 (tfd);
1900       return SANE_STATUS_INVAL;
1901     }
1902 
1903   if (dumpinquiry)
1904     {
1905       DBG (0, "\nCamera information:\n~~~~~~~~~~~~~~~~~\n\n");
1906       DBG (0, "Model...........: DC%x\n", dc20_info->model);
1907       DBG (0, "Firmware version: %d.%d\n", dc20_info->ver_major,
1908 	   dc20_info->ver_minor);
1909       DBG (0, "Pictures........: %d/%d\n", dc20_info->pic_taken,
1910 	   dc20_info->pic_taken + dc20_info->pic_left);
1911       DBG (0, "Resolution......: %s\n",
1912 	   dc20_info->flags.low_res ? "low" : "high");
1913       DBG (0, "Battery state...: %s\n",
1914 	   dc20_info->flags.low_batt ? "low" : "good");
1915     }
1916 
1917   if (CameraInfo.pic_taken == 0)
1918     {
1919 /*
1920 		sod[DC25_OPT_IMAGE_NUMBER].cap |= SANE_CAP_INACTIVE;
1921 */
1922       image_range.min = 0;
1923       dc25_opt_image_number = 0;
1924 
1925     }
1926   else
1927     {
1928 /*
1929 		sod[DC25_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE;
1930 */
1931       image_range.min = 1;
1932     }
1933 
1934   return SANE_STATUS_GOOD;
1935 }
1936 
1937 void
sane_exit(void)1938 sane_exit (void)
1939 {
1940 }
1941 
1942 /* Device select/open/close */
1943 
1944 static const SANE_Device dev[] = {
1945   {
1946    "0",
1947    "Kodak",
1948    "DC-25",
1949    "still camera"},
1950 };
1951 
1952 SANE_Status
sane_get_devices(const SANE_Device *** device_list, SANE_Bool __sane_unused__ local_only)1953 sane_get_devices (const SANE_Device *** device_list,
1954 		  SANE_Bool __sane_unused__ local_only)
1955 {
1956   static const SANE_Device *devlist[] = {
1957     dev + 0, 0
1958   };
1959 
1960   DBG (127, "sane_get_devices called\n");
1961 
1962   if (dc20_info == NULL)
1963     {
1964       return SANE_STATUS_INVAL;
1965     }
1966   *device_list = devlist;
1967   return SANE_STATUS_GOOD;
1968 }
1969 
1970 SANE_Status
sane_open(SANE_String_Const devicename, SANE_Handle * handle)1971 sane_open (SANE_String_Const devicename, SANE_Handle * handle)
1972 {
1973   int i;
1974 
1975   DBG (127, "sane_open for device %s\n", devicename);
1976   if (!devicename[0])
1977     {
1978       i = 0;
1979     }
1980   else
1981     {
1982       for (i = 0; i < NELEMS (dev); ++i)
1983 	{
1984 	  if (strcmp (devicename, dev[i].name) == 0)
1985 	    {
1986 	      break;
1987 	    }
1988 	}
1989     }
1990 
1991   if (i >= NELEMS (dev))
1992     {
1993       return SANE_STATUS_INVAL;
1994     }
1995 
1996   if (is_open)
1997     {
1998       return SANE_STATUS_DEVICE_BUSY;
1999     }
2000 
2001   is_open = 1;
2002   *handle = MAGIC;
2003 
2004   if (dc20_info == NULL)
2005     {
2006       DBG (1, "No device info\n");
2007     }
2008 
2009   DBG (3, "sane_open: pictures taken=%d\n", dc20_info->pic_taken);
2010 
2011   return SANE_STATUS_GOOD;
2012 }
2013 
2014 void
sane_close(SANE_Handle handle)2015 sane_close (SANE_Handle handle)
2016 {
2017   DBG (127, "sane_close called\n");
2018   if (handle == MAGIC)
2019     is_open = 0;
2020 
2021   if (pp)
2022     {
2023       free_pixmap (pp);
2024       pp = NULL;
2025     }
2026 
2027   close_dc20 (tfd);
2028 
2029   DBG (127, "sane_close returning\n");
2030 }
2031 
2032 const SANE_Option_Descriptor *
sane_get_option_descriptor(SANE_Handle handle, SANE_Int option)2033 sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
2034 {
2035   if (handle != MAGIC || !is_open)
2036     return NULL;		/* wrong device */
2037   if (option < 0 || option >= NELEMS (sod))
2038     return NULL;
2039   return &sod[option];
2040 }
2041 
2042 SANE_Status
sane_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int * info)2043 sane_control_option (SANE_Handle handle, SANE_Int option,
2044 		     SANE_Action action, void *value, SANE_Int * info)
2045 {
2046   SANE_Int myinfo = info_flags;
2047   SANE_Status status;
2048 
2049   info_flags = 0;
2050 
2051   DBG (127, "control_option(handle=%p,opt=%s,act=%s,val=%p,info=%p)\n",
2052        handle, sod[option].title,
2053        (action ==
2054 	SANE_ACTION_SET_VALUE ? "SET" : (action ==
2055 					 SANE_ACTION_GET_VALUE ? "GET" :
2056 					 "SETAUTO")), value, (void *)info);
2057 
2058   if (handle != MAGIC || !is_open)
2059     return SANE_STATUS_INVAL;	/* Unknown handle ... */
2060 
2061   if (option < 0 || option >= NELEMS (sod))
2062     return SANE_STATUS_INVAL;	/* Unknown option ... */
2063 
2064   switch (action)
2065     {
2066     case SANE_ACTION_SET_VALUE:
2067       status = sanei_constrain_value (sod + option, value, &myinfo);
2068       if (status != SANE_STATUS_GOOD)
2069 	{
2070 	  DBG (1, "Constraint error in control_option\n");
2071 	  return status;
2072 	}
2073 
2074       switch (option)
2075 	{
2076 	case DC25_OPT_IMAGE_NUMBER:
2077 	  dc25_opt_image_number = *(SANE_Word *) value;
2078 /*			myinfo |= SANE_INFO_RELOAD_OPTIONS; */
2079 	  break;
2080 
2081 	case DC25_OPT_THUMBS:
2082 	  dc25_opt_thumbnails = !!*(SANE_Word *) value;
2083 	  myinfo |= SANE_INFO_RELOAD_PARAMS;
2084 
2085 	  if (dc25_opt_thumbnails)
2086 	    {
2087 	      /*
2088 	       * DC20 thumbnail are 80x60 grayscale, DC25
2089 	       * thumbnails are color.
2090 	       */
2091 	      parms.format =
2092 		(CameraInfo.model == 0x25) ? SANE_FRAME_RGB : SANE_FRAME_GRAY;
2093 	      parms.bytes_per_line = 80 * 3;
2094 	      parms.pixels_per_line = 80;
2095 	      parms.lines = 60;
2096 	    }
2097 	  else
2098 	    {
2099 	      parms.format = SANE_FRAME_RGB;
2100 	      if (dc20_info->flags.low_res)
2101 		{
2102 		  parms.bytes_per_line = 320 * 3;
2103 		  parms.pixels_per_line = 320;
2104 		  parms.lines = 243;
2105 		}
2106 	      else
2107 		{
2108 		  parms.bytes_per_line = 500 * 3;
2109 		  parms.pixels_per_line = 500;
2110 		  parms.lines = 373;
2111 		}
2112 	    }
2113 	  break;
2114 
2115 	case DC25_OPT_SNAP:
2116 	  dc25_opt_snap = !!*(SANE_Word *) value;
2117 	  myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
2118 	  if (dc25_opt_snap)
2119 	    {
2120 	      sod[DC25_OPT_LOWRES].cap &= ~SANE_CAP_INACTIVE;
2121 	    }
2122 	  else
2123 	    {
2124 	      sod[DC25_OPT_LOWRES].cap |= SANE_CAP_INACTIVE;
2125 	    }
2126 	  break;
2127 
2128 	case DC25_OPT_LOWRES:
2129 	  dc25_opt_lowres = !!*(SANE_Word *) value;
2130 	  myinfo |= SANE_INFO_RELOAD_PARAMS;
2131 
2132 	  if (!dc25_opt_thumbnails)
2133 	    {
2134 
2135 	      parms.format = SANE_FRAME_RGB;
2136 
2137 	      if (dc20_info->flags.low_res)
2138 		{
2139 		  parms.bytes_per_line = 320 * 3;
2140 		  parms.pixels_per_line = 320;
2141 		  parms.lines = 243;
2142 		}
2143 	      else
2144 		{
2145 		  parms.bytes_per_line = 500 * 3;
2146 		  parms.pixels_per_line = 500;
2147 		  parms.lines = 373;
2148 		}
2149 
2150 	    }
2151 	  break;
2152 
2153 	case DC25_OPT_CONTRAST:
2154 	  dc25_opt_contrast = *(SANE_Word *) value;
2155 	  break;
2156 
2157 	case DC25_OPT_GAMMA:
2158 	  dc25_opt_gamma = *(SANE_Word *) value;
2159 	  break;
2160 
2161 	case DC25_OPT_ERASE:
2162 	  dc25_opt_erase = !!*(SANE_Word *) value;
2163 
2164 	  /*
2165 	   * erase and erase_one are mutually exclusive.  If
2166 	   * this one is turned on, the other must be off
2167 	   */
2168 	  if (dc25_opt_erase && dc25_opt_erase_one)
2169 	    {
2170 	      dc25_opt_erase_one = SANE_FALSE;
2171 	      myinfo |= SANE_INFO_RELOAD_OPTIONS;
2172 	    }
2173 	  break;
2174 
2175 	case DC25_OPT_ERASE_ONE:
2176 	  dc25_opt_erase_one = !!*(SANE_Word *) value;
2177 
2178 	  /*
2179 	   * erase and erase_one are mutually exclusive.  If
2180 	   * this one is turned on, the other must be off
2181 	   */
2182 	  if (dc25_opt_erase_one && dc25_opt_erase)
2183 	    {
2184 	      dc25_opt_erase = SANE_FALSE;
2185 	      myinfo |= SANE_INFO_RELOAD_OPTIONS;
2186 	    }
2187 	  break;
2188 
2189 	case DC25_OPT_DEFAULT:
2190 
2191 	  dc25_opt_contrast = SANE_FIX (DC25_OPT_CONTRAST_DEFAULT);
2192 	  dc25_opt_gamma = SANE_FIX (DC25_OPT_GAMMA_DEFAULT);
2193 	  myinfo |= SANE_INFO_RELOAD_OPTIONS;
2194 	  break;
2195 
2196 	default:
2197 	  return SANE_STATUS_INVAL;
2198 	}
2199       break;
2200 
2201     case SANE_ACTION_GET_VALUE:
2202       switch (option)
2203 	{
2204 	case 0:
2205 	  *(SANE_Word *) value = NELEMS (sod);
2206 	  break;
2207 
2208 	case DC25_OPT_IMAGE_NUMBER:
2209 	  *(SANE_Word *) value = dc25_opt_image_number;
2210 	  break;
2211 
2212 	case DC25_OPT_THUMBS:
2213 	  *(SANE_Word *) value = dc25_opt_thumbnails;
2214 	  break;
2215 
2216 	case DC25_OPT_SNAP:
2217 	  *(SANE_Word *) value = dc25_opt_snap;
2218 	  break;
2219 
2220 	case DC25_OPT_LOWRES:
2221 	  *(SANE_Word *) value = dc25_opt_lowres;
2222 	  break;
2223 
2224 	case DC25_OPT_CONTRAST:
2225 	  *(SANE_Word *) value = dc25_opt_contrast;
2226 	  break;
2227 
2228 	case DC25_OPT_GAMMA:
2229 	  *(SANE_Word *) value = dc25_opt_gamma;
2230 	  break;
2231 
2232 	case DC25_OPT_ERASE:
2233 	  *(SANE_Word *) value = dc25_opt_erase;
2234 	  break;
2235 
2236 	case DC25_OPT_ERASE_ONE:
2237 	  *(SANE_Word *) value = dc25_opt_erase_one;
2238 	  break;
2239 
2240 	default:
2241 	  return SANE_STATUS_INVAL;
2242 	}
2243       break;
2244 
2245     case SANE_ACTION_SET_AUTO:
2246       switch (option)
2247 	{
2248 #if 0
2249 	case DC25_OPT_CONTRAST:
2250 	  dc25_opt_contrast = SANE_FIX (DC25_OPT_CONTRAST_DEFAULT);
2251 	  break;
2252 
2253 	case DC25_OPT_GAMMA:
2254 	  dc25_opt_gamma = SANE_FIX (DC25_OPT_GAMMA_DEFAULT);
2255 	  break;
2256 #endif
2257 
2258 	default:
2259 	  return SANE_STATUS_UNSUPPORTED;	/* We are DUMB */
2260 	}
2261     }
2262 
2263   if (info)
2264     *info = myinfo;
2265 
2266   return SANE_STATUS_GOOD;
2267 }
2268 
2269 SANE_Status
sane_get_parameters(SANE_Handle handle, SANE_Parameters * params)2270 sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
2271 {
2272   int rc = SANE_STATUS_GOOD;
2273 
2274   DBG (127, "sane_get_params called\n");
2275 
2276   if (handle != MAGIC || !is_open)
2277     rc = SANE_STATUS_INVAL;	/* Unknown handle ... */
2278 
2279 
2280   *params = parms;
2281   return rc;
2282 }
2283 
2284 static unsigned char thumb_pck[] = THUMBS_PCK;
2285 
2286 static unsigned char pic_pck[] = PICS_PCK;
2287 
2288 static int bytes_in_buffer;
2289 static int bytes_read_from_buffer;
2290 static SANE_Byte buffer[1024];
2291 static int total_bytes_read;
2292 static SANE_Bool started = SANE_FALSE;
2293 static int outbytes;
2294 
2295 SANE_Status
sane_start(SANE_Handle handle)2296 sane_start (SANE_Handle handle)
2297 {
2298   int n, i;
2299   FILE *f;
2300 
2301   DBG (127, "sane_start called, handle=%lx\n", (u_long) handle);
2302 
2303   if (handle != MAGIC || !is_open ||
2304       (dc25_opt_image_number == 0 && dc25_opt_snap == SANE_FALSE))
2305     return SANE_STATUS_INVAL;	/* Unknown handle ... */
2306 
2307   if (started)
2308     {
2309       return SANE_STATUS_EOF;
2310     }
2311 
2312   if (dc25_opt_snap)
2313     {
2314 
2315       /*
2316        * Don't allow picture unless there is room in the
2317        * camera.
2318        */
2319       if (CameraInfo.pic_left == 0)
2320 	{
2321 	  DBG (3, "No room to store new picture\n");
2322 	  return SANE_STATUS_INVAL;
2323 	}
2324 
2325       /*
2326        * DC-20 can only change resolution when camer is empty.
2327        * DC-25 can do it any time.
2328        */
2329       if (CameraInfo.model != 0x20 || CameraInfo.pic_taken == 0)
2330 	{
2331 	  if (change_res (tfd, dc25_opt_lowres) == -1)
2332 	    {
2333 	      DBG (1, "Failed to set resolution\n");
2334 	      return SANE_STATUS_INVAL;
2335 	    }
2336 	}
2337 
2338       /*
2339        * Not sure why this delay is needed, but it seems to help:
2340        */
2341 #ifdef HAVE_USLEEP
2342       usleep (10);
2343 #else
2344       sleep (1);
2345 #endif
2346       if (shoot (tfd) == -1)
2347 	{
2348 	  DBG (1, "Failed to snap new picture\n");
2349 	  return SANE_STATUS_INVAL;
2350 	}
2351       else
2352 	{
2353 	  info_flags |= SANE_INFO_RELOAD_OPTIONS;
2354 	  CameraInfo.pic_taken++;
2355 	  CameraInfo.pic_left--;
2356 	  dc25_opt_image_number = CameraInfo.pic_taken;
2357 	  if (image_range.min == 0)
2358 	    image_range.min = 1;
2359 	  image_range.max++;
2360 	  sod[DC25_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE;
2361 	}
2362     }
2363 
2364   if (dc25_opt_thumbnails)
2365     {
2366 
2367       /*
2368        * For thumbnails, we can do things right where we
2369        * start the download, and grab the first block
2370        * from the camera.  The reamining blocks will be
2371        * fetched as necessary by sane_read().
2372        */
2373       thumb_pck[3] = (unsigned char) dc25_opt_image_number;
2374 
2375       if (send_pck (tfd, thumb_pck) == -1)
2376 	{
2377 	  DBG (4, "sane_start: error: send_pck returned -1\n");
2378 	  return SANE_STATUS_INVAL;
2379 	}
2380 
2381       if (read_data (tfd, buffer, 1024) == -1)
2382 	{
2383 	  DBG (4, "sane_start: read_data failed\n");
2384 	  return SANE_STATUS_INVAL;
2385 	}
2386 
2387       /*
2388        * DC20 thumbnail are 80x60 grayscale, DC25
2389        * thumbnails are color.
2390        */
2391       parms.format =
2392 	(CameraInfo.model == 0x25) ? SANE_FRAME_RGB : SANE_FRAME_GRAY;
2393       parms.bytes_per_line = 80 * 3;	/* 80 pixels, 3 colors */
2394       parms.pixels_per_line = 80;
2395       parms.lines = 60;
2396 
2397       bytes_in_buffer = 1024;
2398       bytes_read_from_buffer = 0;
2399 
2400     }
2401   else
2402     {
2403       /*
2404        * We do something a little messy, and violates the SANE
2405        * philosophy.  However, since it is fairly tricky to
2406        * convert the DC2x "comet" files on the fly, we read in
2407        * the entire data stream in sane_open(), and use convert_pic
2408        * to convert it to an in-memory pixpmap.  Then when
2409        * sane_read() is called, we fill the requests from
2410        * memory.  A good project for me (or some kind volunteer)
2411        * would be to rewrite this and move the actual download
2412        * to sane_read().  However, one argument for keeping it
2413        * this way is that the data comes down pretty fast, and
2414        * it helps to dedicate the processor to this task.  We
2415        * might get serial port overruns if we try to do other
2416        * things at the same time.
2417        *
2418        * Also, as a side note, I was constantly getting serial
2419        * port overruns on a 90MHz pentium until I used hdparm
2420        * to set the "-u1" flag on the system drives.
2421        */
2422       char tmpnamebuf[] = TMPFILE_PATTERN;
2423 
2424       int fd = mkstemp (tmpnamebuf);
2425       if (fd == -1)
2426         {
2427           DBG (0, "Unable to make temp file %s\n", tmpnamebuf);
2428           return SANE_STATUS_INVAL;
2429         }
2430 
2431       f = fdopen (fd, "wb");
2432       if (f == NULL)
2433 	{
2434 	  DBG (0, "Unable to fdopen tmp file\n");
2435 	  return SANE_STATUS_INVAL;
2436 	}
2437 
2438       strcpy ((char *) buffer, COMET_MAGIC);
2439       fwrite (buffer, 1, COMET_HEADER_SIZE, f);
2440 
2441       pic_pck[3] = (unsigned char) dc25_opt_image_number;
2442 
2443       if (send_pck (tfd, pic_pck) == -1)
2444 	{
2445 	  DBG (4, "sane_start: error: send_pck returned -1\n");
2446 	  return SANE_STATUS_INVAL;
2447 	}
2448 
2449       if (read_data (tfd, buffer, 1024) == -1)
2450 	{
2451 	  DBG (5, "sane_start: read_data failed\n");
2452 	  return SANE_STATUS_INVAL;
2453 	}
2454 
2455       if (buffer[4] == 0)
2456 	{			/* hi-res image */
2457 	  DBG (5, "sane_start: hi-res image\n");
2458 	  n = 122;
2459 
2460 	  parms.bytes_per_line = 500 * 3;	/* 3 colors */
2461 	  parms.pixels_per_line = 500;
2462 	  parms.lines = 373;
2463 
2464 	  bytes_in_buffer = 1024;
2465 	  bytes_read_from_buffer = 0;
2466 	}
2467       else
2468 	{
2469 	  n = 61;
2470 	  DBG (5, "sane_start: low-res image\n");
2471 
2472 	  parms.bytes_per_line = 320 * 3;	/* 3 Colors */
2473 	  parms.pixels_per_line = 320;
2474 	  parms.lines = 243;
2475 
2476 	  bytes_in_buffer = 1024;
2477 	  bytes_read_from_buffer = 0;
2478 	}
2479 
2480 
2481       fwrite (buffer, 1, 1024, f);
2482 
2483       for (i = 1; i < n; i++)
2484 	{
2485 	  if (read_data (tfd, buffer, 1024) == -1)
2486 	    {
2487 	      DBG (5, "sane_start: read_data failed\n");
2488 	      return SANE_STATUS_INVAL;
2489 	    }
2490 	  fwrite (buffer, 1, 1024, f);
2491 	}
2492 
2493       if (end_of_data (tfd) == -1)
2494 	{
2495 	  fclose (f);
2496 	  DBG (4, "sane_open: end_of_data error\n");
2497 	  return SANE_STATUS_INVAL;
2498 	}
2499       else
2500 	{
2501 	  fclose (f);
2502 	  if (convert_pic (tmpnamebuf, SAVE_ADJASPECT | SAVE_24BITS) == -1)
2503 	    {
2504 	      DBG (3, "sane_open: unable to convert\n");
2505 	      return SANE_STATUS_INVAL;
2506 	    }
2507 	  unlink (tmpnamebuf);
2508 	  outbytes = 0;
2509 	}
2510     }
2511 
2512   started = SANE_TRUE;
2513   total_bytes_read = 0;
2514 
2515   return SANE_STATUS_GOOD;
2516 }
2517 
2518 
2519 SANE_Status
sane_read(SANE_Handle __sane_unused__ handle, SANE_Byte * data, SANE_Int max_length, SANE_Int * length)2520 sane_read (SANE_Handle __sane_unused__ handle, SANE_Byte * data,
2521 	   SANE_Int max_length, SANE_Int * length)
2522 {
2523   DBG (127, "sane_read called, maxlen=%d\n", max_length);
2524 
2525   if ( ! started ) {
2526 	return SANE_STATUS_INVAL;
2527   }
2528 
2529   if (dc25_opt_thumbnails)
2530     {
2531       if (total_bytes_read == THUMBSIZE)
2532 	{
2533 	  if (dc25_opt_erase || dc25_opt_erase_one)
2534 	    {
2535 
2536 	      if (erase (tfd) == -1)
2537 		{
2538 		  DBG (1, "Failed to erase memory\n");
2539 		  return SANE_STATUS_INVAL;
2540 		}
2541 
2542 	      dc25_opt_erase = SANE_FALSE;
2543 	      dc25_opt_erase_one = SANE_FALSE;
2544 	      info_flags |= SANE_INFO_RELOAD_OPTIONS;
2545 
2546 	      if (get_info (tfd) == NULL)
2547 		{
2548 		  DBG (2, "error: could not get info\n");
2549 		  close_dc20 (tfd);
2550 		  return SANE_STATUS_INVAL;
2551 		}
2552 	      DBG (10, "Call get_info!, image range=%d,%d\n", image_range.min,
2553 		   image_range.max);
2554 	    }
2555 	  return SANE_STATUS_EOF;
2556 	}
2557 
2558       *length = 0;
2559       if (!(bytes_in_buffer - bytes_read_from_buffer))
2560 	{
2561 	  if (read_data (tfd, buffer, 1024) == -1)
2562 	    {
2563 	      DBG (5, "sane_read: read_data failed\n");
2564 	      return SANE_STATUS_INVAL;
2565 	    }
2566 	  bytes_in_buffer = 1024;
2567 	  bytes_read_from_buffer = 0;
2568 	}
2569 
2570       while (bytes_read_from_buffer < bytes_in_buffer &&
2571 	     max_length && total_bytes_read < THUMBSIZE)
2572 	{
2573 	  *data++ = buffer[bytes_read_from_buffer++];
2574 	  (*length)++;
2575 	  max_length--;
2576 	  total_bytes_read++;
2577 	}
2578 
2579       if (total_bytes_read == THUMBSIZE)
2580 	{
2581 	  if (end_of_data (tfd) == -1)
2582 	    {
2583 	      DBG (4, "sane_read: end_of_data error\n");
2584 	      return SANE_STATUS_INVAL;
2585 	    }
2586 	  else
2587 	    {
2588 	      return SANE_STATUS_GOOD;
2589 	    }
2590 	}
2591       else
2592 	{
2593 	  return SANE_STATUS_GOOD;
2594 	}
2595     }
2596   else
2597     {
2598       int i;
2599       int filesize = parms.bytes_per_line * parms.lines;
2600 
2601       /*
2602        * If outbytes is zero, then this is the first time
2603        * we've been called, so update the contrast table.
2604        * The formula is something I came up with that has the
2605        * following properties:
2606        * 1) It's a smooth curve that provides the effect I wanted
2607        *    (bright pixels are made brighter, dim pixels are made
2608        *    dimmer)
2609        * 2) The contrast parameter can be adjusted to provide
2610        *    different amounts of contrast.
2611        * 3) A parameter of 1.0 can be used to pass the data
2612        *    through unchanged (but values around 1.75 look
2613        *    a lot better
2614        */
2615       if (outbytes == 0)
2616 	{
2617 	  double d;
2618 	  double cont = SANE_UNFIX (dc25_opt_contrast);
2619 
2620 	  for (i = 0; i < 256; i++)
2621 	    {
2622 	      d = (i * 2.0) / 255 - 1.0;
2623 	      d =
2624 		((-pow (1 - d, cont)) + 1) * (d >=
2625 					      0) + (((pow (d + 1, cont)) -
2626 						     1)) * (d < 0);
2627 	      contrast_table[i] = (d * 127.5) + 127.5;
2628 /*
2629 				fprintf (stderr,"%03d %03d\n",i,contrast_table[i]);
2630 */
2631 	    }
2632 	}
2633 
2634       /* We're done, so return EOF */
2635       if (outbytes >= filesize)
2636 	{
2637 	  free_pixmap (pp);
2638 	  pp = NULL;
2639 
2640 	  if (dc25_opt_erase || dc25_opt_erase_one)
2641 	    {
2642 	      if (erase (tfd) == -1)
2643 		{
2644 		  DBG (1, "Failed to erase memory\n");
2645 		  return SANE_STATUS_INVAL;
2646 		}
2647 	    }
2648 
2649 	  if (get_info (tfd) == NULL)
2650 	    {
2651 	      DBG (2, "error: could not get info\n");
2652 	      close_dc20 (tfd);
2653 	      return SANE_STATUS_INVAL;
2654 	    }
2655 	  DBG (10, "Call get_info!, image range=%d,%d\n", image_range.min,
2656 	       image_range.max);
2657 
2658 	  get_info (tfd);
2659 
2660           *length=0;
2661 
2662 	  return SANE_STATUS_EOF;
2663 	}
2664 
2665       if (max_length > filesize - outbytes)
2666 	{
2667 	  *length = filesize - outbytes;
2668 	}
2669       else
2670 	{
2671 	  *length = max_length;
2672 	}
2673 
2674       memcpy (data, pp->planes + outbytes, *length);
2675       outbytes += *length;
2676 
2677 
2678       for (i = 0; i < *length; i++)
2679 	{
2680 	  data[i] = contrast_table[data[i]];
2681 	}
2682 
2683       return SANE_STATUS_GOOD;
2684 
2685     }
2686 }
2687 
2688 void
sane_cancel(SANE_Handle __sane_unused__ handle)2689 sane_cancel (SANE_Handle __sane_unused__ handle)
2690 {
2691   DBG (127, "sane_cancel() called\n");
2692   started = SANE_FALSE;
2693 }
2694 
2695 SANE_Status
sane_set_io_mode(SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking)2696 sane_set_io_mode (SANE_Handle __sane_unused__ handle,
2697 		  SANE_Bool __sane_unused__ non_blocking)
2698 {
2699   /* sane_set_io_mode() is only valid during a scan */
2700   if (started)
2701     {
2702       if (non_blocking == SANE_FALSE)
2703 	{
2704 	  return SANE_STATUS_GOOD;
2705 	}
2706       else
2707 	{
2708 	  return SANE_STATUS_UNSUPPORTED;
2709 	}
2710     }
2711   else
2712     {
2713       /* We aren't currently scanning */
2714       return SANE_STATUS_INVAL;
2715     }
2716 }
2717 
2718 SANE_Status
sane_get_select_fd(SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ * fd)2719 sane_get_select_fd (SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ * fd)
2720 {
2721   return SANE_STATUS_UNSUPPORTED;
2722 }
2723