diff options
Diffstat (limited to 'backend/canon_dr.c')
-rw-r--r-- | backend/canon_dr.c | 1627 |
1 files changed, 383 insertions, 1244 deletions
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; i<len; i+=dbwidth){ - memcpy(front+flen,buf+i,bwidth); + if(s->duplex_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; i<len; i+=2){ front[flen++] = buf[i]; @@ -5250,6 +5341,22 @@ copy_duplex(struct scanner *s, unsigned char * buf, int len) return ret; } +/* + * Reverse memcpy designed to mirror a line of data. + * Use stride size to account for the number of bytes per pixel + */ +static void rmemcpy(void* dest, const void* src, size_t count, size_t stride) { + char* dstptr = (char*)dest; + char* srcptr = (char*)src; + srcptr += count; + while (count) { + srcptr -= stride; + memcpy(dstptr, srcptr, stride); + dstptr += stride; + count -= stride; + } +} + /* downsample a single line from scanner's size to user's size */ /* and copy into final buffer */ static SANE_Status @@ -5288,7 +5395,46 @@ copy_line(struct scanner *s, unsigned char * buff, int side) switch (s->s.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;i<spwidth;i++) + line[i*3] = line[i*3+1] = line[i*3+2] = buff[i*3]; + break; + case COLOR_GREEN: + for(i=0;i<spwidth;i++) + line[i*3] = line[i*3+1] = line[i*3+2] = buff[i*3+1]; + break; + case COLOR_BLUE: + for(i=0;i<spwidth;i++) + line[i*3] = line[i*3+1] = line[i*3+2] = buff[i*3+2]; + break; + case COLOR_EN_RED: + for(i=0;i<spwidth;i++){ + line[i*3] = (buff[i*3+1] + buff[i*3+2])/2; + line[i*3+1] = buff[i*3+1]; + line[i*3+2] = buff[i*3+2]; + } + break; + case COLOR_EN_GREEN: + for(i=0;i<spwidth;i++){ + line[i*3] = buff[i*3]; + line[i*3+1] = (buff[i*3] + buff[i*3+2])/2; + line[i*3+2] = buff[i*3+2]; + } + break; + case COLOR_EN_BLUE: + for(i=0;i<spwidth;i++){ + line[i*3] = buff[i*3]; + line[i*3+1] = buff[i*3+1]; + line[i*3+2] = (buff[i*3] + buff[i*3+1])/2; + } + break; + } + } + else{ + memcpy(line, buff, sbwidth); + } break; case MODE_GRAYSCALE: @@ -5337,7 +5483,7 @@ copy_line(struct scanner *s, unsigned char * buff, int side) switch (s->i.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_color<s->threshold)?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; - } + ret = sane_get_parameters((SANE_Handle) s, &s->s_params); - if(0){ - int i; - for(i=0;i<width;i++){ - if(topBuf[i] >=0 && topBuf[i] < height) - s->buffers[side][topBuf[i]*width+i] = 0; - } - } + /*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){ - botBuf = getTransitionsY(s,side,0); - if(!botBuf){ - DBG (5, "buffer_deskew: cant gTY\n"); - ret = SANE_STATUS_NO_MEM; - goto cleanup; - } + 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); - /* 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); - - /* 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<width; i++){ - if(botBuf[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; - } + 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]); - 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; i<bot; i++){ - memcpy(line, s->buffers[side] + i*bwidth + left, bytes); - memcpy(s->buffers[side] + s->i.bytes_sent[side], line, bytes); - s->i.bytes_sent[side] += bytes; - } - - 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){ - - case MODE_COLOR: - for(i=w; i<t-w-(w*d); i+=w){ - for(j=1; j<pw-1-d; j++){ - - int thresh = 255*3; - int outer[] = {0,0,0}; - int hits = 0; - - /*loop over rows and columns in window */ - for(k=0; k<d; k++){ - for(l=0; l<d; l++){ - int tmp = 0; - - for(n=0; n<3; n++){ - tmp += s->buffers[side][i + j*3 + k*w + l*3 + n]; - } - - if(tmp < thresh) - thresh = tmp; - } - } - - thresh = (thresh + 255*3 + 255*3)/3; - - /*loop over rows and columns around window */ - for(k=-1; k<d+1; k++){ - for(l=-1; l<d+1; l++){ - - int tmp[3]; - - /* dont count pixels in the window */ - if(k != -1 && k != d && l != -1 && l != d) - continue; - - for(n=0; n<3; n++){ - tmp[n] = s->buffers[side][i + j*3 + k*w + l*3 + n]; - outer[n] += tmp[n]; - } - if(tmp[0]+tmp[1]+tmp[2] < thresh){ - hits++; - break; - } - } - } - - for(n=0; n<3; n++){ - outer[n] /= (4*d + 4); - } - - /*no hits, overwrite with avg surrounding color*/ - if(!hits){ - for(k=0; k<d; k++){ - for(l=0; l<d; l++){ - for(n=0; n<3; n++){ - s->buffers[side][i + j*3 + k*w + l*3 + n] = outer[n]; - } - } - } - } - - } - } - break; - - case MODE_GRAYSCALE: - for(i=w; i<t-w-(w*d); i+=w){ - for(j=1; j<w-1-d; j++){ - - int thresh = 255; - int outer = 0; - int hits = 0; - - for(k=0; k<d; k++){ - for(l=0; l<d; l++){ - if(s->buffers[side][i + j + k*w + l] < thresh) - thresh = s->buffers[side][i + j + k*w + l]; - } - } - - thresh = (thresh + 255 + 255)/3; - - /*loop over rows and columns around window */ - for(k=-1; k<d+1; k++){ - for(l=-1; l<d+1; l++){ - - int tmp = 0; + ret = sane_get_parameters((SANE_Handle) s, &s->s_params); - /* 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; k<d; k++){ - for(l=0; l<d; l++){ - s->buffers[side][i + j + k*w + l] = outer; - } - } - } - - } - } - break; - - case MODE_LINEART: - case MODE_HALFTONE: - for(i=w; i<t-w-(w*d); i+=w){ - for(j=1; j<pw-1-d; j++){ - - int curr = 0; - int hits = 0; - - for(k=0; k<d; k++){ - for(l=0; l<d; l++){ - curr += s->buffers[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; k<d+1; k++){ - for(l=-1; l<d+1; l++){ - - /* dont count pixels in the window */ - if(k != -1 && k != d && l != -1 && l != d) - continue; - - hits += s->buffers[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; k<d; k++){ - for(l=0; l<d; l++){ - s->buffers[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; i<width; i++){ - buff[i] = lastLine; - - /* load the near and far windows with repeated copy of first pixel */ - near = 0; - for(k=0; k<depth; k++){ - near += s->buffers[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; k<depth; k++){ - far -= s->buffers[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; i<width; i++){ - buff[i] = lastLine; - - /* load the near window with first pixel */ - near = s->buffers[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;i<width-7;i++){ - int sum = 0; - for(j=1;j<=7;j++){ - if(abs(buff[i+j] - buff[i]) < s->i.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; i<height; i++){ - buff[i] = lastCol; - - /* load the near and far windows with repeated copy of first pixel */ - near = 0; - for(k=0; k<depth; k++){ - near += s->buffers[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; k<depth; k++){ - far -= s->buffers[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; i<height; i++){ - buff[i] = lastCol; - - /* load the near window with first pixel */ - near = s->buffers[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;i<height-7;i++){ - int sum = 0; - for(j=1;j<=7;j++){ - if(abs(buff[i+j] - buff[i]) < s->i.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<slopes;j++){ - - /* find central value of this 'bucket' */ - slopeCenter[j] = ( - (double)j*(maxSlope-minSlope)/slopes+minSlope - + (double)(j+1)*(maxSlope-minSlope)/slopes+minSlope - )/2; - - /* scale value from the requested range into an inverted 100-1 range - * input close to 0 makes output close to 100 */ - slopeScale[j] = 101 - fabs(slopeCenter[j])*100/absMaxSlope; - } - - /* build an array of pretty-print values for offset */ - offsetCenter = calloc(offsets,sizeof(double)); - if(!offsetCenter){ - DBG(5,"getLine: cant load offsetCenter\n"); - ret = SANE_STATUS_NO_MEM; - goto cleanup; - } - - /* build an array of scaling factors for offset */ - offsetScale = calloc(offsets,sizeof(int)); - if(!offsetScale){ - DBG(5,"getLine: cant load offsetScale\n"); - ret = SANE_STATUS_NO_MEM; - goto cleanup; - } - - for(j=0;j<offsets;j++){ - - /* find central value of this 'bucket'*/ - offsetCenter[j] = ( - (double)j/offsets*(maxOffset-minOffset)+minOffset - + (double)(j+1)/offsets*(maxOffset-minOffset)+minOffset - )/2; - - /* scale value from the requested range into an inverted 100-1 range - * input close to 0 makes output close to 100 */ - offsetScale[j] = 101 - fabs(offsetCenter[j])*100/absMaxOffset; - } - - /* build 2-d array of 'density', divided into slope and offset ranges */ - lines = calloc(slopes, sizeof(int *)); - if(!lines){ - DBG(5,"getLine: cant load lines\n"); - ret = SANE_STATUS_NO_MEM; + 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; } - for(i=0;i<slopes;i++){ - if(!(lines[i] = calloc(offsets, sizeof(int)))){ - DBG(5,"getLine: cant load lines %d\n",i); - ret = SANE_STATUS_NO_MEM; - goto cleanup; - } - } - - for(i=0;i<width;i++){ - for(j=i+1;j<width && j<i+width/3;j++){ - - /*FIXME: check for invalid (min/max) values?*/ - rise = buff[j] - buff[i]; - run = j-i; - - slope = (double)rise/run; - if(slope >= 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<slopes;i++){ - for(j=0;j<offsets;j++){ - if(lines[i][j] > 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<slopes;i++){ - for(j=0;j<offsets;j++){ - lines[i][j] = lines[i][j] * slopeScale[i] * offsetScale[j] / maxDensity; - if(lines[i][j] > *finDensity){ - *finDensity = lines[i][j]; - *finSlope = slopeCenter[i]; - *finOffset = offsetCenter[j]; - } - } - } - - if(0){ - DBG(15,"offsetCenter: "); - for(j=0;j<offsets;j++){ - DBG(15," %+04.0f",offsetCenter[j]); - } - DBG(15,"\n"); - - DBG(15,"offsetScale: "); - for(j=0;j<offsets;j++){ - DBG(15," %04d",offsetScale[j]); - } - DBG(15,"\n"); - - for(i=0;i<slopes;i++){ - DBG(15,"slope: %02d %+02.2f %03d:",i,slopeCenter[i],slopeScale[i]); - for(j=0;j<offsets;j++){ - DBG(15,"% 5d",lines[i][j]/100); - } - DBG(15,"\n"); - } - } - - /* dont forget to cleanup */ cleanup: - for(i=0;i<10;i++){ - if(lines[i]) - free(lines[i]); - } - if(lines) - free(lines); - if(slopeCenter) - free(slopeCenter); - if(slopeScale) - free(slopeScale); - if(offsetCenter) - free(offsetCenter); - if(offsetScale) - free(offsetScale); - - DBG(10,"getLine: finish\n"); - + DBG (10, "buffer_despeck: finish\n"); return ret; } -/* Repeatedly find the best range of slope and offset via Hough transform. - * Shift the ranges thru 4 different positions to avoid splitting data - * across multiple bins (false positive). Home-in on the most likely upper - * line of the paper inside the image. Return the 'best' line. */ -SANE_Status -getEdgeIterate (int width, int height, int resolution, -int * buff, double * finSlope, int * finXInter, int * finYInter) +/* 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; - int slopes = 11; - int offsets = 11; - double maxSlope = 1; - double minSlope = -1; - int maxOffset = resolution/6; - int minOffset = -resolution/6; + DBG (10, "buffer_isblank: start\n"); - double topSlope = 0; - int topOffset = 0; - int topDensity = 0; - - int i,j; - int pass = 0; - - DBG(10,"getEdgeIterate: start\n"); - - while(pass++ < 7){ - double sStep = (maxSlope-minSlope)/slopes; - int oStep = (maxOffset-minOffset)/offsets; - - double slope = 0; - int offset = 0; - int density = 0; - int go = 0; - - topSlope = 0; - topOffset = 0; - topDensity = 0; - - /* find lines 4 times with slightly moved params, - * to bypass binning errors, highest density wins */ - for(i=0;i<2;i++){ - double sStep2 = sStep*i/2; - for(j=0;j<2;j++){ - int oStep2 = oStep*j/2; - ret = getLine(height,width,buff,slopes,minSlope+sStep2,maxSlope+sStep2,offsets,minOffset+oStep2,maxOffset+oStep2,&slope,&offset,&density); - if(ret){ - DBG(5,"getEdgeIterate: getLine error %d\n",ret); - return ret; - } - DBG(15,"getEdgeIterate: %d %d %+0.4f %d %d\n",i,j,slope,offset,density); - - if(density > topDensity){ - topSlope = slope; - topOffset = offset; - topDensity = density; - } - } - } + ret = sane_get_parameters((SANE_Handle) s, &s->s_params); - DBG(15,"getEdgeIterate: ok %+0.4f %d %d\n",topSlope,topOffset,topDensity); + ret = sanei_magic_isBlank2(&s->s_params, s->buffers[side], + s->u.dpi_x, s->u.dpi_y, s->swskip); - /* 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; + if(ret == SANE_STATUS_NO_DOCS){ + DBG (5, "buffer_isblank: blank!\n"); + status = 1; } - else{ - *finYInter = 0; - *finXInter = 0; - *finSlope = 0; + else if(ret){ + DBG (5, "buffer_isblank: error %d\n",ret); } - DBG(10,"getEdgeIterate: finish\n"); - - return 0; + DBG (10, "buffer_isblank: finished\n"); + return status; } -/* 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) +/* 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) { - 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<width;i++){ - - if(top[i] < height){ - int tyi = top[i] - (slope * i); - int txi = tyi/-slope; - - if(topXInter > 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<width;i++){ - - if(bot[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; + if( + (s->swdeskew || s->swdespeck || s->swcrop) + && s->s.format != SANE_FRAME_JPEG + ){ + return 1; } - 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<height; i++) { - int shiftY = centerY - i; - - for (j=0; j<pwidth; j++) { - int shiftX = centerX - j; - int sourceX, sourceY; - - sourceX = centerX - (int)(shiftX * slopeCos + shiftY * slopeSin); - if (sourceX < 0 || sourceX >= pwidth) - continue; - - sourceY = centerY + (int)(-shiftY * slopeCos + shiftX * slopeSin); - if (sourceY < 0 || sourceY >= height) - continue; - - for (k=0; k<depth; k++) { - outbuf[i*bwidth+j*depth+k] - = s->buffers[side][sourceY*bwidth+sourceX*depth+k]; - } - } - } - break; - - case MODE_LINEART: - case MODE_HALFTONE: - memset(outbuf,(bg_color<s->threshold)?0xff:0x00,s->i.bytes_tot[side]); - - for (i=0; i<height; i++) { - int shiftY = centerY - i; - - for (j=0; j<pwidth; j++) { - int shiftX = centerX - j; - int sourceX, sourceY; - - sourceX = centerX - (int)(shiftX * slopeCos + shiftY * slopeSin); - if (sourceX < 0 || sourceX >= 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; } |