diff options
Diffstat (limited to 'backend/hp3500.c')
-rw-r--r-- | backend/hp3500.c | 836 |
1 files changed, 603 insertions, 233 deletions
diff --git a/backend/hp3500.c b/backend/hp3500.c index 48a8035..26fe071 100644 --- a/backend/hp3500.c +++ b/backend/hp3500.c @@ -84,6 +84,7 @@ #include <string.h> #include <ctype.h> #include <time.h> +#include <math.h> #include <sys/types.h> #include <unistd.h> @@ -145,6 +146,7 @@ enum hp3500_option OPT_MODE, OPT_BRIGHTNESS, OPT_CONTRAST, + OPT_GAMMA, NUM_OPTIONS }; @@ -189,6 +191,8 @@ struct hp3500_data int brightness; int contrast; + double gamma; + SANE_Option_Descriptor opt[NUM_OPTIONS]; SANE_Device sane; }; @@ -218,6 +222,8 @@ static const SANE_Range range_brightness = { 0, 255, 0 }; static const SANE_Range range_contrast = { 0, 255, 0 }; +static const SANE_Range range_gamma = + { SANE_FIX (0.2), SANE_FIX(4.0), SANE_FIX(0.01) }; #define HP3500_COLOR_SCAN 0 @@ -233,6 +239,7 @@ static int reader_process (void *); static void calculateDerivedValues (struct hp3500_data *scanner); static void do_reset (struct hp3500_data *scanner); static void do_cancel (struct hp3500_data *scanner); +static size_t max_string_size(char const **); /* * used by sane_get_devices @@ -382,6 +389,7 @@ sane_open (SANE_String_Const name, SANE_Handle * handle) scanner->mode = 0; scanner->brightness = 128; scanner->contrast = 64; + scanner->gamma = 2.2; calculateDerivedValues (scanner); return SANE_STATUS_GOOD; @@ -536,6 +544,10 @@ sane_control_option (SANE_Handle handle, SANE_Int option, *(SANE_Word *) val = scanner->contrast; return SANE_STATUS_GOOD; + case OPT_GAMMA: + *(SANE_Word *) val = SANE_FIX(scanner->gamma); + return SANE_STATUS_GOOD; + case OPT_BRIGHTNESS: *(SANE_Word *) val = scanner->brightness; return SANE_STATUS_GOOD; @@ -649,6 +661,10 @@ sane_control_option (SANE_Handle handle, SANE_Int option, case OPT_CONTRAST: scanner->contrast = *(SANE_Word *) val; return SANE_STATUS_GOOD; + + case OPT_GAMMA: + scanner->gamma = SANE_UNFIX(*(SANE_Word *) val); + return SANE_STATUS_GOOD; } /* switch */ } /* else */ return SANE_STATUS_INVAL; @@ -703,7 +719,7 @@ sane_start (SANE_Handle handle) scanner->reader_pid = sanei_thread_begin (reader_process, scanner); time (&scanner->last_scan); - if (scanner->reader_pid == -1) + if (!sanei_thread_is_valid (scanner->reader_pid)) { DBG (MSG_ERR, "cannot fork reader process.\n"); DBG (MSG_ERR, "%s", strerror (errno)); @@ -952,7 +968,7 @@ attachScanner (const char *devicename) dev->devicename = strdup (devicename); dev->sfd = -1; dev->last_scan = 0; - dev->reader_pid = -1; + dev->reader_pid = (SANE_Pid) -1; dev->pipe_r = dev->pipe_w = -1; dev->sane.name = dev->devicename; @@ -1064,8 +1080,9 @@ init_options (struct hp3500_data *scanner) opt->title = SANE_TITLE_SCAN_MODE; opt->desc = SANE_DESC_SCAN_MODE; opt->type = SANE_TYPE_STRING; + opt->size = max_string_size(scan_mode_list); opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; - opt->constraint.string_list = scan_mode_list; + opt->constraint.string_list = (SANE_String_Const *) scan_mode_list; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; opt = scanner->opt + OPT_BRIGHTNESS; @@ -1086,6 +1103,16 @@ init_options (struct hp3500_data *scanner) opt->constraint.range = &range_contrast; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + opt = scanner->opt + OPT_GAMMA; + opt->name = SANE_NAME_ANALOG_GAMMA; + opt->title = SANE_TITLE_ANALOG_GAMMA; + opt->desc = SANE_DESC_ANALOG_GAMMA; + opt->type = SANE_TYPE_FIXED; + opt->unit = SANE_UNIT_NONE; + opt->constraint_type = SANE_CONSTRAINT_RANGE; + opt->constraint.range = &range_gamma; + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + return SANE_STATUS_GOOD; } @@ -1098,7 +1125,7 @@ do_reset (struct hp3500_data *scanner) static void do_cancel (struct hp3500_data *scanner) { - if (scanner->reader_pid != -1) + if (sanei_thread_is_valid (scanner->reader_pid)) { if (sanei_thread_kill (scanner->reader_pid) == 0) @@ -1749,14 +1776,14 @@ rt_set_basic_calibration (unsigned char *regs, int greengain, int blueoffset1, int blueoffset2, int bluegain) { - regs[0x05] = redoffset1; - regs[0x02] = redoffset2; + regs[0x02] = redoffset1; + regs[0x05] = redoffset2; regs[0x08] = redgain; - regs[0x06] = greenoffset1; - regs[0x03] = greenoffset2; + regs[0x03] = greenoffset1; + regs[0x06] = greenoffset2; regs[0x09] = greengain; - regs[0x07] = blueoffset1; - regs[0x04] = blueoffset2; + regs[0x04] = blueoffset1; + regs[0x07] = blueoffset2; regs[0x0a] = bluegain; return 0; } @@ -1765,13 +1792,36 @@ static int rt_set_calibration_addresses (unsigned char *regs, unsigned redaddr, unsigned greenaddr, - unsigned blueaddr, unsigned endaddr) + unsigned blueaddr, + unsigned endaddr, + unsigned width) { + unsigned endpage = (endaddr + 31) / 32; + unsigned scanline_pages = ((width + 1) * 3 + 31) / 32; + + /* Red, green and blue detailed calibration addresses */ + regs[0x84] = redaddr; regs[0x8e] = (regs[0x8e] & 0x0f) | ((redaddr >> 4) & 0xf0); rt_set_value_lsbfirst (regs, 0x85, 2, greenaddr); rt_set_value_lsbfirst (regs, 0x87, 2, blueaddr); - rt_set_value_lsbfirst (regs, 0x89, 2, (endaddr + 31) / 32); + + /* I don't know what the next three are used for, but each buffer commencing + * at 0x80 and 0x82 needs to hold a full scan line. + */ + + rt_set_value_lsbfirst (regs, 0x80, 2, endpage); + rt_set_value_lsbfirst (regs, 0x82, 2, endpage + scanline_pages); + rt_set_value_lsbfirst (regs, 0x89, 2, endpage + scanline_pages * 2); + + /* I don't know what this is, but it seems to be a number of pages that can hold + * 16 complete scan lines, but not calculated as an offset from any other page + */ + + rt_set_value_lsbfirst (regs, 0x51, 2, (48 * (width + 1) + 31) / 32); + + /* I don't know what this is either, but this is what the Windows driver does */ + rt_set_value_lsbfirst (regs, 0x8f, 2, 0x1c00); return 0; } @@ -1798,6 +1848,13 @@ rt_set_data_feed_on (unsigned char *regs) } static int +rt_set_data_feed_off (unsigned char *regs) +{ + regs[0xb2] |= 0x04; + return 0; +} + +static int rt_enable_ccd (unsigned char *regs, int enable) { if (enable) @@ -2245,10 +2302,14 @@ rt_nvram_read (int block, int location, unsigned char *data, int bytes) return 0; } +/* This is what we want as the initial registers, not what they + * are at power on time. In particular 13 bytes at 0x10 are + * different, and the byte at 0x94 is different. + */ static unsigned char initial_regs[] = { /* 0x00 */ 0xf5, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08 */ 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, - /* 0x10 */ 0xe1, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0xfc, + /* 0x10 */ 0x81, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, /* 0x18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, /* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x19, @@ -2264,7 +2325,7 @@ static unsigned char initial_regs[] = { /* 0x78 */ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80 */ 0x0f, 0x02, 0x4b, 0x02, 0x00, 0xec, 0x19, 0xd8, /* 0x88 */ 0x2d, 0x87, 0x02, 0xff, 0x3f, 0x78, 0x60, 0x00, - /* 0x90 */ 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x90 */ 0x1c, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, /* 0x98 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0 */ 0x00, 0x00, 0x00, 0x0c, 0x27, 0x64, 0x00, 0x00, /* 0xa8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -2363,14 +2424,14 @@ static struct resolution_parameters resparms[] = { /* My values - all work */ /*res r39 rC3 rC6 freq cph0s rgo gbo intra mmcm d3 tg stepsz */ {1200, 3, 6, 4, 2, 1, 22, 22, 4, 2, 1, RT_NORMAL_TG, 0x157b}, - {600, 15, 6, 4, 1, 0, 9, 10, 0, 2, 1, RT_NORMAL_TG, 0x055e}, + {600, 15, 6, 4, 1, 1, 9, 10, 0, 2, 1, RT_NORMAL_TG, 0x055e}, {400, 3, 1, 4, 1, 1, 6, 6, 1, 2, 1, RT_NORMAL_TG, 0x157b}, - {300, 15, 3, 4, 1, 0, 5, 4, 0, 2, 1, RT_NORMAL_TG, 0x02af}, - {200, 7, 1, 4, 1, 0, 3, 3, 0, 2, 1, RT_NORMAL_TG, 0x055e}, - {150, 15, 3, 1, 1, 0, 2, 2, 0, 2, 1, RT_NORMAL_TG, 0x02af}, - {100, 3, 1, 3, 1, 0, 1, 1, 0, 2, 1, RT_NORMAL_TG, 0x0abd}, - {75, 15, 3, 3, 1, 0, 1, 1, 0, 2, 1, RT_NORMAL_TG, 0x02af}, - {50, 15, 1, 1, 1, 0, 0, 0, 0, 2, 1, RT_NORMAL_TG, 0x055e}, + {300, 15, 3, 4, 1, 1, 5, 4, 0, 2, 1, RT_NORMAL_TG, 0x02af}, + {200, 7, 1, 4, 1, 1, 3, 3, 0, 2, 1, RT_NORMAL_TG, 0x055e}, + {150, 15, 3, 1, 1, 1, 2, 2, 0, 2, 1, RT_NORMAL_TG, 0x02af}, + {100, 3, 1, 3, 1, 1, 1, 1, 0, 2, 1, RT_NORMAL_TG, 0x0abd}, + {75, 15, 3, 3, 1, 1, 1, 1, 0, 2, 1, RT_NORMAL_TG, 0x02af}, + {50, 15, 1, 1, 1, 1, 0, 0, 0, 2, 1, RT_NORMAL_TG, 0x055e}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; @@ -2597,6 +2658,90 @@ constrain (int val, int min, int max) return val; } +#if 0 +static void +sram_dump_byte(FILE *fp, + unsigned char const *left, + unsigned leftstart, + unsigned leftlimit, + unsigned char const *right, + unsigned rightstart, + unsigned rightlimit, + unsigned idx) +{ + unsigned ridx = rightstart + idx; + unsigned lidx = leftstart + idx; + + putc(' ', fp); + if (rightstart < rightlimit && leftstart < leftlimit && left[lidx] != right[ridx]) + fputs("<b>", fp); + if (leftstart < leftlimit) + fprintf(fp, "%02x", left[lidx]); + else + fputs(" ", fp); + if (rightstart < rightlimit && leftstart < leftlimit && left[lidx] != right[ridx]) + fputs("</b>", fp); +} + +static void +dump_sram_to_file(char const *fname, + unsigned char const *expected, + unsigned end_calibration_offset) +{ + FILE *fp = fopen(fname, "w"); + rt_set_sram_page(0); + + if (fp) + { + unsigned char buf[1024]; + unsigned loc = 0; + + fprintf(fp, "<html><head></head><body><pre>\n"); + while (loc < end_calibration_offset) + { + unsigned byte = 0; + + rt_read_sram(1024, buf); + + while (byte < 1024) + { + unsigned idx = 0; + + fprintf(fp, "%06x:", loc); + do + { + sram_dump_byte(fp, buf, byte, 1024, expected, loc, end_calibration_offset, idx); + } while (++idx & 0x7); + fprintf(fp, " -"); + do + { + sram_dump_byte(fp, buf, byte, 1024, expected, loc, end_calibration_offset, idx); + } while (++idx & 0x7); + + idx = 0; + fputs(" ", fp); + + do + { + sram_dump_byte(fp, expected, loc, end_calibration_offset, buf, byte, 1024, idx); + } while (++idx & 0x7); + fprintf(fp, " -"); + do + { + sram_dump_byte(fp, expected, loc, end_calibration_offset, buf, byte, 1024, idx); + } while (++idx & 0x7); + + + fputs("\n", fp); + byte += 16; + loc += 16; + } + } + fprintf(fp, "</pre></body></html>"); + fclose(fp); + } +} +#endif static int rts8801_doscan (unsigned width, @@ -2610,13 +2755,13 @@ rts8801_doscan (unsigned width, int oddfirst, unsigned char const *calib_info, int merged_channels, - detailed_calibration_data const *detailed_calib_data) + double *postprocess_offsets, + double *postprocess_gains) { unsigned rowbytes = 0; unsigned output_rowbytes = 0; unsigned channels = 0; unsigned total_rows = 0; - unsigned bytesperchannel; unsigned char *row_buffer; unsigned char *output_buffer; unsigned buffered_rows; @@ -2638,7 +2783,7 @@ rts8801_doscan (unsigned width, channels = 3; rowbytes = width * 3; - bytesperchannel = width; + switch (colour) { case HP3500_GRAY_SCAN: @@ -2710,32 +2855,27 @@ rts8801_doscan (unsigned width, if (!rows_to_begin || !--rows_to_begin) { unsigned char *outnow = output_buffer; + unsigned x; - for (i = 0; - i < (merged_channels ? rowbytes : width); - i += merged_channels ? channels : 1) + for (i = x = 0; + x < width; + ++x, i += merged_channels ? channels : 1) { for (j = 0; j < channels; ++j) { unsigned pix = (unsigned char) channel_data[j][i & 1][i]; - if (detailed_calib_data) - { - unsigned char const *calib_start = - detailed_calib_data->channeldata[j] + - 2 * - detailed_calib_data-> - resolution_divisor * i / - (merged_channels ? channels : 1); - pix = - constrain ((int) pix - - (int) calib_start[0], 0, - 255); - pix = - constrain (pix * calib_start[1] / - 0x40, 0, 255); - } + if (postprocess_gains && postprocess_offsets) + { + int ppidx = j * width + x; + + pix = constrain ( pix + * postprocess_gains[ppidx] + - postprocess_offsets[ppidx], + 0, + 255); + } *outnow++ = pix; } } @@ -2818,6 +2958,9 @@ static unsigned local_sram_size; static unsigned char r93setting; #define RTS8801_F_SUPPRESS_MOVEMENT 1 +#define RTS8801_F_LAMP_OFF 2 +#define RTS8801_F_NO_DISPLACEMENTS 4 +#define RTS8801_F_ODDX 8 static int find_resolution_index (unsigned resolution) @@ -2848,7 +2991,8 @@ rts8801_fullscan (unsigned x, int green_calib_offset, int blue_calib_offset, int end_calib_offset, - detailed_calibration_data const *detailed_calib_data) + double *postprocess_offsets, + double *postprocess_gains) { int ires, jres; int tg_setting; @@ -2856,6 +3000,10 @@ rts8801_fullscan (unsigned x, unsigned char offdutytime; int result; int scan_frequency; + unsigned intra_channel_offset; + unsigned red_green_offset; + unsigned green_blue_offset; + unsigned total_offsets; ires = find_resolution_index (xresolution); jres = find_resolution_index (yresolution); @@ -2889,47 +3037,32 @@ rts8801_fullscan (unsigned x, rt_set_movement_pattern (regs, 0x800000); - - tg_setting = resparms[jres].tg; - rt_set_ccd_shift_clock_multiplier (regs, tg_info[tg_setting].tg_cph0p); - rt_set_ccd_clock_reset_interval (regs, tg_info[tg_setting].tg_crsp); - rt_set_ccd_clamp_clock_multiplier (regs, tg_info[tg_setting].tg_cclpp); - - - rt_set_one_register (0xc6, 0); - rt_set_one_register (0xc6, 0); - - rt_set_step_size (regs, resparms[jres].step_size); - rt_set_direction_forwards (regs); - rt_set_stop_when_rewound (regs, 0); - rt_set_data_feed_on (regs); - rt_set_calibration_addresses (regs, 0, 0, 0, 0); + rt_set_calibration_addresses (regs, 0, 0, 0, 0, 0); rt_set_basic_calibration (regs, calib_info[0], calib_info[1], calib_info[2], calib_info[3], calib_info[4], calib_info[5], calib_info[6], calib_info[7], calib_info[8]); regs[0x0b] = 0x70; /* If set to 0x71, the alternative, all values are low */ + regs[0x40] &= 0xc0; if (red_calib_offset >= 0 && green_calib_offset >= 0 - && blue_calib_offset >= 0 && - yresolution < 400) + && blue_calib_offset >= 0) { rt_set_calibration_addresses (regs, red_calib_offset, green_calib_offset, blue_calib_offset, - end_calib_offset); + end_calib_offset, + w); regs[0x40] |= 0x2f; - detailed_calib_data = 0; } else if (end_calib_offset >= 0) { rt_set_calibration_addresses (regs, 0x600, 0x600, 0x600, - end_calib_offset); - regs[0x40] &= 0xc0; + end_calib_offset, w); } rt_set_channel (regs, RT_CHANNEL_ALL); @@ -2937,23 +3070,9 @@ rts8801_fullscan (unsigned x, rt_set_merge_channels (regs, 1); rt_set_colour_mode (regs, 1); - rt_set_motor_movement_clock_multiplier (regs, - resparms[jres]. - motor_movement_clock_multiplier); - - rt_set_cdss (regs, tg_info[tg_setting].tg_cdss1, - tg_info[tg_setting].tg_cdss2); - rt_set_cdsc (regs, tg_info[tg_setting].tg_cdsc1, - tg_info[tg_setting].tg_cdsc2); - rt_update_after_setting_cdss2 (regs); - rt_set_last_sram_page (regs, (local_sram_size - 1) >> 5); - regs[0x39] = resparms[jres].reg_39_value; - regs[0xc3] = (regs[0xc3] & 0xf8) | resparms[jres].reg_c3_value; - regs[0xc6] = (regs[0xc6] & 0xf8) | resparms[jres].reg_c6_value; scan_frequency = resparms[jres].scan_frequency; - rt_set_scan_frequency (regs, scan_frequency); rt_set_cph0s (regs, resparms[ires].cph0s); if (resparms[ires].d3_bit_3_value) regs[0xd3] |= 0x08; @@ -2962,15 +3081,10 @@ rts8801_fullscan (unsigned x, if (flags & RTS8801_F_SUPPRESS_MOVEMENT) regs[0xc3] &= 0x7f; - rt_set_horizontal_resolution (regs, xresolution); - rt_set_noscan_distance (regs, y * scan_frequency - 1); - rt_set_total_distance (regs, scan_frequency * - (y + - h + - resparms[jres].red_green_offset + - resparms[jres].green_blue_offset + - resparms[jres].intra_channel_offset) - 1); + regs[0xb2] &= 0xf7; + + rt_set_horizontal_resolution (regs, xresolution); rt_set_scanline_start (regs, x * (1200 / xresolution) / @@ -2982,6 +3096,118 @@ rts8801_fullscan (unsigned x, (resparms[ires].cph0s ? 1 : 2) / (resparms[ires].d3_bit_3_value ? 1 : 2)); + if (flags & RTS8801_F_NO_DISPLACEMENTS) + { + red_green_offset = green_blue_offset = intra_channel_offset = 0; + } + else + { + red_green_offset = resparms[jres].red_green_offset; + green_blue_offset = resparms[jres].green_blue_offset; + intra_channel_offset = resparms[jres].intra_channel_offset; + } + total_offsets = red_green_offset + green_blue_offset + intra_channel_offset; + if (y > total_offsets + 2) + y -= total_offsets; + h += total_offsets; + + if (yresolution > 75 && !(flags & RTS8801_F_SUPPRESS_MOVEMENT)) + { + int rmres = find_resolution_index (50); + + if (rmres >= 0) + { + int factor = yresolution / 50; + int fastres = y / factor; + int remainder = y % factor; + + while (remainder < 2) + { + --fastres; + remainder += factor; + } + + if (fastres >= 3) + { + y = remainder; + + rt_set_noscan_distance(regs, fastres * resparms[rmres].scan_frequency - 2); + rt_set_total_distance(regs, fastres * resparms[rmres].scan_frequency - 1); + + rt_set_scan_frequency(regs, 1); + + tg_setting = resparms[rmres].tg; + rt_set_ccd_shift_clock_multiplier (regs, tg_info[tg_setting].tg_cph0p); + rt_set_ccd_clock_reset_interval (regs, tg_info[tg_setting].tg_crsp); + rt_set_ccd_clamp_clock_multiplier (regs, tg_info[tg_setting].tg_cclpp); + + rt_set_one_register (0xc6, 0); + rt_set_one_register (0xc6, 0); + + rt_set_step_size (regs, resparms[rmres].step_size); + + rt_set_motor_movement_clock_multiplier (regs, + resparms[rmres]. + motor_movement_clock_multiplier); + + rt_set_cdss (regs, tg_info[tg_setting].tg_cdss1, + tg_info[tg_setting].tg_cdss2); + rt_set_cdsc (regs, tg_info[tg_setting].tg_cdsc1, + tg_info[tg_setting].tg_cdsc2); + rt_update_after_setting_cdss2 (regs); + + regs[0x39] = resparms[rmres].reg_39_value; + regs[0xc3] = (regs[0xc3] & 0xf8) | resparms[rmres].reg_c3_value; + regs[0xc6] = (regs[0xc6] & 0xf8) | resparms[rmres].reg_c6_value; + + rt_set_data_feed_off (regs); + + rt_set_all_registers (regs); + + rt_set_one_register (0x2c, regs[0x2c]); + + if (DBG_LEVEL >= 5) + dump_registers (regs); + + rt_start_moving (); + while (rt_is_moving ()); + } + } + } + + + rt_set_noscan_distance (regs, y * scan_frequency - 1); + rt_set_total_distance (regs, scan_frequency * (y + h) - 1); + + rt_set_scan_frequency (regs, scan_frequency); + + tg_setting = resparms[jres].tg; + + rt_set_ccd_shift_clock_multiplier (regs, tg_info[tg_setting].tg_cph0p); + rt_set_ccd_clock_reset_interval (regs, tg_info[tg_setting].tg_crsp); + rt_set_ccd_clamp_clock_multiplier (regs, tg_info[tg_setting].tg_cclpp); + + rt_set_one_register (0xc6, 0); + rt_set_one_register (0xc6, 0); + + rt_set_step_size (regs, resparms[jres].step_size); + + rt_set_motor_movement_clock_multiplier (regs, + resparms[jres]. + motor_movement_clock_multiplier); + + rt_set_cdss (regs, tg_info[tg_setting].tg_cdss1, + tg_info[tg_setting].tg_cdss2); + rt_set_cdsc (regs, tg_info[tg_setting].tg_cdsc1, + tg_info[tg_setting].tg_cdsc2); + rt_update_after_setting_cdss2 (regs); + + regs[0x39] = resparms[jres].reg_39_value; + regs[0xc3] = (regs[0xc3] & 0xf8) | resparms[jres].reg_c3_value; + regs[0xc6] = (regs[0xc6] & 0xf8) | resparms[jres].reg_c6_value; + + rt_set_data_feed_on (regs); + rt_set_all_registers (regs); rt_set_one_register (0x2c, regs[0x2c]); @@ -2992,12 +3218,13 @@ rts8801_fullscan (unsigned x, result = rts8801_doscan (w, h, colour, - resparms[jres].red_green_offset, - resparms[jres].green_blue_offset, - resparms[jres].intra_channel_offset, + red_green_offset, + green_blue_offset, + intra_channel_offset, cbfunc, param, (x & 1), calib_info, - (regs[0x2f] & 0x04) != 0, detailed_calib_data); - + (regs[0x2f] & 0x04) != 0, + postprocess_offsets, + postprocess_gains); return result; } @@ -3080,6 +3307,11 @@ sum_channel (unsigned char *p, int n, int bytwo) static int do_warmup = 1; +#define DETAILED_PASS_COUNT 3 +#define DETAILED_PASS_OFFSETS 0 +#define DETAILED_PASS_GAINS_FIRSTPASS 1 +#define DETAILED_PASS_GAINS_SECONDPASS 2 + static int rts8801_scan (unsigned x, unsigned y, @@ -3090,26 +3322,22 @@ rts8801_scan (unsigned x, unsigned brightness, unsigned contrast, rts8801_callback cbfunc, - void *param) + void *param, + double gamma) { unsigned char calib_info[9]; unsigned char calibbuf[2400]; struct dcalibdata dcd; struct calibdata cd; unsigned char *detail_buffer = 0; - int iCalibOffset; - int iCalibX; int iCalibY; - int iCalibWidth; int iCalibTarget; - int iCalibPixels; int iMoveFlags = 0; - unsigned int aiLow[3] = { 0, 0, 0 }; - unsigned int aiHigh[3] = { 256, 256, 256 }; - unsigned aiBestOffset[3]; + unsigned aiBestOffset[6]; + int aiPassed[6]; int i; unsigned j; - int anychanged; + int k; int calibration_size; unsigned char *pDetailedCalib; int red_calibration_offset; @@ -3121,7 +3349,13 @@ rts8801_scan (unsigned x, int resolution_index; int detailed_calibration_rows = 50; unsigned char *tdetail_buffer; - detailed_calibration_data detailed_calib_data; + int pass; + int onechanged; + double *postprocess_gains; + double *postprocess_offsets; + int needs_postprocessed_calibration = 0; + double contrast_adjust = (double) contrast / 64; + int brightness_adjust = brightness - 0x80; /* Initialise and power up */ @@ -3150,99 +3384,101 @@ rts8801_scan (unsigned x, calib_info[2] = calib_info[5] = calib_info[8] = 1; - calib_info[0] = calib_info[1] = calib_info[3] = calib_info[4] = - calib_info[6] = calib_info[7] = 0xb4; - - iCalibOffset = 0; /* Note that horizontal resolution is always 600dpi for calibration. 330 is 110 dots in (for R,G,B channels) */ - iCalibX = 1; - iCalibPixels = 50; - iCalibY = (resolution == 25) ? 1 : 2; /* Was 1200 / resolution, which would take us past the calibration area for 50dpi */ - iCalibWidth = 100; + iCalibY = (resolution == 25) ? 1 : 2; iCalibTarget = 550; + + rt_turn_off_lamp(); - for (i = 0; i < 3; ++i) - aiBestOffset[i] = 0xb4; - + for (i = 0; i < 6; ++i) + { + aiBestOffset[i] = 0xbf; + aiPassed[i] = 0; + } + do { DBG (30, "Initial calibration pass commences\n"); - anychanged = 0; - - for (i = 0; i < 3; ++i) - { - aiBestOffset[i] = (aiHigh[i] + aiLow[i] + 1) / 2; - } + onechanged = 0; for (i = 0; i < 3; ++i) - calib_info[i * 3] = calib_info[i * 3 + 1] = aiBestOffset[i]; - + { + calib_info[i * 3] = aiBestOffset[i]; + calib_info[i * 3 + 1] = aiBestOffset[i + 3]; + } + cd.buffer = calibbuf; cd.space = sizeof (calibbuf); DBG (30, "Commencing scan for initial calibration pass\n"); - rts8801_fullscan (iCalibX, iCalibY, iCalibWidth, 2, 600, resolution, + rts8801_fullscan (1401, iCalibY, 100, 2, 400, resolution, HP3500_COLOR_SCAN, (rts8801_callback) storefunc, &cd, - calib_info, iMoveFlags, -1, -1, -1, -1, 0); + calib_info, iMoveFlags, -1, -1, -1, -1, 0, 0); DBG (30, "Completed scan for initial calibration pass\n"); - iMoveFlags = RTS8801_F_SUPPRESS_MOVEMENT; + iMoveFlags = RTS8801_F_SUPPRESS_MOVEMENT | RTS8801_F_NO_DISPLACEMENTS; + iCalibY = 2; - for (i = 0; i < 3; ++i) + for (i = 0; i < 6; ++i) { int sum; - if (aiBestOffset[i] >= 255) + if (aiBestOffset[i] >= 255 || aiPassed[i] > 2) continue; - sum = sum_channel (calibbuf + iCalibOffset + i, iCalibPixels, 0); + sum = sum_channel (calibbuf + i, 50, 1); DBG (20, "channel[%d] sum = %d (target %d)\n", i, sum, iCalibTarget); - if (sum >= iCalibTarget) - aiHigh[i] = aiBestOffset[i]; - else - aiLow[i] = aiBestOffset[i]; + if (sum < iCalibTarget) + { + onechanged = 1; + ++aiBestOffset[i]; + } + else + { + ++aiPassed[i]; + } } DBG (30, "Initial calibration pass completed\n"); } - while (aiLow[0] < aiHigh[0] - 1 && aiLow[1] < aiHigh[1] - 1 - && aiLow[1] < aiHigh[1] + 1); + while (onechanged); DBG (20, "Offsets calculated\n"); - cd.buffer = calibbuf; - cd.space = sizeof (calibbuf); - DBG (20, "Scanning for part 2 of initial calibration\n"); - rts8801_fullscan (iCalibX + 2100, iCalibY, iCalibWidth, 2, 600, resolution, - HP3500_COLOR_SCAN, (rts8801_callback) storefunc, &cd, - calib_info, RTS8801_F_SUPPRESS_MOVEMENT, -1, -1, -1, -1, - 0); - DBG (20, "Scan for part 2 of initial calibration completed\n"); - DBG (20, "Initial calibration completed\n"); + rt_turn_on_lamp(); + usleep(500000); tdetail_buffer = (unsigned char *) malloc (w * 3 * detailed_calibration_rows); - aiLow[0] = aiLow[1] = aiLow[2] = 1; - aiHigh[0] = aiHigh[1] = aiHigh[2] = 64; + for (i = 0; i < 3; ++i) + { + calib_info[i * 3 + 2] = 1; + aiPassed[i] = 0; + } + do { struct dcalibdata dcdt; - for (i = 0; i < 3; ++i) - calib_info[i * 3 + 2] = (aiLow[i] + aiHigh[i]) / 2; - dcdt.buffers[0] = tdetail_buffer; dcdt.buffers[1] = (tdetail_buffer + w * detailed_calibration_rows); dcdt.buffers[2] = (dcdt.buffers[1] + w * detailed_calibration_rows); dcdt.pixelsperrow = w; dcdt.pixelnow = dcdt.channelnow = dcdt.firstrowdone = 0; + DBG (20, "Scanning for part 2 of initial calibration\n"); rts8801_fullscan (x, 4, w, detailed_calibration_rows + 1, resolution, resolution, HP3500_COLOR_SCAN, (rts8801_callback) accumfunc, &dcdt, calib_info, - RTS8801_F_SUPPRESS_MOVEMENT, -1, -1, -1, -1, 0); + RTS8801_F_SUPPRESS_MOVEMENT | RTS8801_F_NO_DISPLACEMENTS, -1, -1, -1, -1, 0, 0); + DBG (20, "Scan for part 2 of initial calibration completed\n"); + + onechanged = 0; for (i = 0; i < 3; ++i) { int largest = 1; - for (j = 0; j < w; ++j) + if (aiPassed[i] > 2 || calib_info[i * 3 + 2] >= 63) + continue; + + for (j = 0; j < w; ++j) { int val = calcmedian (dcdt.buffers[i], j, w, detailed_calibration_rows); @@ -3252,16 +3488,17 @@ rts8801_scan (unsigned x, } if (largest < 0xe0) - aiLow[i] = calib_info[i * 3 + 2]; - else - aiHigh[i] = calib_info[i * 3 + 2]; + { + ++calib_info[i * 3 + 2]; + onechanged = 1; + } + else + { + ++aiPassed[i]; + } } } - while (aiLow[0] < aiHigh[0] - 1 && aiLow[1] < aiHigh[1] - 1 - && aiLow[1] < aiHigh[1] + 1); - - for (i = 0; i < 3; ++i) - calib_info[i * 3 + 2] = aiLow[i]; + while (onechanged); for (i = 0; i < 3; ++i) { @@ -3282,15 +3519,7 @@ rts8801_scan (unsigned x, dcd.buffers[1] = (detail_buffer + w * detailed_calibration_rows); dcd.buffers[2] = (dcd.buffers[1] + w * detailed_calibration_rows); dcd.pixelsperrow = w; - dcd.pixelnow = dcd.channelnow = dcd.firstrowdone = 0; - - DBG (10, "Performing detailed calibration scan\n"); - rts8801_fullscan (x, iCalibY, w, detailed_calibration_rows + 1, - resolution, resolution, HP3500_COLOR_SCAN, - (rts8801_callback) accumfunc, &dcd, calib_info, - RTS8801_F_SUPPRESS_MOVEMENT, -1, -1, -1, -1, 0); - DBG (10, "Detailed calibration scan completed\n"); /* And now for the detailed calibration */ resolution_index = find_resolution_index (resolution); @@ -3301,101 +3530,221 @@ rts8801_scan (unsigned x, base_resolution *= 2; resolution_divisor = base_resolution / resolution; - calibration_size = w * resolution_divisor * 6 + 1536; - red_calibration_offset = 1536; - blue_calibration_offset = - red_calibration_offset + w * resolution_divisor * 2; + calibration_size = w * resolution_divisor * 6 + 1568 + 96; + red_calibration_offset = 0x600; green_calibration_offset = - blue_calibration_offset + w * resolution_divisor * 2; - end_calibration_offset = + red_calibration_offset + w * resolution_divisor * 2; + blue_calibration_offset = green_calibration_offset + w * resolution_divisor * 2; + end_calibration_offset = + blue_calibration_offset + w * resolution_divisor * 2; pDetailedCalib = (unsigned char *) malloc (calibration_size); memset (pDetailedCalib, 0, calibration_size); + for (i = 0; i < 3; ++i) { int idx = - (i == 0) ? red_calibration_offset : (i == - 1) ? green_calibration_offset : - blue_calibration_offset; - double g = calib_info[i * 3 + 2]; + (i == 0) ? red_calibration_offset : + (i == 1) ? green_calibration_offset : + blue_calibration_offset; for (j = 0; j < 256; j++) - { - int val = j; - - if (val < 0) - val = 0; - if (val > 255) - val = 255; - pDetailedCalib[i * 512 + j * 2] = val; - pDetailedCalib[i * 512 + j * 2 + 1] = val; - } + { + /* Gamma table - appears to be 256 byte pairs for each input + * range (so the first entry cover inputs in the range 0 to 1, + * the second 1 to 2, and so on), mapping that input range + * (including the fractional parts within it) to an output + * range. + */ + pDetailedCalib[i * 512 + j * 2] = j; + pDetailedCalib[i * 512 + j * 2 + 1] = j; + } for (j = 0; j < w; ++j) - { - int multnow; - int offnow; - - /* This seems to be the approach for reg 0x40 & 0x3f == 0x27, which allows detailed - * calibration to return either higher or lower values. - */ - int k; - - { - double denom1 = - calcmedian (dcd.buffers[i], j, w, detailed_calibration_rows); - double f = 0xff / (denom1 - 2 * g); - double contrast_adjust = (double) (contrast + 1) / 64; - - multnow = f * 64 * contrast_adjust; - offnow = 4 * g + 128 - brightness; - } - if (multnow < 0) - multnow = 0; - if (multnow > 255) - multnow = 255; - if (offnow < 0) - offnow = 0; - if (offnow > 255) - offnow = 255; - - for (k = 0; k < resolution_divisor; ++k) - { - /*multnow = j * resolution_divisor + k; */ - pDetailedCalib[idx++] = offnow; /* Subtract this value from the result */ - pDetailedCalib[idx++] = multnow; /* Then multiply by this value divided by 0x40 */ - } - } + { + for (k = 0; k < resolution_divisor; ++k) + { + pDetailedCalib[idx++] = 0; + pDetailedCalib[idx++] = 0x80; + } + } } - DBG (10, "\n"); - rt_set_sram_page (0); rt_set_one_register (0x93, r93setting); rt_write_sram (calibration_size, pDetailedCalib); - /* And finally, perform the scan */ + postprocess_gains = (double *) malloc(sizeof(double) * 3 * w); + postprocess_offsets = (double *) malloc(sizeof(double) * 3 * w); + + for (pass = 0; pass < DETAILED_PASS_COUNT; ++pass) + { + int ppidx = 0; + + DBG (10, "Performing detailed calibration scan %d\n", pass); + + switch (pass) + { + case DETAILED_PASS_OFFSETS: + rt_turn_off_lamp(); + usleep(500000); /* To be sure it has gone off */ + break; + + case DETAILED_PASS_GAINS_FIRSTPASS: + rt_turn_on_lamp(); + usleep(500000); /* Give the lamp time to settle */ + break; + } + + dcd.pixelnow = dcd.channelnow = dcd.firstrowdone = 0; + rts8801_fullscan (x, iCalibY, w, detailed_calibration_rows + 1, + resolution, resolution, HP3500_COLOR_SCAN, + (rts8801_callback) accumfunc, &dcd, + calib_info, + RTS8801_F_SUPPRESS_MOVEMENT | RTS8801_F_NO_DISPLACEMENTS, + red_calibration_offset, + green_calibration_offset, + blue_calibration_offset, + end_calibration_offset, + 0, 0); + + DBG (10, " Detailed calibration scan %d completed\n", pass); + + for (i = 0; i < 3; ++i) + { + int idx = + (i == 0) ? red_calibration_offset : + (i == 1) ? green_calibration_offset : + blue_calibration_offset; + + for (j = 0; j < w; ++j) + { + double multnow = 0x80; + int offnow = 0; + + /* This seems to be the approach for reg 0x40 & 0x3f == 0x27, which allows detailed + * calibration to return either higher or lower values. + */ + + { + double denom1 = + calcmedian (dcd.buffers[i], j, w, detailed_calibration_rows); + + switch (pass) + { + case DETAILED_PASS_OFFSETS: + /* The offset is the number needed to be subtracted from "black" at detailed gain = 0x80, + * which is the value we started with. For the next round, pull the gain down to 0x20. Our + * next scan is a test scan to confirm the offset works. + */ + multnow = 0x20; + offnow = denom1; + break; + + case DETAILED_PASS_GAINS_FIRSTPASS: + multnow = 128.0 / denom1 * 0x20; /* Then bring it up to whatever we need to hit 192 */ + if (multnow > 255) + multnow = 255; + offnow = pDetailedCalib[idx]; + break; + + case DETAILED_PASS_GAINS_SECONDPASS: + multnow = 255.0 / denom1 * contrast_adjust * pDetailedCalib[idx+1]; /* And finally to 255 */ + offnow = pDetailedCalib[idx] - brightness_adjust * 0x80 / multnow; + + if (offnow < 0) + { + postprocess_offsets[ppidx] = multnow * offnow / 0x80; + offnow = 0; + needs_postprocessed_calibration = 1; + } + else if (offnow > 255) + { + postprocess_offsets[ppidx] = multnow * (offnow - 255) / 0x80; + offnow = 255; + needs_postprocessed_calibration = 1; + } + else + { + postprocess_offsets[ppidx] = 0; + } + if (multnow > 255) + { + postprocess_gains[ppidx] = multnow / 255; + multnow = 255; + needs_postprocessed_calibration = 1; + } + else + { + postprocess_gains[ppidx] = 1.0; + } + break; + } + } + if (offnow > 255) + offnow = 255; + + for (k = 0; k < resolution_divisor; ++k) + { + pDetailedCalib[idx++] = offnow; /* Subtract this value from the result at gains = 0x80*/ + pDetailedCalib[idx++] = multnow; /* Then multiply by this value divided by 0x80 */ + } + ++ppidx; + } + } + + if (pass == DETAILED_PASS_GAINS_SECONDPASS) + { + /* Build gamma table */ + unsigned char *redgamma = pDetailedCalib; + unsigned char *greengamma = redgamma + 512; + unsigned char *bluegamma = greengamma + 512; + double val; + double invgamma = 1.0l / gamma; + + *redgamma++ = *bluegamma++ = *greengamma++ = 0; + + /* The windows driver does a linear interpolation for the next 19 boundaries */ + val = pow (20.0l / 255, invgamma) * 255; + + for (j = 1; j <= 20; ++j) + { + *redgamma++ = *bluegamma++ = *greengamma++ = val * j / 20 + 0.5; + *redgamma++ = *bluegamma++ = *greengamma++ = val * j / 20 + 0.5; + } + + for (; j <= 255; ++j) + { + val = pow((double) j / 255, invgamma) * 255; + + *redgamma++ = *bluegamma++ = *greengamma++ = val + 0.5; + *redgamma++ = *bluegamma++ = *greengamma++ = val + 0.5; + } + *redgamma++ = *bluegamma++ = *greengamma++ = 255; + } + + DBG (10, "\n"); + + rt_set_sram_page (0); + rt_set_one_register (0x93, r93setting); + rt_write_sram (calibration_size, pDetailedCalib); + } + /* And finally, perform the scan */ DBG (10, "Scanning\n"); rts8801_rewind (); - detailed_calib_data.channeldata[0] = - pDetailedCalib + red_calibration_offset; - detailed_calib_data.channeldata[1] = - pDetailedCalib + green_calibration_offset; - detailed_calib_data.channeldata[2] = - pDetailedCalib + blue_calibration_offset; - detailed_calib_data.resolution_divisor = resolution_divisor; - rts8801_fullscan (x, y, w, h, resolution, resolution, colour, cbfunc, param, calib_info, 0, red_calibration_offset, green_calibration_offset, blue_calibration_offset, end_calibration_offset, - &detailed_calib_data); + needs_postprocessed_calibration ? postprocess_offsets : 0, + needs_postprocessed_calibration ? postprocess_gains : 0); rt_turn_off_lamp (); + rts8801_rewind (); rt_set_powersave_mode (1); @@ -3403,6 +3752,12 @@ rts8801_scan (unsigned x, free (pDetailedCalib); if (detail_buffer) free (detail_buffer); + if (tdetail_buffer) + free(tdetail_buffer); + if (postprocess_gains) + free(postprocess_gains); + if (postprocess_offsets) + free(postprocess_offsets); return 0; } @@ -3466,7 +3821,6 @@ reader_process (void *pv) sigaction (SIGTERM, &act, 0); } - /* Warm up the lamp again if our last scan ended more than 5 minutes ago. */ time (&t); do_warmup = (t - scanner->last_scan) > 300; @@ -3498,9 +3852,25 @@ reader_process (void *pv) scanner->actres_pixels.right - scanner->actres_pixels.left, scanner->actres_pixels.bottom - scanner->actres_pixels.top, scanner->resolution, scanner->mode, scanner->brightness, - scanner->contrast, (rts8801_callback) writefunc, &winfo) >= 0) + scanner->contrast, (rts8801_callback) writefunc, &winfo, + scanner->gamma) >= 0) status = SANE_STATUS_GOOD; status = SANE_STATUS_IO_ERROR; close (scanner->pipe_w); return status; } + +static size_t +max_string_size (char const **strings) +{ + size_t size, max_size = 0; + SANE_Int i; + + for (i = 0; strings[i]; ++i) + { + size = strlen (strings[i]) + 1; + if (size > max_size) + max_size = size; + } + return max_size; +} |