From abd0f368687f55213e32a5948f42904ce88a3442 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Voltz?= Date: Mon, 6 Sep 2010 21:02:32 +0200 Subject: [PATCH 1/6] add options and flag for software enhancements --- backend/genesys.c | 86 +++++++++++++++++++++++++++++++++++++++++++ backend/genesys.h | 4 ++ backend/genesys_low.h | 3 ++ 3 files changed, 93 insertions(+) diff --git a/backend/genesys.c b/backend/genesys.c index 6631a1bf2..c43233066 100644 --- a/backend/genesys.c +++ b/backend/genesys.c @@ -117,6 +117,12 @@ static SANE_String_Const source_list[] = { 0 }; +static SANE_Range swdespeck_range = { + 0, + 9, + 1 +}; + static SANE_Range time_range = { 0, /* minimum */ 60, /* maximum */ @@ -611,6 +617,7 @@ sanei_genesys_get_status (Genesys_Device * dev, uint8_t * status) return sanei_genesys_read_register (dev, 0x41, status); } +#if 0 /* returns pixels per line from register set */ /*candidate for moving into chip specific files?*/ static int @@ -643,6 +650,7 @@ genesys_dpiset (Genesys_Register_Set * reg) return dpiset; } +#endif /** read the number of valid words in scanner's RAM * ie registers 42-43-44 @@ -5913,6 +5921,16 @@ calc_parameters (Genesys_Scanner * s) else s->dev->settings.threshold_curve=0; + /* some digital processing requires the whole picture to be buffered */ + if (s->val[OPT_SWDESPECK].b || s->val[OPT_SWCROP].b || s->val[OPT_SWDESKEW].b) + { + s->dev->buffer_image=SANE_TRUE; + } + else + { + s->dev->buffer_image=SANE_TRUE; + } + return status; } @@ -6196,6 +6214,45 @@ init_options (Genesys_Scanner * s) DBG (DBG_info, "init_options: custom gamma disabled\n"); } + /* software base image enhancements, these are consuming as many + * memory than used by the full scanned image and may fail at high + * resolution + */ + /* software deskew */ + s->opt[OPT_SWDESKEW].name = "swdeskew"; + s->opt[OPT_SWDESKEW].title = "Software deskew"; + s->opt[OPT_SWDESKEW].desc = "Request backend to rotate skewed pages digitally"; + s->opt[OPT_SWDESKEW].type = SANE_TYPE_BOOL; + s->opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + s->val[OPT_SWDESKEW].b = SANE_FALSE; + + /* software deskew */ + s->opt[OPT_SWDESPECK].name = "swdespeck"; + s->opt[OPT_SWDESPECK].title = "Software despeck"; + s->opt[OPT_SWDESPECK].desc = "Request backend to remove lone dots digitally"; + s->opt[OPT_SWDESPECK].type = SANE_TYPE_BOOL; + s->opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + s->val[OPT_SWDESPECK].b = SANE_FALSE; + + /* software despeckle radius */ + s->opt[OPT_DESPECK].name = "despeck"; + s->opt[OPT_DESPECK].title = "Software despeckle diameter"; + s->opt[OPT_DESPECK].desc = "Maximum diameter of lone dots to remove from scan"; + s->opt[OPT_DESPECK].type = SANE_TYPE_INT; + s->opt[OPT_DESPECK].unit = SANE_UNIT_NONE; + s->opt[OPT_DESPECK].constraint_type = SANE_CONSTRAINT_RANGE; + s->opt[OPT_DESPECK].constraint.range = &swdespeck_range; + s->opt[OPT_DESPECK].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + s->val[OPT_DESPECK].w= 1; + + /* crop by software */ + s->opt[OPT_SWCROP].name = "swcrop"; + s->opt[OPT_SWCROP].title = "Software crop"; + s->opt[OPT_SWCROP].desc = "Request backend to remove border from pages digitally"; + s->opt[OPT_SWCROP].type = SANE_TYPE_BOOL; + s->opt[OPT_SWCROP].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + s->val[OPT_SWCROP].b = SANE_FALSE; + /* "Extras" group: */ s->opt[OPT_EXTRAS_GROUP].title = SANE_I18N ("Extras"); s->opt[OPT_EXTRAS_GROUP].desc = ""; @@ -7195,6 +7252,10 @@ get_option_value (Genesys_Scanner * s, int option, void *val) case OPT_DISABLE_DYNAMIC_LINEART: case OPT_DISABLE_INTERPOLATION: case OPT_LAMP_OFF_TIME: + case OPT_SWDESKEW: + case OPT_SWCROP: + case OPT_SWDESPECK: + case OPT_DESPECK: *(SANE_Word *) val = s->val[option].w; break; case OPT_CUSTOM_GAMMA: @@ -7307,12 +7368,28 @@ set_option_value (Genesys_Scanner * s, int option, void *val, case OPT_THRESHOLD: case OPT_THRESHOLD_CURVE: case OPT_DISABLE_DYNAMIC_LINEART: + case OPT_SWCROP: + case OPT_SWDESKEW: + case OPT_DESPECK: case OPT_DISABLE_INTERPOLATION: case OPT_PREVIEW: s->val[option].w = *(SANE_Word *) val; RIE (calc_parameters (s)); *myinfo |= SANE_INFO_RELOAD_PARAMS; break; + case OPT_SWDESPECK: + s->val[option].w = *(SANE_Word *) val; + if (s->val[OPT_SWDESPECK].b == SANE_TRUE) + { + ENABLE(OPT_DESPECK); + } + else + { + DISABLE(OPT_DESPECK); + } + RIE (calc_parameters (s)); + *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; + break; case OPT_SOURCE: if (strcmp (s->val[option].s, val) != 0) { /* something changed */ @@ -7707,6 +7784,15 @@ sane_start (SANE_Handle handle) s->scanning = SANE_TRUE; + /* if one of the software enhancement option is selsected, + * we do the scan internally, process picture then put it an internall + * buffer. Since cropping may change scan parameters, we recompute them + * at the end */ + if (s->dev->buffer_image) + { + /* scan image to buffer */ + } + DBG (DBG_proc, "sane_start: exit\n"); return SANE_STATUS_GOOD; } diff --git a/backend/genesys.h b/backend/genesys.h index d95513603..040e95b87 100644 --- a/backend/genesys.h +++ b/backend/genesys.h @@ -93,6 +93,10 @@ enum Genesys_Option OPT_GAMMA_VECTOR_R, OPT_GAMMA_VECTOR_G, OPT_GAMMA_VECTOR_B, + OPT_SWDESKEW, + OPT_SWCROP, + OPT_SWDESPECK, + OPT_DESPECK, OPT_EXTRAS_GROUP, OPT_LAMP_OFF_TIME, diff --git a/backend/genesys_low.h b/backend/genesys_low.h index 4f4802b49..c244c10c3 100644 --- a/backend/genesys_low.h +++ b/backend/genesys_low.h @@ -650,6 +650,9 @@ struct Genesys_Device size_t len; /**> number of even pixels */ size_t cur; /**> current pixel position within sub window */ Genesys_Buffer oe_buffer; /**> buffer to handle even/odd data */ + + SANE_Bool buffer_image; /**> when true the scanned picutre is first buffered + * to a software image enhancements */ }; typedef struct Genesys_USB_Device_Entry From ba5b0f897adcc6e666b700eeed109c67a3a89bed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Voltz?= Date: Tue, 7 Sep 2010 08:55:36 +0200 Subject: [PATCH 2/6] add image buffering when digital enhancements are selected --- backend/genesys.c | 126 ++++++++++++++++++++++++++++++++++++++---- backend/genesys_low.h | 3 +- 2 files changed, 117 insertions(+), 12 deletions(-) diff --git a/backend/genesys.c b/backend/genesys.c index c43233066..ab5b7f917 100644 --- a/backend/genesys.c +++ b/backend/genesys.c @@ -6877,6 +6877,85 @@ write_calibration (Genesys_Device * dev) fclose (fp); } +/** @brief buffer scanned picture + * In order to allow digital processing, we must be able to put all the + * scanned picture in a buffer. + */ +static SANE_Status +genesys_buffer_image(Genesys_Scanner *s) +{ + SANE_Status status=SANE_STATUS_GOOD; + size_t maximum; /**> maximum bytes size of the scan */ + size_t len; /**> length of scanned data read */ + size_t total; /**> total of butes read */ + size_t size; /**> size of image buffer */ + size_t read_size; /**> size of reads */ + int lines; /** number of lines of the scan */ + Genesys_Device *dev=s->dev; + + /* compute maximum number of lines for the scan */ + if(s->params.lines>0) + { + lines=s->params.bytes_per_line * s->params.lines; + } + else + { + lines=(SANE_UNFIX(dev->model->y_size)*dev->settings.yres)/MM_PER_INCH; + } + DBG(DBG_info, "%s: buffering %d lines of %d bytes\n",__FUNCTION__, lines,s->params.bytes_per_line); + + /* maximum bytes to read */ + maximum=s->params.bytes_per_line * lines; + + /* initial size of the read buffer */ + size=((2048*2048)/s->params.bytes_per_line)*s->params.bytes_per_line; + + /* read size */ + read_size=size/2; + + /* allocate memory */ + dev->img_buffer=(SANE_Byte *)malloc(size); + if(dev->img_buffer==NULL) + { + DBG (DBG_error, "%s: digital processing requires too much memory.\nConsider disabling it\n",__FUNCTION__); + return SANE_STATUS_NO_MEM; + } + + /* loop reading data until we reach maximu or EOF */ + total=0; + while(totalread_size) + { + len=read_size; + } + status = genesys_read_ordered_data (dev, dev->img_buffer+total, &len); + if(status!=SANE_STATUS_EOF && status!=SANE_STATUS_GOOD) + { + free(s->dev->img_buffer); + DBG (DBG_error, "%s: %s buffering failed\n", __FUNCTION__, sane_strstatus (status)); + return status; + } + total+=len; + + /* do we need to enlarge read buffer ? */ + if(total+read_size>size && status != SANE_STATUS_EOF) + { + size+=read_size; + dev->img_buffer=(SANE_Byte *)realloc(dev->img_buffer,size); + if(dev->img_buffer==NULL) + { + DBG (DBG_error0, "%s: digital processing requires too much memory.\nConsider disabling it\n",__FUNCTION__); + return SANE_STATUS_NO_MEM; + } + } + } + s->dev->total_bytes_to_read=total; + s->dev->total_bytes_read=0; + return SANE_STATUS_GOOD; +} + /* -------------------------- SANE API functions ------------------------- */ SANE_Status @@ -7067,6 +7146,7 @@ sane_open (SANE_String_Const devicename, SANE_Handle * handle) s->dev->white_average_data = NULL; s->dev->dark_average_data = NULL; s->dev->calibration_cache = NULL; + s->dev->img_buffer = NULL; /* insert newly opened handle into list of open handles: */ s->next = first_handle; @@ -7228,7 +7308,7 @@ get_option_value (Genesys_Scanner * s, int option, void *val) case OPT_BR_X: case OPT_BR_Y: *(SANE_Word *) val = s->val[option].w; - /* switch coordinate tokeep them coherent */ + /* switch coordinate to keep them coherent */ if (s->val[OPT_TL_X].w >= s->val[OPT_BR_X].w) { tmp=s->val[OPT_BR_X].w; @@ -7759,7 +7839,7 @@ SANE_Status sane_start (SANE_Handle handle) { Genesys_Scanner *s = handle; - SANE_Status status; + SANE_Status status=SANE_STATUS_GOOD; DBG (DBG_proc, "sane_start: start\n"); @@ -7784,17 +7864,17 @@ sane_start (SANE_Handle handle) s->scanning = SANE_TRUE; - /* if one of the software enhancement option is selsected, - * we do the scan internally, process picture then put it an internall + /* if one of the software enhancement option is selected, + * we do the scan internally, process picture then put it an internal * buffer. Since cropping may change scan parameters, we recompute them * at the end */ if (s->dev->buffer_image) { - /* scan image to buffer */ + status=genesys_buffer_image(s); } DBG (DBG_proc, "sane_start: exit\n"); - return SANE_STATUS_GOOD; + return status; } SANE_Status @@ -7802,7 +7882,8 @@ sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { Genesys_Scanner *s = handle; - SANE_Status status; + Genesys_Device *dev=s->dev; + SANE_Status status=SANE_STATUS_GOOD; size_t local_len; if (!s) @@ -7835,11 +7916,29 @@ sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, DBG (DBG_proc, "sane_read: start, %d maximum bytes required\n", max_len); local_len = max_len; - status = genesys_read_ordered_data (s->dev, buf, &local_len); + + /* if image hasn't been buffered, read data from scanner */ + if(!dev->buffer_image) + { + status = genesys_read_ordered_data (dev, buf, &local_len); + } + else /* read data from buffer */ + { + if(dev->total_bytes_read+local_len>dev->total_bytes_to_read) + { + local_len=dev->total_bytes_to_read-dev->total_bytes_read; + } + memcpy(buf,dev->img_buffer+dev->total_bytes_read,local_len); + dev->total_bytes_read+=local_len; + if(dev->total_bytes_read>=dev->total_bytes_to_read) + { + status=SANE_STATUS_EOF; + } + } *len = local_len; return status; -} +} void sane_cancel (SANE_Handle handle) @@ -7851,11 +7950,16 @@ sane_cancel (SANE_Handle handle) s->scanning = SANE_FALSE; s->dev->read_active = SANE_FALSE; + if(s->dev->img_buffer!=NULL) + { + free(s->dev->img_buffer); + s->dev->img_buffer=NULL; + } status = s->dev->model->cmd_set->end_scan (s->dev, s->dev->reg, SANE_TRUE); if (status != SANE_STATUS_GOOD) { - DBG (DBG_error, "sane_cancel: Failed to end scan: %s\n", + DBG (DBG_error, "sane_cancel: failed to end scan: %s\n", sane_strstatus (status)); return; } @@ -7883,7 +7987,7 @@ sane_cancel (SANE_Handle handle) } } - /*enable power saving mode */ + /* enable power saving mode */ status = s->dev->model->cmd_set->save_power (s->dev, SANE_TRUE); if (status != SANE_STATUS_GOOD) { diff --git a/backend/genesys_low.h b/backend/genesys_low.h index c244c10c3..feab4c619 100644 --- a/backend/genesys_low.h +++ b/backend/genesys_low.h @@ -651,8 +651,9 @@ struct Genesys_Device size_t cur; /**> current pixel position within sub window */ Genesys_Buffer oe_buffer; /**> buffer to handle even/odd data */ - SANE_Bool buffer_image; /**> when true the scanned picutre is first buffered + SANE_Bool buffer_image; /**> when true the scanned picture is first buffered * to a software image enhancements */ + SANE_Byte *img_buffer; /**> image buffer where the scanned picture is stored */ }; typedef struct Genesys_USB_Device_Entry From e084a04fcfafc3542b61f0a3820f8436d0b9b46b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Voltz?= Date: Thu, 9 Sep 2010 06:20:44 +0200 Subject: [PATCH 3/6] working software cropping --- backend/Makefile.am | 2 +- backend/Makefile.in | 4 +- backend/genesys.c | 264 ++++++++++++++++++++++++----------------- backend/genesys_conv.c | 61 ++++++++++ 4 files changed, 217 insertions(+), 114 deletions(-) diff --git a/backend/Makefile.am b/backend/Makefile.am index 6b533b328..8eb16cc0d 100644 --- a/backend/Makefile.am +++ b/backend/Makefile.am @@ -485,7 +485,7 @@ libgenesys_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=genesys nodist_libsane_genesys_la_SOURCES = genesys-s.c libsane_genesys_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=genesys libsane_genesys_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) -libsane_genesys_la_LIBADD = $(COMMON_LIBS) libgenesys.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS) +libsane_genesys_la_LIBADD = $(COMMON_LIBS) libgenesys.la ../sanei/sanei_magic.lo ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS) EXTRA_DIST += genesys.conf.in # TODO: Why are this distributed but not compiled? EXTRA_DIST += genesys_conv.c genesys_conv_hlp.c genesys_devices.c diff --git a/backend/Makefile.in b/backend/Makefile.in index fb86fc3ba..8506a6cf5 100644 --- a/backend/Makefile.in +++ b/backend/Makefile.in @@ -574,7 +574,7 @@ libsane_fujitsu_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ libsane_genesys_la_DEPENDENCIES = $(COMMON_LIBS) libgenesys.la \ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo sane_strstatus.lo \ - ../sanei/sanei_usb.lo $(am__DEPENDENCIES_1) \ + ../sanei/sanei_usb.lo ../sanei/sanei_magic.lo $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) nodist_libsane_genesys_la_OBJECTS = libsane_genesys_la-genesys-s.lo libsane_genesys_la_OBJECTS = $(nodist_libsane_genesys_la_OBJECTS) @@ -1998,7 +1998,7 @@ libgenesys_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=genesys nodist_libsane_genesys_la_SOURCES = genesys-s.c libsane_genesys_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=genesys libsane_genesys_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) -libsane_genesys_la_LIBADD = $(COMMON_LIBS) libgenesys.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS) +libsane_genesys_la_LIBADD = $(COMMON_LIBS) libgenesys.la ../sanei/sanei_magic.lo ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS) libgphoto2_i_la_SOURCES = gphoto2.c gphoto2.h libgphoto2_i_la_CPPFLAGS = $(AM_CPPFLAGS) @GPHOTO2_CPPFLAGS@ -DBACKEND_NAME=gphoto2 nodist_libsane_gphoto2_la_SOURCES = gphoto2-s.c diff --git a/backend/genesys.c b/backend/genesys.c index ab5b7f917..2729eff18 100644 --- a/backend/genesys.c +++ b/backend/genesys.c @@ -73,6 +73,7 @@ #include "../include/sane/sanei_usb.h" #include "../include/sane/sanei_config.h" #include "../include/_stdint.h" +#include "../include/sane/sanei_magic.h" #include "genesys.h" #include "genesys_devices.c" @@ -3116,57 +3117,63 @@ compute_gl843_coefficients (Genesys_Device * dev, unsigned int coeff, unsigned int target) { - uint16_t *buffer=(uint16_t *)*shading_data,*darkptr,*whiteptr; + uint16_t *buffer = (uint16_t *) * shading_data, *darkptr, *whiteptr; int size; - int i, count; + unsigned int i, count; uint16_t val; - darkptr=(uint16_t *)dev->dark_average_data; - whiteptr=(uint16_t *)dev->white_average_data; + darkptr = (uint16_t *) dev->dark_average_data; + whiteptr = (uint16_t *) dev->white_average_data; - size=pixels*2*3*256/252*2+512; - free(buffer); - buffer= (unsigned short *)malloc(size); - if (buffer == NULL) - return 0; + size = pixels * 2 * 3 * 256 / 252 * 2 + 512; + free (buffer); + buffer = (unsigned short *) malloc (size); + if (buffer == NULL) + return 0; - /* offset */ - buffer=buffer+12; - count=12; - for(i=0; i contains 16bit words in little endian */ uint8_t channels; - unsigned int x, j, src, dst; int o; unsigned int length; /**> number of shading calibration data words */ - unsigned int i, res, factor; + unsigned int x, j, i, res, factor; int cmat[3]; /**> matrix of color channels */ unsigned int coeff, target_code, val, avgpixels, dk, words_per_color = 0; unsigned int target_dark, target_bright, br; @@ -5361,10 +5366,10 @@ genesys_read_ordered_data (Genesys_Device * dev, SANE_Byte * destination, #endif DBG (DBG_info, "genesys_read_ordered_data: %lu lines left by output\n", - ((dev->total_bytes_to_read - dev->total_bytes_read) * 8) / + ((dev->total_bytes_to_read - dev->total_bytes_read) * 8UL) / (dev->settings.pixels * channels * depth)); DBG (DBG_info, "genesys_read_ordered_data: %lu lines left by input\n", - ((dev->read_bytes_left + dev->read_buffer.avail) * 8) / + ((dev->read_bytes_left + dev->read_buffer.avail) * 8UL) / (src_pixels * channels * depth)); if (channels == 1) @@ -5928,7 +5933,7 @@ calc_parameters (Genesys_Scanner * s) } else { - s->dev->buffer_image=SANE_TRUE; + s->dev->buffer_image=SANE_FALSE; } return status; @@ -6884,76 +6889,99 @@ write_calibration (Genesys_Device * dev) static SANE_Status genesys_buffer_image(Genesys_Scanner *s) { - SANE_Status status=SANE_STATUS_GOOD; + SANE_Status status = SANE_STATUS_GOOD; size_t maximum; /**> maximum bytes size of the scan */ - size_t len; /**> length of scanned data read */ - size_t total; /**> total of butes read */ - size_t size; /**> size of image buffer */ + size_t len; /**> length of scanned data read */ + size_t total; /**> total of butes read */ + size_t size; /**> size of image buffer */ size_t read_size; /**> size of reads */ - int lines; /** number of lines of the scan */ - Genesys_Device *dev=s->dev; + int lines; /** number of lines of the scan */ + Genesys_Device *dev = s->dev; - /* compute maximum number of lines for the scan */ - if(s->params.lines>0) - { - lines=s->params.bytes_per_line * s->params.lines; - } - else - { - lines=(SANE_UNFIX(dev->model->y_size)*dev->settings.yres)/MM_PER_INCH; - } - DBG(DBG_info, "%s: buffering %d lines of %d bytes\n",__FUNCTION__, lines,s->params.bytes_per_line); + /* compute maximum number of lines for the scan */ + if (s->params.lines > 0) + { + lines = s->params.lines; + } + else + { + lines = + (SANE_UNFIX (dev->model->y_size) * dev->settings.yres) / MM_PER_INCH; + } + DBG (DBG_info, "%s: buffering %d lines of %d bytes\n", __FUNCTION__, lines, + s->params.bytes_per_line); - /* maximum bytes to read */ - maximum=s->params.bytes_per_line * lines; + /* maximum bytes to read */ + maximum = s->params.bytes_per_line * lines; - /* initial size of the read buffer */ - size=((2048*2048)/s->params.bytes_per_line)*s->params.bytes_per_line; + /* initial size of the read buffer */ + size = + ((2048 * 2048) / s->params.bytes_per_line) * s->params.bytes_per_line; - /* read size */ - read_size=size/2; + /* read size */ + read_size = size / 2; - /* allocate memory */ - dev->img_buffer=(SANE_Byte *)malloc(size); - if(dev->img_buffer==NULL) - { - DBG (DBG_error, "%s: digital processing requires too much memory.\nConsider disabling it\n",__FUNCTION__); - return SANE_STATUS_NO_MEM; - } + /* allocate memory */ + dev->img_buffer = (SANE_Byte *) malloc (size); + if (dev->img_buffer == NULL) + { + DBG (DBG_error, + "%s: digital processing requires too much memory.\nConsider disabling it\n", + __FUNCTION__); + return SANE_STATUS_NO_MEM; + } - /* loop reading data until we reach maximu or EOF */ - total=0; - while(totalread_size) - { - len=read_size; - } - status = genesys_read_ordered_data (dev, dev->img_buffer+total, &len); - if(status!=SANE_STATUS_EOF && status!=SANE_STATUS_GOOD) - { - free(s->dev->img_buffer); - DBG (DBG_error, "%s: %s buffering failed\n", __FUNCTION__, sane_strstatus (status)); - return status; - } - total+=len; + /* loop reading data until we reach maximu or EOF */ + total = 0; + while (total < maximum && status != SANE_STATUS_EOF) + { + len = size - maximum; + if (len > read_size) + { + len = read_size; + } + status = genesys_read_ordered_data (dev, dev->img_buffer + total, &len); + if (status != SANE_STATUS_EOF && status != SANE_STATUS_GOOD) + { + free (s->dev->img_buffer); + DBG (DBG_error, "%s: %s buffering failed\n", __FUNCTION__, + sane_strstatus (status)); + return status; + } + total += len; - /* do we need to enlarge read buffer ? */ - if(total+read_size>size && status != SANE_STATUS_EOF) - { - size+=read_size; - dev->img_buffer=(SANE_Byte *)realloc(dev->img_buffer,size); - if(dev->img_buffer==NULL) - { - DBG (DBG_error0, "%s: digital processing requires too much memory.\nConsider disabling it\n",__FUNCTION__); - return SANE_STATUS_NO_MEM; - } - } - } - s->dev->total_bytes_to_read=total; - s->dev->total_bytes_read=0; - return SANE_STATUS_GOOD; + /* do we need to enlarge read buffer ? */ + if (total + read_size > size && status != SANE_STATUS_EOF) + { + size += read_size; + dev->img_buffer = (SANE_Byte *) realloc (dev->img_buffer, size); + if (dev->img_buffer == NULL) + { + DBG (DBG_error0, + "%s: digital processing requires too much memory.\nConsider disabling it\n", + __FUNCTION__); + return SANE_STATUS_NO_MEM; + } + } + } + + /* update counters */ + dev->total_bytes_to_read = total; + dev->total_bytes_read = 0; + + /* update params */ + s->params.lines = total / s->params.bytes_per_line; + if (DBG_LEVEL >= DBG_io2) + { + sanei_genesys_write_pnm_file ("unprocessed.pnm", + dev->img_buffer, + s->params.depth, + s->params.format==SANE_FRAME_RGB ? 3:1, + s->params.pixels_per_line, + s->params.lines); + } + + return SANE_STATUS_GOOD; } /* -------------------------- SANE API functions ------------------------- */ @@ -6975,6 +7003,9 @@ sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) /* init usb use */ sanei_usb_init (); + /* init sanei_magic */ + sanei_magic_init(); + DBG (DBG_info, "sane_init: %s endian machine\n", #ifdef WORDS_BIGENDIAN "big" @@ -7814,7 +7845,11 @@ sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) DBG (DBG_proc, "sane_get_parameters: start\n"); - RIE (calc_parameters (s)); + /* don't recompute parameters once data reading is active, ie during scan */ + if(s->dev->read_active == SANE_FALSE) + { + RIE (calc_parameters (s)); + } if (params) { *params = s->params; @@ -7824,6 +7859,7 @@ sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) * don't know the real document height. */ if (s->dev->model->is_sheetfed == SANE_TRUE + && s->dev->read_active == SANE_FALSE && s->val[OPT_BR_Y].w == s->opt[OPT_BR_Y].constraint.range->max) { params->lines = -1; @@ -7870,7 +7906,13 @@ sane_start (SANE_Handle handle) * at the end */ if (s->dev->buffer_image) { - status=genesys_buffer_image(s); + RIE(genesys_buffer_image(s)); + + /* crop image if required */ + if(s->val[OPT_SWCROP].b == SANE_TRUE) + { + RIE(genesys_crop(s)); + } } DBG (DBG_proc, "sane_start: exit\n"); diff --git a/backend/genesys_conv.c b/backend/genesys_conv.c index f41c76278..beb454795 100644 --- a/backend/genesys_conv.c +++ b/backend/genesys_conv.c @@ -264,3 +264,64 @@ genesys_shrink_lines_1 ( return SANE_STATUS_GOOD; } + +/** Look in image for likely left/right/bottom paper edges, then crop image. + * Since failing to crop isn't fatal, we always return SANE_STATUS_GOOD . + */ +static SANE_Status +genesys_crop(Genesys_Scanner *s) +{ + SANE_Status status = SANE_STATUS_GOOD; + Genesys_Device *dev = s->dev; + int top = 0; + int bottom = 0; + int left = 0; + int right = 0; + + DBG (DBG_proc, "%s: start\n", __FUNCTION__); + + /* first find edges if any */ + status = sanei_magic_findEdges (&s->params, + dev->img_buffer, + dev->settings.xres, + dev->settings.yres, + &top, + &bottom, + &left, + &right); + if (status != SANE_STATUS_GOOD) + { + DBG (DBG_info, "%s: bad or no edges, bailing\n", __FUNCTION__); + goto cleanup; + } + DBG (DBG_io, "%s: t:%d b:%d l:%d r:%d\n", __FUNCTION__, top, bottom, left, + right); + + /* now crop the image */ + status = + sanei_magic_crop (&(s->params), dev->img_buffer, top, bottom, 0, right); + if (status) + { + DBG (DBG_warn, "%s: failed to crop\n", __FUNCTION__); + goto cleanup; + } + + if (DBG_LEVEL >= DBG_io2) + { + sanei_genesys_write_pnm_file ("cropped.pnm", + dev->img_buffer, + s->params.depth, + s->params.format==SANE_FRAME_RGB ? 3:1, + s->params.pixels_per_line, + s->params.lines); + } + + /* update counters to new image size */ + dev->total_bytes_to_read = s->params.bytes_per_line * s->params.lines; + +cleanup: + DBG (DBG_proc, "%s: completed\n", __FUNCTION__); + return SANE_STATUS_GOOD; +} + +/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */ From 056994c6c47e32d362a9d2de7c61c80e77c71fda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Voltz?= Date: Sun, 12 Sep 2010 21:20:19 +0200 Subject: [PATCH 4/6] use same estimation method for getTransX than in getTransY --- sanei/sanei_magic.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sanei/sanei_magic.c b/sanei/sanei_magic.c index 33fce2bb4..e8daf50e8 100644 --- a/sanei/sanei_magic.c +++ b/sanei/sanei_magic.c @@ -723,8 +723,8 @@ getTopEdge(int width, int height, int resolution, { SANE_Status ret = SANE_STATUS_GOOD; - int slopes = 11; - int offsets = 11; + int slopes = 31; + int offsets = 31; double maxSlope = 1; double minSlope = -1; int maxOffset = resolution/6; @@ -1340,7 +1340,7 @@ sanei_magic_getTransX ( near += buffer[i*bwidth + j*depth + k]; } - if(abs(near - far) > winLen*depth*9){ + if(abs(near - far) > 50*winLen*depth - near*40/255){ buff[i] = j; break; } From 7ff262109e96e519e36987aa9b58ca424b221fc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Voltz?= Date: Sun, 12 Sep 2010 21:24:45 +0200 Subject: [PATCH 5/6] working deskew and crop --- backend/genesys.c | 20 +++++++++-- backend/genesys_conv.c | 81 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 87 insertions(+), 14 deletions(-) diff --git a/backend/genesys.c b/backend/genesys.c index 2729eff18..252dc2628 100644 --- a/backend/genesys.c +++ b/backend/genesys.c @@ -119,7 +119,7 @@ static SANE_String_Const source_list[] = { }; static SANE_Range swdespeck_range = { - 0, + 1, 9, 1 }; @@ -5927,7 +5927,9 @@ calc_parameters (Genesys_Scanner * s) s->dev->settings.threshold_curve=0; /* some digital processing requires the whole picture to be buffered */ - if (s->val[OPT_SWDESPECK].b || s->val[OPT_SWCROP].b || s->val[OPT_SWDESKEW].b) + /* no digital processing takes place when doing preview */ + if ((s->val[OPT_SWDESPECK].b || s->val[OPT_SWCROP].b || s->val[OPT_SWDESKEW].b) + &&(!s->val[OPT_PREVIEW].b)) { s->dev->buffer_image=SANE_TRUE; } @@ -6248,7 +6250,7 @@ init_options (Genesys_Scanner * s) s->opt[OPT_DESPECK].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_DESPECK].constraint.range = &swdespeck_range; s->opt[OPT_DESPECK].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; - s->val[OPT_DESPECK].w= 1; + s->val[OPT_DESPECK].w = 1; /* crop by software */ s->opt[OPT_SWCROP].name = "swcrop"; @@ -7908,6 +7910,18 @@ sane_start (SANE_Handle handle) { RIE(genesys_buffer_image(s)); + /* deskew image if required */ + if(s->val[OPT_SWDESKEW].b == SANE_TRUE) + { + RIE(genesys_deskew(s)); + } + + /* despeck image if required */ + if(s->val[OPT_SWDESPECK].b == SANE_TRUE) + { + RIE(genesys_despeck(s)); + } + /* crop image if required */ if(s->val[OPT_SWCROP].b == SANE_TRUE) { diff --git a/backend/genesys_conv.c b/backend/genesys_conv.c index beb454795..b8660df97 100644 --- a/backend/genesys_conv.c +++ b/backend/genesys_conv.c @@ -299,22 +299,12 @@ genesys_crop(Genesys_Scanner *s) /* now crop the image */ status = - sanei_magic_crop (&(s->params), dev->img_buffer, top, bottom, 0, right); + sanei_magic_crop (&(s->params), dev->img_buffer, top, bottom, left, right); if (status) { DBG (DBG_warn, "%s: failed to crop\n", __FUNCTION__); goto cleanup; } - - if (DBG_LEVEL >= DBG_io2) - { - sanei_genesys_write_pnm_file ("cropped.pnm", - dev->img_buffer, - s->params.depth, - s->params.format==SANE_FRAME_RGB ? 3:1, - s->params.pixels_per_line, - s->params.lines); - } /* update counters to new image size */ dev->total_bytes_to_read = s->params.bytes_per_line * s->params.lines; @@ -324,4 +314,73 @@ cleanup: return SANE_STATUS_GOOD; } +/** Look in image for likely upper and left paper edges, then rotate + * image so that upper left corner of paper is upper left of image. + * @return since failure doens't prevent scanning, we always return + * SANE_STATUS_GOOD + */ +static SANE_Status +genesys_deskew(Genesys_Scanner *s) +{ + SANE_Status status; + Genesys_Device *dev = s->dev; + + int x = 0, y = 0, bg; + double slope = 0; + + DBG (DBG_proc, "%s: start\n", __FUNCTION__); + + bg=0; + if(s->params.format==SANE_FRAME_GRAY && s->params.depth == 1) + { + bg=0xff; + } + status = sanei_magic_findSkew (&s->params, + dev->img_buffer, + dev->sensor.optical_res, + dev->sensor.optical_res, + &x, + &y, + &slope); + if (status!=SANE_STATUS_GOOD) + { + DBG (DBG_error, "%s: bad findSkew, bailing\n", __FUNCTION__); + return SANE_STATUS_GOOD; + } + DBG(DBG_info, "%s: slope=%f => %f\n",__FUNCTION__,slope, (slope/M_PI_2)*90); + /* rotate image slope is in [-PI/2,PI/2] + * positive values rotate trigonometric direction wise */ + status = sanei_magic_rotate (&s->params, + dev->img_buffer, + x, + y, + slope, + bg); + if (status!=SANE_STATUS_GOOD) + { + DBG (DBG_error, "%s: rotate error: %s", __FUNCTION__, sane_strstatus(status)); + } + + DBG (DBG_proc, "%s: completed\n", __FUNCTION__); + return SANE_STATUS_GOOD; +} + +/** remove lone dots + * @return since failure doens't prevent scanning, we always return + * SANE_STATUS_GOOD + */ +static SANE_Status +genesys_despeck(Genesys_Scanner *s) +{ + if(sanei_magic_despeck(&s->params, + s->dev->img_buffer, + s->val[OPT_DESPECK].w)!=SANE_STATUS_GOOD) + { + DBG (DBG_error, "%s: bad despeck, bailing\n",__FUNCTION__); + } + + return SANE_STATUS_GOOD; +} + + /* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */ From 09f099ec035b84f22fb69260fce7dfa2f1ffd928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Voltz?= Date: Sun, 12 Sep 2010 21:40:25 +0200 Subject: [PATCH 6/6] update for new sofware features --- doc/sane-genesys.man | 44 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/doc/sane-genesys.man b/doc/sane-genesys.man index 0289ee2ac..ee0713513 100644 --- a/doc/sane-genesys.man +++ b/doc/sane-genesys.man @@ -1,7 +1,7 @@ .TH "sane\-genesys" "5" "18 Jun 2010" "@PACKAGEVERSION@" "SANE Scanner Access Now Easy" .IX sane\-genesys .SH "NAME" -sane\-genesys \- SANE backend for GL646, GL841 and GL847 based USB flatbed scanners +sane\-genesys \- SANE backend for GL646, GL841, GL843 and GL847 based USB flatbed scanners .SH "DESCRIPTION" The .B sane\-genesys @@ -41,7 +41,7 @@ or syslog) to the sane\-devel mailing list. Even if the scanner's name is only slightly different from the models mentioned above, please let me know. .PP If you own a scanner that isn't detected by the genesys backend but has a GL646, -GL847 or GL847 chipset, you can try to add it to the backend. +GL841, GL843 or GL847 chipset, you can try to add it to the backend. .PP .SH "CALIBRATION" To give correct image quality, sheet fed scanners need to be calibrated using the @@ -55,21 +55,55 @@ will have to be redone. .SH EXTRAS SCAN OPTIONS .B \-\-lamp\-off\-time number +.RS The lamp will be turned off after the given time (in minutes). A value of 0 means that the lamp won't be turned off. +.RE .B \-\-threshold percent +.RS 0..100% (in steps of 1). Select minimum brightness to get a white point. Pixels whith brightness below that value will be scanned as black. +.RE .B \-\-disable-interpolation yes|no +.RS When using high resolutions where the horizontal resolution is smaller than vertical resolution, data is expanded by software to preserve picture geometry. This can be disbled by this option to get real scanned data. +.RE .B \-\-color-filter None|Red|Green|Blue +.RS When using gray or lineart this option selects the used color. Using a color filter will give a monochrome scan. CIS based scanners can to true gray when no filter (None value) is selected. +.RE + +.PP +Additionally, several 'software' options are exposed by the backend. These +are reimplementations of features provided natively by larger scanners, but +running on the host computer. This enables smaller machines to have similar +capabilites. Please note that these features are somewhat simplistic, and +may not perform as well as the native implementations. Note also that these +features all require that the driver cache the entire image in memory. This +will almost certainly result in a reduction of scanning speed. +.PP +.B \-\-swcrop +.RS + Requests the driver to detect the extremities of the paper within the larger +image, and crop the empty edges. +.RE +.PP +.B \-\-swdeskew +.RS + Requests the driver to detect the rotation of the paper within the larger +image, and counter the rotation. +.RE +.PP +.B \-\-swdespeck \-\-despeck X +.RS + Requests the driver to find and remove dots of X diameter or smaller from the +image, and fill the space with the average surrounding color. .PP .SH "SYSTEM ISSUES" @@ -138,8 +172,12 @@ part. This environment variable controls the debug level for the specific GL841 code part. .TP +.B SANE_DEBUG_GENESYS_GL843 +This environment variable controls the debug level for the specific GL843 code +part. +.TP .B SANE_DEBUG_GENESYS_GL847 -This environment variable controls the debug level for the specific GL841 code +This environment variable controls the debug level for the specific GL847 code part.