From ce2fdfbfbef2be848760b6e3c6e63d1e57284ef3 Mon Sep 17 00:00:00 2001 From: Pierre Willenbrock Date: Wed, 25 Mar 2009 12:57:24 +0000 Subject: [PATCH] Add calibration cache The calibration data is stored in the directory $HOME/.sane --- ChangeLog | 4 + backend/genesys.c | 447 ++++++++++++++++++++++++++++++++++++---- backend/genesys_gl646.c | 4 + backend/genesys_gl841.c | 297 ++++++++++++++++++++++++++ backend/genesys_low.h | 27 +++ 5 files changed, 742 insertions(+), 37 deletions(-) diff --git a/ChangeLog b/ChangeLog index d10e15ebf..72b2e4de3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2009-03-25 Pierre Willenbrock + * backend/genesys.c, backend/genesys_gl646.c, + backend/genesys_gl841.c, backend/genesys_low.h: Add calibration cache + 2009-03-24 m. allan noah * backend/canon_dr.[ch], backend/canon_dr-cmd.h: backend v21 - correct rgb padding macro diff --git a/backend/genesys.c b/backend/genesys.c index 38db645f5..ff839a206 100644 --- a/backend/genesys.c +++ b/backend/genesys.c @@ -2247,7 +2247,9 @@ genesys_dark_shading_calibration (Genesys_Device * dev) FREE_IFNOT_NULL (dev->dark_average_data); - dev->dark_average_data = malloc (channels * 2 * pixels_per_line); + dev->average_size = channels * 2 * pixels_per_line; + + dev->dark_average_data = malloc (dev->average_size); if (!dev->dark_average_data) { DBG (DBG_error, @@ -2378,7 +2380,8 @@ genesys_dummy_dark_shading (Genesys_Device * dev) FREE_IFNOT_NULL (dev->dark_average_data); - dev->dark_average_data = malloc (channels * 2 * pixels_per_line); + dev->average_size = channels * 2 * pixels_per_line; + dev->dark_average_data = malloc (dev->average_size); if (!dev->dark_average_data) { DBG (DBG_error, @@ -2605,7 +2608,9 @@ genesys_dark_white_shading_calibration (Genesys_Device * dev) if (dev->white_average_data) free (dev->white_average_data); - dev->white_average_data = malloc (channels * 2 * pixels_per_line); + dev->average_size = channels * 2 * pixels_per_line; + + dev->white_average_data = malloc (dev->average_size); if (!dev->white_average_data) { DBG (DBG_error, @@ -2783,9 +2788,7 @@ genesys_send_shading_coefficient (Genesys_Device * dev) DBG (DBG_proc, "genesys_send_shading_coefficient\n"); - pixels_per_line = - (genesys_pixels_per_line (dev->calib_reg) - * genesys_dpiset (dev->calib_reg)) / dev->sensor.optical_res; + pixels_per_line = dev->calib_pixels; if (dev->settings.scan_mode == SCAN_MODE_COLOR) /* single pass color */ channels = 3; @@ -3281,6 +3284,158 @@ genesys_send_shading_coefficient (Genesys_Device * dev) return SANE_STATUS_GOOD; } +static SANE_Status +genesys_restore_calibration (Genesys_Device * dev) +{ + SANE_Status status; + Genesys_Calibration_Cache *cache; + + DBG (DBG_proc, "genesys_restore_calibration\n"); + + if (!dev->model->cmd_set->is_compatible_calibration) + return SANE_STATUS_UNSUPPORTED; + + for(cache = dev->calibration_cache; cache; cache = cache->next) + { + status = dev->model->cmd_set->is_compatible_calibration(dev, cache, + SANE_FALSE); + if (status == SANE_STATUS_UNSUPPORTED) + { + continue; + } + else if (status != SANE_STATUS_GOOD) + { + DBG (DBG_error, + "genesys_restore_calibration: fail while checking compatibility: %s\n", + sane_strstatus (status)); + return status; + } + + memcpy(&dev->frontend, &cache->frontend, sizeof(dev->frontend)); + /* don't restore the gamma fields */ + memcpy(&dev->sensor, &cache->sensor, + offsetof(Genesys_Sensor,red_gamma)); + + free(dev->dark_average_data); + free(dev->white_average_data); + + dev->average_size = cache->average_size; + dev->calib_pixels = cache->calib_pixels; + + dev->dark_average_data = (uint8_t*)malloc(cache->average_size); + dev->white_average_data = (uint8_t*)malloc(cache->average_size); + + if (!dev->dark_average_data || !dev->white_average_data) + return SANE_STATUS_NO_MEM; + + memcpy(dev->dark_average_data, + cache->dark_average_data, dev->average_size); + memcpy(dev->white_average_data, + cache->white_average_data, dev->average_size); + + status = genesys_send_shading_coefficient (dev); + + if (status != SANE_STATUS_GOOD) + { + DBG (DBG_error, + "genesys_send_shading_coefficient: failed to send shading data: %s\n", + sane_strstatus (status)); + return status; + } + + DBG (DBG_proc, "genesys_restore_calibration: restored\n"); + return SANE_STATUS_GOOD; + } + DBG (DBG_proc, "genesys_restore_calibration: completed(nothing found)\n"); + return SANE_STATUS_UNSUPPORTED; +} + +static SANE_Status +genesys_save_calibration (Genesys_Device * dev) +{ + SANE_Status status; + Genesys_Calibration_Cache *cache; + uint8_t *tmp; + + DBG (DBG_proc, "genesys_save_calibration\n"); + + if (!dev->model->cmd_set->is_compatible_calibration) + return SANE_STATUS_UNSUPPORTED; + + for(cache = dev->calibration_cache; cache; cache = cache->next) + { + status = dev->model->cmd_set->is_compatible_calibration(dev, cache, + SANE_TRUE); + if (status == SANE_STATUS_UNSUPPORTED) + { + continue; + } + else if (status != SANE_STATUS_GOOD) + { + DBG (DBG_error, + "genesys_save_calibration: fail while checking compatibility: %s\n", + sane_strstatus (status)); + return status; + } + break; + } + + if(cache) + { + if(dev->average_size > cache->average_size) + { + cache->average_size = dev->average_size; + + tmp = (uint8_t*)realloc(cache->dark_average_data, + cache->average_size); + if (!tmp) + return SANE_STATUS_NO_MEM; + cache->dark_average_data = tmp; + + tmp = (uint8_t*)realloc(cache->white_average_data, + cache->average_size); + if (!tmp) + return SANE_STATUS_NO_MEM; + cache->white_average_data = tmp; + } + } + else + { + cache = malloc(sizeof(*cache)); + if (!cache) + return SANE_STATUS_NO_MEM; + + memset(cache, 0, sizeof(*cache)); + + cache->next = dev->calibration_cache; + dev->calibration_cache = cache; + + cache->average_size = dev->average_size; + + cache->dark_average_data = (uint8_t*)malloc(cache->average_size); + if (!cache->dark_average_data) + return SANE_STATUS_NO_MEM; + cache->white_average_data = (uint8_t*)malloc(cache->average_size); + if (!cache->white_average_data) + return SANE_STATUS_NO_MEM; + + memcpy(&cache->used_setup, &dev->current_setup, + sizeof(cache->used_setup)); + } + + memcpy(&cache->frontend, &dev->frontend, sizeof(cache->frontend)); + memcpy(&cache->sensor, &dev->sensor, sizeof(cache->sensor)); + + cache->calib_pixels = dev->calib_pixels; + memcpy(cache->dark_average_data, dev->dark_average_data, + cache->average_size); + memcpy(cache->white_average_data, dev->white_average_data, + cache->average_size); + + DBG (DBG_proc, "genesys_save_calibration: completed\n"); + return SANE_STATUS_GOOD; +} + static SANE_Status genesys_flatbed_calibration (Genesys_Device * dev) { @@ -3445,6 +3600,10 @@ genesys_flatbed_calibration (Genesys_Device * dev) return status; } + dev->calib_pixels = + (genesys_pixels_per_line (dev->calib_reg) + * genesys_dpiset (dev->calib_reg)) / dev->sensor.optical_res; + if (dev->model->flags & GENESYS_FLAG_DARK_WHITE_CALIBRATION) { status = genesys_dark_white_shading_calibration (dev); @@ -3769,44 +3928,57 @@ genesys_start_scan (Genesys_Device * dev) } } - /* calibration : sheetfed scanners can't calibrate before each scan */ - /* so we use a NO_CALIBRATION flags for those scanners */ - if (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION) + status = genesys_restore_calibration (dev); + if(status == SANE_STATUS_UNSUPPORTED) { - /* TODO send predefined calibration values from default - * values or built from a calibration scan */ - /* send custom or generic gamma tables depending on flag */ - if (dev->model->flags & GENESYS_FLAG_CUSTOM_GAMMA) + /* calibration : sheetfed scanners can't calibrate before each scan */ + /* so we use a NO_CALIBRATION flags for those scanners */ + if (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION) { - /* use custom gamma table */ - status = dev->model->cmd_set->send_gamma_table (dev, 0); + /* TODO send predefined calibration values from default + * values or built from a calibration scan */ + /* send custom or generic gamma tables depending on flag */ + if (dev->model->flags & GENESYS_FLAG_CUSTOM_GAMMA) + { + /* use custom gamma table */ + status = dev->model->cmd_set->send_gamma_table (dev, 0); + } + else + { + /* send default gamma table if no custom gamma */ + status = dev->model->cmd_set->send_gamma_table (dev, 1); + } + if (status != SANE_STATUS_GOOD) + { + DBG (DBG_error, + "genesys_start_scan: failed to init gamma table: %s\n", + sane_strstatus (status)); + return status; + } + + /* head hasn't moved */ + dev->scanhead_position_in_steps = 0; } else { - /* send default gamma table if no custom gamma */ - status = dev->model->cmd_set->send_gamma_table (dev, 1); - } - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_start_scan: failed to init gamma table: %s\n", - sane_strstatus (status)); - return status; - } - - /* head hasn't moved */ - dev->scanhead_position_in_steps = 0; + status = genesys_flatbed_calibration (dev); + if (status != SANE_STATUS_GOOD) + { + DBG (DBG_error, + "genesys_start_scan: failed to do flatbed calibration: %s\n", + sane_strstatus (status)); + return status; + } + + genesys_save_calibration(dev); + } } - else + else if(status != SANE_STATUS_GOOD) { - status = genesys_flatbed_calibration (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_start_scan: failed to do flatbed calibration: %s\n", - sane_strstatus (status)); - return status; - } + DBG (DBG_error, + "genesys_start_scan: failed to restore calibration: %s\n", + sane_strstatus (status)); + return status; } status = dev->model->cmd_set->init_regs_for_scan (dev); @@ -5312,6 +5484,167 @@ probe_genesys_devices (void) return status; } +/* this should be changed if one of the substructures of + Genesys_Calibration_Cache change, but it must be changed if there are + changes that don't change size -- at least for now, as we store most + of Genesys_Calibration_Cache as is. +*/ +#define CALIBRATION_VERSION 1 + +static void +read_calibration(Genesys_Device * dev) +{ + FILE *fp; + uint8_t vers = 0; + uint32_t size = 0; + struct Genesys_Calibration_Cache *cache; + + DBG (DBG_proc, "read_calibration: enter\n"); + fp = fopen(dev->calib_file,"rb"); + if (!fp) + { + DBG ( DBG_info, "Calibration: Cannot open %s\n", dev->calib_file ); + DBG (DBG_proc, "read_calibration: exit\n"); + return; + } + + /* these two checks ensure that most bad things cannot happen */ + fread(&vers,1,1,fp); + if (vers != CALIBRATION_VERSION) + { + DBG ( DBG_info, "Calibration: Bad version\n" ); + fclose(fp); + DBG (DBG_proc, "read_calibration: exit\n"); + return; + } + fread(&size,4,1,fp); + if (size != sizeof(struct Genesys_Calibration_Cache)) + { + DBG ( DBG_info, "Calibration: Size of calibration cache struct differs\n" ); + fclose(fp); + DBG (DBG_proc, "read_calibration: exit\n"); + return; + } + + while(!feof(fp)) + { + DBG (DBG_info, "read_calibration: reading one record\n"); + cache = (struct Genesys_Calibration_Cache *)malloc(sizeof(*cache)); + + if (!cache) + { + DBG (DBG_error, "read_calibration: could not allocate cache struct\n"); + break; + } + +#define BILT1( x ) \ + do \ + { \ + if ((x) < 1) \ + { \ + free(cache); \ + DBG (DBG_warn, "read_calibration: partial calibration record\n"); \ + break; \ + } \ + } while(0) + + + if (fread(&cache->used_setup,sizeof(cache->used_setup),1,fp) < 1) + { /* eof is only detected here */ + free(cache); + break; + } + BILT1(fread(&cache->last_calibration,sizeof(cache->last_calibration),1,fp)); + BILT1(fread(&cache->frontend,sizeof(cache->frontend),1,fp)); + /* the gamma (and later) fields are not stored */ + BILT1(fread(&cache->sensor,offsetof(Genesys_Sensor,red_gamma),1,fp)); + BILT1(fread(&cache->calib_pixels,sizeof(cache->calib_pixels),1,fp)); + BILT1(fread(&cache->average_size,sizeof(cache->average_size),1,fp)); + + /* Make sure we don't do bad things if someone feeds us a forged/ + sufficiently corrupted calibration file. + gl843 can do up to 0x5800 pixels. add some slack for the + dummy/blank pixel mess */ + if (cache->average_size > 0xb000+0x100) + { + DBG (DBG_error, "read_calibration: bad size of calibration data\n"); + free(cache); + break; + } + + cache->white_average_data = (uint8_t*)malloc(cache->average_size); + cache->dark_average_data = (uint8_t*)malloc(cache->average_size); + + if (!cache->white_average_data || !cache->dark_average_data) + { + FREE_IFNOT_NULL(cache->white_average_data); + FREE_IFNOT_NULL(cache->dark_average_data); + free(cache); + DBG (DBG_error, "read_calibration: could not allocate space for average data\n"); + break; + } + + if (fread(cache->white_average_data,cache->average_size,1,fp) < 1) + { + DBG (DBG_warn, "read_calibration: partial calibration record\n"); + free(cache->white_average_data); + free(cache->dark_average_data); + free(cache); + break; + } + if (fread(cache->dark_average_data,cache->average_size,1,fp) < 1) + { + DBG (DBG_warn, "read_calibration: partial calibration record\n"); + free(cache->white_average_data); + free(cache->dark_average_data); + free(cache); + break; + } +#undef BILT1 + DBG (DBG_info, "read_calibration: adding record to list\n"); + cache->next = dev->calibration_cache; + dev->calibration_cache = cache; + } + + fclose(fp); + DBG (DBG_proc, "read_calibration: exit\n"); +} + +static void +write_calibration(Genesys_Device * dev) +{ + FILE *fp; + uint8_t vers = 0; + uint32_t size = 0; + struct Genesys_Calibration_Cache *cache; + fp = fopen(dev->calib_file,"wb"); + if (!fp) + { + DBG ( DBG_info, "Calibration: Cannot open %s\n", dev->calib_file ); + return; + } + + vers = CALIBRATION_VERSION; + fwrite(&vers,1,1,fp); + size = sizeof(struct Genesys_Calibration_Cache); + fwrite(&size,4,1,fp); + + for(cache = dev->calibration_cache; cache; cache = cache->next) + { + fwrite(&cache->used_setup,sizeof(cache->used_setup),1,fp); + fwrite(&cache->last_calibration,sizeof(cache->last_calibration),1,fp); + fwrite(&cache->frontend,sizeof(cache->frontend),1,fp); + /* the gamma (and later) fields are not stored */ + fwrite(&cache->sensor,offsetof(Genesys_Sensor,red_gamma),1,fp); + fwrite(&cache->calib_pixels,sizeof(cache->calib_pixels),1,fp); + fwrite(&cache->average_size,sizeof(cache->average_size),1,fp); + fwrite(cache->white_average_data,cache->average_size,1,fp); + fwrite(cache->dark_average_data,cache->average_size,1,fp); + } + + fclose(fp); +} + /* -------------------------- SANE API functions ------------------------- */ SANE_Status @@ -5424,6 +5757,8 @@ sane_open (SANE_String_Const devicename, SANE_Handle * handle) Genesys_Device *dev; SANE_Status status; Genesys_Scanner *s; + char tmp_str[PATH_MAX]; + char *ptr; DBG (DBG_proc, "sane_open: start (devicename = `%s')\n", devicename); @@ -5499,6 +5834,7 @@ sane_open (SANE_String_Const devicename, SANE_Handle * handle) s->dev->read_active = SANE_FALSE; s->dev->white_average_data = NULL; s->dev->dark_average_data = NULL; + s->dev->calibration_cache = NULL; /* insert newly opened handle into list of open handles: */ s->next = first_handle; @@ -5518,6 +5854,31 @@ sane_open (SANE_String_Const devicename, SANE_Handle * handle) RIE (dev->model->cmd_set->init (dev)); + /* here is the place to fetch a stored calibration cache */ + + /* create calibration-filename + lifted from plustek-usb.c + */ + /* we should add a unique identifying feature to the file name + to support multiple scanners of the same model, but to my + knowledge, there is no such thing in these scanners. + (At least the usb serial is always "0".) + */ + + ptr = getenv ("HOME"); + if( NULL == ptr ) { + sprintf( tmp_str, "/tmp/%s.cal", s->dev->model->name ); + } else { + sprintf( tmp_str, "%s/.sane/%s.cal", ptr, s->dev->model->name ); + } + s->dev->calib_file = strdup( tmp_str ); + DBG( DBG_info, "Calibration filename set to:\n" ); + DBG( DBG_info, ">%s<\n", s->dev->calib_file ); + + /* now open file, fetch calibration records */ + + read_calibration(s->dev); + DBG (DBG_proc, "sane_open: exit\n"); return SANE_STATUS_GOOD; } @@ -5526,6 +5887,7 @@ void sane_close (SANE_Handle handle) { Genesys_Scanner *prev, *s; + Genesys_Calibration_Cache *cache, *next_cache; DBG (DBG_proc, "sane_close: start\n"); @@ -5543,6 +5905,17 @@ sane_close (SANE_Handle handle) return; /* oops, not a handle we know about */ } + /* here is the place to store calibration cache */ + write_calibration(s->dev); + + for(cache = s->dev->calibration_cache; cache; cache = next_cache) + { + next_cache = cache->next; + free(cache->dark_average_data); + free(cache->white_average_data); + free(cache); + } + sanei_genesys_buffer_free (&(s->dev->read_buffer)); sanei_genesys_buffer_free (&(s->dev->lines_buffer)); sanei_genesys_buffer_free (&(s->dev->shrink_buffer)); diff --git a/backend/genesys_gl646.c b/backend/genesys_gl646.c index 277996112..b9f761dc6 100644 --- a/backend/genesys_gl646.c +++ b/backend/genesys_gl646.c @@ -4821,10 +4821,14 @@ static Genesys_Command_Set gl646_cmd_set = { gl646_bulk_write_register, gl646_bulk_write_data, gl646_bulk_read_data, + gl646_update_hardware_sensors, + gl646_load_document, gl646_detect_document_end, gl646_eject_document, + + NULL,/*is_compatible_calibration*/ }; SANE_Status diff --git a/backend/genesys_gl841.c b/backend/genesys_gl841.c index 88c35c2a9..e9f6f3943 100644 --- a/backend/genesys_gl841.c +++ b/backend/genesys_gl841.c @@ -3077,6 +3077,273 @@ dummy \ scanned lines return SANE_STATUS_GOOD; } +static SANE_Status +gl841_calculate_current_setup (Genesys_Device * dev) +{ + int channels; + int depth; + int start; + + float xres;/*dpi*/ + float yres;/*dpi*/ + float startx;/*optical_res, from dummy_pixel+1*/ + float pixels; + float lines; + int color_filter; + + int used_res; + int used_pixels; + unsigned int lincnt; + int exposure_time, exposure_time2, led_exposure; + int i; + int stagger; + + int slope_dpi = 0; + int dummy = 0; + int scan_step_type = 1; + int scan_power_mode = 0; + int max_shift; + + SANE_Bool half_ccd; /* false: full CCD res is used, true, half max CCD res is used */ + int optical_res; + + DBG (DBG_info, + "gl841_calculate_current_setup settings:\n" + "Resolution: %uDPI\n" + "Lines : %u\n" + "PPL : %u\n" + "Startpos : %.3f/%.3f\n" + "Scan mode : %d\n\n", + dev->settings.yres, dev->settings.lines, dev->settings.pixels, + dev->settings.tl_x, dev->settings.tl_y, dev->settings.scan_mode); + +/* channels */ + if (dev->settings.scan_mode == 4) /* single pass color */ + channels = 3; + else + channels = 1; + +/* depth */ + depth = dev->settings.depth; + if (dev->settings.scan_mode == 0) + depth = 1; + +/* start */ + start = SANE_UNFIX (dev->model->x_offset); + + start += dev->settings.tl_x; + + start = (start * dev->sensor.optical_res) / MM_PER_INCH; + + + xres = dev->settings.xres;/*dpi*/ + yres = dev->settings.yres;/*dpi*/ + startx = start;/*optical_res, from dummy_pixel+1*/ + pixels = dev->settings.pixels; + lines = dev->settings.lines; + color_filter = dev->settings.color_filter; + + + DBG (DBG_info, + "gl841_calculate_current_setup settings:\n" + "Resolution : %gDPI/%gDPI\n" + "Lines : %g\n" + "PPL : %g\n" + "Startpos : %g\n" + "Depth/Channels: %u/%u\n\n", + xres, yres, lines, pixels, + startx, + depth, channels); + +/* half_ccd */ + /* we have 2 domains for ccd: xres below or above half ccd max dpi */ + if (dev->sensor.optical_res < 2 * xres) { + half_ccd = SANE_FALSE; + } else { + half_ccd = SANE_TRUE; + } + +/* optical_res */ + + optical_res = dev->sensor.optical_res; + if (half_ccd) + optical_res /= 2; + +/* stagger */ + + if ((!half_ccd) && (dev->model->flags & GENESYS_FLAG_STAGGERED_LINE)) + stagger = (4 * yres) / dev->motor.base_ydpi; + else + stagger = 0; + DBG (DBG_info, "gl841_calculate_current_setup: stagger=%d lines\n", + stagger); + +/* used_res */ + i = optical_res / xres; + +/* gl841 supports 1/1 1/2 1/3 1/4 1/5 1/6 1/8 1/10 1/12 1/15 averaging */ + + if (i < 2) /* optical_res >= xres > optical_res/2 */ + used_res = optical_res; + else if (i < 3) /* optical_res/2 >= xres > optical_res/3 */ + used_res = optical_res/2; + else if (i < 4) /* optical_res/3 >= xres > optical_res/4 */ + used_res = optical_res/3; + else if (i < 5) /* optical_res/4 >= xres > optical_res/5 */ + used_res = optical_res/4; + else if (i < 6) /* optical_res/5 >= xres > optical_res/6 */ + used_res = optical_res/5; + else if (i < 8) /* optical_res/6 >= xres > optical_res/8 */ + used_res = optical_res/6; + else if (i < 10) /* optical_res/8 >= xres > optical_res/10 */ + used_res = optical_res/8; + else if (i < 12) /* optical_res/10 >= xres > optical_res/12 */ + used_res = optical_res/10; + else if (i < 15) /* optical_res/12 >= xres > optical_res/15 */ + used_res = optical_res/12; + else + used_res = optical_res/15; + + /* compute scan parameters values */ + /* pixels are allways given at half or full CCD optical resolution */ + /* use detected left margin and fixed value */ +/* start */ + /* add x coordinates */ + start = + ((dev->sensor.CCD_start_xoffset + startx) * used_res) / + dev->sensor.optical_res; + +/* needs to be aligned for used_res */ + start = (start * optical_res) / used_res; + + start += dev->sensor.dummy_pixel + 1; + + if (stagger > 0) + start |= 1; + + /* compute correct pixels number */ +/* pixels */ + used_pixels = + (pixels * optical_res) / xres; + + /* round up pixels number if needed */ + if (used_pixels * xres < pixels * optical_res) + used_pixels++; + +/* dummy */ + /* dummy lines: may not be usefull, for instance 250 dpi works with 0 or 1 + dummy line. Maybe the dummy line adds correctness since the motor runs + slower (higher dpi) + */ +/* for cis this creates better aligned color lines: +dummy \ scanned lines + 0: R G B R ... + 1: R G B - R ... + 2: R G B - - R ... + 3: R G B - - - R ... + 4: R G B - - - - R ... + 5: R G B - - - - - R ... + 6: R G B - - - - - - R ... + 7: R G B - - - - - - - R ... + 8: R G B - - - - - - - - R ... + 9: R G B - - - - - - - - - R ... + 10: R G B - - - - - - - - - - R ... + 11: R G B - - - - - - - - - - - R ... + 12: R G B - - - - - - - - - - - - R ... + 13: R G B - - - - - - - - - - - - - R ... + 14: R G B - - - - - - - - - - - - - - R ... + 15: R G B - - - - - - - - - - - - - - - R ... + -- pierre + */ + dummy = 0; + +/* slope_dpi */ +/* cis color scan is effectively a gray scan with 3 gray lines per color + line and a FILTER of 0 */ + if (dev->model->is_cis) + slope_dpi = yres*channels; + else + slope_dpi = yres; + + slope_dpi = slope_dpi * (1 + dummy); + +/* scan_step_type */ +/* Try to do at least 4 steps per line. if that is impossible we will have to + live with that + */ + if (yres*4 < dev->motor.base_ydpi + || dev->motor.max_step_type <= 0) + scan_step_type = 0; + else if (yres*4 < dev->motor.base_ydpi*2 + || dev->motor.max_step_type <= 1) + scan_step_type = 1; + else + scan_step_type = 2; + + led_exposure = gl841_get_led_exposure(dev); + +/* exposure_time */ + exposure_time = sanei_genesys_exposure_time2( + dev, + slope_dpi, + scan_step_type, + start+used_pixels,/*+tgtime? currently done in sanei_genesys_exposure_time2 with tgtime = 32 pixel*/ + led_exposure, + scan_power_mode); + + while(scan_power_mode + 1 < dev->motor.power_mode_count) { + exposure_time2 = sanei_genesys_exposure_time2( + dev, + slope_dpi, + scan_step_type, + start+used_pixels,/*+tgtime? currently done in sanei_genesys_exposure_time2 with tgtime = 32 pixel*/ + led_exposure, + scan_power_mode + 1); + if (exposure_time < exposure_time2) + break; + exposure_time = exposure_time2; + scan_power_mode++; + } + + DBG (DBG_info, "gl841_calculate_current_setup : exposure_time=%d pixels\n", + exposure_time); + +/* max_shift */ + /* scanned area must be enlarged by max color shift needed */ + /* all values are assumed >= 0 */ + if (channels > 1) + { + max_shift = dev->model->ld_shift_r; + if (dev->model->ld_shift_b > max_shift) + max_shift = dev->model->ld_shift_b; + if (dev->model->ld_shift_g > max_shift) + max_shift = dev->model->ld_shift_g; + max_shift = + (max_shift * yres) / dev->motor.base_ydpi; + } + else + { + max_shift = 0; + } + +/* lincnt */ + lincnt = lines + max_shift + stagger; + + dev->current_setup.pixels = (used_pixels * used_res)/optical_res; + dev->current_setup.lines = lincnt; + dev->current_setup.depth = depth; + dev->current_setup.channels = channels; + dev->current_setup.exposure_time = exposure_time; + dev->current_setup.xres = used_res; + dev->current_setup.yres = yres; + dev->current_setup.half_ccd = half_ccd; + dev->current_setup.stagger = stagger; + dev->current_setup.max_shift = max_shift + stagger; + + DBG (DBG_proc, "gl841_calculate_current_setup: completed\n"); + return SANE_STATUS_GOOD; +} + static void gl841_set_motor_power (Genesys_Register_Set * regs, SANE_Bool set) { @@ -5290,7 +5557,36 @@ sanei_gl841_repark_head (Genesys_Device * dev) return status; } +static SANE_Status +gl841_is_compatible_calibration (Genesys_Device * dev, + Genesys_Calibration_Cache *cache, + int for_overwrite) +{ + SANE_Status status; + DBG (DBG_proc, "gl841_is_compatible_calibration\n"); + + status = gl841_calculate_current_setup (dev); + + if (status != SANE_STATUS_GOOD) + { + DBG (DBG_error, + "gl841_is_compatible_calibration: failed to calculate current setup: %s\n", + sane_strstatus (status)); + return status; + } + + DBG (DBG_proc, "gl841_is_compatible_calibration: checking\n"); + + if (dev->current_setup.channels != cache->used_setup.channels) + return SANE_STATUS_UNSUPPORTED; + if (dev->current_setup.half_ccd != cache->used_setup.half_ccd) + return SANE_STATUS_UNSUPPORTED; + + DBG (DBG_proc, "gl841_is_compatible_calibration: completed\n"); + + return SANE_STATUS_GOOD; +} /* * initialize ASIC : registers, motor tables, and gamma tables @@ -5560,6 +5856,7 @@ static Genesys_Command_Set gl841_cmd_set = { gl841_detect_document_end, gl841_eject_document, + gl841_is_compatible_calibration, }; SANE_Status diff --git a/backend/genesys_low.h b/backend/genesys_low.h index 809b65d20..4fbe32696 100644 --- a/backend/genesys_low.h +++ b/backend/genesys_low.h @@ -294,6 +294,7 @@ Genesys_Color_Order; /* Forward typedefs */ typedef struct Genesys_Device Genesys_Device; struct Genesys_Scanner; +typedef struct Genesys_Calibration_Cache Genesys_Calibration_Cache; /** * Scanner command set description. @@ -392,6 +393,11 @@ typedef struct Genesys_Command_Set * eject document from scanner */ SANE_Status (*eject_document) (Genesys_Device * dev); + + SANE_Status (*is_compatible_calibration) ( + Genesys_Device * dev, + Genesys_Calibration_Cache *cache, + SANE_Bool for_overwrite); } Genesys_Command_Set; typedef struct Genesys_Model @@ -508,10 +514,27 @@ typedef struct Genesys_Buffer size_t avail; /* data bytes currently in buffer */ } Genesys_Buffer; +struct Genesys_Calibration_Cache +{ + Genesys_Current_Setup used_setup;/* used to check if entry is compatible */ + time_t last_calibration; + + Genesys_Frontend frontend; + Genesys_Sensor sensor; + + size_t calib_pixels; + size_t average_size; + uint8_t *white_average_data; + uint8_t *dark_average_data; + + struct Genesys_Calibration_Cache *next; +}; + struct Genesys_Device { SANE_Int dn; SANE_String file_name; + SANE_String calib_file; Genesys_Model *model; Genesys_Register_Set reg[GENESYS_MAX_REGS]; @@ -526,6 +549,8 @@ struct Genesys_Device uint8_t control[6]; time_t init_date; + size_t average_size; + size_t calib_pixels; uint8_t *white_average_data; uint8_t *dark_average_data; uint16_t dark[3]; @@ -551,6 +576,8 @@ struct Genesys_Device Genesys_Current_Setup current_setup; /* contains the real used values */ + Genesys_Calibration_Cache *calibration_cache; + struct Genesys_Device *next; };