diff --git a/backend/Makefile.am b/backend/Makefile.am index aeb7ce698..a0f22c4cb 100644 --- a/backend/Makefile.am +++ b/backend/Makefile.am @@ -345,7 +345,7 @@ libcanon_dr_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon_dr nodist_libsane_canon_dr_la_SOURCES = canon_dr-s.c libsane_canon_dr_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon_dr libsane_canon_dr_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) -libsane_canon_dr_la_LIBADD = $(COMMON_LIBS) libcanon_dr.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(USB_LIBS) +libsane_canon_dr_la_LIBADD = $(COMMON_LIBS) libcanon_dr.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) EXTRA_DIST += canon_dr.conf.in libcanon_pp_la_SOURCES = canon_pp.c canon_pp.h canon_pp-io.c canon_pp-io.h canon_pp-dev.c canon_pp-dev.h diff --git a/backend/Makefile.in b/backend/Makefile.in index e648c670d..55856d4b5 100644 --- a/backend/Makefile.in +++ b/backend/Makefile.in @@ -1812,7 +1812,7 @@ libcanon_dr_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon_dr nodist_libsane_canon_dr_la_SOURCES = canon_dr-s.c libsane_canon_dr_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon_dr libsane_canon_dr_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) -libsane_canon_dr_la_LIBADD = $(COMMON_LIBS) libcanon_dr.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(USB_LIBS) +libsane_canon_dr_la_LIBADD = $(COMMON_LIBS) libcanon_dr.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) libcanon_pp_la_SOURCES = canon_pp.c canon_pp.h canon_pp-io.c canon_pp-io.h canon_pp-dev.c canon_pp-dev.h libcanon_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon_pp nodist_libsane_canon_pp_la_SOURCES = canon_pp-s.c diff --git a/backend/canon_dr.c b/backend/canon_dr.c index a12b4658a..59f1250f3 100644 --- a/backend/canon_dr.c +++ b/backend/canon_dr.c @@ -60,6 +60,7 @@ Section 5 - calibration functions Section 6 - sane_close functions Section 7 - misc functions + Section 8 - image processing functions Changes: v1 2008-10-29, MAN @@ -235,6 +236,11 @@ - correct some debug message - better handling of EOF - add intermediate param struct to existing user and scan versions + v33 2009-07-23, MAN + - add software brightness/contrast for dumb scanners + - add blocking mode to allow full-page manipulation options to run + - add swdespeck option and support code + - add swdeskew and swcrop options (disabled) SANE FLOW DIAGRAM @@ -295,7 +301,7 @@ #include "canon_dr.h" #define DEBUG 1 -#define BUILD 30 +#define BUILD 33 /* values for SANE_DEBUG_CANON_DR env var: - errors 5 @@ -1169,6 +1175,7 @@ init_model (struct scanner *s) s->duplex_interlace = DUPLEX_INTERLACE_2510; s->need_ccal = 1; s->need_fcal = 1; + s->sw_lut = 1; /*s->invert_tly = 1;*/ /*only in Y direction, so we trash them in X*/ @@ -1193,6 +1200,7 @@ init_model (struct scanner *s) s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_RRGGBB; s->duplex_interlace = DUPLEX_INTERLACE_FBFB; s->need_fcal_buffer = 1; + s->sw_lut = 1; /*lies*/ s->can_halftone=0; @@ -1881,6 +1889,51 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) opt->cap = SANE_CAP_INACTIVE; } + /*deskew by software*/ + if(option==OPT_SWDESKEW){ + opt->name = "swdeskew"; + opt->title = "Software deskew"; + opt->desc = "Request driver to rotate skewed pages digitally"; + opt->type = SANE_TYPE_BOOL; + if (1) + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } + + /*software despeckle radius*/ + if(option==OPT_SWDESPECK){ + + opt->name = "swdespeck"; + opt->title = "Software despeckle diameter"; + opt->desc = "Maximum diameter of lone dots to remove from scan"; + opt->type = SANE_TYPE_INT; + opt->unit = SANE_UNIT_NONE; + opt->constraint_type = SANE_CONSTRAINT_RANGE; + opt->constraint.range = &s->swdespeck_range; + s->swdespeck_range.quant=1; + + if(1){ + s->swdespeck_range.min=0; + s->swdespeck_range.max=9; + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + } + else + opt->cap = SANE_CAP_INACTIVE; + } + + /*crop by software*/ + if(option==OPT_SWCROP){ + opt->name = "swcrop"; + opt->title = "Software crop"; + opt->desc = "Request driver to remove border from pages digitally"; + opt->type = SANE_TYPE_BOOL; + if (1) + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } + /*staple detection*/ if(option==OPT_STAPLEDETECT){ opt->name = "stapledetect"; @@ -2216,6 +2269,18 @@ sane_control_option (SANE_Handle handle, SANE_Int option, *val_p = s->rollerdeskew; return SANE_STATUS_GOOD; + case OPT_SWDESKEW: + *val_p = s->swdeskew; + return SANE_STATUS_GOOD; + + case OPT_SWDESPECK: + *val_p = s->swdespeck; + return SANE_STATUS_GOOD; + + case OPT_SWCROP: + *val_p = s->swcrop; + return SANE_STATUS_GOOD; + case OPT_STAPLEDETECT: *val_p = s->stapledetect; return SANE_STATUS_GOOD; @@ -2507,6 +2572,18 @@ sane_control_option (SANE_Handle handle, SANE_Int option, s->rollerdeskew = val_c; return SANE_STATUS_GOOD; + case OPT_SWDESKEW: + s->swdeskew = val_c; + return SANE_STATUS_GOOD; + + case OPT_SWDESPECK: + s->swdespeck = val_c; + return SANE_STATUS_GOOD; + + case OPT_SWCROP: + s->swcrop = val_c; + return SANE_STATUS_GOOD; + case OPT_STAPLEDETECT: s->stapledetect = val_c; return SANE_STATUS_GOOD; @@ -3118,6 +3195,12 @@ sane_start (SANE_Handle handle) return SANE_STATUS_INVAL; } + /* note if user has requested an option that will require us to + * hold the entire image in memory before we send it to them */ + s->blocking_mode = 0; + if(s->swdeskew || s->swdespeck || s->swcrop) + s->blocking_mode = 1; + /* batch start? inititalize struct and scanner */ if(!s->started){ @@ -3141,6 +3224,13 @@ sane_start (SANE_Handle handle) goto errors; } + /* load the brightness/contrast lut with linear slope for calibration */ + ret = load_lut (s->lut, 8, 8, 0, 255, 0, 0); + if (ret != SANE_STATUS_GOOD) { + DBG (5, "sane_start: ERROR: cannot load lut\n"); + goto errors; + } + /* AFE cal */ if((ret = calibrate_AFE(s))){ DBG (5, "sane_start: ERROR: cannot cal afe\n"); @@ -3214,6 +3304,13 @@ sane_start (SANE_Handle handle) goto errors; } + /* load the brightness/contrast lut with user choices */ + ret = load_lut (s->lut, 8, 8, 0, 255, s->contrast, s->brightness); + if (ret != SANE_STATUS_GOOD) { + DBG (5, "sane_start: ERROR: cannot load lut\n"); + goto errors; + } + /* grab next page */ ret = object_position (s, SANE_TRUE); if (ret != SANE_STATUS_GOOD) { @@ -3304,14 +3401,11 @@ sane_start (SANE_Handle handle) DBG (15, "started=%d, side=%d, source=%d\n", s->started, s->side, s->u.source); -#if 0 - s->blocking_mode = 1; - /* 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 * API has no way to inform the frontend of this, - * so we block. yuck */ + * so we block and buffer. yuck */ if(s->blocking_mode){ /* get image */ @@ -3326,10 +3420,14 @@ sane_start (SANE_Handle handle) goto errors; } - /* finished buffering, adjust image as required */ DBG (5, "sane_start: OK: done buffering\n"); + + /* finished buffering, adjust image as required */ + if(s->swdespeck){ + buffer_despeck(s,s->side); + } + } -#endif ret = check_for_cancel(s); s->reading = 0; @@ -4256,6 +4354,14 @@ copy_simplex(struct scanner *s, unsigned char * buf, int len, int side) } } + /* apply brightness and contrast if hardware cannot do it */ + if(s->sw_lut && (s->s.mode == MODE_COLOR || s->s.mode == MODE_GRAYSCALE)){ + DBG (15, "copy_simplex: apply brightness/contrast\n"); + for(j=0; js.valid_Bpl; j++){ + line[j] = s->lut[line[j]]; + } + } + /*copy the line into the buffer*/ ret = copy_line(s,line,side); if(ret){ @@ -6333,3 +6439,298 @@ sane_get_select_fd (SANE_Handle h, SANE_Int *fdp) DBG (15, "%p %d\n", h, *fdp); return SANE_STATUS_UNSUPPORTED; } + +/* + * @@ Section 8 - Image processing functions + */ + +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; ibuffers[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; kbuffers[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; kbuffers[side][i + j*3 + k*w + l*3 + n] = outer[n]; + } + } + } + } + + } + } + break; + + case MODE_GRAYSCALE: + for(i=w; ibuffers[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; kbuffers[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; +} + +static SANE_Status +buffer_deskew(struct scanner *s, int side) +{ + SANE_Status ret = SANE_STATUS_GOOD; + + DBG (10, "buffer_deskew: start\n"); + + DBG (10, "buffer_deskew: finish\n"); + return ret; +} + +static SANE_Status +buffer_crop(struct scanner *s, int side) +{ + SANE_Status ret = SANE_STATUS_GOOD; + + DBG (10, "buffer_crop: start\n"); + + DBG (10, "buffer_crop: finish\n"); + return ret; +} + +/* Function to build a lookup table (LUT), often + used by scanners to implement brightness/contrast/gamma + or by backends to speed binarization/thresholding + + offset and slope inputs are -127 to +127 + + slope rotates line around central input/output val, + 0 makes horizontal line + + pos zero neg + . x . . x + . x . . x + out . x .xxxxxxxxxxx . x + . x . . x + ....x....... ............ .......x.... + in in in + + offset moves line vertically, and clamps to output range + 0 keeps the line crossing the center of the table + + pos zero neg + . xxxxxxxx . xx . + . x . x . + out x . x . x + . . x . x + ............ xx.......... xxxxxxxx.... + in in + + out_min/max provide bounds on output values, + useful when building thresholding lut. + 0 and 255 are good defaults otherwise. + */ +static SANE_Status +load_lut (unsigned char * lut, + int in_bits, int out_bits, + int out_min, int out_max, + int slope, int offset) +{ + SANE_Status ret = SANE_STATUS_GOOD; + int i, j; + double shift, rise; + int max_in_val = (1 << in_bits) - 1; + int max_out_val = (1 << out_bits) - 1; + unsigned char * lut_p = lut; + + DBG (10, "load_lut: start %d %d\n", slope, offset); + + /* slope is converted to rise per unit run: + * 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 + * 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; + + /* line must stay vertically centered, so figure + * out vertical offset at central input value */ + shift = (double)max_out_val/2 - (rise*max_in_val/2); + + /* convert the user offset setting to scale of output + * first [-127,127] to [-1,1] + * then to [-max_out_val/2,max_out_val/2]*/ + shift += (double)offset / 127 * max_out_val / 2; + + for(i=0;i<=max_in_val;i++){ + j = rise*i + shift; + + if(jout_max){ + j=out_max; + } + + *lut_p=j; + lut_p++; + } + + hexdump(5, "load_lut: ", lut, max_in_val+1); + + DBG (10, "load_lut: finish\n"); + return ret; +} diff --git a/backend/canon_dr.h b/backend/canon_dr.h index 574ee922d..fa77624c9 100644 --- a/backend/canon_dr.h +++ b/backend/canon_dr.h @@ -40,6 +40,9 @@ enum scanner_Option OPT_DF_THICKNESS, OPT_DF_LENGTH, OPT_ROLLERDESKEW, + OPT_SWDESKEW, + OPT_SWDESPECK, + OPT_SWCROP, OPT_STAPLEDETECT, OPT_DROPOUT_COLOR_F, OPT_DROPOUT_COLOR_B, @@ -185,6 +188,8 @@ struct scanner int duplex_interlace; /* different models interlace sides differently */ int jpeg_interlace; /* different models interlace jpeg sides differently */ + int sw_lut; /* no hardware brightness/contrast support */ + int reverse_by_mode[6]; /* mode specific */ /* --------------------------------------------------------------------- */ @@ -227,6 +232,7 @@ struct scanner /*advanced group*/ SANE_String_Const compress_list[3]; SANE_Range compress_arg_range; + SANE_Range swdespeck_range; SANE_String_Const do_color_list[8]; /*sensor group*/ @@ -235,7 +241,7 @@ struct scanner /* --------------------------------------------------------------------- */ /* changeable vars to hold user input. modified by SANE_Options above */ - /* the user image params (for final image output) */ + /* the user's requested image params */ /* exposed in standard and geometry option groups */ struct img_params u; @@ -254,6 +260,9 @@ struct scanner int dropout_color_b; int buffermode; int rollerdeskew; + int swdeskew; + int swdespeck; + int swcrop; int stapledetect; /* --------------------------------------------------------------------- */ @@ -269,6 +278,9 @@ struct scanner /* the intermediate image params (like user, but possible higher depth) */ struct img_params i; + /* the brightness/contrast LUT for dumb scanners */ + unsigned char lut[256]; + /* --------------------------------------------------------------------- */ /* values which are set by calibration functions */ int c_res; @@ -528,6 +540,13 @@ static SANE_Status copy_simplex(struct scanner *s, unsigned char * buf, int len, static SANE_Status copy_duplex(struct scanner *s, unsigned char * buf, int len); static SANE_Status copy_line(struct scanner *s, unsigned char * buf, int side); +static SANE_Status buffer_despeck(struct scanner *s, int side); +static SANE_Status buffer_deskew(struct scanner *s, int side); +static SANE_Status buffer_crop(struct scanner *s, int side); + +static SANE_Status load_lut (unsigned char * lut, int in_bits, int out_bits, + int out_min, int out_max, int slope, int offset); + static SANE_Status read_from_buffer(struct scanner *s, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len, int side); static SANE_Status image_buffers (struct scanner *s, int setup);