From 58912f68c2489bcee787599837447e0d64dfd61a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Wed, 24 May 2017 21:03:56 +0200 Subject: New upstream version 1.0.27 --- backend/canon_dr.c | 1655 +++++++++++++--------------------------------------- 1 file changed, 397 insertions(+), 1258 deletions(-) (limited to 'backend/canon_dr.c') diff --git a/backend/canon_dr.c b/backend/canon_dr.c index 381cfd6..3c058ab 100644 --- a/backend/canon_dr.c +++ b/backend/canon_dr.c @@ -3,7 +3,7 @@ This file is part of the SANE package, and implements a SANE backend for various Canon DR-series scanners. - Copyright (C) 2008-2010 m. allan noah + Copyright (C) 2008-2016 m. allan noah Yabarana Corp. www.yabarana.com provided significant funding EvriChart, Inc. www.evrichart.com provided funding and loaned equipment @@ -314,8 +314,27 @@ v50 2015-08-23, MAN - DR-C125 adds duplex padding on back side - initial support for DR-C225 - v51 2015-08-25, MAN + v51 2015-08-25, MAN (SANE 1.0.25) - DR-C125 does not invert_tly, does need sw_lut + v52 2015-11-03, MAN + - set can_color=1 by default (recent models dont have 'C' in name) + - enable jpeg for DR-6080 + - add must_downsample and must_fully_buffer + - improve dropout option handling + - add software dropout implementation for downsampled modes + v53 2015-11-06, MAN + - replace image processing methods with sanei_magic + - add swskip option + - reorder geometry group options + - use bg_color to fill missing image data + v54 2015-11-21, MAN + - br_x and br_y locked to page_width/height until changed + v55 2016-03-19, MAN + - fixed-width scanners were calculating left-side offset incorrectly in color + - initial support for DR-F120 + - rename all DUPLEX_INTERLACE_* to indicate start and end of line + v56 2016-08-23, MAN + - initial support for P-150 SANE FLOW DIAGRAM @@ -360,12 +379,13 @@ #include "../include/sane/sanei_usb.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_config.h" +#include "../include/sane/sanei_magic.h" #include "canon_dr-cmd.h" #include "canon_dr.h" #define DEBUG 1 -#define BUILD 51 +#define BUILD 56 /* values for SANE_DEBUG_CANON_DR env var: - errors 5 @@ -618,7 +638,7 @@ sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) global_extra_status = buf; } - /* DUPLEXOFFSET: < 1200 */ + /* DUPLEXOFFSET: < 2400 */ else if (!strncmp (lp, "duplex-offset", 13) && isspace (lp[13])) { int buf; @@ -626,9 +646,9 @@ sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) lp = sanei_config_skip_whitespace (lp); buf = atoi (lp); - if (buf > 1200) { + if (buf > 2400) { DBG (5, "sane_get_devices: config option \"duplex-offset\" " - "(%d) is > 1200, ignoring!\n", buf); + "(%d) is > 2400, ignoring!\n", buf); continue; } @@ -1262,18 +1282,21 @@ init_model (struct scanner *s) s->max_x_fb = s->max_x; s->max_y_fb = s->max_y; - /* generic settings missing from vpd */ - if (strstr (s->model_name,"C")){ - s->can_color = 1; - } + /* missing from vpd- we will unset this for b&w machines below */ + s->can_color = 1; /* specific settings missing from vpd */ - if (strstr (s->model_name,"DR-9080") - || strstr (s->model_name,"DR-7580")){ + if (strstr (s->model_name,"DR-9080")){ s->has_comp_JPEG = 1; s->rgb_format = 2; } + else if (strstr (s->model_name,"DR-6080") + || strstr (s->model_name,"DR-7580")){ + s->has_comp_JPEG = 1; + s->can_color = 0; + } + else if (strstr (s->model_name,"DR-7090")){ s->has_flatbed = 1; } @@ -1318,7 +1341,7 @@ init_model (struct scanner *s) s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_RRGGBB; s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_rRgGbB; s->gray_interlace[SIDE_BACK] = GRAY_INTERLACE_gG; - s->duplex_interlace = DUPLEX_INTERLACE_FBFB; + s->duplex_interlace = DUPLEX_INTERLACE_FBfb; s->need_ccal = 1; s->need_fcal = 1; /*s->duplex_offset = 432; now set in config file*/ @@ -1398,7 +1421,7 @@ init_model (struct scanner *s) s->even_Bpl = 1; s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_RRGGBB; s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_RRGGBB; - s->duplex_interlace = DUPLEX_INTERLACE_FBFB; + s->duplex_interlace = DUPLEX_INTERLACE_FBfb; s->need_fcal_buffer = 1; s->bg_color = 0x08; /*s->duplex_offset = 840; now set in config file*/ @@ -1422,6 +1445,7 @@ init_model (struct scanner *s) s->ppl_mod = 32; s->reverse_by_mode[MODE_LINEART] = 0; s->reverse_by_mode[MODE_HALFTONE] = 0; + s->can_color = 0; } else if (strstr (s->model_name,"DR-5020")){ @@ -1432,16 +1456,33 @@ init_model (struct scanner *s) s->ppl_mod = 32; s->reverse_by_mode[MODE_LINEART] = 0; s->reverse_by_mode[MODE_HALFTONE] = 0; + s->can_color = 0; + } + + /* all copied from P-215 */ + else if (strstr (s->model_name, "P-150")) { + s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_rRgGbB; + s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_RRGGBB; + s->gray_interlace[SIDE_FRONT] = GRAY_INTERLACE_gG; + s->duplex_interlace = DUPLEX_INTERLACE_FBfb; + s->need_ccal = 1; + s->invert_tly = 1; + s->unknown_byte2 = 0x88; + s->rgb_format = 1; + s->has_ssm_pay_head_len = 1; + s->ppl_mod = 8; + s->ccal_version = 3; + s->can_read_sensors = 1; + s->has_card = 1; } else if (strstr (s->model_name, "P-208")) { s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_RRGGBB; s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_rRgGbB; s->gray_interlace[SIDE_BACK] = GRAY_INTERLACE_gG; - s->duplex_interlace = DUPLEX_INTERLACE_FBFB; + s->duplex_interlace = DUPLEX_INTERLACE_FBfb; s->need_ccal = 1; s->invert_tly = 1; - s->can_color = 1; s->unknown_byte2 = 0x88; s->rgb_format = 1; s->has_ssm_pay_head_len = 1; @@ -1454,10 +1495,9 @@ init_model (struct scanner *s) s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_rRgGbB; s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_RRGGBB; s->gray_interlace[SIDE_FRONT] = GRAY_INTERLACE_gG; - s->duplex_interlace = DUPLEX_INTERLACE_FBFB; + s->duplex_interlace = DUPLEX_INTERLACE_FBfb; s->need_ccal = 1; s->invert_tly = 1; - s->can_color = 1; s->unknown_byte2 = 0x88; s->rgb_format = 1; s->has_ssm_pay_head_len = 1; @@ -1485,7 +1525,6 @@ init_model (struct scanner *s) s->has_comp_JPEG = 1; s->rgb_format = 1; - s->can_color = 1; s->has_df_ultra = 1; s->color_inter_by_res[DPI_100] = COLOR_INTERLACE_GBR; @@ -1500,7 +1539,7 @@ init_model (struct scanner *s) s->can_write_panel = 0; s->has_ssm = 0; s->has_ssm2 = 1; - s->duplex_interlace = DUPLEX_INTERLACE_FFBB; + s->duplex_interlace = DUPLEX_INTERLACE_FfBb; s->duplex_offset_side = SIDE_FRONT; /*lies*/ @@ -1526,7 +1565,6 @@ init_model (struct scanner *s) s->has_comp_JPEG = 1; s->rgb_format = 1; - s->can_color = 1; s->has_df_ultra = 1; s->color_inter_by_res[DPI_100] = COLOR_INTERLACE_GBR; @@ -1541,7 +1579,7 @@ init_model (struct scanner *s) s->can_write_panel = 0; s->has_ssm = 0; s->has_ssm2 = 1; - s->duplex_interlace = DUPLEX_INTERLACE_FFBB; + s->duplex_interlace = DUPLEX_INTERLACE_FfBb; s->duplex_offset_side = SIDE_BACK; /*lies*/ @@ -1563,7 +1601,6 @@ init_model (struct scanner *s) s->ccal_version = 3; s->need_fcal = 1; s->sw_lut = 1; - s->can_color = 1; s->rgb_format = 1; /*s->duplex_offset = 400; now set in config file*/ @@ -1585,14 +1622,13 @@ init_model (struct scanner *s) s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_RRGGBB; s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_rRgGbB; s->gray_interlace[SIDE_BACK] = GRAY_INTERLACE_gG; - s->duplex_interlace = DUPLEX_INTERLACE_FBFB; + s->duplex_interlace = DUPLEX_INTERLACE_FBfb; s->unknown_byte2 = 0x88; s->need_ccal = 1; s->ccal_version = 3; s->need_fcal = 1; s->invert_tly = 1; - s->can_color = 1; s->rgb_format = 1; /*s->duplex_offset = 400; now set in config file*/ @@ -1609,6 +1645,51 @@ init_model (struct scanner *s) s->valid_x = 8.5 * 1200; } + else if (strstr (s->model_name,"DR-F120")){ + /* TODO items: + * * has_rif = 0 ? is this correct + * * has_comp_JPEG = 0 ? is this correct + * * need_ccal = need_fcal = need_fcal_buffer = ccal_version = 0 ? is this correct + */ + + /* Required for USB coms */ + s->has_ssm = 0; + s->has_ssm2 = 1; + + /*missing*/ + s->std_res_x[DPI_100] = 1; + s->std_res_y[DPI_100] = 1; + // DPI_150 not supported + s->std_res_x[DPI_200] = 1; + s->std_res_y[DPI_200] = 1; + s->std_res_x[DPI_300] = 1; + s->std_res_y[DPI_300] = 1; + // DPI_400 not supported + s->std_res_x[DPI_600]= 1; + s->std_res_y[DPI_600] = 1; + // DPI_1200 not supported + // NOTE: This scanner supports higher resolutions + // in the Y direction, but 600 is maximum in X + + // This is true however only the ADF is ever selected in hardware + // FIXME: What extra option is needed to select this in the USB comms + s->has_flatbed = 1; + + /* duplex */ + s->duplex_interlace = DUPLEX_INTERLACE_fFBb; + s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_GBR; + s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_GBR; + s->color_inter_by_res[DPI_100] = COLOR_INTERLACE_RGB; + s->color_inter_by_res[DPI_600] = COLOR_INTERLACE_RGB; + s->duplex_offset_side = SIDE_BACK; + + /* weirdness */ + s->fixed_width = 1; + + /* lies */ + s->can_halftone = 0; + } + DBG (10, "init_model: finish\n"); return SANE_STATUS_GOOD; @@ -2239,7 +2320,7 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) if (i > 1){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - if (s->u.mode != MODE_COLOR && s->u.mode != MODE_GRAYSCALE){ + if ( must_downsample(s) || s->s.mode < MODE_GRAYSCALE ){ opt->cap |= SANE_CAP_INACTIVE; } } @@ -2361,6 +2442,24 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) opt->cap = SANE_CAP_INACTIVE; } + /* Software blank page skip */ + if(option==OPT_SWSKIP){ + + opt->name = "swskip"; + opt->title = SANE_I18N ("Software blank skip percentage"); + opt->desc = SANE_I18N("Request driver to discard pages with low percentage of dark pixels"); + opt->type = SANE_TYPE_FIXED; + opt->unit = SANE_UNIT_PERCENT; + opt->constraint_type = SANE_CONSTRAINT_RANGE; + opt->constraint.range = &s->swskip_range; + + s->swskip_range.quant=SANE_FIX(0.10001); + s->swskip_range.min=SANE_FIX(0); + s->swskip_range.max=SANE_FIX(100); + + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + } + /*staple detection*/ if(option==OPT_STAPLEDETECT){ opt->name = "stapledetect"; @@ -2751,12 +2850,16 @@ sane_control_option (SANE_Handle handle, SANE_Int option, *val_p = s->swcrop; return SANE_STATUS_GOOD; + case OPT_SWSKIP: + *val_p = SANE_FIX(s->swskip); + return SANE_STATUS_GOOD; + case OPT_STAPLEDETECT: *val_p = s->stapledetect; return SANE_STATUS_GOOD; case OPT_DROPOUT_COLOR_F: - switch (s->dropout_color_f) { + switch (s->dropout_color[SIDE_FRONT]) { case COLOR_NONE: strcpy (val, STRING_NONE); break; @@ -2782,7 +2885,7 @@ sane_control_option (SANE_Handle handle, SANE_Int option, return SANE_STATUS_GOOD; case OPT_DROPOUT_COLOR_B: - switch (s->dropout_color_b) { + switch (s->dropout_color[SIDE_BACK]) { case COLOR_NONE: strcpy (val, STRING_NONE); break; @@ -3003,6 +3106,14 @@ sane_control_option (SANE_Handle handle, SANE_Int option, if (s->u.page_x == FIXED_MM_TO_SCANNER_UNIT(val_c)) return SANE_STATUS_GOOD; + /* if full width image, and paper size is changed, + change the image size to match new paper */ + if (s->u.tl_x == 0 && s->u.br_x == s->u.page_x){ + DBG (20, "sane_control_option: br_x tracking page_width\n"); + s->u.br_x = FIXED_MM_TO_SCANNER_UNIT(val_c); + *info |= SANE_INFO_RELOAD_PARAMS; + } + s->u.page_x = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_OPTIONS; @@ -3012,6 +3123,14 @@ sane_control_option (SANE_Handle handle, SANE_Int option, if (s->u.page_y == FIXED_MM_TO_SCANNER_UNIT(val_c)) return SANE_STATUS_GOOD; + /* if full height image, and paper size is changed, + change the image size to match new paper */ + if (s->u.tl_y == 0 && s->u.br_y == s->u.page_y){ + DBG (20, "sane_control_option: br_y tracking page_height\n"); + s->u.br_y = FIXED_MM_TO_SCANNER_UNIT(val_c); + *info |= SANE_INFO_RELOAD_PARAMS; + } + s->u.page_y = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_OPTIONS; @@ -3072,42 +3191,46 @@ sane_control_option (SANE_Handle handle, SANE_Int option, s->swcrop = val_c; return SANE_STATUS_GOOD; + case OPT_SWSKIP: + s->swskip = SANE_UNFIX(val_c); + return SANE_STATUS_GOOD; + case OPT_STAPLEDETECT: s->stapledetect = val_c; return SANE_STATUS_GOOD; case OPT_DROPOUT_COLOR_F: if (!strcmp(val, STRING_NONE)) - s->dropout_color_f = COLOR_NONE; + s->dropout_color[SIDE_FRONT] = COLOR_NONE; else if (!strcmp(val, STRING_RED)) - s->dropout_color_f = COLOR_RED; + s->dropout_color[SIDE_FRONT] = COLOR_RED; else if (!strcmp(val, STRING_GREEN)) - s->dropout_color_f = COLOR_GREEN; + s->dropout_color[SIDE_FRONT] = COLOR_GREEN; else if (!strcmp(val, STRING_BLUE)) - s->dropout_color_f = COLOR_BLUE; + s->dropout_color[SIDE_FRONT] = COLOR_BLUE; else if (!strcmp(val, STRING_EN_RED)) - s->dropout_color_f = COLOR_EN_RED; + s->dropout_color[SIDE_FRONT] = COLOR_EN_RED; else if (!strcmp(val, STRING_EN_GREEN)) - s->dropout_color_f = COLOR_EN_GREEN; + s->dropout_color[SIDE_FRONT] = COLOR_EN_GREEN; else if (!strcmp(val, STRING_EN_BLUE)) - s->dropout_color_f = COLOR_EN_BLUE; + s->dropout_color[SIDE_FRONT] = COLOR_EN_BLUE; return SANE_STATUS_GOOD; case OPT_DROPOUT_COLOR_B: if (!strcmp(val, STRING_NONE)) - s->dropout_color_b = COLOR_NONE; + s->dropout_color[SIDE_BACK] = COLOR_NONE; else if (!strcmp(val, STRING_RED)) - s->dropout_color_b = COLOR_RED; + s->dropout_color[SIDE_BACK] = COLOR_RED; else if (!strcmp(val, STRING_GREEN)) - s->dropout_color_b = COLOR_GREEN; + s->dropout_color[SIDE_BACK] = COLOR_GREEN; else if (!strcmp(val, STRING_BLUE)) - s->dropout_color_b = COLOR_BLUE; + s->dropout_color[SIDE_BACK] = COLOR_BLUE; else if (!strcmp(val, STRING_EN_RED)) - s->dropout_color_b = COLOR_EN_RED; + s->dropout_color[SIDE_BACK] = COLOR_EN_RED; else if (!strcmp(val, STRING_EN_GREEN)) - s->dropout_color_b = COLOR_EN_GREEN; + s->dropout_color[SIDE_BACK] = COLOR_EN_GREEN; else if (!strcmp(val, STRING_EN_BLUE)) - s->dropout_color_b = COLOR_EN_BLUE; + s->dropout_color[SIDE_BACK] = COLOR_EN_BLUE; return SANE_STATUS_GOOD; case OPT_BUFFERMODE: @@ -3344,6 +3467,11 @@ ssm_do (struct scanner *s) return ret; } + if(s->s.mode == MODE_COLOR){ + DBG (10, "ssm_do: unneeded, finishing\n"); + return ret; + } + if(s->has_ssm){ unsigned char cmd[SET_SCAN_MODE_len]; @@ -3366,7 +3494,7 @@ ssm_do (struct scanner *s) set_SSM_DO_unk1(out, 0x03); - switch(s->dropout_color_f){ + switch(s->dropout_color[SIDE_FRONT]){ case COLOR_RED: set_SSM_DO_unk2(out, 0x05); set_SSM_DO_f_do(out,SSM_DO_red); @@ -3393,7 +3521,7 @@ ssm_do (struct scanner *s) break; } - switch(s->dropout_color_b){ + switch(s->dropout_color[SIDE_BACK]){ case COLOR_RED: set_SSM_DO_unk2(out, 0x05); set_SSM_DO_b_do(out,SSM_DO_red); @@ -3444,7 +3572,7 @@ ssm_do (struct scanner *s) memset(out,0,outLen); - switch(s->dropout_color_f){ + switch(s->dropout_color[SIDE_FRONT]){ case COLOR_RED: set_SSM2_DO_do(out,SSM_DO_red); break; @@ -4138,9 +4266,7 @@ sane_start (SANE_Handle handle) * tell the user the size of the image. the sane * API has no way to inform the frontend of this, * so we block and buffer. yuck */ - if( (s->swdeskew || s->swdespeck || s->swcrop) - && s->s.format != SANE_FRAME_JPEG - ){ + if(must_fully_buffer(s)){ /* get image */ while(!s->s.eof[s->side] && !ret){ @@ -4166,7 +4292,16 @@ sane_start (SANE_Handle handle) if(s->swdespeck){ buffer_despeck(s,s->side); } - + if(s->swskip){ + /* Skipping means throwing out this image. + * Pretend the user read the whole thing + * and call sane_start again. + * This assumes we are running in batch mode. */ + if(buffer_isblank(s,s->side)){ + s->u.eof[s->side] = 1; + return sane_start(handle); + } + } } ret = check_for_cancel(s); @@ -4752,23 +4887,7 @@ read_from_scanner(struct scanner *s, int side, int exact) /* this is non-jpeg data, fill remainder, change rx'd size */ else{ - - DBG (15, "read_from_scanner: eof: %d %d\n", s->i.bytes_tot[side], s->i.bytes_sent[side]); - - /* clone the last line repeatedly until the end */ - while(s->i.bytes_tot[side] > s->i.bytes_sent[side]){ - memcpy( - s->buffers[side]+s->i.bytes_sent[side]-s->i.Bpl, - s->buffers[side]+s->i.bytes_sent[side], - s->i.Bpl - ); - s->i.bytes_sent[side] += s->i.Bpl; - } - - DBG (15, "read_from_scanner: eof2: %d %d\n", s->i.bytes_tot[side], s->i.bytes_sent[side]); - - /* pretend we got all the data from scanner */ - s->s.bytes_sent[side] = s->s.bytes_tot[side]; + fill_image(s,side); } s->i.eof[side] = 1; @@ -4890,40 +5009,8 @@ read_from_scanner_duplex(struct scanner *s,int exact) /* this is non-jpeg data, fill remainder, change rx'd size */ else{ - - DBG (15, "read_from_scanner_duplex: eof: %d %d %d %d\n", - s->i.bytes_tot[SIDE_FRONT], s->i.bytes_sent[SIDE_FRONT], - s->i.bytes_tot[SIDE_BACK], s->i.bytes_sent[SIDE_BACK] - ); - - /* clone the last line repeatedly until the end */ - while(s->i.bytes_tot[SIDE_FRONT] > s->i.bytes_sent[SIDE_FRONT]){ - memcpy( - s->buffers[SIDE_FRONT]+s->i.bytes_sent[SIDE_FRONT]-s->i.Bpl, - s->buffers[SIDE_FRONT]+s->i.bytes_sent[SIDE_FRONT], - s->i.Bpl - ); - s->i.bytes_sent[SIDE_FRONT] += s->i.Bpl; - } - - /* clone the last line repeatedly until the end */ - while(s->i.bytes_tot[SIDE_BACK] > s->i.bytes_sent[SIDE_BACK]){ - memcpy( - s->buffers[SIDE_BACK]+s->i.bytes_sent[SIDE_BACK]-s->i.Bpl, - s->buffers[SIDE_BACK]+s->i.bytes_sent[SIDE_BACK], - s->i.Bpl - ); - s->i.bytes_sent[SIDE_BACK] += s->i.Bpl; - } - - DBG (15, "read_from_scanner_duplex: eof2: %d %d %d %d\n", - s->i.bytes_tot[SIDE_FRONT], s->i.bytes_sent[SIDE_FRONT], - s->i.bytes_tot[SIDE_BACK], s->i.bytes_sent[SIDE_BACK] - ); - - /* pretend we got all the data from scanner */ - s->s.bytes_sent[SIDE_FRONT] = s->s.bytes_tot[SIDE_FRONT]; - s->s.bytes_sent[SIDE_BACK] = s->s.bytes_tot[SIDE_BACK]; + fill_image(s,SIDE_FRONT); + fill_image(s,SIDE_BACK); } s->i.eof[SIDE_FRONT] = 1; @@ -5222,16 +5309,20 @@ copy_duplex(struct scanner *s, unsigned char * buf, int len) } /* full line of front, then full line of back */ - else if(s->duplex_interlace == DUPLEX_INTERLACE_FFBB){ + else if(s->duplex_interlace == DUPLEX_INTERLACE_FfBb || s->duplex_interlace == DUPLEX_INTERLACE_fFBb){ for(i=0; iduplex_interlace == DUPLEX_INTERLACE_FfBb){ + memcpy(front+flen,buf+i,bwidth); + }else{ + rmemcpy(front+flen,buf+i,bwidth,3); // only 24bit color is supported + } flen+=bwidth; memcpy(back+blen,buf+i+bwidth,bwidth); blen+=bwidth; } } - /*just alternating bytes, FBFBFB*/ + /*just alternating bytes, FBfb*/ else { for(i=0; is.mode) { case MODE_COLOR: - memcpy(line, buff, sbwidth); + if(must_downsample(s) && s->dropout_color[side]){ + switch(s->dropout_color[side]){ + case COLOR_RED: + for(i=0;ii.mode) { case MODE_COLOR: - memcpy(s->buffers[side]+s->i.bytes_sent[side], line+offset, ibwidth); + memcpy(s->buffers[side]+s->i.bytes_sent[side], line+(offset*3), ibwidth); s->i.bytes_sent[side] += ibwidth; break; @@ -5410,6 +5556,43 @@ read_from_buffer(struct scanner *s, SANE_Byte * buf, SANE_Int max_len, return ret; } +/* fill remainder of buffer with background if scanner stops early */ +static SANE_Status +fill_image(struct scanner *s,int side) +{ + SANE_Status ret=SANE_STATUS_GOOD; + + unsigned char bg_color = calc_bg_color(s); + int fill_bytes = s->i.bytes_tot[side]-s->i.bytes_sent[side]; + + if(!fill_bytes){ + return ret; + } + + DBG (15, "fill_image: side:%d bytes:%d bg_color:%02x\n", side, fill_bytes, bg_color); + + /* fill the rest with bg_color */ + memset(s->buffers[side]+s->i.bytes_sent[side],bg_color,fill_bytes); + + /* pretend we got all the data from scanner */ + s->i.bytes_sent[side] = s->i.bytes_tot[side]; + s->s.bytes_sent[side] = s->s.bytes_tot[side]; + + return ret; +} + +/* return the bg color based on scanner settings */ +static unsigned char +calc_bg_color(struct scanner *s) +{ + unsigned char bg_color = s->lut[s->bg_color]; + + if(s->u.mode <= MODE_HALFTONE) + bg_color = (bg_colorthreshold)?0xff:0x00; + + return bg_color; +} + /* * @@ Section 5 - calibration functions */ @@ -7144,18 +7327,20 @@ wait_scanner(struct scanner *s) NULL, 0, NULL, NULL ); - + + // some scanners (such as DR-F120) are OK but will not respond to commands + // when in sleep mode. By checking the sense it wakes them up. if (ret != SANE_STATUS_GOOD) { - DBG(5,"WARNING: Brain-dead scanner. Hitting with stick\n"); + DBG(5,"WARNING: Brain-dead scanner. Hitting with request sense.\n"); ret = do_cmd ( - s, 0, 1, + s, 1, 1, cmd, cmdLen, NULL, 0, NULL, NULL ); } if (ret != SANE_STATUS_GOOD) { - DBG(5,"WARNING: Brain-dead scanner. Hitting with stick again\n"); + DBG(5,"WARNING: Brain-dead scanner. Hitting with stick instead.\n"); ret = do_cmd ( s, 0, 1, cmd, cmdLen, @@ -7168,7 +7353,7 @@ wait_scanner(struct scanner *s) DBG (5, "wait_scanner: error '%s'\n", sane_strstatus (ret)); } - DBG (10, "wait_scanner: finish\n"); + DBG (10, "wait_scanner: finish (status=%d)\n", ret); return ret; } @@ -7350,102 +7535,40 @@ buffer_deskew(struct scanner *s, int side) { SANE_Status ret = SANE_STATUS_GOOD; - int pwidth = s->i.width; - int width = s->i.Bpl; - int height = s->i.height; - - double TSlope = 0; - int TXInter = 0; - int TYInter = 0; - double TSlopeHalf = 0; - int TOffsetHalf = 0; - - double LSlope = 0; - int LXInter = 0; - int LYInter = 0; - double LSlopeHalf = 0; - int LOffsetHalf = 0; - - int rotateX = 0; - int rotateY = 0; - - int * topBuf = NULL, * botBuf = NULL; + unsigned char bg_color = calc_bg_color(s); DBG (10, "buffer_deskew: start\n"); - /* get buffers for edge detection */ - topBuf = getTransitionsY(s,side,1); - if(!topBuf){ - DBG (5, "buffer_deskew: cant gTY\n"); - ret = SANE_STATUS_NO_MEM; - goto cleanup; - } - - if(0){ - int i; - for(i=0;i=0 && topBuf[i] < height) - s->buffers[side][topBuf[i]*width+i] = 0; - } - } + ret = sane_get_parameters((SANE_Handle) s, &s->s_params); - botBuf = getTransitionsY(s,side,0); - if(!botBuf){ - DBG (5, "buffer_deskew: cant gTY\n"); - ret = SANE_STATUS_NO_MEM; - goto cleanup; - } + /*only find skew on first image from a page, or if first image had error */ + if(s->side == SIDE_FRONT || s->u.source == SOURCE_ADF_BACK || s->deskew_stat){ - /* find best top line */ - ret = getEdgeIterate (pwidth, height, s->i.dpi_y, topBuf, - &TSlope, &TXInter, &TYInter); - if(ret){ - DBG(5,"buffer_deskew: gEI error: %d",ret); - goto cleanup; - } - DBG(15,"top: %04.04f %d %d\n",TSlope,TXInter,TYInter); + s->deskew_stat = sanei_magic_findSkew( + &s->s_params,s->buffers[side],s->u.dpi_x,s->u.dpi_y, + &s->deskew_vals[0],&s->deskew_vals[1],&s->deskew_slope); - /* slope is too shallow, don't want to divide by 0 */ - if(fabs(TSlope) < 0.0001){ - DBG(15,"buffer_deskew: slope too shallow: %0.08f\n",TSlope); - goto cleanup; + if(s->deskew_stat){ + DBG (5, "buffer_deskew: bad findSkew, bailing\n"); + goto cleanup; + } } - - /* find best left line, perpendicular to top line */ - LSlope = (double)-1/TSlope; - ret = getEdgeSlope (pwidth, height, topBuf, botBuf, LSlope, - &LXInter, &LYInter); - if(ret){ - DBG(5,"buffer_deskew: gES error: %d",ret); - goto cleanup; + /* backside images can use a 'flipped' version of frontside data */ + else{ + s->deskew_slope *= -1; + s->deskew_vals[0] = s->s_params.pixels_per_line - s->deskew_vals[0]; } - DBG(15,"buffer_deskew: left: %04.04f %d %d\n",LSlope,LXInter,LYInter); - - /* find point about which to rotate */ - TSlopeHalf = tan(atan(TSlope)/2); - TOffsetHalf = LYInter; - DBG(15,"buffer_deskew: top half: %04.04f %d\n",TSlopeHalf,TOffsetHalf); - - LSlopeHalf = tan((atan(LSlope) + ((LSlope < 0)?-M_PI_2:M_PI_2))/2); - LOffsetHalf = - LSlopeHalf * TXInter; - DBG(15,"buffer_deskew: left half: %04.04f %d\n",LSlopeHalf,LOffsetHalf); - rotateX = (LOffsetHalf-TOffsetHalf) / (TSlopeHalf-LSlopeHalf); - rotateY = TSlopeHalf * rotateX + TOffsetHalf; - DBG(15,"buffer_deskew: rotate: %d %d\n",rotateX,rotateY); + ret = sanei_magic_rotate(&s->s_params,s->buffers[side], + s->deskew_vals[0],s->deskew_vals[1],s->deskew_slope,bg_color); - ret = rotateOnCenter (s, side, rotateX, rotateY, TSlope); if(ret){ - DBG(5,"buffer_deskew: gES error: %d",ret); + DBG(5,"buffer_deskew: rotate error: %d",ret); + ret = SANE_STATUS_GOOD; goto cleanup; } cleanup: - if(topBuf) - free(topBuf); - if(botBuf) - free(botBuf); - DBG (10, "buffer_deskew: finish\n"); return ret; } @@ -7458,173 +7581,50 @@ buffer_crop(struct scanner *s, int side) { SANE_Status ret = SANE_STATUS_GOOD; - int bwidth = s->i.Bpl; - int width = s->i.width; - int height = s->i.height; - - int top = 0; - int bot = 0; - int left = width; - int right = 0; - - int * topBuf = NULL, * botBuf = NULL; - int * leftBuf = NULL, * rightBuf = NULL; - int leftCount = 0, rightCount = 0, botCount = 0; - int i; - DBG (10, "buffer_crop: start\n"); - /* get buffers to find sides and bottom */ - topBuf = getTransitionsY(s,side,1); - if(!topBuf){ - DBG (5, "buffer_crop: no topBuf\n"); - ret = SANE_STATUS_NO_MEM; - goto cleanup; - } - - botBuf = getTransitionsY(s,side,0); - if(!botBuf){ - DBG (5, "buffer_crop: no botBuf\n"); - ret = SANE_STATUS_NO_MEM; - goto cleanup; - } + ret = sane_get_parameters((SANE_Handle) s, &s->s_params); - leftBuf = getTransitionsX(s,side,1); - if(!leftBuf){ - DBG (5, "buffer_crop: no leftBuf\n"); - ret = SANE_STATUS_NO_MEM; - goto cleanup; - } + ret = sanei_magic_findEdges( + &s->s_params,s->buffers[side],s->u.dpi_x,s->u.dpi_y, + &s->crop_vals[0],&s->crop_vals[1],&s->crop_vals[2],&s->crop_vals[3]); - rightBuf = getTransitionsX(s,side,0); - if(!rightBuf){ - DBG (5, "buffer_crop: no rightBuf\n"); - ret = SANE_STATUS_NO_MEM; + if(ret){ + DBG (5, "buffer_crop: bad edges, bailing\n"); + ret = SANE_STATUS_GOOD; goto cleanup; } - /* loop thru top and bottom lists, look for l and r extremes */ - for(i=0; i topBuf[i]){ - if(left > i){ - left = i; - } - - leftCount++; - if(leftCount > 3){ - break; - } - } - else{ - leftCount = 0; - left = width; - } - } - - for(i=width-1; i>=0; i--){ - if(botBuf[i] > topBuf[i]){ - if(right < i){ - right = i; - } - - rightCount++; - if(rightCount > 3){ - break; - } - } - else{ - rightCount = 0; - right = -1; - } - } - - /* loop thru left and right lists, look for bottom extreme */ - for(i=height-1; i>=0; i--){ - if(rightBuf[i] > leftBuf[i]){ - if(bot < i){ - bot = i; - } + DBG (15, "buffer_crop: t:%d b:%d l:%d r:%d\n", + s->crop_vals[0],s->crop_vals[1],s->crop_vals[2],s->crop_vals[3]); - botCount++; - if(botCount > 3){ - break; - } - } - else{ - botCount = 0; - bot = -1; - } + /* if we will later binarize this image, make sure the width + * is a multiple of 8 pixels, by adjusting the right side */ + if ( must_downsample(s) && s->u.mode < MODE_GRAYSCALE ){ + s->crop_vals[3] -= (s->crop_vals[3]-s->crop_vals[2]) % 8; } - DBG (15, "buffer_crop: t:%d b:%d l:%d r:%d\n",top,bot,left,right); - /* now crop the image */ - /*FIXME: crop duplex backside at same time?*/ - if(left < right && top < bot){ - - int pixels = 0; - int bytes = 0; - unsigned char * line = NULL; - - /*convert left and right to bytes, figure new byte and pixel width */ - switch (s->i.mode) { - - case MODE_COLOR: - pixels = right-left; - bytes = pixels * 3; - left *= 3; - right *= 3; - break; - - case MODE_GRAYSCALE: - pixels = right-left; - bytes = right-left; - break; - - case MODE_LINEART: - case MODE_HALFTONE: - left /= 8; - right = (right+7)/8; - bytes = right-left; - pixels = bytes * 8; - break; - } - - DBG (15, "buffer_crop: l:%d r:%d p:%d b:%d\n",left,right,pixels,bytes); - - line = malloc(bytes); - if(!line){ - DBG (5, "buffer_crop: no line\n"); - ret = SANE_STATUS_NO_MEM; - goto cleanup; - } - - s->i.bytes_sent[side] = 0; - - for(i=top; ibuffers[side] + i*bwidth + left, bytes); - memcpy(s->buffers[side] + s->i.bytes_sent[side], line, bytes); - s->i.bytes_sent[side] += bytes; - } + ret = sanei_magic_crop(&s->s_params,s->buffers[side], + s->crop_vals[0],s->crop_vals[1],s->crop_vals[2],s->crop_vals[3]); - s->i.bytes_tot[side] = s->i.bytes_sent[side]; - s->i.width = pixels; - s->i.height = bot-top; - s->i.Bpl = bytes; - - free(line); + if(ret){ + DBG (5, "buffer_crop: bad crop, bailing\n"); + ret = SANE_STATUS_GOOD; + goto cleanup; } - cleanup: - if(topBuf) - free(topBuf); - if(botBuf) - free(botBuf); - if(leftBuf) - free(leftBuf); - if(rightBuf) - free(rightBuf); - + /* need to update user with new size */ + s->i.width = s->s_params.pixels_per_line; + s->i.height = s->s_params.lines; + s->i.Bpl = s->s_params.bytes_per_line; + + /* update image size counter to new, smaller size */ + s->i.bytes_tot[side] = s->s_params.lines * s->s_params.bytes_per_line; + s->i.bytes_sent[side] = s->i.bytes_tot[side]; + s->u.bytes_sent[side] = 0; + + cleanup: DBG (10, "buffer_crop: finish\n"); return ret; } @@ -7636,938 +7636,77 @@ static SANE_Status buffer_despeck(struct scanner *s, int side) { SANE_Status ret = SANE_STATUS_GOOD; - int i,j,k,l,n; - int w = s->i.Bpl; - int pw = s->i.width; - int h = s->i.height; - int t = w*h; - int d = s->swdespeck; DBG (10, "buffer_despeck: start\n"); - switch (s->i.mode){ + ret = sane_get_parameters((SANE_Handle) s, &s->s_params); - case MODE_COLOR: - for(i=w; ibuffers[side][i + j*3 + k*w + l*3 + n]; - } - - if(tmp < thresh) - thresh = tmp; - } - } + ret = sanei_magic_despeck(&s->s_params,s->buffers[side],s->swdespeck); + if(ret){ + DBG (5, "buffer_despeck: bad despeck, bailing\n"); + ret = SANE_STATUS_GOOD; + goto cleanup; + } - thresh = (thresh + 255*3 + 255*3)/3; - - /*loop over rows and columns around window */ - for(k=-1; kbuffers[side][i + j*3 + k*w + l*3 + n]; - outer[n] += tmp[n]; - } - if(tmp[0]+tmp[1]+tmp[2] < thresh){ - hits++; - break; - } - } - } +/* Look if image has too few dark pixels.*/ +static int +buffer_isblank(struct scanner *s, int side) +{ + SANE_Status ret = SANE_STATUS_GOOD; + int status = 0; - for(n=0; n<3; n++){ - outer[n] /= (4*d + 4); - } + DBG (10, "buffer_isblank: start\n"); - /*no hits, overwrite with avg surrounding color*/ - if(!hits){ - for(k=0; kbuffers[side][i + j*3 + k*w + l*3 + n] = outer[n]; - } - } - } - } + ret = sane_get_parameters((SANE_Handle) s, &s->s_params); - } - } - break; + ret = sanei_magic_isBlank2(&s->s_params, s->buffers[side], + s->u.dpi_x, s->u.dpi_y, s->swskip); - case MODE_GRAYSCALE: - for(i=w; ibuffers[side][i + j + k*w + l] < thresh) - thresh = s->buffers[side][i + j + k*w + l]; - } - } +/* certain options require the entire image to + * be collected from the scanner before we can + * tell the user the size of the image. */ +static int +must_fully_buffer(struct scanner *s) +{ - thresh = (thresh + 255 + 255)/3; - - /*loop over rows and columns around window */ - for(k=-1; kswdeskew || s->swdespeck || s->swcrop) + && s->s.format != SANE_FRAME_JPEG + ){ + return 1; + } - int tmp = 0; + return 0; +} - /* dont count pixels in the window */ - if(k != -1 && k != d && l != -1 && l != d) - continue; - - tmp = s->buffers[side][i + j + k*w + l]; - - if(tmp < thresh){ - hits++; - break; - } - - outer += tmp; - } - } - - outer /= (4*d + 4); - - /*no hits, overwrite with avg surrounding color*/ - if(!hits){ - for(k=0; kbuffers[side][i + j + k*w + l] = outer; - } - } - } - - } - } - break; - - case MODE_LINEART: - case MODE_HALFTONE: - for(i=w; ibuffers[side][i + k*w + (j+l)/8] >> (7-(j+l)%8) & 1; - } - } - - if(!curr) - continue; - - /*loop over rows and columns around window */ - for(k=-1; kbuffers[side][i + k*w + (j+l)/8] >> (7-(j+l)%8) & 1; - - if(hits) - break; - } - } - - /*no hits, overwrite with white*/ - if(!hits){ - for(k=0; kbuffers[side][i + k*w + (j+l)/8] &= ~(1 << (7-(j+l)%8)); - } - } - } - - } - } - break; - - default: - break; - } - - DBG (10, "buffer_despeck: finish\n"); - return ret; -} - -/* Loop thru the image width and look for first color change in each column. - * Return a malloc'd array. Caller is responsible for freeing. */ -int * -getTransitionsY (struct scanner *s, int side, int top) -{ - int * buff; - - int i, j, k; - int near, far; - int winLen = 9; - - int width = s->i.width; - int height = s->i.height; - int depth = 1; - - /* defaults for bottom-up */ - int firstLine = height-1; - int lastLine = -1; - int direction = -1; - - DBG (10, "getTransitionsY: start\n"); - - buff = calloc(width,sizeof(int)); - if(!buff){ - DBG (5, "getTransitionsY: no buff\n"); - return NULL; - } - - /* override for top-down */ - if(top){ - firstLine = 0; - lastLine = height; - direction = 1; - } - - /* load the buff array with y value for first color change from edge - * gray/color uses a different algo from binary/halftone */ - switch (s->i.mode) { - - case MODE_COLOR: - depth = 3; - - case MODE_GRAYSCALE: - - for(i=0; ibuffers[side][(firstLine*width+i) * depth + k]; - } - near *= winLen; - far = near; - - /* move windows, check delta */ - for(j=firstLine+direction; j!=lastLine; j+=direction){ - - int farLine = j-winLen*2*direction; - int nearLine = j-winLen*direction; - - if(farLine < 0 || farLine >= height){ - farLine = firstLine; - } - if(nearLine < 0 || nearLine >= height){ - nearLine = firstLine; - } - - for(k=0; kbuffers[side][(farLine*width+i)*depth+k]; - far += s->buffers[side][(nearLine*width+i)*depth+k]; - - near -= s->buffers[side][(nearLine*width+i)*depth+k]; - near += s->buffers[side][(j*width+i)*depth+k]; - } - - if(abs(near - far) > winLen*depth*9){ - buff[i] = j; - break; - } - } - } - break; - - case MODE_LINEART: - case MODE_HALFTONE: - for(i=0; ibuffers[side][(firstLine*width+i)/8] >> (7-(i%8)) & 1; - - /* move */ - for(j=firstLine+direction; j!=lastLine; j+=direction){ - if((s->buffers[side][(j*width+i)/8] >> (7-(i%8)) & 1) != near){ - buff[i] = j; - break; - } - } - } - break; - - } - - /* blast any stragglers with no neighbors within .5 inch */ - for(i=0;ii.dpi_y/2) - sum++; - } - if(sum < 2) - buff[i] = lastLine; - } - - DBG (10, "getTransitionsY: finish\n"); - - return buff; -} - -/* Loop thru the image height and look for first color change in each row. - * Return a malloc'd array. Caller is responsible for freeing. */ -int * -getTransitionsX (struct scanner *s, int side, int left) -{ - int * buff; - - int i, j, k; - int near, far; - int winLen = 9; - - int bwidth = s->i.Bpl; - int width = s->i.width; - int height = s->i.height; - int depth = 1; - - /* defaults for right-first */ - int firstCol = width-1; - int lastCol = -1; - int direction = -1; - - DBG (10, "getTransitionsX: start\n"); - - buff = calloc(height,sizeof(int)); - if(!buff){ - DBG (5, "getTransitionsY: no buff\n"); - return NULL; - } - - /* override for left-first*/ - if(left){ - firstCol = 0; - lastCol = width; - direction = 1; - } - - /* load the buff array with x value for first color change from edge - * gray/color uses a different algo from binary/halftone */ - switch (s->i.mode) { - - case MODE_COLOR: - depth = 3; - - case MODE_GRAYSCALE: - - for(i=0; ibuffers[side][i*bwidth + k]; - } - near *= winLen; - far = near; - - /* move windows, check delta */ - for(j=firstCol+direction; j!=lastCol; j+=direction){ - - int farCol = j-winLen*2*direction; - int nearCol = j-winLen*direction; - - if(farCol < 0 || farCol >= width){ - farCol = firstCol; - } - if(nearCol < 0 || nearCol >= width){ - nearCol = firstCol; - } - - for(k=0; kbuffers[side][i*bwidth + farCol*depth + k]; - far += s->buffers[side][i*bwidth + nearCol*depth + k]; - - near -= s->buffers[side][i*bwidth + nearCol*depth + k]; - near += s->buffers[side][i*bwidth + j*depth + k]; - } - - if(abs(near - far) > winLen*depth*9){ - buff[i] = j; - break; - } - } - } - break; - - case MODE_LINEART: - case MODE_HALFTONE: - for(i=0; ibuffers[side][i*bwidth + firstCol/8] >> (7-(firstCol%8)) & 1; - - /* move */ - for(j=firstCol+direction; j!=lastCol; j+=direction){ - if((s->buffers[side][i*bwidth + j/8] >> (7-(j%8)) & 1) != near){ - buff[i] = j; - break; - } - } - } - break; - - } - - /* blast any stragglers with no neighbors within .5 inch */ - for(i=0;ii.dpi_x/2) - sum++; - } - if(sum < 2) - buff[i] = lastCol; - } - - DBG (10, "getTransitionsX: finish\n"); - - return buff; -} - -/* Loop thru a getTransitions array, and use a simplified Hough transform - * to divide likely edges into a 2-d array of bins. Then weight each - * bin based on its angle and offset. Return the 'best' bin. */ -static SANE_Status -getLine (int height, int width, int * buff, - int slopes, double minSlope, double maxSlope, - int offsets, int minOffset, int maxOffset, - double * finSlope, int * finOffset, int * finDensity) -{ - SANE_Status ret = 0; - - int ** lines = NULL; - int i, j; - int rise, run; - double slope; - int offset; - int sIndex, oIndex; - int hWidth = width/2; - - double * slopeCenter = NULL; - int * slopeScale = NULL; - double * offsetCenter = NULL; - int * offsetScale = NULL; - - int maxDensity = 1; - double absMaxSlope = fabs(maxSlope); - double absMinSlope = fabs(minSlope); - int absMaxOffset = abs(maxOffset); - int absMinOffset = abs(minOffset); - - DBG(10,"getLine: start %+0.4f %+0.4f %d %d\n", - minSlope,maxSlope,minOffset,maxOffset); - - /*silence compiler*/ - height = height; - - if(absMaxSlope < absMinSlope) - absMaxSlope = absMinSlope; - - if(absMaxOffset < absMinOffset) - absMaxOffset = absMinOffset; - - /* build an array of pretty-print values for slope */ - slopeCenter = calloc(slopes,sizeof(double)); - if(!slopeCenter){ - DBG(5,"getLine: cant load slopeCenter\n"); - ret = SANE_STATUS_NO_MEM; - goto cleanup; - } - - /* build an array of scaling factors for slope */ - slopeScale = calloc(slopes,sizeof(int)); - if(!slopeScale){ - DBG(5,"getLine: cant load slopeScale\n"); - ret = SANE_STATUS_NO_MEM; - goto cleanup; - } - - for(j=0;j= maxSlope || slope < minSlope) - continue; - - /* offset in center of width, not y intercept! */ - offset = slope * hWidth + buff[i] - slope * i; - if(offset >= maxOffset || offset < minOffset) - continue; - - sIndex = (slope - minSlope) * slopes/(maxSlope-minSlope); - if(sIndex >= slopes) - continue; - - oIndex = (offset - minOffset) * offsets/(maxOffset-minOffset); - if(oIndex >= offsets) - continue; - - lines[sIndex][oIndex]++; - } - } - - /* go thru array, and find most dense line (highest number) */ - for(i=0;i maxDensity) - maxDensity = lines[i][j]; - } - } - - DBG(15,"getLine: maxDensity %d\n",maxDensity); - - *finSlope = 0; - *finOffset = 0; - *finDensity = 0; - - /* go thru array, and scale densities to % of maximum, plus adjust for - * prefered (smaller absolute value) slope and offset */ - for(i=0;i *finDensity){ - *finDensity = lines[i][j]; - *finSlope = slopeCenter[i]; - *finOffset = offsetCenter[j]; - } - } - } - - if(0){ - DBG(15,"offsetCenter: "); - for(j=0;j topDensity){ - topSlope = slope; - topOffset = offset; - topDensity = density; - } - } - } - - DBG(15,"getEdgeIterate: ok %+0.4f %d %d\n",topSlope,topOffset,topDensity); - - /* did not find anything promising on first pass, - * give up instead of fixating on some small, pointless feature */ - if(pass == 1 && topDensity < width/5){ - DBG(5,"getEdgeIterate: density too small %d %d\n",topDensity,width); - topOffset = 0; - topSlope = 0; - break; - } - - /* if slope can zoom in some more, do so. */ - if(sStep >= 0.0001){ - minSlope = topSlope - sStep; - maxSlope = topSlope + sStep; - go = 1; - } - - /* if offset can zoom in some more, do so. */ - if(oStep){ - minOffset = topOffset - oStep; - maxOffset = topOffset + oStep; - go = 1; - } - - /* cannot zoom in more, bail out */ - if(!go){ - break; - } - - DBG(15,"getEdgeIterate: zoom: %+0.4f %+0.4f %d %d\n", - minSlope,maxSlope,minOffset,maxOffset); - } - - /* topOffset is in the center of the image, - * convert to x and y intercept */ - if(topSlope != 0){ - *finYInter = topOffset - topSlope * width/2; - *finXInter = *finYInter / -topSlope; - *finSlope = topSlope; - } - else{ - *finYInter = 0; - *finXInter = 0; - *finSlope = 0; - } - - DBG(10,"getEdgeIterate: finish\n"); - - return 0; -} - -/* find the left side of paper by moving a line - * perpendicular to top slope across the image - * the 'left-most' point on the paper is the - * one with the smallest X intercept - * return x and y intercepts */ -SANE_Status -getEdgeSlope (int width, int height, int * top, int * bot, - double slope, int * finXInter, int * finYInter) -{ - - int i; - int topXInter, topYInter; - int botXInter, botYInter; - int leftCount; - - DBG(10,"getEdgeSlope: start\n"); - - topXInter = width; - topYInter = 0; - leftCount = 0; - - for(i=0;i txi){ - topXInter = txi; - topYInter = tyi; - } - - leftCount++; - if(leftCount > 5){ - break; - } - } - else{ - topXInter = width; - topYInter = 0; - leftCount = 0; - } - } - - botXInter = width; - botYInter = 0; - leftCount = 0; - - for(i=0;i -1){ - - int byi = bot[i] - (slope * i); - int bxi = byi/-slope; - - if(botXInter > bxi){ - botXInter = bxi; - botYInter = byi; - } - - leftCount++; - if(leftCount > 5){ - break; - } - } - else{ - botXInter = width; - botYInter = 0; - leftCount = 0; - } - } - - if(botXInter < topXInter){ - *finXInter = botXInter; - *finYInter = botYInter; - } - else{ - *finXInter = topXInter; - *finYInter = topYInter; - } - - DBG(10,"getEdgeSlope: finish\n"); - - return 0; -} - -/* function to do a simple rotation by a given slope, around - * a given point. The point can be outside of image to get - * proper edge alignment. Unused areas filled with bg color - * FIXME: Do in-place rotation to save memory */ -SANE_Status -rotateOnCenter (struct scanner *s, int side, - int centerX, int centerY, double slope) +/* certain scanners require the mode of the + * image to be changed in software. */ +static int +must_downsample(struct scanner *s) { - double slopeRad = -atan(slope); - double slopeSin = sin(slopeRad); - double slopeCos = cos(slopeRad); - - int bwidth = s->i.Bpl; - int pwidth = s->i.width; - int height = s->i.height; - int depth = 1; - int bg_color = s->lut[s->bg_color]; - - unsigned char * outbuf; - int i, j, k; - - DBG(10,"rotateOnCenter: start: %d %d\n",centerX,centerY); - - outbuf = malloc(s->i.bytes_tot[side]); - if(!outbuf){ - DBG(15,"rotateOnCenter: no outbuf\n"); - return SANE_STATUS_NO_MEM; - } - - switch (s->i.mode){ - - case MODE_COLOR: - depth = 3; - - case MODE_GRAYSCALE: - memset(outbuf,bg_color,s->i.bytes_tot[side]); - - for (i=0; i= pwidth) - continue; - - sourceY = centerY + (int)(-shiftY * slopeCos + shiftX * slopeSin); - if (sourceY < 0 || sourceY >= height) - continue; - - for (k=0; kbuffers[side][sourceY*bwidth+sourceX*depth+k]; - } - } - } - break; - - case MODE_LINEART: - case MODE_HALFTONE: - memset(outbuf,(bg_colorthreshold)?0xff:0x00,s->i.bytes_tot[side]); - - for (i=0; i= pwidth) - continue; - - sourceY = centerY + (int)(-shiftY * slopeCos + shiftX * slopeSin); - if (sourceY < 0 || sourceY >= height) - continue; - - /* wipe out old bit */ - outbuf[i*bwidth + j/8] &= ~(1 << (7-(j%8))); - - /* fill in new bit */ - outbuf[i*bwidth + j/8] |= - ((s->buffers[side][sourceY*bwidth + sourceX/8] - >> (7-(sourceX%8))) & 1) << (7-(j%8)); - } - } - break; + if(s->s.mode != s->i.mode + && s->compress != COMP_JPEG + ){ + return 1; } - memcpy(s->buffers[side],outbuf,s->i.bytes_tot[side]); - - free(outbuf); - - DBG(10,"rotateOnCenter: finish\n"); - return 0; } -- cgit v1.2.3 From 1687222e1b9e74c89cafbb5910e72d8ec7bfd40f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Wed, 31 Jul 2019 16:59:49 +0200 Subject: New upstream version 1.0.28 --- backend/canon_dr.c | 743 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 499 insertions(+), 244 deletions(-) (limited to 'backend/canon_dr.c') diff --git a/backend/canon_dr.c b/backend/canon_dr.c index 3c058ab..64aec31 100644 --- a/backend/canon_dr.c +++ b/backend/canon_dr.c @@ -10,6 +10,7 @@ Canon, USA. www.usa.canon.com loaned equipment HPrint hprint.com.br provided funding and testing for DR-2510 support Stone-IT www.stone-it.com provided funding for DR-2010 and DR-2050 support + Smartmatic www.smartmatic.com provided testing and changes for DR-X10C support -------------------------------------------------------------------------- @@ -165,7 +166,7 @@ - correct rgb padding macro - skip send_panel and ssm_df commands for DR-20xx scanners v22 2009-03-25, MAN - - add deinterlacing code for DR-2510C in duplex and color + - add deinterlacing code for DR-2510C in duplex and color v23 2009-03-27, MAN - rewrite all image data processing code - handle more image interlacing formats @@ -335,6 +336,8 @@ - rename all DUPLEX_INTERLACE_* to indicate start and end of line v56 2016-08-23, MAN - initial support for P-150 + v57 2019-02-24, manuarg + - complete support for X-10, including hardware cropping SANE FLOW DIAGRAM @@ -352,7 +355,7 @@ . . - sane_start() : start image acquisition . . - sane_get_parameters() : returns actual scan parameters . . - sane_read() : read image data (from pipe) - . . (sane_read called multiple times; after sane_read returns EOF, + . . (sane_read called multiple times; after sane_read returns EOF, . . loop may continue with sane_start which may return a 2nd page . . when doing duplex scans, or load the next page from the ADF) . . @@ -385,14 +388,14 @@ #include "canon_dr.h" #define DEBUG 1 -#define BUILD 56 +#define BUILD 57 /* values for SANE_DEBUG_CANON_DR env var: - errors 5 - function trace 10 - function detail 15 - get/setopt cmds 20 - - scsi/usb trace 25 + - scsi/usb trace 25 - scsi/usb writes 30 - scsi/usb reads 31 - useless noise 35 @@ -455,7 +458,7 @@ static struct scanner *scanner_devList = NULL; /* * Called by SANE initially. - * + * * From the SANE spec: * This function must be called before any other SANE function can be * called. The behavior of a SANE backend is undefined if this @@ -487,7 +490,7 @@ sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) /* * Called by SANE to find out about supported devices. - * + * * From the SANE spec: * This function can be used to query the list of devices that are * available. If the function executes successfully, it stores a @@ -500,7 +503,7 @@ sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) * returned (devices directly attached to the machine that SANE is * running on). If it is false, the device list includes all remote * devices that are accessible to the SANE library. - * + * * SANE does not require that this function is called before a * sane_open() call is performed. A device name may be specified * explicitly by a user which would make it unnecessary and @@ -544,42 +547,42 @@ sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) CANON_DR_CONFIG_FILE); while (sanei_config_read (line, PATH_MAX, fp)) { - + lp = line; - + /* ignore comments */ if (*lp == '#') continue; - + /* skip empty lines */ if (*lp == 0) continue; - + if (!strncmp ("option", lp, 6) && isspace (lp[6])) { - + lp += 6; lp = sanei_config_skip_whitespace (lp); - + /* BUFFERSIZE: > 4K */ if (!strncmp (lp, "buffer-size", 11) && isspace (lp[11])) { - + int buf; lp += 11; lp = sanei_config_skip_whitespace (lp); buf = atoi (lp); - + if (buf < 4096) { DBG (5, "sane_get_devices: config option \"buffer-size\" " "(%d) is < 4096, ignoring!\n", buf); continue; } - + if (buf > global_buffer_size_default) { DBG (5, "sane_get_devices: config option \"buffer-size\" " "(%d) is > %d, scanning problems may result\n", buf, global_buffer_size_default); } - + DBG (15, "sane_get_devices: setting \"buffer-size\" to %d\n", buf); @@ -588,24 +591,24 @@ sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) /* PADDED READ: we clamp to 0 or 1 */ else if (!strncmp (lp, "padded-read", 11) && isspace (lp[11])) { - + int buf; lp += 11; lp = sanei_config_skip_whitespace (lp); buf = atoi (lp); - + if (buf < 0) { DBG (5, "sane_get_devices: config option \"padded-read\" " "(%d) is < 0, ignoring!\n", buf); continue; } - + if (buf > 1) { DBG (5, "sane_get_devices: config option \"padded-read\" " "(%d) is > 1, ignoring!\n", buf); continue; } - + DBG (15, "sane_get_devices: setting \"padded-read\" to %d\n", buf); @@ -614,24 +617,24 @@ sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) /* EXTRA STATUS: we clamp to 0 or 1 */ else if (!strncmp (lp, "extra-status", 12) && isspace (lp[12])) { - + int buf; lp += 12; lp = sanei_config_skip_whitespace (lp); buf = atoi (lp); - + if (buf < 0) { DBG (5, "sane_get_devices: config option \"extra-status\" " "(%d) is < 0, ignoring!\n", buf); continue; } - + if (buf > 1) { DBG (5, "sane_get_devices: config option \"extra-status\" " "(%d) is > 1, ignoring!\n", buf); continue; } - + DBG (15, "sane_get_devices: setting \"extra-status\" to %d\n", buf); @@ -640,18 +643,18 @@ sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) /* DUPLEXOFFSET: < 2400 */ else if (!strncmp (lp, "duplex-offset", 13) && isspace (lp[13])) { - + int buf; lp += 13; lp = sanei_config_skip_whitespace (lp); buf = atoi (lp); - + if (buf > 2400) { DBG (5, "sane_get_devices: config option \"duplex-offset\" " "(%d) is > 2400, ignoring!\n", buf); continue; } - + if (buf < 0) { DBG (5, "sane_get_devices: config option \"duplex-offset\" " "(%d) is < 0, ignoring!\n", buf); @@ -671,7 +674,7 @@ sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) lp = sanei_config_skip_whitespace (lp); strncpy(global_vendor_name, lp, 8); global_vendor_name[8] = 0; - + DBG (15, "sane_get_devices: setting \"vendor-name\" to %s\n", global_vendor_name); } @@ -683,7 +686,7 @@ sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) lp = sanei_config_skip_whitespace (lp); strncpy(global_model_name, lp, 16); global_model_name[16] = 0; - + DBG (15, "sane_get_devices: setting \"model-name\" to %s\n", global_model_name); } @@ -695,7 +698,7 @@ sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) lp = sanei_config_skip_whitespace (lp); strncpy(global_version_name, lp, 4); global_version_name[4] = 0; - + DBG (15, "sane_get_devices: setting \"version-name\" to %s\n", global_version_name); } @@ -801,8 +804,8 @@ attach_one_usb (const char *device_name) return attach_one(device_name,CONNECTION_USB); } -/* build the scanner struct and link to global list - * unless struct is already loaded, then pretend +/* build the scanner struct and link to global list + * unless struct is already loaded, then pretend */ static SANE_Status attach_one (const char *device_name, int connType) @@ -974,8 +977,8 @@ connect_fd (struct scanner *s) if(ret == SANE_STATUS_GOOD){ - /* first generation usb scanners can get flaky if not closed - * properly after last use. very first commands sent to device + /* first generation usb scanners can get flaky if not closed + * properly after last use. very first commands sent to device * must be prepared to correct this- see wait_scanner() */ ret = wait_scanner(s); if (ret != SANE_STATUS_GOOD) { @@ -1016,9 +1019,9 @@ init_inquire (struct scanner *s) set_IN_return_size (cmd, inLen); set_IN_evpd (cmd, 0); set_IN_page_code (cmd, 0); - + ret = do_cmd ( - s, 1, 0, + s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen @@ -1323,7 +1326,7 @@ init_model (struct scanner *s) s->std_res_y[DPI_400]=1; s->std_res_x[DPI_600]=1; s->std_res_y[DPI_600]=1; - + /*weirdness*/ s->has_ssm = 0; s->has_ssm2 = 1; @@ -1522,7 +1525,7 @@ init_model (struct scanner *s) s->std_res_y[DPI_400]=1; s->std_res_x[DPI_600]=1; s->std_res_y[DPI_600]=1; - + s->has_comp_JPEG = 1; s->rgb_format = 1; s->has_df_ultra = 1; @@ -1562,7 +1565,7 @@ init_model (struct scanner *s) s->std_res_y[DPI_400]=1; s->std_res_x[DPI_600]=1; s->std_res_y[DPI_600]=1; - + s->has_comp_JPEG = 1; s->rgb_format = 1; s->has_df_ultra = 1; @@ -1690,6 +1693,31 @@ init_model (struct scanner *s) s->can_halftone = 0; } + else if (strstr (s->model_name,"DR-X10C")){ + + /* Required for USB coms */ + s->has_ssm = 0; + s->has_ssm2 = 1; + + /* missing */ + s->std_res_x[DPI_100]=1; + s->std_res_y[DPI_100]=1; + s->std_res_x[DPI_150]=1; + s->std_res_y[DPI_150]=1; + s->std_res_x[DPI_200]=1; + s->std_res_y[DPI_200]=1; + s->std_res_x[DPI_240]=1; + s->std_res_y[DPI_240]=1; + s->std_res_x[DPI_300]=1; + s->std_res_y[DPI_300]=1; + s->std_res_x[DPI_400]=1; + s->std_res_y[DPI_400]=1; + s->std_res_x[DPI_600]=1; + s->std_res_y[DPI_600]=1; + + s->has_hwcrop = 1; + } + DBG (10, "init_model: finish\n"); return SANE_STATUS_GOOD; @@ -1801,9 +1829,9 @@ init_options (struct scanner *s) s->opt[i].cap = SANE_CAP_INACTIVE; } - /* go ahead and setup the first opt, because - * frontend may call control_option on it - * before calling get_option_descriptor + /* go ahead and setup the first opt, because + * frontend may call control_option on it + * before calling get_option_descriptor */ s->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; @@ -1831,7 +1859,7 @@ sane_open (SANE_String_Const name, SANE_Handle * handle) struct scanner *dev = NULL; struct scanner *s = NULL; SANE_Status ret; - + DBG (10, "sane_open: start\n"); if(scanner_devList){ @@ -1852,7 +1880,7 @@ sane_open (SANE_String_Const name, SANE_Handle * handle) } else{ DBG (15, "sane_open: device %s requested\n", name); - + for (dev = scanner_devList; dev; dev = dev->next) { if (strcmp (dev->sane.name, name) == 0 || strcmp (dev->device_name, name) == 0) { /*always allow sanei devname*/ @@ -1928,7 +1956,7 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) } if(s->has_adf){ s->source_list[i++]=STRING_ADFFRONT; - + if(s->has_back){ s->source_list[i++]=STRING_ADFBACK; } @@ -1938,7 +1966,7 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) } if(s->has_card){ s->source_list[i++]=STRING_CARDFRONT; - + if(s->has_back){ s->source_list[i++]=STRING_CARDBACK; } @@ -1974,7 +2002,7 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) s->mode_list[i++]=STRING_COLOR; } s->mode_list[i]=NULL; - + opt->name = SANE_NAME_SCAN_MODE; opt->title = SANE_TITLE_SCAN_MODE; opt->desc = SANE_DESC_SCAN_MODE; @@ -2041,14 +2069,14 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) s->res_list[++i] = 1200; } s->res_list[0] = i; - + opt->name = SANE_NAME_SCAN_RESOLUTION; opt->title = SANE_TITLE_SCAN_RESOLUTION; opt->desc = SANE_DESC_SCAN_RESOLUTION; opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_DPI; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - + if(s->step_y_res){ s->res_range.min = s->min_y_res; s->res_range.max = s->max_y_res; @@ -2078,7 +2106,7 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) s->tl_x_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_x); s->tl_x_range.max = SCANNER_UNIT_TO_FIXED_MM(get_page_width(s)); s->tl_x_range.quant = MM_PER_UNIT_FIX; - + opt->name = SANE_NAME_SCAN_TL_X; opt->title = SANE_TITLE_SCAN_TL_X; opt->desc = SANE_DESC_SCAN_TL_X; @@ -2096,7 +2124,7 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) s->tl_y_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_y); s->tl_y_range.max = SCANNER_UNIT_TO_FIXED_MM(get_page_height(s)); s->tl_y_range.quant = MM_PER_UNIT_FIX; - + opt->name = SANE_NAME_SCAN_TL_Y; opt->title = SANE_TITLE_SCAN_TL_Y; opt->desc = SANE_DESC_SCAN_TL_Y; @@ -2114,7 +2142,7 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) s->br_x_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_x); s->br_x_range.max = SCANNER_UNIT_TO_FIXED_MM(get_page_width(s)); s->br_x_range.quant = MM_PER_UNIT_FIX; - + opt->name = SANE_NAME_SCAN_BR_X; opt->title = SANE_TITLE_SCAN_BR_X; opt->desc = SANE_DESC_SCAN_BR_X; @@ -2132,7 +2160,7 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) s->br_y_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_y); s->br_y_range.max = SCANNER_UNIT_TO_FIXED_MM(get_page_height(s)); s->br_y_range.quant = MM_PER_UNIT_FIX; - + opt->name = SANE_NAME_SCAN_BR_Y; opt->title = SANE_TITLE_SCAN_BR_Y; opt->desc = SANE_DESC_SCAN_BR_Y; @@ -2370,7 +2398,7 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) /*double feed by thickness */ if(option==OPT_DF_THICKNESS){ - + opt->name = "df-thickness"; opt->title = "DF by thickness"; opt->desc = "Detect double feeds using thickness sensor"; @@ -2482,7 +2510,7 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) s->do_color_list[5] = STRING_EN_GREEN; s->do_color_list[6] = STRING_EN_BLUE; s->do_color_list[7] = NULL; - + opt->name = "dropout-front"; opt->title = "Dropout color front"; opt->desc = "One-pass scanners use only one color during gray or binary scanning, useful for colored paper or ink"; @@ -2510,7 +2538,7 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) s->do_color_list[5] = STRING_EN_GREEN; s->do_color_list[6] = STRING_EN_BLUE; s->do_color_list[7] = NULL; - + opt->name = "dropout-back"; opt->title = "Dropout color back"; opt->desc = "One-pass scanners use only one color during gray or binary scanning, useful for colored paper or ink"; @@ -2551,6 +2579,18 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) opt->constraint_type = SANE_CONSTRAINT_NONE; } + /*hardware crop*/ + if(option==OPT_HW_CROP){ + opt->name = "hwcrop"; + opt->title = "Hardware crop"; + opt->desc = "Request scanner to crop image automatically"; + opt->type = SANE_TYPE_BOOL; + if (s->has_hwcrop) + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } + /* "Sensor" group ------------------------------------------------------ */ if(option==OPT_SENSOR_GROUP){ opt->name = SANE_NAME_SENSORS; @@ -2671,7 +2711,7 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) /** * Gets or sets an option value. - * + * * From the SANE spec: * This function is used to set or inquire the current value of option * number n of the device represented by handle h. The manner in which @@ -2682,7 +2722,7 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) * area pointed to by v must be big enough to hold the entire option * value (determined by member size in the corresponding option * descriptor). - * + * * The only exception to this rule is that when setting the value of a * string option, the string pointed to by argument v may be shorter * since the backend will stop reading the option value upon @@ -2918,6 +2958,10 @@ sane_control_option (SANE_Handle handle, SANE_Int option, *val_p = s->side; return SANE_STATUS_GOOD; + case OPT_HW_CROP: + *val_p = s->hwcrop; + return SANE_STATUS_GOOD; + /* Sensor Group */ case OPT_START: ret = read_panel(s,OPT_START); @@ -2999,7 +3043,7 @@ sane_control_option (SANE_Handle handle, SANE_Int option, * below. */ switch (option) { - + /* Mode Group */ case OPT_SOURCE: if (!strcmp (val, STRING_ADFFRONT)) { @@ -3024,7 +3068,7 @@ sane_control_option (SANE_Handle handle, SANE_Int option, tmp = SOURCE_FLATBED; } - if (s->u.source == tmp) + if (s->u.source == tmp) return SANE_STATUS_GOOD; s->u.source = tmp; @@ -3056,7 +3100,7 @@ sane_control_option (SANE_Handle handle, SANE_Int option, case OPT_RES: - if (s->u.dpi_x == val_c && s->u.dpi_y == val_c) + if (s->u.dpi_x == val_c && s->u.dpi_y == val_c) return SANE_STATUS_GOOD; s->u.dpi_x = val_c; @@ -3237,6 +3281,10 @@ sane_control_option (SANE_Handle handle, SANE_Int option, s->buffermode = val_c; return SANE_STATUS_GOOD; + case OPT_HW_CROP: + s->hwcrop = val_c; + return SANE_STATUS_GOOD; + } } /* else */ @@ -3251,25 +3299,25 @@ ssm_buffer (struct scanner *s) DBG (10, "ssm_buffer: start\n"); if(s->has_ssm){ - + unsigned char cmd[SET_SCAN_MODE_len]; size_t cmdLen = SET_SCAN_MODE_len; - + unsigned char out[SSM_PAY_len]; size_t outLen = SSM_PAY_len; - + memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SET_SCAN_MODE_code); set_SSM_pf(cmd, 1); set_SSM_pay_len(cmd, outLen); - + memset(out,0,outLen); if(s->has_ssm_pay_head_len){ set_SSM_pay_head_len(out, SSM_PAY_HEAD_len); } set_SSM_page_code(out, SM_pc_buffer); set_SSM_page_len(out, SSM_PAGE_len); - + if(s->s.source == SOURCE_ADF_DUPLEX || s->s.source == SOURCE_CARD_DUPLEX){ set_SSM_BUFF_duplex(out, 1); } @@ -3288,7 +3336,7 @@ ssm_buffer (struct scanner *s) if(0){ set_SSM_BUFF_unk(out,1); } - + ret = do_cmd ( s, 1, 0, cmd, cmdLen, @@ -3301,20 +3349,20 @@ ssm_buffer (struct scanner *s) unsigned char cmd[SET_SCAN_MODE2_len]; size_t cmdLen = SET_SCAN_MODE2_len; - + unsigned char out[SSM2_PAY_len]; size_t outLen = SSM2_PAY_len; - + memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SET_SCAN_MODE2_code); set_SSM2_page_code(cmd, SM2_pc_buffer); set_SSM2_pay_len(cmd, outLen); - + memset(out,0,outLen); set_SSM2_BUFF_unk(out, !s->buffermode); set_SSM2_BUFF_unk2(out, 0x40); set_SSM2_BUFF_sync(out, !s->buffermode); - + ret = do_cmd ( s, 1, 0, cmd, cmdLen, @@ -3338,52 +3386,52 @@ ssm_df (struct scanner *s) SANE_Status ret = SANE_STATUS_GOOD; DBG (10, "ssm_df: start\n"); - + if(!s->has_df){ DBG (10, "ssm_df: unsupported, finishing\n"); return ret; } - + if(s->has_ssm){ unsigned char cmd[SET_SCAN_MODE_len]; size_t cmdLen = SET_SCAN_MODE_len; - + unsigned char out[SSM_PAY_len]; size_t outLen = SSM_PAY_len; - + memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SET_SCAN_MODE_code); set_SSM_pf(cmd, 1); set_SSM_pay_len(cmd, outLen); - + memset(out,0,outLen); if(s->has_ssm_pay_head_len){ set_SSM_pay_head_len(out, SSM_PAY_HEAD_len); } set_SSM_page_code(out, SM_pc_df); set_SSM_page_len(out, SSM_PAGE_len); - + /* deskew by roller */ if(s->rollerdeskew){ set_SSM_DF_deskew_roll(out, 1); } - + /* staple detection */ if(s->stapledetect){ set_SSM_DF_staple(out, 1); } - + /* thickness */ if(s->df_thickness){ set_SSM_DF_thick(out, 1); } - + /* length */ if(s->df_length){ set_SSM_DF_len(out, 1); } - + ret = do_cmd ( s, 1, 0, cmd, cmdLen, @@ -3397,7 +3445,7 @@ ssm_df (struct scanner *s) unsigned char cmd[SET_SCAN_MODE2_len]; size_t cmdLen = SET_SCAN_MODE2_len; - + unsigned char out[SSM2_PAY_len]; size_t outLen = SSM2_PAY_len; @@ -3407,11 +3455,11 @@ ssm_df (struct scanner *s) set_SCSI_opcode(cmd, SET_SCAN_MODE2_code); set_SSM2_page_code(cmd, SM2_pc_ultra); set_SSM2_pay_len(cmd, outLen); - + memset(out,0,outLen); set_SSM2_ULTRA_top(out, 0); set_SSM2_ULTRA_bot(out, 0); - + ret = do_cmd ( s, 1, 0, cmd, cmdLen, @@ -3424,19 +3472,24 @@ ssm_df (struct scanner *s) set_SCSI_opcode(cmd, SET_SCAN_MODE2_code); set_SSM2_page_code(cmd, SM2_pc_df); set_SSM2_pay_len(cmd, outLen); - + memset(out,0,outLen); - + /* thickness */ if(s->df_thickness){ set_SSM2_DF_thick(out, 1); } - + /* length */ if(s->df_length){ set_SSM2_DF_len(out, 1); } - + + /* staple detection */ + if(s->stapledetect){ + set_SSM2_DF_staple(out, 1); + } + ret = do_cmd ( s, 1, 0, cmd, cmdLen, @@ -3455,6 +3508,53 @@ ssm_df (struct scanner *s) return ret; } +static SANE_Status +ssm2_hw_enhancement (struct scanner *s) +{ + SANE_Status ret = SANE_STATUS_GOOD; + + DBG (10, "ssm2_hw_enhancement: start\n"); + + if(s->has_ssm2){ + unsigned char cmd[SET_SCAN_MODE2_len]; + size_t cmdLen = SET_SCAN_MODE2_len; + + unsigned char out[SSM2_PAY_len]; + size_t outLen = SSM2_PAY_len; + + memset(cmd,0,cmdLen); + set_SCSI_opcode(cmd, SET_SCAN_MODE2_code); + set_SSM2_page_code(cmd, SM2_pc_hw_enhancement); + set_SSM2_pay_len(cmd, outLen); + + memset(out,0,outLen); + + if(s->rollerdeskew){ + set_SSM2_roller_deskew(out, 1); + } + + if(s->hwcrop){ + set_SSM2_hw_crop(out, 1); + } + + ret = do_cmd ( + s, 1, 0, + cmd, cmdLen, + out, outLen, + NULL, NULL + ); + + } + + else{ + DBG (10, "ssm2_hw_enhancement: unsupported\n"); + } + + DBG (10, "ssm2_hw_enhancement: finish\n"); + + return ret; +} + static SANE_Status ssm_do (struct scanner *s) { @@ -3466,7 +3566,7 @@ ssm_do (struct scanner *s) DBG (10, "ssm_do: unsupported, finishing\n"); return ret; } - + if(s->s.mode == MODE_COLOR){ DBG (10, "ssm_do: unneeded, finishing\n"); return ret; @@ -3476,24 +3576,24 @@ ssm_do (struct scanner *s) unsigned char cmd[SET_SCAN_MODE_len]; size_t cmdLen = SET_SCAN_MODE_len; - + unsigned char out[SSM_PAY_len]; size_t outLen = SSM_PAY_len; - + memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SET_SCAN_MODE_code); set_SSM_pf(cmd, 1); set_SSM_pay_len(cmd, outLen); - + memset(out,0,outLen); if(s->has_ssm_pay_head_len){ set_SSM_pay_head_len(out, SSM_PAY_HEAD_len); } set_SSM_page_code(out, SM_pc_dropout); set_SSM_page_len(out, SSM_PAGE_len); - + set_SSM_DO_unk1(out, 0x03); - + switch(s->dropout_color[SIDE_FRONT]){ case COLOR_RED: set_SSM_DO_unk2(out, 0x05); @@ -3520,7 +3620,7 @@ ssm_do (struct scanner *s) set_SSM_DO_f_en(out,SSM_DO_blue); break; } - + switch(s->dropout_color[SIDE_BACK]){ case COLOR_RED: set_SSM_DO_unk2(out, 0x05); @@ -3547,7 +3647,7 @@ ssm_do (struct scanner *s) set_SSM_DO_b_en(out,SSM_DO_blue); break; } - + ret = do_cmd ( s, 1, 0, cmd, cmdLen, @@ -3561,17 +3661,17 @@ ssm_do (struct scanner *s) unsigned char cmd[SET_SCAN_MODE2_len]; size_t cmdLen = SET_SCAN_MODE2_len; - + unsigned char out[SSM2_PAY_len]; size_t outLen = SSM2_PAY_len; - + memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SET_SCAN_MODE2_code); set_SSM2_page_code(cmd, SM2_pc_dropout); set_SSM2_pay_len(cmd, outLen); - + memset(out,0,outLen); - + switch(s->dropout_color[SIDE_FRONT]){ case COLOR_RED: set_SSM2_DO_do(out,SSM_DO_red); @@ -3599,6 +3699,46 @@ ssm_do (struct scanner *s) out, outLen, NULL, NULL ); + + if(ret == SANE_STATUS_GOOD && + (s->s.source == SOURCE_ADF_DUPLEX || s->s.source == SOURCE_CARD_DUPLEX)){ + + memset(cmd,0,cmdLen); + set_SCSI_opcode(cmd, SET_SCAN_MODE2_code); + set_SSM2_page_code(cmd, SM2_pc_dropout); + set_SSM2_DO_side(cmd, SIDE_BACK); + set_SSM2_pay_len(cmd, outLen); + + memset(out,0,outLen); + + switch(s->dropout_color[SIDE_BACK]){ + case COLOR_RED: + set_SSM2_DO_do(out,SSM_DO_red); + break; + case COLOR_GREEN: + set_SSM2_DO_do(out,SSM_DO_green); + break; + case COLOR_BLUE: + set_SSM2_DO_do(out,SSM_DO_blue); + break; + case COLOR_EN_RED: + set_SSM2_DO_en(out,SSM_DO_red); + break; + case COLOR_EN_GREEN: + set_SSM2_DO_en(out,SSM_DO_green); + break; + case COLOR_EN_BLUE: + set_SSM2_DO_en(out,SSM_DO_blue); + break; + } + + ret = do_cmd ( + s, 1, 0, + cmd, cmdLen, + out, outLen, + NULL, NULL + ); + } } else{ @@ -3622,7 +3762,7 @@ read_sensors(struct scanner *s,SANE_Int option) size_t inLen = R_SENSORS_len; DBG (10, "read_sensors: start %d\n", option); - + if(!s->can_read_sensors){ DBG (10, "read_sensors: unsupported, finishing\n"); return ret; @@ -3638,14 +3778,14 @@ read_sensors(struct scanner *s,SANE_Int option) set_SCSI_opcode(cmd, READ_code); set_R_datatype_code (cmd, SR_datatype_sensors); set_R_xfer_length (cmd, inLen); - + ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); - + if (ret == SANE_STATUS_GOOD || ret == SANE_STATUS_EOF) { /*set flags indicating there is data to read*/ memset(s->sensors_read,1,sizeof(s->sensors_read)); @@ -3656,12 +3796,12 @@ read_sensors(struct scanner *s,SANE_Int option) ret = SANE_STATUS_GOOD; } } - + if(option) s->sensors_read[option-OPT_ADF_LOADED] = 0; DBG (10, "read_sensors: finish\n"); - + return ret; } @@ -3677,7 +3817,7 @@ read_panel(struct scanner *s,SANE_Int option) size_t inLen = R_PANEL_len; DBG (10, "read_panel: start %d\n", option); - + if(!s->can_read_panel){ DBG (10, "read_panel: unsupported, finishing\n"); return ret; @@ -3693,14 +3833,14 @@ read_panel(struct scanner *s,SANE_Int option) set_SCSI_opcode(cmd, READ_code); set_R_datatype_code (cmd, SR_datatype_panel); set_R_xfer_length (cmd, inLen); - + ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); - + if (ret == SANE_STATUS_GOOD || ret == SANE_STATUS_EOF) { /*set flags indicating there is data to read*/ memset(s->panel_read,1,sizeof(s->panel_read)); @@ -3717,12 +3857,12 @@ read_panel(struct scanner *s,SANE_Int option) ret = SANE_STATUS_GOOD; } } - + if(option) s->panel_read[option-OPT_START] = 0; DBG (10, "read_panel: finish %d\n",s->panel_counter); - + return ret; } @@ -3752,23 +3892,111 @@ send_panel(struct scanner *s) memset(out,0,outLen); set_S_PANEL_enable_led(out,s->panel_enable_led); set_S_PANEL_counter(out,s->panel_counter); - + ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); - + if (ret == SANE_STATUS_EOF) { ret = SANE_STATUS_GOOD; } - + DBG (10, "send_panel: finish %d\n", ret); - + return ret; } +/* + * Request the size of the scanned image + */ +/* we should really be updating s->s and s->i instead */ +static SANE_Status +get_pixelsize(struct scanner *s) +{ + SANE_Status ret = SANE_STATUS_GOOD; + + unsigned char cmd[READ_len]; + size_t cmdLen = READ_len; + + unsigned char in[R_PSIZE_len]; + size_t inLen = R_PSIZE_len; + + int i = 0; + const int MAX_TRIES = 5; + + DBG (10, "get_pixelsize: start\n"); + + if(!s->hwcrop){ + DBG (10, "get_pixelsize: unneeded, finishing\n"); + return ret; + } + + memset(cmd,0,cmdLen); + set_SCSI_opcode(cmd, READ_code); + set_R_datatype_code(cmd, SR_datatype_pixelsize); + set_R_xfer_lid(cmd, 0x02); + set_R_xfer_length(cmd, inLen); + + /* May need to retry/block until the scanner is done */ + for(i=0;i 0 && + get_R_PSIZE_length(in) > 0){ + + DBG (15, "get_pixelsize: w:%d h:%d\n", + get_R_PSIZE_width(in) * s->u.dpi_x / 1200, + get_R_PSIZE_length(in) * s->u.dpi_y / 1200); + + /* + * Round up to byte boundary if needed. + * For 1 bpp the resulting size may not fit in a byte boundary. + */ + int remainder = (get_R_PSIZE_width(in) * s->u.dpi_x / 1200) % 8; + + if (s->u.mode < MODE_GRAYSCALE && remainder) + { + int rounded_up = (8 - remainder) + (get_R_PSIZE_width(in) * s->u.dpi_x / 1200); + + s->u.br_x = rounded_up * 1200 / s->u.dpi_x; + } + else{ + s->u.br_x = get_R_PSIZE_width(in); + } + + s->u.tl_x = 0; + s->u.br_y = get_R_PSIZE_length(in); + s->u.tl_y = 0; + + s->u.page_x = s->u.br_x; + s->u.page_y = s->u.br_y; + + update_params(s,0); + clean_params(s); + break; + } + + else{ + DBG (10, "get_pixelsize: error reading, status = %d w:%d h:%d\n", + ret, get_R_PSIZE_width(in), get_R_PSIZE_length(in)); + ret = SANE_STATUS_INVAL; + usleep(1000000); + } + } + DBG (10, "get_pixelsize: finish\n"); + + return ret; +} + /* * @@ Section 4 - SANE scanning functions */ @@ -3783,7 +4011,7 @@ send_panel(struct scanner *s) * completion of that request. Outside of that window, the returned * values are best-effort estimates of what the parameters will be * when sane_start() gets invoked. - * + * * Calling this function before a scan has actually started allows, * for example, to get an estimate of how big the scanned image will * be. The parameters passed to this function are the handle h of the @@ -3795,7 +4023,7 @@ sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { SANE_Status ret = SANE_STATUS_GOOD; struct scanner *s = (struct scanner *) handle; - + DBG (10, "sane_get_parameters: start\n"); if(!s->started){ @@ -3825,14 +4053,14 @@ sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) DBG(15,"sane_get_parameters: area: tlx=%d, brx=%d, tly=%d, bry=%d\n", s->i.tl_x, s->i.br_x, s->i.tl_y, s->i.br_y); - DBG (15, "sane_get_parameters: params: ppl=%d, Bpl=%d, lines=%d\n", + DBG (15, "sane_get_parameters: params: ppl=%d, Bpl=%d, lines=%d\n", params->pixels_per_line, params->bytes_per_line, params->lines); - DBG (15, "sane_get_parameters: params: format=%d, depth=%d, last=%d\n", + DBG (15, "sane_get_parameters: params: format=%d, depth=%d, last=%d\n", params->format, params->depth, params->last_frame); DBG (10, "sane_get_parameters: finish\n"); - + return ret; } @@ -3840,9 +4068,9 @@ SANE_Status update_params(struct scanner *s, int calib) { SANE_Status ret = SANE_STATUS_GOOD; - + DBG (10, "update_params: start\n"); - + s->u.width = (s->u.br_x - s->u.tl_x) * s->u.dpi_x / 1200; s->u.height = (s->u.br_y - s->u.tl_y) * s->u.dpi_y / 1200; @@ -3884,7 +4112,7 @@ update_params(struct scanner *s, int calib) s->u.br_x, s->u.tl_x, s->u.dpi_x, s->u.br_y, s->u.tl_y, s->u.dpi_y); /* some scanners are limited in their valid scan params - * make a second version of the params struct, but + * make a second version of the params struct, but * override the user's values with what the scanner can actually do */ memcpy(&s->s,&s->u,sizeof(struct img_params)); @@ -3971,7 +4199,7 @@ update_params(struct scanner *s, int calib) /* round lines up to even number */ s->s.height += s->s.height % 2; - + DBG (15, "update_params: scan params: w:%d h:%d m:%d f:%d b:%d\n", s->s.width, s->s.height, s->s.mode, s->s.format, s->s.bpp); DBG (15, "update_params: scan params: B:%d vB:%d vw:%d\n", @@ -4011,12 +4239,12 @@ SANE_Status update_i_params(struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; - + DBG (10, "update_i_params: start\n"); s->i.width = s->u.width; s->i.Bpl = s->u.Bpl; - + DBG (10, "update_i_params: finish\n"); return ret; } @@ -4105,6 +4333,16 @@ sane_start (SANE_Handle handle) DBG (5, "sane_start: ERROR: cannot send panel\n"); } + /* we should really be updating s->s and s->i instead */ + if(s->hwcrop){ + s->u.br_x = s->max_x; + s->u.tl_x = 0; + s->u.br_y = s->max_y; + s->u.tl_y = 0; + s->u.page_x = s->max_x; + s->u.page_y = s->max_y; + } + /* load our own private copy of scan params */ ret = update_params(s,0); if (ret != SANE_STATUS_GOOD) { @@ -4140,6 +4378,12 @@ sane_start (SANE_Handle handle) goto errors; } + ret = ssm2_hw_enhancement(s); + if (ret != SANE_STATUS_GOOD) { + DBG (5, "sane_start: ERROR: cannot ssm2 hw enhancement\n"); + goto errors; + } + /* clean scan params for new scan */ ret = clean_params(s); if (ret != SANE_STATUS_GOOD) { @@ -4169,7 +4413,7 @@ sane_start (SANE_Handle handle) DBG (5, "sane_start: ERROR: cannot load page\n"); goto errors; } - + /* wait for scanner to finish load */ ret = wait_scanner (s); if (ret != SANE_STATUS_GOOD) { @@ -4185,6 +4429,12 @@ sane_start (SANE_Handle handle) goto errors; } + ret = get_pixelsize(s); + if (ret != SANE_STATUS_GOOD) { + DBG (5, "sane_start: ERROR: cannot get pixel size\n"); + goto errors; + } + s->started = 1; } @@ -4235,7 +4485,7 @@ sane_start (SANE_Handle handle) } } } - + /* small, buffering scanners check for more pages by reading counter */ else{ ret = read_panel (s, OPT_COUNTER); @@ -4251,6 +4501,12 @@ sane_start (SANE_Handle handle) DBG (5, "sane_start: diff counter (%d/%d)\n", s->prev_page,s->panel_counter); } + + ret = get_pixelsize(s); + if (ret != SANE_STATUS_GOOD) { + DBG (5, "sane_start: ERROR: cannot get pixel size\n"); + goto errors; + } } } @@ -4261,9 +4517,9 @@ sane_start (SANE_Handle handle) DBG (15, "started=%d, side=%d, source=%d\n", s->started, s->side, s->u.source); - /* certain options require the entire image to + /* certain options require the entire image to * be collected from the scanner before we can - * tell the user the size of the image. the sane + * tell the user the size of the image. the sane * API has no way to inform the frontend of this, * so we block and buffer. yuck */ if(must_fully_buffer(s)){ @@ -4294,7 +4550,7 @@ sane_start (SANE_Handle handle) } if(s->swskip){ /* Skipping means throwing out this image. - * Pretend the user read the whole thing + * Pretend the user read the whole thing * and call sane_start again. * This assumes we are running in batch mode. */ if(buffer_isblank(s,s->side)){ @@ -4349,7 +4605,7 @@ clean_params (struct scanner *s) s->s.bytes_tot[0]=0; s->s.bytes_tot[1]=0; - /* store the number of front bytes */ + /* store the number of front bytes */ if ( s->u.source != SOURCE_ADF_BACK && s->u.source != SOURCE_CARD_BACK ) s->u.bytes_tot[SIDE_FRONT] = s->u.Bpl * s->u.height; @@ -4359,8 +4615,8 @@ clean_params (struct scanner *s) if ( s->s.source != SOURCE_ADF_BACK && s->s.source != SOURCE_CARD_BACK ) s->s.bytes_tot[SIDE_FRONT] = s->s.Bpl * s->s.height; - /* store the number of back bytes */ - if ( s->u.source == SOURCE_ADF_DUPLEX || s->u.source == SOURCE_ADF_BACK + /* store the number of back bytes */ + if ( s->u.source == SOURCE_ADF_DUPLEX || s->u.source == SOURCE_ADF_BACK || s->u.source == SOURCE_CARD_DUPLEX || s->u.source == SOURCE_CARD_BACK ) s->u.bytes_tot[SIDE_BACK] = s->u.Bpl * s->u.height; @@ -4421,14 +4677,14 @@ set_window (struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; - /* The command specifies the number of bytes in the data phase - * the data phase has a header, followed by 1 window desc block + /* The command specifies the number of bytes in the data phase + * the data phase has a header, followed by 1 window desc block * the header specifies the number of bytes in 1 window desc block */ unsigned char cmd[SET_WINDOW_len]; size_t cmdLen = SET_WINDOW_len; - + unsigned char out[SW_header_len + SW_desc_len]; size_t outLen = SW_header_len + SW_desc_len; @@ -4484,9 +4740,9 @@ set_window (struct scanner *s) /*convert our common -127 to +127 range into HW's range *FIXME: this code assumes hardware range of 0-255 */ set_WD_brightness (desc1, s->brightness+128); - + set_WD_threshold (desc1, s->threshold); - + /*convert our common -127 to +127 range into HW's range *FIXME: this code assumes hardware range of 0-255 */ set_WD_contrast (desc1, s->contrast+128); @@ -4593,7 +4849,7 @@ object_position (struct scanner *s, int i_load) /* * Issues SCAN command. - * + * * (This doesn't actually read anything, it just tells the scanner * to start scanning.) */ @@ -4641,7 +4897,7 @@ start_scan (struct scanner *s, int type) /* * Called by SANE to read data. - * + * * From the SANE spec: * This function is used to read image data from the device * represented by handle h. Argument buf is a pointer to a memory @@ -4649,7 +4905,7 @@ start_scan (struct scanner *s, int type) * returned is stored in *len. A backend must set this to zero when * the call fails (i.e., when a status other than SANE_STATUS_GOOD is * returned). - * + * * When the call succeeds, the number of bytes returned can be * anywhere in the range from 0 to maxlen bytes. */ @@ -4698,7 +4954,7 @@ sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len } } } - + /* simplex or non-alternating duplex */ else{ if(!s->s.eof[s->side]){ @@ -4719,7 +4975,7 @@ sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len ret = read_from_buffer(s,buf,max_len,len,s->side); if(ret) goto errors; - + ret = check_for_cancel(s); s->reading = 0; @@ -4813,13 +5069,13 @@ read_from_scanner(struct scanner *s, int side, int exact) size_t i; for(i=0;ijpeg_stage == JPEG_STAGE_NONE && in[i] == 0xff){ s->jpeg_ff_offset=0; continue; } - + s->jpeg_ff_offset++; /* last byte was an ff, this byte is SOF */ @@ -4827,7 +5083,7 @@ read_from_scanner(struct scanner *s, int side, int exact) s->jpeg_stage = JPEG_STAGE_SOF; continue; } - + if(s->jpeg_stage == JPEG_STAGE_SOF){ /* lines in start of frame, overwrite it */ @@ -4839,7 +5095,7 @@ read_from_scanner(struct scanner *s, int side, int exact) in[i] = s->s.height & 0xff; continue; } - + /* width in start of frame, overwrite it */ if(s->jpeg_ff_offset == 7){ in[i] = (s->s.width >> 8) & 0xff; @@ -4883,7 +5139,7 @@ read_from_scanner(struct scanner *s, int side, int exact) s->s.bytes_tot[side] = s->s.bytes_sent[side]; s->i.bytes_tot[side] = s->i.bytes_sent[side]; s->u.bytes_tot[side] = s->i.bytes_sent[side]; - } + } /* this is non-jpeg data, fill remainder, change rx'd size */ else{ @@ -5025,7 +5281,7 @@ read_from_scanner_duplex(struct scanner *s,int exact) return ret; } -/* these functions copy image data from input buffer to scanner struct +/* these functions copy image data from input buffer to scanner struct * descrambling it, and putting it in the right side buffer */ /* NOTE: they assume buffer is scanline aligned */ static SANE_Status @@ -5051,7 +5307,7 @@ copy_simplex(struct scanner *s, unsigned char * buf, int len, int side) s->s.bytes_sent[side] += len; return ret; } - + DBG (15, "copy_simplex: per-line copy\n"); line = malloc(bwidth); @@ -5061,7 +5317,7 @@ copy_simplex(struct scanner *s, unsigned char * buf, int len, int side) for(i=0; is.bytes_sent[side] / bwidth; - + /*increment number of bytes rx'd from scanner*/ s->s.bytes_sent[side] += bwidth; @@ -5075,9 +5331,9 @@ copy_simplex(struct scanner *s, unsigned char * buf, int len, int side) line_next = 0; if(s->s.format == SANE_FRAME_GRAY){ - + switch (s->gray_interlace[side]) { - + /* one line has the following format: ggg...GGG * where the 'capital' letters are the beginning of the line */ case GRAY_INTERLACE_gG: @@ -5086,10 +5342,10 @@ copy_simplex(struct scanner *s, unsigned char * buf, int len, int side) line[line_next++] = buf[i+j]; } break; - + case GRAY_INTERLACE_2510: DBG (17, "copy_simplex: gray, 2510\n"); - + /* first read head (third byte of every three) */ for(j=bwidth-1;j>=0;j-=3){ line[line_next++] = buf[i+j]; @@ -5109,11 +5365,11 @@ copy_simplex(struct scanner *s, unsigned char * buf, int len, int side) break; } } - + else if (s->s.format == SANE_FRAME_RGB){ - + switch (inter) { - + /* scanner returns color data as bgrbgr... */ case COLOR_INTERLACE_BGR: DBG (17, "copy_simplex: color, BGR\n"); @@ -5123,7 +5379,7 @@ copy_simplex(struct scanner *s, unsigned char * buf, int len, int side) line[line_next++] = buf[i+j*3]; } break; - + /* scanner returns color data as gbrgbr... */ case COLOR_INTERLACE_GBR: DBG (17, "copy_simplex: color, GBR\n"); @@ -5133,7 +5389,7 @@ copy_simplex(struct scanner *s, unsigned char * buf, int len, int side) line[line_next++] = buf[i+j*3+1]; } break; - + /* scanner returns color data as brgbrg... */ case COLOR_INTERLACE_BRG: DBG (17, "copy_simplex: color, BRG\n"); @@ -5143,7 +5399,7 @@ copy_simplex(struct scanner *s, unsigned char * buf, int len, int side) line[line_next++] = buf[i+j*3]; } break; - + /* one line has the following format: RRR...rrrGGG...gggBBB...bbb */ case COLOR_INTERLACE_RRGGBB: DBG (17, "copy_simplex: color, RRGGBB\n"); @@ -5153,7 +5409,7 @@ copy_simplex(struct scanner *s, unsigned char * buf, int len, int side) line[line_next++] = buf[i+2*pwidth+j]; } break; - + /* one line has the following format: rrr...RRRggg...GGGbbb...BBB * where the 'capital' letters are the beginning of the line */ case COLOR_INTERLACE_rRgGbB: @@ -5164,10 +5420,10 @@ copy_simplex(struct scanner *s, unsigned char * buf, int len, int side) line[line_next++] = buf[i+2*pwidth+j]; } break; - + case COLOR_INTERLACE_2510: DBG (17, "copy_simplex: color, 2510\n"); - + /* first read head (third byte of every three) */ for(j=t-1;j>=0;j-=3){ line[line_next++] = buf[i+j]; @@ -5193,7 +5449,7 @@ copy_simplex(struct scanner *s, unsigned char * buf, int len, int side) break; } } - + /* nothing sent above? just copy one line of the block */ /* used by uninterlaced gray/color */ if(!line_next){ @@ -5201,14 +5457,14 @@ copy_simplex(struct scanner *s, unsigned char * buf, int len, int side) memcpy(line+line_next,buf+i,bwidth); line_next = bwidth; } - + /* invert image if scanner needs it for this mode */ if(s->reverse_by_mode[s->s.mode]){ for(j=0; jf_offset[side]){ DBG (17, "copy_simplex: apply offset\n"); @@ -5227,7 +5483,7 @@ copy_simplex(struct scanner *s, unsigned char * buf, int len, int side) line[j] = curr; } } - + /* apply brightness and contrast if hardware cannot do it */ if(s->sw_lut && (s->s.mode == MODE_COLOR || s->s.mode == MODE_GRAYSCALE)){ DBG (17, "copy_simplex: apply brightness/contrast\n"); @@ -5283,7 +5539,7 @@ copy_duplex(struct scanner *s, unsigned char * buf, int len) DBG (10, "copy_duplex: 2510\n"); for(i=0; ii.width != s->s.width){ offset = ((s->valid_x-s->i.page_x) / 2 + s->i.tl_x) * s->i.dpi_x/1200; @@ -5481,12 +5737,12 @@ copy_line(struct scanner *s, unsigned char * buff, int side) /* change mode, store line in buffer */ switch (s->i.mode) { - + case MODE_COLOR: memcpy(s->buffers[side]+s->i.bytes_sent[side], line+(offset*3), ibwidth); s->i.bytes_sent[side] += ibwidth; break; - + case MODE_GRAYSCALE: for(i=0;i remain) bytes = remain; - + *len = bytes; - + /*FIXME this needs to timeout eventually */ if(!bytes){ DBG(5,"read_from_buffer: nothing to do\n"); return SANE_STATUS_GOOD; } - + DBG(15, "read_from_buffer: si:%d to:%d tx:%d bu:%d pa:%d\n", side, s->i.bytes_tot[side], s->u.bytes_sent[side], max_len, bytes); @@ -5701,7 +5957,7 @@ calibrate_AFE (struct scanner *s) DBG (5, "calibrate_AFE: ERROR: cannot set window\n"); goto cleanup; } - + /* first pass (black offset), lamp off, no offset/gain/exposure */ DBG (15, "calibrate_AFE: offset\n"); @@ -5925,7 +6181,7 @@ calibrate_fine_buffer (struct scanner *s) DBG (5, "calibrate_fine_buffer: ERROR: cannot load offset buffers\n"); goto cleanup; } - + DBG (5, "calibrate_fine_buffer: %d %x\n", s->s.dpi_x/10, s->s.dpi_x/10); memset(cmd,0,cmdLen); @@ -5952,13 +6208,13 @@ calibrate_fine_buffer (struct scanner *s) /*color mode, expand offset across all three channels? */ if(s->s.format == SANE_FRAME_RGB){ for(j=0; js.valid_width; j++){ - - /*red*/ + + /*red*/ s->f_offset[i][j*3] = in[j*2+i]; if(s->f_offset[i][j*3] < 1) s->f_offset[i][j*3] = 1; - /*green and blue, same as red*/ + /*green and blue, same as red*/ s->f_offset[i][j*3+1] = s->f_offset[i][j*3+2] = s->f_offset[i][j*3]; } } @@ -5995,10 +6251,10 @@ calibrate_fine_buffer (struct scanner *s) int codes[] = {R_FINE_uid_red,R_FINE_uid_green,R_FINE_uid_blue}; for(k=0;k<3;k++){ - + set_R_xfer_uid (cmd, codes[k]); inLen = reqLen; - + hexdump(15, "cmd:", cmd, cmdLen); ret = do_cmd ( @@ -6009,12 +6265,12 @@ calibrate_fine_buffer (struct scanner *s) ); if (ret != SANE_STATUS_GOOD) goto cleanup; - + for(i=0;i<2;i++){ for(j=0; js.valid_width; j++){ - + s->f_gain[i][j*3+k] = in[j*2+i]*3/4; - + if(s->f_gain[i][j*3+k] < 1) s->f_gain[i][j*3+k] = 1; } @@ -6027,7 +6283,7 @@ calibrate_fine_buffer (struct scanner *s) set_R_xfer_uid (cmd, R_FINE_uid_gray); inLen = reqLen; - + hexdump(15, "cmd:", cmd, cmdLen); ret = do_cmd ( @@ -6038,12 +6294,12 @@ calibrate_fine_buffer (struct scanner *s) ); if (ret != SANE_STATUS_GOOD) goto cleanup; - + for(i=0;i<2;i++){ for(j=0; js.valid_width; j++){ - + s->f_gain[i][j] = in[j*2+i]*3/4; - + if(s->f_gain[i][j] < 1) s->f_gain[i][j] = 1; } @@ -6127,7 +6383,7 @@ calibrate_fine (struct scanner *s) DBG (5, "calibrate_fine: ERROR: cannot load buffers\n"); goto cleanup; } - + /*blast the existing fine cal data so reading code wont apply it*/ ret = offset_buffers(s,0); ret = gain_buffers(s,0); @@ -6138,14 +6394,14 @@ calibrate_fine (struct scanner *s) DBG (5, "calibrate_fine: ERROR: cannot ssm buffer\n"); goto cleanup; } - + /* set window command */ ret = set_window(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_fine: ERROR: cannot set window\n"); goto cleanup; } - + /*handle fifth pass (fine offset), lamp off*/ DBG (15, "calibrate_fine: offset\n"); ret = calibration_scan(s,0xff); @@ -6159,7 +6415,7 @@ calibrate_fine (struct scanner *s) DBG (5, "calibrate_fine: ERROR: cannot load offset buffers\n"); goto cleanup; } - + for(i=0;i<2;i++){ for(j=0; js.valid_Bpl; j++){ min = 0; @@ -6184,7 +6440,7 @@ calibrate_fine (struct scanner *s) DBG (5, "calibrate_fine: ERROR: cannot load gain buffers\n"); goto cleanup; } - + for(i=0;i<2;i++){ for(j=0; js.valid_Bpl; j++){ max = 0; @@ -6231,14 +6487,14 @@ calibration_scan (struct scanner *s, int scan) DBG (5, "calibration_scan: ERROR: cannot clean_params\n"); return ret; } - + /* start scanning */ ret = start_scan (s,scan); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibration_scan: ERROR: cannot start_scan\n"); return ret; } - + while(!s->s.eof[SIDE_FRONT] && !s->s.eof[SIDE_BACK]){ ret = read_from_scanner_duplex(s,1); } @@ -6260,7 +6516,7 @@ write_AFE(struct scanner *s) size_t cmdLen = COR_CAL_len; /*use the longest payload for buffer*/ - unsigned char pay[CC3_pay_len]; + unsigned char pay[CC3_pay_len]; size_t payLen = CC3_pay_len; DBG (10, "write_AFE: start\n"); @@ -6272,7 +6528,7 @@ write_AFE(struct scanner *s) set_SCSI_opcode(cmd, COR_CAL_code); set_CC_version(cmd,CC3_pay_ver); set_CC_xferlen(cmd,payLen); - + memset(pay,0,payLen); set_CC3_gain_f_r(pay,s->c_gain[SIDE_FRONT]); @@ -6282,7 +6538,7 @@ write_AFE(struct scanner *s) set_CC3_off_f_r(pay,s->c_offset[SIDE_FRONT]); set_CC3_off_f_g(pay,s->c_offset[SIDE_FRONT]); set_CC3_off_f_b(pay,s->c_offset[SIDE_FRONT]); - + set_CC3_exp_f_r(pay,s->c_exposure[SIDE_FRONT][CHAN_RED]); set_CC3_exp_f_g(pay,s->c_exposure[SIDE_FRONT][CHAN_GREEN]); set_CC3_exp_f_b(pay,s->c_exposure[SIDE_FRONT][CHAN_BLUE]); @@ -6307,7 +6563,7 @@ write_AFE(struct scanner *s) set_SCSI_opcode(cmd, COR_CAL_code); set_CC_version(cmd,CC_pay_ver); set_CC_xferlen(cmd,payLen); - + memset(pay,0,payLen); set_CC_f_gain(pay,s->c_gain[SIDE_FRONT]); set_CC_unk1(pay,1); @@ -6319,7 +6575,7 @@ write_AFE(struct scanner *s) set_CC_exp_f_r2(pay,s->c_exposure[SIDE_FRONT][CHAN_RED]); set_CC_exp_f_g2(pay,s->c_exposure[SIDE_FRONT][CHAN_GREEN]); set_CC_exp_f_b2(pay,s->c_exposure[SIDE_FRONT][CHAN_BLUE]); - + set_CC_b_gain(pay,s->c_gain[SIDE_BACK]); set_CC_b_offset(pay,s->c_offset[SIDE_BACK]); set_CC_exp_b_r1(pay,s->c_exposure[SIDE_BACK][CHAN_RED]); @@ -6414,26 +6670,26 @@ gain_buffers (struct scanner *s, int setup) * @@ Section 6 - SANE cleanup functions */ /* - * Cancels a scan. + * Cancels a scan. * * It has been said on the mailing list that sane_cancel is a bit of a * misnomer because it is routinely called to signal the end of a * batch - quoting David Mosberger-Tang: - * + * * > In other words, the idea is to have sane_start() be called, and * > collect as many images as the frontend wants (which could in turn * > consist of multiple frames each as indicated by frame-type) and - * > when the frontend is done, it should call sane_cancel(). + * > when the frontend is done, it should call sane_cancel(). * > Sometimes it's better to think of sane_cancel() as "sane_stop()" * > but that name would have had some misleading connotations as * > well, that's why we stuck with "cancel". - * + * * The current consensus regarding duplex and ADF scans seems to be * the following call sequence: sane_start; sane_read (repeat until * EOF); sane_start; sane_read... and then call sane_cancel if the * batch is at an end. I.e. do not call sane_cancel during the run but * as soon as you get a SANE_STATUS_NO_DOCS. - * + * * From the SANE spec: * This function is used to immediately or as quickly as possible * cancel the currently pending operation of the device represented by @@ -6478,13 +6734,13 @@ check_for_cancel(struct scanner *s) if(s->started && s->cancelled){ unsigned char cmd[CANCEL_len]; size_t cmdLen = CANCEL_len; - + DBG (15, "check_for_cancel: cancelling\n"); - + /* cancel scan */ memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, CANCEL_code); - + ret = do_cmd ( s, 1, 0, cmd, cmdLen, @@ -6494,7 +6750,7 @@ check_for_cancel(struct scanner *s) if(ret){ DBG (5, "check_for_cancel: ignoring bad cancel: %d\n",ret); } - + ret = object_position(s,SANE_FALSE); if(ret){ DBG (5, "check_for_cancel: ignoring bad eject: %d\n",ret); @@ -6516,7 +6772,7 @@ check_for_cancel(struct scanner *s) /* * Ends use of the scanner. - * + * * From the SANE spec: * This function terminates the association between the device handle * passed in argument h and the device it represents. If the device is @@ -6560,7 +6816,7 @@ disconnect_fd (struct scanner *s) /* * Terminates the backend. - * + * * From the SANE spec: * This function must be called to terminate use of a backend. The * function will first close all device handles that still might be @@ -6983,19 +7239,19 @@ do_usb_cmd(struct scanner *s, int runRS, int shortTime, DBG(5,"cmd: no mem\n"); return SANE_STATUS_NO_MEM; } - + /* build a USB packet around the SCSI command */ cmdBuffer[3] = cmdLength-4; cmdBuffer[5] = 1; cmdBuffer[6] = 0x90; memcpy(cmdBuffer+cmdOffset,cmdBuff,cmdLen); - + /* write the command out */ DBG(25, "cmd: writing %d bytes, timeout %d\n", (int)cmdLength, cmdTimeout); hexdump(30, "cmd: >>", cmdBuffer, cmdLength); ret = sanei_usb_write_bulk(s->fd, cmdBuffer, &cmdActual); DBG(25, "cmd: wrote %d bytes, retVal %d\n", (int)cmdActual, ret); - + if(cmdLength != cmdActual){ DBG(5,"cmd: wrong size %d/%d\n", (int)cmdLength, (int)cmdActual); free(cmdBuffer); @@ -7043,19 +7299,19 @@ do_usb_cmd(struct scanner *s, int runRS, int shortTime, DBG(5,"out: no mem\n"); return SANE_STATUS_NO_MEM; } - + /* build a USB packet around the SCSI command */ outBuffer[3] = outLength-4; outBuffer[5] = 2; outBuffer[6] = 0xb0; memcpy(outBuffer+outOffset,outBuff,outLen); - + /* write the command out */ DBG(25, "out: writing %d bytes, timeout %d\n", (int)outLength, outTimeout); hexdump(30, "out: >>", outBuffer, outLength); ret = sanei_usb_write_bulk(s->fd, outBuffer, &outActual); DBG(25, "out: wrote %d bytes, retVal %d\n", (int)outActual, ret); - + if(outLength != outActual){ DBG(5,"out: wrong size %d/%d\n", (int)outLength, (int)outActual); free(outBuffer); @@ -7102,7 +7358,7 @@ do_usb_cmd(struct scanner *s, int runRS, int shortTime, DBG(5,"in: no mem\n"); return SANE_STATUS_NO_MEM; } - + DBG(25, "in: reading %d bytes, timeout %d\n", (int)inActual, inTimeout); ret = sanei_usb_read_bulk(s->fd, inBuffer, &inActual); DBG(25, "in: read %d bytes, retval %d\n", (int)inActual, ret); @@ -7161,11 +7417,11 @@ do_usb_cmd(struct scanner *s, int runRS, int shortTime, ret = SANE_STATUS_EOF; DBG(5,"in: short read, %d/%d\n", (int)inLength,(int)inActual); } - + /* ignore the USB packet around the SCSI command */ *inLen = inActual - inOffset; memcpy(inBuff,inBuffer+inOffset,*inLen); - + free(inBuffer); } @@ -7214,12 +7470,12 @@ do_usb_status(struct scanner *s, int runRS, int shortTime, size_t * extraLength) DBG(5,"stat: no mem\n"); return SANE_STATUS_NO_MEM; } - + DBG(25, "stat: reading %d bytes, timeout %d\n", (int)statLength, statTimeout); ret = sanei_usb_read_bulk(s->fd, statBuffer, &statActual); DBG(25, "stat: read %d bytes, retval %d\n", (int)statActual, ret); hexdump(30, "stat: <<", statBuffer, statActual); - + /*weird status*/ if(ret != SANE_STATUS_GOOD){ DBG(5,"stat: clearing error '%s'\n",sane_strstatus(ret)); @@ -7232,7 +7488,7 @@ do_usb_status(struct scanner *s, int runRS, int shortTime, size_t * extraLength) } /*inspect the status byte of the response*/ else if(statBuffer[statOffset]){ - DBG(5,"stat: status %d\n",statBuffer[statLength-1-4]); + DBG(5,"stat: status %d\n",statBuffer[statOffset]); ret = do_usb_clear(s,0,runRS); } @@ -7286,7 +7542,7 @@ do_usb_clear(struct scanner *s, int clear, int runRS) rs_in, &rs_inLen ); DBG(25,"rs sub call <<\n"); - + if(ret2 == SANE_STATUS_EOF){ DBG(5,"rs: got EOF, returning IO_ERROR\n"); return SANE_STATUS_IO_ERROR; @@ -7309,7 +7565,7 @@ do_usb_clear(struct scanner *s, int clear, int runRS) } static SANE_Status -wait_scanner(struct scanner *s) +wait_scanner(struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; @@ -7360,11 +7616,11 @@ wait_scanner(struct scanner *s) /* Some scanners have per-resolution * color interlacing values, but most - * don't. This helper can tell the + * don't. This helper can tell the * difference. */ static int -get_color_inter(struct scanner *s, int side, int res) +get_color_inter(struct scanner *s, int side, int res) { int i; for(i=0;iu.page_x; @@ -7408,7 +7664,7 @@ get_page_width(struct scanner *s) * due to using FB or overscan. */ static int -get_page_height(struct scanner *s) +get_page_height(struct scanner *s) { int height = s->u.page_y; @@ -7600,7 +7856,7 @@ buffer_crop(struct scanner *s, int side) /* if we will later binarize this image, make sure the width * is a multiple of 8 pixels, by adjusting the right side */ - if ( must_downsample(s) && s->u.mode < MODE_GRAYSCALE ){ + if ( must_downsample(s) && s->u.mode < MODE_GRAYSCALE ){ s->crop_vals[3] -= (s->crop_vals[3]-s->crop_vals[2]) % 8; } @@ -7618,13 +7874,13 @@ buffer_crop(struct scanner *s, int side) s->i.width = s->s_params.pixels_per_line; s->i.height = s->s_params.lines; s->i.Bpl = s->s_params.bytes_per_line; - + /* update image size counter to new, smaller size */ s->i.bytes_tot[side] = s->s_params.lines * s->s_params.bytes_per_line; s->i.bytes_sent[side] = s->i.bytes_tot[side]; s->u.bytes_sent[side] = 0; - - cleanup: + + cleanup: DBG (10, "buffer_crop: finish\n"); return ret; } @@ -7679,7 +7935,7 @@ buffer_isblank(struct scanner *s, int side) return status; } -/* certain options require the entire image to +/* certain options require the entire image to * be collected from the scanner before we can * tell the user the size of the image. */ static int @@ -7696,7 +7952,7 @@ must_fully_buffer(struct scanner *s) return 0; } -/* certain scanners require the mode of the +/* certain scanners require the mode of the * image to be changed in software. */ static int must_downsample(struct scanner *s) @@ -7714,7 +7970,7 @@ must_downsample(struct scanner *s) used by scanners to implement brightness/contrast/gamma or by backends to speed binarization/thresholding - offset and slope inputs are -127 to +127 + offset and slope inputs are -127 to +127 slope rotates line around central input/output val, 0 makes horizontal line @@ -7730,9 +7986,9 @@ must_downsample(struct scanner *s) offset moves line vertically, and clamps to output range 0 keeps the line crossing the center of the table - pos zero neg + pos zero neg . xxxxxxxx . xx . - . x . x . + . x . x . out x . x . x . . x . x ............ xx.......... xxxxxxxx.... @@ -7761,7 +8017,7 @@ load_lut (unsigned char * lut, * first [-127,127] to [-.999,.999] * then to [-PI/4,PI/4] then [0,PI/2] * then take the tangent (T.O.A) - * then multiply by the normal linear slope + * then multiply by the normal linear slope * because the table may not be square, i.e. 1024x256*/ rise = tan((double)slope/128 * M_PI_4 + M_PI_4) * max_out_val / max_in_val; @@ -7793,4 +8049,3 @@ load_lut (unsigned char * lut, DBG (10, "load_lut: finish\n"); return ret; } - -- cgit v1.2.3