xref: /third_party/backends/backend/dc25.c (revision 141cc406)
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
114static SANE_Bool is_open = 0;
115
116static SANE_Byte dc25_opt_image_number = 1;	/* Image to load */
117static SANE_Bool dc25_opt_thumbnails;	/* Load thumbnails */
118static SANE_Bool dc25_opt_snap;	/* Take new picture */
119static SANE_Bool dc25_opt_lowres;	/* Use low resoluiton */
120#define DC25_OPT_CONTRAST_DEFAULT 1.6
121						/* Contrast enhancement */
122static SANE_Fixed dc25_opt_contrast = SANE_FIX (DC25_OPT_CONTRAST_DEFAULT);
123#define DC25_OPT_GAMMA_DEFAULT 4.5
124						/* Gamma correction (10x) */
125static SANE_Fixed dc25_opt_gamma = SANE_FIX (DC25_OPT_GAMMA_DEFAULT);
126static SANE_Bool dc25_opt_erase;	/* Erase all after download */
127static SANE_Bool dc25_opt_erase_one;	/* Erase one after download */
128static SANE_Bool dumpinquiry;
129
130static SANE_Int info_flags;
131
132static int tfd;			/* Camera File Descriptor */
133static char tty_name[PATH_MAX];
134#define DEF_TTY_NAME "/dev/ttyS0"
135
136static speed_t tty_baud = DEFAULT_TTY_BAUD;
137#define TMPFILE_PATTERN "/tmp/dc25XXXXXX";
138
139static Dc20Info *dc20_info;
140static Dc20Info CameraInfo;
141
142static SANE_Byte contrast_table[256];
143
144static struct pixmap *pp;
145
146static 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
152static 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
158static SANE_Range image_range = {
159  0,
160  14,
161  0
162};
163
164static 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
329static 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
341static 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 */
349static 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
361static struct termios tty_orig;
362
363static int
364send_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
393static int
394init_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
506static void
507close_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
535static unsigned char info_pck[] = INFO_PCK;
536
537static Dc20Info *
538get_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
607static int
608read_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
680static int
681end_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
737static int columns = HIGH_WIDTH,
738  right_margin = HIGH_RIGHT_MARGIN, camera_header_size = HIGH_CAMERA_HEADER;
739static int low_i = -1, high_i = -1, norm_percentage = NORM_PERCENTAGE;
740static float saturation = SATURATION,
741  rfactor = RFACTOR, gfactor = GFACTOR, bfactor = BFACTOR;
742
743static void
744set_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
765static void
766interpolate_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
800static void
801interpolate_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
921static void
922adjust_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
1016static int
1017min3 (int x, int y, int z)
1018{
1019  return (x < y ? (x < z ? x : z) : (y < z ? y : z));
1020}
1021
1022static int
1023max3 (int x, int y, int z)
1024{
1025  return (x > y ? (x > z ? x : z) : (y > z ? y : z));
1026}
1027
1028static void
1029determine_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/*
1096if (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
1107static unsigned char *
1108make_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
1133static int
1134lookup_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
1144static int
1145output_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
1205static int
1206comet_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
1280static int
1281convert_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
1418static struct pixmap *
1419alloc_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
1458static void
1459free_pixmap (struct pixmap *p)
1460{
1461  if (p)
1462    {
1463      free (p->planes);
1464      free (p);
1465    }
1466}
1467
1468static int
1469set_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
1507static int
1508zoom_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
1570static int
1571zoom_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
1633static unsigned char shoot_pck[] = SHOOT_PCK;
1634
1635static int
1636shoot (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
1722static unsigned char erase_pck[] = ERASE_PCK;
1723
1724static int
1725erase (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
1781static unsigned char res_pck[] = RES_PCK;
1782
1783static int
1784change_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
1807SANE_Status
1808sane_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
1937void
1938sane_exit (void)
1939{
1940}
1941
1942/* Device select/open/close */
1943
1944static const SANE_Device dev[] = {
1945  {
1946   "0",
1947   "Kodak",
1948   "DC-25",
1949   "still camera"},
1950};
1951
1952SANE_Status
1953sane_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
1970SANE_Status
1971sane_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
2014void
2015sane_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
2032const SANE_Option_Descriptor *
2033sane_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
2042SANE_Status
2043sane_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
2269SANE_Status
2270sane_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
2284static unsigned char thumb_pck[] = THUMBS_PCK;
2285
2286static unsigned char pic_pck[] = PICS_PCK;
2287
2288static int bytes_in_buffer;
2289static int bytes_read_from_buffer;
2290static SANE_Byte buffer[1024];
2291static int total_bytes_read;
2292static SANE_Bool started = SANE_FALSE;
2293static int outbytes;
2294
2295SANE_Status
2296sane_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
2519SANE_Status
2520sane_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
2688void
2689sane_cancel (SANE_Handle __sane_unused__ handle)
2690{
2691  DBG (127, "sane_cancel() called\n");
2692  started = SANE_FALSE;
2693}
2694
2695SANE_Status
2696sane_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
2718SANE_Status
2719sane_get_select_fd (SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ * fd)
2720{
2721  return SANE_STATUS_UNSUPPORTED;
2722}
2723