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