diff options
Diffstat (limited to 'backend/hp5590.c')
-rw-r--r-- | backend/hp5590.c | 2150 |
1 files changed, 1670 insertions, 480 deletions
diff --git a/backend/hp5590.c b/backend/hp5590.c index fabf40a..b206406 100644 --- a/backend/hp5590.c +++ b/backend/hp5590.c @@ -1,6 +1,8 @@ /* sane - Scanner Access Now Easy. Copyright (C) 2007 Ilia Sotnikov <hostcc@gmail.com> HP ScanJet 4570c support by Markham Thomas + ADF page detection and high DPI fixes by Bernard Badeer + scanbd integration by Damiano Scaramuzza and Bernard Badeer This file is part of the SANE package. This program is free software; you can redistribute it and/or @@ -59,47 +61,161 @@ #include "../include/sane/saneopts.h" #include "hp5590_cmds.c" #include "hp5590_low.c" +#include "../include/sane/sanei_net.h" /* Debug levels */ -#define DBG_err 0 -#define DBG_proc 10 -#define DBG_verbose 20 +#define DBG_err 0 +#define DBG_proc 10 +#define DBG_verbose 20 +#define DBG_details 30 #define hp5590_assert(exp) if(!(exp)) { \ - DBG (DBG_err, "Assertion '%s' failed at %s:%u\n", #exp, __FILE__, __LINE__);\ - return SANE_STATUS_INVAL; \ + DBG (DBG_err, "Assertion '%s' failed at %s:%u\n", #exp, __FILE__, __LINE__);\ + return SANE_STATUS_INVAL; \ } #define hp5590_assert_void_return(exp) if(!(exp)) { \ - DBG (DBG_err, "Assertion '%s' failed at %s:%u\n", #exp, __FILE__, __LINE__);\ - return; \ + DBG (DBG_err, "Assertion '%s' failed at %s:%u\n", #exp, __FILE__, __LINE__);\ + return; \ } +#define MY_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MY_MAX(a, b) (((a) > (b)) ? (a) : (b)) + /* #define HAS_WORKING_COLOR_48 */ -#define BUILD 7 -#define USB_TIMEOUT 30 * 1000 +#define BUILD 8 +#define USB_TIMEOUT 30 * 1000 static SANE_Word res_list[] = { 6, 100, 200, 300, 600, 1200, 2400 }; -#define SANE_VALUE_SCAN_SOURCE_FLATBED SANE_I18N("Flatbed") -#define SANE_VALUE_SCAN_SOURCE_ADF SANE_I18N("ADF") -#define SANE_VALUE_SCAN_SOURCE_ADF_DUPLEX SANE_I18N("ADF Duplex") -#define SANE_VALUE_SCAN_SOURCE_TMA_SLIDES SANE_I18N("TMA Slides") -#define SANE_VALUE_SCAN_SOURCE_TMA_NEGATIVES SANE_I18N("TMA Negatives") +#define SANE_VALUE_SCAN_SOURCE_FLATBED SANE_I18N("Flatbed") +#define SANE_VALUE_SCAN_SOURCE_ADF SANE_I18N("ADF") +#define SANE_VALUE_SCAN_SOURCE_ADF_DUPLEX SANE_I18N("ADF Duplex") +#define SANE_VALUE_SCAN_SOURCE_TMA_SLIDES SANE_I18N("TMA Slides") +#define SANE_VALUE_SCAN_SOURCE_TMA_NEGATIVES SANE_I18N("TMA Negatives") +static SANE_String_Const +sources_list[] = { + SANE_VALUE_SCAN_SOURCE_FLATBED, + SANE_VALUE_SCAN_SOURCE_ADF, + SANE_VALUE_SCAN_SOURCE_ADF_DUPLEX, + SANE_VALUE_SCAN_SOURCE_TMA_SLIDES, + SANE_VALUE_SCAN_SOURCE_TMA_NEGATIVES, + NULL +}; + +#define SANE_VALUE_SCAN_MODE_COLOR_24 SANE_VALUE_SCAN_MODE_COLOR +#define SANE_VALUE_SCAN_MODE_COLOR_48 SANE_I18N("Color (48 bits)") +#define HAS_WORKING_COLOR_48 1 + +#define SANE_NAME_LAMP_TIMEOUT "extend-lamp-timeout" +#define SANE_TITLE_LAMP_TIMEOUT SANE_I18N("Extend lamp timeout") +#define SANE_DESC_LAMP_TIMEOUT SANE_I18N("Extends lamp timeout (from 15 minutes to 1 hour)") +#define SANE_NAME_WAIT_FOR_BUTTON "wait-for-button" +#define SANE_TITLE_WAIT_FOR_BUTTON SANE_I18N("Wait for button") +#define SANE_DESC_WAIT_FOR_BUTTON SANE_I18N("Waits for button before scanning") +#define SANE_NAME_BUTTON_PRESSED "button-pressed" +#define SANE_TITLE_BUTTON_PRESSED SANE_I18N("Last button pressed") +#define SANE_DESC_BUTTON_PRESSED SANE_I18N("Get ID of last button pressed (read only)") +#define SANE_NAME_LCD_COUNTER "counter-value" +#define SANE_TITLE_LCD_COUNTER SANE_I18N("LCD counter") +#define SANE_DESC_LCD_COUNTER SANE_I18N("Get value of LCD counter (read only)") +#define SANE_NAME_COLOR_LED "color-led" +#define SANE_TITLE_COLOR_LED SANE_I18N("Color LED indicator") +#define SANE_DESC_COLOR_LED SANE_I18N("Get value of LED indicator (read only)") +#define SANE_NAME_DOC_IN_ADF "doc-in-adf" +#define SANE_TITLE_DOC_IN_ADF SANE_I18N("Document available in ADF") +#define SANE_DESC_DOC_IN_ADF SANE_I18N("Get state of document-available indicator in ADF (read only)") +#define SANE_NAME_OVERWRITE_EOP_PIXEL "hide-eop-pixel" +#define SANE_TITLE_OVERWRITE_EOP_PIXEL SANE_I18N("Hide end-of-page pixel") +#define SANE_DESC_OVERWRITE_EOP_PIXEL SANE_I18N("Hide end-of-page indicator pixels and overwrite with neighbor pixels") +#define SANE_NAME_TRAILING_LINES_MODE "trailing-lines-mode" +#define SANE_TITLE_TRAILING_LINES_MODE SANE_I18N("Filling mode of trailing lines after scan data (ADF)") +#define SANE_DESC_TRAILING_LINES_MODE SANE_I18N("raw = raw scan data, last = repeat last scan line, raster = b/w raster, "\ + "white = white color, black = black color, color = RGB or gray color value") +#define SANE_NAME_TRAILING_LINES_COLOR "trailing-lines-color" +#define SANE_TITLE_TRAILING_LINES_COLOR SANE_I18N("RGB or gray color value for filling mode 'color'") +#define SANE_DESC_TRAILING_LINES_COLOR SANE_I18N("Color value for trailing lines filling mode 'color'. "\ + "RGB color as r*65536+256*g+b or gray value (default=violet or gray)") + +#define BUTTON_PRESSED_VALUE_COUNT 11 +#define BUTTON_PRESSED_VALUE_NONE_KEY "none" +#define BUTTON_PRESSED_VALUE_POWER_KEY "power" +#define BUTTON_PRESSED_VALUE_SCAN_KEY "scan" +#define BUTTON_PRESSED_VALUE_COLLECT_KEY "collect" +#define BUTTON_PRESSED_VALUE_FILE_KEY "file" +#define BUTTON_PRESSED_VALUE_EMAIL_KEY "email" +#define BUTTON_PRESSED_VALUE_COPY_KEY "copy" +#define BUTTON_PRESSED_VALUE_UP_KEY "up" +#define BUTTON_PRESSED_VALUE_DOWN_KEY "down" +#define BUTTON_PRESSED_VALUE_MODE_KEY "mode" +#define BUTTON_PRESSED_VALUE_CANCEL_KEY "cancel" +#define BUTTON_PRESSED_VALUE_MAX_KEY_LEN 32 +static SANE_String_Const +buttonstate_list[] = { + BUTTON_PRESSED_VALUE_NONE_KEY, + BUTTON_PRESSED_VALUE_POWER_KEY, + BUTTON_PRESSED_VALUE_SCAN_KEY, + BUTTON_PRESSED_VALUE_COLLECT_KEY, + BUTTON_PRESSED_VALUE_FILE_KEY, + BUTTON_PRESSED_VALUE_EMAIL_KEY, + BUTTON_PRESSED_VALUE_COPY_KEY, + BUTTON_PRESSED_VALUE_UP_KEY, + BUTTON_PRESSED_VALUE_DOWN_KEY, + BUTTON_PRESSED_VALUE_MODE_KEY, + BUTTON_PRESSED_VALUE_CANCEL_KEY, + NULL +}; + +#define COLOR_LED_VALUE_COUNT 2 +#define COLOR_LED_VALUE_COLOR_KEY "color" +#define COLOR_LED_VALUE_BLACKWHITE_KEY "black_white" +#define COLOR_LED_VALUE_MAX_KEY_LEN 32 +static SANE_String_Const +colorledstate_list[] = { + COLOR_LED_VALUE_COLOR_KEY, + COLOR_LED_VALUE_BLACKWHITE_KEY, + NULL +}; -#define SANE_VALUE_SCAN_MODE_COLOR_24 SANE_VALUE_SCAN_MODE_COLOR -#define SANE_VALUE_SCAN_MODE_COLOR_48 SANE_I18N("Color (48 bits)") +#define LCD_COUNTER_VALUE_MIN 1 +#define LCD_COUNTER_VALUE_MAX 99 +#define LCD_COUNTER_VALUE_QUANT 1 +static SANE_Range +lcd_counter_range = { + LCD_COUNTER_VALUE_MIN, + LCD_COUNTER_VALUE_MAX, + LCD_COUNTER_VALUE_QUANT +}; -#define SANE_NAME_LAMP_TIMEOUT "extend-lamp-timeout" -#define SANE_TITLE_LAMP_TIMEOUT SANE_I18N("Extend lamp timeout") -#define SANE_DESC_LAMP_TIMEOUT SANE_I18N("Extends lamp timeout (from 15 minutes to 1 hour)") -#define SANE_NAME_WAIT_FOR_BUTTON "wait-for-button" -#define SANE_TITLE_WAIT_FOR_BUTTON SANE_I18N("Wait for button") -#define SANE_DESC_WAIT_FOR_BUTTON SANE_I18N("Waits for button before scanning") +#define TRAILING_LINES_MODE_RAW 0 +#define TRAILING_LINES_MODE_LAST 1 +#define TRAILING_LINES_MODE_RASTER 2 +#define TRAILING_LINES_MODE_WHITE 3 +#define TRAILING_LINES_MODE_BLACK 4 +#define TRAILING_LINES_MODE_COLOR 5 +#define TRAILING_LINES_MODE_VALUE_COUNT 6 + +#define TRAILING_LINES_MODE_RAW_KEY "raw" +#define TRAILING_LINES_MODE_LAST_KEY "last" +#define TRAILING_LINES_MODE_RASTER_KEY "raster" +#define TRAILING_LINES_MODE_WHITE_KEY "white" +#define TRAILING_LINES_MODE_BLACK_KEY "black" +#define TRAILING_LINES_MODE_COLOR_KEY "color" +#define TRAILING_LINES_MODE_MAX_KEY_LEN 24 +static SANE_String_Const +trailingmode_list[] = { + TRAILING_LINES_MODE_RAW_KEY, + TRAILING_LINES_MODE_LAST_KEY, + TRAILING_LINES_MODE_RASTER_KEY, + TRAILING_LINES_MODE_WHITE_KEY, + TRAILING_LINES_MODE_BLACK_KEY, + TRAILING_LINES_MODE_COLOR_KEY, + NULL +}; -#define MAX_SCAN_SOURCE_VALUE_LEN 24 -#define MAX_SCAN_MODE_VALUE_LEN 24 +#define MAX_SCAN_SOURCE_VALUE_LEN 24 +#define MAX_SCAN_MODE_VALUE_LEN 24 static SANE_Range range_x, range_y, range_qual; @@ -126,29 +242,51 @@ enum hp5590_opt_idx { HP5590_OPT_RESOLUTION, HP5590_OPT_LAMP_TIMEOUT, HP5590_OPT_WAIT_FOR_BUTTON, + HP5590_OPT_BUTTON_PRESSED, + HP5590_OPT_COLOR_LED, + HP5590_OPT_LCD_COUNTER, + HP5590_OPT_DOC_IN_ADF, HP5590_OPT_PREVIEW, + HP5590_OPT_OVERWRITE_EOP_PIXEL, + HP5590_OPT_TRAILING_LINES_MODE, + HP5590_OPT_TRAILING_LINES_COLOR, HP5590_OPT_LAST }; struct hp5590_scanner { - struct scanner_info *info; - enum proto_flags proto_flags; - SANE_Device sane; - SANE_Int dn; - float br_x, br_y, tl_x, tl_y; - unsigned int dpi; - enum color_depths depth; - enum scan_sources source; - SANE_Bool extend_lamp_timeout; - SANE_Bool wait_for_button; - SANE_Bool preview; - unsigned int quality; - SANE_Option_Descriptor *opts; - struct hp5590_scanner *next; - unsigned int image_size; - SANE_Int transferred_image_size; - void *bulk_read_state; - SANE_Bool scanning; + struct scanner_info *info; + enum proto_flags proto_flags; + SANE_Device sane; + SANE_Int dn; + float br_x, br_y, tl_x, tl_y; + unsigned int dpi; + enum color_depths depth; + enum scan_sources source; + SANE_Bool extend_lamp_timeout; + SANE_Bool wait_for_button; + SANE_Bool preview; + unsigned int quality; + SANE_Option_Descriptor *opts; + struct hp5590_scanner *next; + unsigned long long image_size; + unsigned long long transferred_image_size; + void *bulk_read_state; + SANE_Bool scanning; + SANE_Bool overwrite_eop_pixel; + SANE_Byte *eop_last_line_data; + unsigned int eop_last_line_data_rpos; + SANE_Int eop_trailing_lines_mode; + SANE_Int eop_trailing_lines_color; + SANE_Byte *adf_next_page_lines_data; + unsigned int adf_next_page_lines_data_size; + unsigned int adf_next_page_lines_data_rpos; + unsigned int adf_next_page_lines_data_wpos; + SANE_Byte *one_line_read_buffer; + unsigned int one_line_read_buffer_rpos; + SANE_Byte *color_shift_line_buffer1; + unsigned int color_shift_buffered_lines1; + SANE_Byte *color_shift_line_buffer2; + unsigned int color_shift_buffered_lines2; }; static @@ -157,19 +295,19 @@ struct hp5590_scanner *scanners_list; /******************************************************************************/ static SANE_Status calc_image_params (struct hp5590_scanner *scanner, - unsigned int *pixel_bits, - unsigned int *pixels_per_line, - unsigned int *bytes_per_line, - unsigned int *lines, - unsigned int *image_size) + unsigned int *pixel_bits, + unsigned int *pixels_per_line, + unsigned int *bytes_per_line, + unsigned int *lines, + unsigned long long *image_size) { - unsigned int _pixel_bits; - SANE_Status ret; - unsigned int _pixels_per_line; - unsigned int _bytes_per_line; - unsigned int _lines; - unsigned int _image_size; - float var; + unsigned int _pixel_bits; + SANE_Status ret; + unsigned int _pixels_per_line; + unsigned int _bytes_per_line; + unsigned int _lines; + unsigned int _image_size; + float var; DBG (DBG_proc, "%s\n", __func__); @@ -195,7 +333,7 @@ calc_image_params (struct hp5590_scanner *scanner, if (var > _bytes_per_line) _bytes_per_line++; - _image_size = _lines * _bytes_per_line; + _image_size = (unsigned long long) _lines * _bytes_per_line; DBG (DBG_verbose, "%s: pixel_bits: %u, pixels_per_line: %u, " "bytes_per_line: %u, lines: %u, image_size: %u\n", @@ -219,18 +357,18 @@ calc_image_params (struct hp5590_scanner *scanner, return SANE_STATUS_GOOD; } - + /******************************************************************************/ static SANE_Status attach_usb_device (SANE_String_Const devname, - enum hp_scanner_types hp_scanner_type) + enum hp_scanner_types hp_scanner_type) { - struct scanner_info *info; - struct hp5590_scanner *scanner, *ptr; - unsigned int max_count, count; - SANE_Int dn; - SANE_Status ret; - const struct hp5590_model *hp5590_model; + struct scanner_info *info; + struct hp5590_scanner *scanner, *ptr; + unsigned int max_count, count; + SANE_Int dn; + SANE_Status ret; + const struct hp5590_model *hp5590_model; DBG (DBG_proc, "%s: Opening USB device\n", __func__); if (sanei_usb_open (devname, &dn) != SANE_STATUS_GOOD) @@ -242,7 +380,7 @@ attach_usb_device (SANE_String_Const devname, return ret; if (hp5590_init_scanner (dn, hp5590_model->proto_flags, - &info, hp_scanner_type) != 0) + &info, hp_scanner_type) != 0) return SANE_STATUS_IO_ERROR; DBG (1, "%s: found HP%s scanner at '%s'\n", @@ -250,13 +388,13 @@ attach_usb_device (SANE_String_Const devname, DBG (DBG_verbose, "%s: Reading max scan count\n", __func__); if (hp5590_read_max_scan_count (dn, hp5590_model->proto_flags, - &max_count) != 0) + &max_count) != 0) return SANE_STATUS_IO_ERROR; DBG (DBG_verbose, "%s: Max Scanning count %u\n", __func__, max_count); DBG (DBG_verbose, "%s: Reading scan count\n", __func__); if (hp5590_read_scan_count (dn, hp5590_model->proto_flags, - &count) != 0) + &count) != 0) return SANE_STATUS_IO_ERROR; DBG (DBG_verbose, "%s: Scanning count %u\n", __func__, count); @@ -282,6 +420,18 @@ attach_usb_device (SANE_String_Const devname, scanner->info = info; scanner->bulk_read_state = NULL; scanner->opts = NULL; + scanner->eop_last_line_data = NULL; + scanner->eop_last_line_data_rpos = 0; + scanner->adf_next_page_lines_data = NULL; + scanner->adf_next_page_lines_data_size = 0; + scanner->adf_next_page_lines_data_rpos = 0; + scanner->adf_next_page_lines_data_wpos = 0; + scanner->one_line_read_buffer = NULL; + scanner->one_line_read_buffer_rpos = 0; + scanner->color_shift_line_buffer1 = NULL; + scanner->color_shift_buffered_lines1 = 0; + scanner->color_shift_line_buffer2 = NULL; + scanner->color_shift_buffered_lines2 = 0; if (!scanners_list) scanners_list = scanner; @@ -326,11 +476,11 @@ attach_hp7650 (SANE_String_Const devname) SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize) { - SANE_Status ret; - SANE_Word vendor_id, product_id; - + SANE_Status ret; + SANE_Word vendor_id, product_id; + DBG_INIT(); - + DBG (1, "SANE backed for HP ScanJet 4500C/4570C/5500C/5550C/5590/7650 %u.%u.%u\n", SANE_CURRENT_MAJOR, V_MINOR, BUILD); DBG (1, "(c) Ilia Sotnikov <hostcc@gmail.com>\n"); @@ -389,7 +539,34 @@ void sane_exit (void) for (ptr = scanners_list; ptr; ptr = pnext) { if (ptr->opts != NULL) - free (ptr->opts); + free (ptr->opts); + if (ptr->eop_last_line_data != NULL) { + free (ptr->eop_last_line_data); + ptr->eop_last_line_data = NULL; + ptr->eop_last_line_data_rpos = 0; + } + if (ptr->adf_next_page_lines_data != NULL) { + free (ptr->adf_next_page_lines_data); + ptr->adf_next_page_lines_data = NULL; + ptr->adf_next_page_lines_data_size = 0; + ptr->adf_next_page_lines_data_wpos = 0; + ptr->adf_next_page_lines_data_rpos = 0; + } + if (ptr->one_line_read_buffer != NULL) { + free (ptr->one_line_read_buffer); + ptr->one_line_read_buffer = NULL; + ptr->one_line_read_buffer_rpos = 0; + } + if (ptr->color_shift_line_buffer1 != NULL) { + free (ptr->color_shift_line_buffer1); + ptr->color_shift_line_buffer1 = NULL; + ptr->color_shift_buffered_lines1 = 0; + } + if (ptr->color_shift_line_buffer2 != NULL) { + free (ptr->color_shift_line_buffer2); + ptr->color_shift_line_buffer2 = NULL; + ptr->color_shift_buffered_lines2 = 0; + } pnext = ptr->next; free (ptr); } @@ -428,11 +605,8 @@ sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { - struct hp5590_scanner *ptr; - SANE_Option_Descriptor *opts; - unsigned int available_sources; - SANE_String_Const *sources_list; - unsigned int source_idx; + struct hp5590_scanner *ptr; + SANE_Option_Descriptor *opts; DBG (DBG_proc, "%s: device name: %s\n", __func__, devicename); @@ -444,19 +618,31 @@ sane_open (SANE_String_Const devicename, SANE_Handle * handle) ptr = scanners_list; } else { for (ptr = scanners_list; - ptr && strcmp (ptr->sane.name, devicename) != 0; - ptr = ptr->next); + ptr && strcmp (ptr->sane.name, devicename) != 0; + ptr = ptr->next); } if (!ptr) return SANE_STATUS_INVAL; + /* DS: Without this after the first scan (and sane_close) + * it was impossible to use again the read_buttons usb routine. + * Function sane_close puts dn = -1. Now sane_open needs to open + * the usb communication again. + */ + if (ptr->dn < 0) { + DBG (DBG_proc, "%s: Reopening USB device\n", __func__); + if (sanei_usb_open (ptr->sane.name, &ptr->dn) != SANE_STATUS_GOOD) + return SANE_STATUS_IO_ERROR; + DBG (DBG_proc, "%s: USB device reopened\n", __func__); + } + ptr->tl_x = 0; ptr->tl_y = 0; ptr->br_x = ptr->info->max_size_x; ptr->br_y = ptr->info->max_size_y; ptr->dpi = res_list[1]; - ptr->depth = DEPTH_BW; + ptr->depth = DEPTH_BW; ptr->source = SOURCE_FLATBED; ptr->extend_lamp_timeout = SANE_FALSE; ptr->wait_for_button = SANE_FALSE; @@ -464,6 +650,9 @@ sane_open (SANE_String_Const devicename, SANE_Handle * handle) ptr->quality = 4; ptr->image_size = 0; ptr->scanning = SANE_FALSE; + ptr->overwrite_eop_pixel = SANE_TRUE; + ptr->eop_trailing_lines_mode = TRAILING_LINES_MODE_LAST; + ptr->eop_trailing_lines_color = 0x7f007f; *handle = ptr; @@ -542,29 +731,7 @@ sane_open (SANE_String_Const devicename, SANE_Handle * handle) opts[HP5590_OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; opts[HP5590_OPT_MODE].constraint.string_list = mode_list; - available_sources = 1; /* Flatbed is always available */ - if (ptr->info->features & FEATURE_ADF) - available_sources += 2; - if (ptr->info->features & FEATURE_TMA) - available_sources += 2; - available_sources++; /* Count terminating NULL */ - sources_list = malloc (available_sources * sizeof (SANE_String_Const)); - if (!sources_list) - return SANE_STATUS_NO_MEM; - source_idx = 0; - sources_list[source_idx++] = SANE_VALUE_SCAN_SOURCE_FLATBED; - if (ptr->info->features & FEATURE_ADF) - { - sources_list[source_idx++] = SANE_VALUE_SCAN_SOURCE_ADF; - sources_list[source_idx++] = SANE_VALUE_SCAN_SOURCE_ADF_DUPLEX; - } - if (ptr->info->features & FEATURE_TMA) - { - sources_list[source_idx++] = SANE_VALUE_SCAN_SOURCE_TMA_SLIDES; - sources_list[source_idx++] = SANE_VALUE_SCAN_SOURCE_TMA_NEGATIVES; - } - sources_list[source_idx] = NULL; - + /* Show all features, check on feature in command line evaluation. */ opts[HP5590_OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE; opts[HP5590_OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE; opts[HP5590_OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE; @@ -605,6 +772,46 @@ sane_open (SANE_String_Const devicename, SANE_Handle * handle) opts[HP5590_OPT_WAIT_FOR_BUTTON].constraint_type = SANE_CONSTRAINT_NONE; opts[HP5590_OPT_WAIT_FOR_BUTTON].constraint.string_list = NULL; + opts[HP5590_OPT_BUTTON_PRESSED].name = SANE_NAME_BUTTON_PRESSED; + opts[HP5590_OPT_BUTTON_PRESSED].title = SANE_TITLE_BUTTON_PRESSED; + opts[HP5590_OPT_BUTTON_PRESSED].desc = SANE_DESC_BUTTON_PRESSED; + opts[HP5590_OPT_BUTTON_PRESSED].type = SANE_TYPE_STRING; + opts[HP5590_OPT_BUTTON_PRESSED].unit = SANE_UNIT_NONE; + opts[HP5590_OPT_BUTTON_PRESSED].size = BUTTON_PRESSED_VALUE_MAX_KEY_LEN; + opts[HP5590_OPT_BUTTON_PRESSED].cap = SANE_CAP_HARD_SELECT | SANE_CAP_SOFT_DETECT; + opts[HP5590_OPT_BUTTON_PRESSED].constraint_type = SANE_CONSTRAINT_STRING_LIST; + opts[HP5590_OPT_BUTTON_PRESSED].constraint.string_list = buttonstate_list; + + opts[HP5590_OPT_COLOR_LED].name = SANE_NAME_COLOR_LED; + opts[HP5590_OPT_COLOR_LED].title = SANE_TITLE_COLOR_LED; + opts[HP5590_OPT_COLOR_LED].desc = SANE_DESC_COLOR_LED; + opts[HP5590_OPT_COLOR_LED].type = SANE_TYPE_STRING; + opts[HP5590_OPT_COLOR_LED].unit = SANE_UNIT_NONE; + opts[HP5590_OPT_COLOR_LED].size = COLOR_LED_VALUE_MAX_KEY_LEN; + opts[HP5590_OPT_COLOR_LED].cap = SANE_CAP_HARD_SELECT | SANE_CAP_SOFT_DETECT; + opts[HP5590_OPT_COLOR_LED].constraint_type = SANE_CONSTRAINT_STRING_LIST; + opts[HP5590_OPT_COLOR_LED].constraint.string_list = colorledstate_list; + + opts[HP5590_OPT_LCD_COUNTER].name = SANE_NAME_LCD_COUNTER; + opts[HP5590_OPT_LCD_COUNTER].title = SANE_TITLE_LCD_COUNTER; + opts[HP5590_OPT_LCD_COUNTER].desc = SANE_DESC_LCD_COUNTER; + opts[HP5590_OPT_LCD_COUNTER].type = SANE_TYPE_INT; + opts[HP5590_OPT_LCD_COUNTER].unit = SANE_UNIT_NONE; + opts[HP5590_OPT_LCD_COUNTER].size = sizeof(SANE_Int); + opts[HP5590_OPT_LCD_COUNTER].cap = SANE_CAP_HARD_SELECT | SANE_CAP_SOFT_DETECT; + opts[HP5590_OPT_LCD_COUNTER].constraint_type = SANE_CONSTRAINT_RANGE; + opts[HP5590_OPT_LCD_COUNTER].constraint.range = &lcd_counter_range; + + opts[HP5590_OPT_DOC_IN_ADF].name = SANE_NAME_DOC_IN_ADF; + opts[HP5590_OPT_DOC_IN_ADF].title = SANE_TITLE_DOC_IN_ADF; + opts[HP5590_OPT_DOC_IN_ADF].desc = SANE_DESC_DOC_IN_ADF; + opts[HP5590_OPT_DOC_IN_ADF].type = SANE_TYPE_BOOL; + opts[HP5590_OPT_DOC_IN_ADF].unit = SANE_UNIT_NONE; + opts[HP5590_OPT_DOC_IN_ADF].size = sizeof(SANE_Bool); + opts[HP5590_OPT_DOC_IN_ADF].cap = SANE_CAP_HARD_SELECT | SANE_CAP_SOFT_DETECT; + opts[HP5590_OPT_DOC_IN_ADF].constraint_type = SANE_CONSTRAINT_NONE; + opts[HP5590_OPT_DOC_IN_ADF].constraint.range = NULL; + opts[HP5590_OPT_PREVIEW].name = SANE_NAME_PREVIEW; opts[HP5590_OPT_PREVIEW].title = SANE_TITLE_PREVIEW; opts[HP5590_OPT_PREVIEW].desc = SANE_DESC_PREVIEW; @@ -615,6 +822,36 @@ sane_open (SANE_String_Const devicename, SANE_Handle * handle) opts[HP5590_OPT_PREVIEW].constraint_type = SANE_CONSTRAINT_NONE; opts[HP5590_OPT_PREVIEW].constraint.string_list = NULL; + opts[HP5590_OPT_OVERWRITE_EOP_PIXEL].name = SANE_NAME_OVERWRITE_EOP_PIXEL; + opts[HP5590_OPT_OVERWRITE_EOP_PIXEL].title = SANE_TITLE_OVERWRITE_EOP_PIXEL; + opts[HP5590_OPT_OVERWRITE_EOP_PIXEL].desc = SANE_DESC_OVERWRITE_EOP_PIXEL; + opts[HP5590_OPT_OVERWRITE_EOP_PIXEL].type = SANE_TYPE_BOOL; + opts[HP5590_OPT_OVERWRITE_EOP_PIXEL].unit = SANE_UNIT_NONE; + opts[HP5590_OPT_OVERWRITE_EOP_PIXEL].size = sizeof(SANE_Bool); + opts[HP5590_OPT_OVERWRITE_EOP_PIXEL].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + opts[HP5590_OPT_OVERWRITE_EOP_PIXEL].constraint_type = SANE_CONSTRAINT_NONE; + opts[HP5590_OPT_OVERWRITE_EOP_PIXEL].constraint.string_list = NULL; + + opts[HP5590_OPT_TRAILING_LINES_MODE].name = SANE_NAME_TRAILING_LINES_MODE; + opts[HP5590_OPT_TRAILING_LINES_MODE].title = SANE_TITLE_TRAILING_LINES_MODE; + opts[HP5590_OPT_TRAILING_LINES_MODE].desc = SANE_DESC_TRAILING_LINES_MODE; + opts[HP5590_OPT_TRAILING_LINES_MODE].type = SANE_TYPE_STRING; + opts[HP5590_OPT_TRAILING_LINES_MODE].unit = SANE_UNIT_NONE; + opts[HP5590_OPT_TRAILING_LINES_MODE].size = TRAILING_LINES_MODE_MAX_KEY_LEN; + opts[HP5590_OPT_TRAILING_LINES_MODE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + opts[HP5590_OPT_TRAILING_LINES_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; + opts[HP5590_OPT_TRAILING_LINES_MODE].constraint.string_list = trailingmode_list; + + opts[HP5590_OPT_TRAILING_LINES_COLOR].name = SANE_NAME_TRAILING_LINES_COLOR; + opts[HP5590_OPT_TRAILING_LINES_COLOR].title = SANE_TITLE_TRAILING_LINES_COLOR; + opts[HP5590_OPT_TRAILING_LINES_COLOR].desc = SANE_DESC_TRAILING_LINES_COLOR; + opts[HP5590_OPT_TRAILING_LINES_COLOR].type = SANE_TYPE_INT; + opts[HP5590_OPT_TRAILING_LINES_COLOR].unit = SANE_UNIT_NONE; + opts[HP5590_OPT_TRAILING_LINES_COLOR].size = sizeof(SANE_Int); + opts[HP5590_OPT_TRAILING_LINES_COLOR].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + opts[HP5590_OPT_TRAILING_LINES_COLOR].constraint_type = SANE_CONSTRAINT_NONE; + opts[HP5590_OPT_TRAILING_LINES_COLOR].constraint.string_list = NULL; + ptr->opts = opts; return SANE_STATUS_GOOD; @@ -646,285 +883,541 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) return &scanner->opts[option]; } +/*************************************DS:Support function read buttons status */ +SANE_Status +read_button_pressed(SANE_Handle handle, enum button_status * button_pressed) +{ + struct hp5590_scanner * scanner = handle; + *button_pressed = BUTTON_NONE; + enum button_status status = BUTTON_NONE; + DBG (DBG_verbose, "%s: Checking button status (device_number = %u) (device_name = %s)\n", __func__, scanner->dn, scanner->sane.name); + SANE_Status ret = hp5590_read_buttons (scanner->dn, scanner->proto_flags, &status); + if (ret != SANE_STATUS_GOOD) + { + DBG (DBG_proc, "%s: Error reading button status (%u)\n", __func__, ret); + return ret; + } + DBG (DBG_verbose, "%s: Button pressed = %d\n", __func__, status); + *button_pressed = status; + return SANE_STATUS_GOOD; +} + +/******************************************************************************/ +SANE_Status +read_lcd_and_led_values(SANE_Handle handle, + SANE_Int * lcd_counter, + enum color_led_status * color_led) +{ + struct hp5590_scanner * scanner = handle; + *lcd_counter = 1; + *color_led = LED_COLOR; + DBG (DBG_verbose, "%s: Reading LCD and LED values (device_number = %u) (device_name = %s)\n", + __func__, scanner->dn, scanner->sane.name); + SANE_Status ret = hp5590_read_lcd_and_led (scanner->dn, scanner->proto_flags, lcd_counter, color_led); + if (ret != SANE_STATUS_GOOD) + { + DBG (DBG_proc, "%s: Error reading LCD and LED values (%u)\n", __func__, ret); + return ret; + } + DBG (DBG_verbose, "%s: LCD = %d, LED = %s\n", __func__, *lcd_counter, + *color_led == LED_BLACKWHITE ? COLOR_LED_VALUE_BLACKWHITE_KEY : COLOR_LED_VALUE_COLOR_KEY); + return SANE_STATUS_GOOD; +} + +/******************************************************************************/ +SANE_Status +read_doc_in_adf_value(SANE_Handle handle, + SANE_Bool * doc_in_adf) +{ + struct hp5590_scanner * scanner = handle; + DBG (DBG_verbose, "%s: Reading state of document-available in ADF (device_number = %u) (device_name = %s)\n", + __func__, scanner->dn, scanner->sane.name); + SANE_Status ret = hp5590_is_data_available (scanner->dn, scanner->proto_flags); + if (ret == SANE_STATUS_GOOD) + *doc_in_adf = SANE_TRUE; + else if (ret == SANE_STATUS_NO_DOCS) + *doc_in_adf = SANE_FALSE; + else + { + DBG (DBG_proc, "%s: Error reading state of document-available in ADF (%u)\n", __func__, ret); + return ret; + } + DBG (DBG_verbose, "%s: doc_in_adf = %s\n", __func__, *doc_in_adf == SANE_FALSE ? "false" : "true"); + return SANE_STATUS_GOOD; +} + /******************************************************************************/ SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, - SANE_Action action, void *value, + SANE_Action action, void *value, SANE_Int * info) { - struct hp5590_scanner *scanner = handle; - + struct hp5590_scanner *scanner = handle; + if (!value) return SANE_STATUS_INVAL; if (!handle) return SANE_STATUS_INVAL; - + if (option >= HP5590_OPT_LAST) return SANE_STATUS_INVAL; if (action == SANE_ACTION_GET_VALUE) { if (option == HP5590_OPT_NUM) - { - DBG(3, "%s: get total number of options - %u\n", __func__, HP5590_OPT_LAST); - *((SANE_Int *) value) = HP5590_OPT_LAST; - return SANE_STATUS_GOOD; - } - + { + DBG(3, "%s: get total number of options - %u\n", __func__, HP5590_OPT_LAST); + *((SANE_Int *) value) = HP5590_OPT_LAST; + return SANE_STATUS_GOOD; + } + if (!scanner->opts) - return SANE_STATUS_INVAL; - + return SANE_STATUS_INVAL; + DBG (DBG_proc, "%s: get option '%s' value\n", __func__, scanner->opts[option].name); if (option == HP5590_OPT_BR_X) - { - *(SANE_Fixed *) value = SANE_FIX (scanner->br_x * 25.4); - } + { + *(SANE_Fixed *) value = SANE_FIX (scanner->br_x * 25.4); + } if (option == HP5590_OPT_BR_Y) - { - *(SANE_Fixed *) value = SANE_FIX (scanner->br_y * 25.4); - } + { + *(SANE_Fixed *) value = SANE_FIX (scanner->br_y * 25.4); + } if (option == HP5590_OPT_TL_X) - { - *(SANE_Fixed *) value = SANE_FIX ((scanner->tl_x) * 25.4); - } + { + *(SANE_Fixed *) value = SANE_FIX ((scanner->tl_x) * 25.4); + } if (option == HP5590_OPT_TL_Y) - { - *(SANE_Fixed *) value = SANE_FIX (scanner->tl_y * 25.4); - } + { + *(SANE_Fixed *) value = SANE_FIX (scanner->tl_y * 25.4); + } if (option == HP5590_OPT_MODE) - { - switch (scanner->depth) { - case DEPTH_BW: - memset (value , 0, scanner->opts[option].size); - memcpy (value, SANE_VALUE_SCAN_MODE_LINEART, strlen (SANE_VALUE_SCAN_MODE_LINEART)); - break; - case DEPTH_GRAY: - memset (value , 0, scanner->opts[option].size); - memcpy (value, SANE_VALUE_SCAN_MODE_GRAY, strlen (SANE_VALUE_SCAN_MODE_GRAY)); - break; - case DEPTH_COLOR_24: - memset (value , 0, scanner->opts[option].size); - memcpy (value, SANE_VALUE_SCAN_MODE_COLOR_24, strlen (SANE_VALUE_SCAN_MODE_COLOR_24)); - break; - case DEPTH_COLOR_48: - memset (value , 0, scanner->opts[option].size); - memcpy (value, SANE_VALUE_SCAN_MODE_COLOR_48, strlen (SANE_VALUE_SCAN_MODE_COLOR_48)); - break; - default: - return SANE_STATUS_INVAL; - } - } + { + switch (scanner->depth) { + case DEPTH_BW: + memset (value , 0, scanner->opts[option].size); + memcpy (value, SANE_VALUE_SCAN_MODE_LINEART, strlen (SANE_VALUE_SCAN_MODE_LINEART)); + break; + case DEPTH_GRAY: + memset (value , 0, scanner->opts[option].size); + memcpy (value, SANE_VALUE_SCAN_MODE_GRAY, strlen (SANE_VALUE_SCAN_MODE_GRAY)); + break; + case DEPTH_COLOR_24: + memset (value , 0, scanner->opts[option].size); + memcpy (value, SANE_VALUE_SCAN_MODE_COLOR_24, strlen (SANE_VALUE_SCAN_MODE_COLOR_24)); + break; + case DEPTH_COLOR_48: + memset (value , 0, scanner->opts[option].size); + memcpy (value, SANE_VALUE_SCAN_MODE_COLOR_48, strlen (SANE_VALUE_SCAN_MODE_COLOR_48)); + break; + default: + return SANE_STATUS_INVAL; + } + } if (option == HP5590_OPT_SOURCE) - { - switch (scanner->source) { - case SOURCE_FLATBED: - memset (value , 0, scanner->opts[option].size); - memcpy (value, SANE_VALUE_SCAN_SOURCE_FLATBED, strlen (SANE_VALUE_SCAN_SOURCE_FLATBED)); - break; - case SOURCE_ADF: - memset (value , 0, scanner->opts[option].size); - memcpy (value, SANE_VALUE_SCAN_SOURCE_ADF, strlen (SANE_VALUE_SCAN_SOURCE_ADF)); - break; - case SOURCE_ADF_DUPLEX: - memset (value , 0, scanner->opts[option].size); - memcpy (value, SANE_VALUE_SCAN_SOURCE_ADF_DUPLEX, strlen (SANE_VALUE_SCAN_SOURCE_ADF_DUPLEX)); - break; - case SOURCE_TMA_SLIDES: - memset (value , 0, scanner->opts[option].size); - memcpy (value, SANE_VALUE_SCAN_SOURCE_TMA_SLIDES, strlen (SANE_VALUE_SCAN_SOURCE_TMA_SLIDES)); - break; - case SOURCE_TMA_NEGATIVES: - memset (value , 0, scanner->opts[option].size); - memcpy (value, SANE_VALUE_SCAN_SOURCE_TMA_NEGATIVES, strlen (SANE_VALUE_SCAN_SOURCE_TMA_NEGATIVES)); - break; - case SOURCE_NONE: - default: - return SANE_STATUS_INVAL; - } - } + { + switch (scanner->source) { + case SOURCE_FLATBED: + memset (value , 0, scanner->opts[option].size); + memcpy (value, SANE_VALUE_SCAN_SOURCE_FLATBED, strlen (SANE_VALUE_SCAN_SOURCE_FLATBED)); + break; + case SOURCE_ADF: + memset (value , 0, scanner->opts[option].size); + memcpy (value, SANE_VALUE_SCAN_SOURCE_ADF, strlen (SANE_VALUE_SCAN_SOURCE_ADF)); + break; + case SOURCE_ADF_DUPLEX: + memset (value , 0, scanner->opts[option].size); + memcpy (value, SANE_VALUE_SCAN_SOURCE_ADF_DUPLEX, strlen (SANE_VALUE_SCAN_SOURCE_ADF_DUPLEX)); + break; + case SOURCE_TMA_SLIDES: + memset (value , 0, scanner->opts[option].size); + memcpy (value, SANE_VALUE_SCAN_SOURCE_TMA_SLIDES, strlen (SANE_VALUE_SCAN_SOURCE_TMA_SLIDES)); + break; + case SOURCE_TMA_NEGATIVES: + memset (value , 0, scanner->opts[option].size); + memcpy (value, SANE_VALUE_SCAN_SOURCE_TMA_NEGATIVES, strlen (SANE_VALUE_SCAN_SOURCE_TMA_NEGATIVES)); + break; + case SOURCE_NONE: + default: + return SANE_STATUS_INVAL; + } + } if (option == HP5590_OPT_RESOLUTION) - { - *(SANE_Int *) value = scanner->dpi; - } + { + *(SANE_Int *) value = scanner->dpi; + } if (option == HP5590_OPT_LAMP_TIMEOUT) - { - *(SANE_Bool *) value = scanner->extend_lamp_timeout; - } + { + *(SANE_Bool *) value = scanner->extend_lamp_timeout; + } if (option == HP5590_OPT_WAIT_FOR_BUTTON) - { - *(SANE_Bool *) value = scanner->wait_for_button; - } + { + *(SANE_Bool *) value = scanner->wait_for_button; + } + + if (option == HP5590_OPT_BUTTON_PRESSED) + { + enum button_status button_pressed = BUTTON_NONE; + SANE_Status ret = read_button_pressed(scanner, &button_pressed); + if (ret != SANE_STATUS_GOOD) + return ret; + switch (button_pressed) { + case BUTTON_POWER: + strncpy (value, BUTTON_PRESSED_VALUE_POWER_KEY, scanner->opts[option].size); + break; + case BUTTON_SCAN: + strncpy (value, BUTTON_PRESSED_VALUE_SCAN_KEY, scanner->opts[option].size); + break; + case BUTTON_COLLECT: + strncpy (value, BUTTON_PRESSED_VALUE_COLLECT_KEY, scanner->opts[option].size); + break; + case BUTTON_FILE: + strncpy (value, BUTTON_PRESSED_VALUE_FILE_KEY, scanner->opts[option].size); + break; + case BUTTON_EMAIL: + strncpy (value, BUTTON_PRESSED_VALUE_EMAIL_KEY, scanner->opts[option].size); + break; + case BUTTON_COPY: + strncpy (value, BUTTON_PRESSED_VALUE_COPY_KEY, scanner->opts[option].size); + break; + case BUTTON_UP: + strncpy (value, BUTTON_PRESSED_VALUE_UP_KEY, scanner->opts[option].size); + break; + case BUTTON_DOWN: + strncpy (value, BUTTON_PRESSED_VALUE_DOWN_KEY, scanner->opts[option].size); + break; + case BUTTON_MODE: + strncpy (value, BUTTON_PRESSED_VALUE_MODE_KEY, scanner->opts[option].size); + break; + case BUTTON_CANCEL: + strncpy (value, BUTTON_PRESSED_VALUE_CANCEL_KEY, scanner->opts[option].size); + break; + case BUTTON_NONE: + default: + strncpy (value, BUTTON_PRESSED_VALUE_NONE_KEY, scanner->opts[option].size); + } + } + + if (option == HP5590_OPT_COLOR_LED) + { + SANE_Int lcd_counter = 0; + enum color_led_status color_led = LED_COLOR; + SANE_Status ret = read_lcd_and_led_values(scanner, &lcd_counter, &color_led); + if (ret != SANE_STATUS_GOOD) + return ret; + switch (color_led) { + case LED_BLACKWHITE: + strncpy (value, COLOR_LED_VALUE_BLACKWHITE_KEY, scanner->opts[option].size); + break; + case LED_COLOR: + default: + strncpy (value, COLOR_LED_VALUE_COLOR_KEY, scanner->opts[option].size); + } + } + + if (option == HP5590_OPT_LCD_COUNTER) + { + SANE_Int lcd_counter = 0; + enum color_led_status color_led = LED_COLOR; + SANE_Status ret = read_lcd_and_led_values(scanner, &lcd_counter, &color_led); + if (ret != SANE_STATUS_GOOD) + return ret; + *(SANE_Int *) value = lcd_counter; + } + + if (option == HP5590_OPT_DOC_IN_ADF) + { + SANE_Bool doc_in_adf = SANE_FALSE; + SANE_Status ret = read_doc_in_adf_value(scanner, &doc_in_adf); + if (ret != SANE_STATUS_GOOD) + return ret; + *(SANE_Bool *) value = doc_in_adf; + } if (option == HP5590_OPT_PREVIEW) - { - *(SANE_Bool *) value = scanner->preview; - } + { + *(SANE_Bool *) value = scanner->preview; + } + + if (option == HP5590_OPT_OVERWRITE_EOP_PIXEL) + { + *(SANE_Bool *) value = scanner->overwrite_eop_pixel; + } + + if (option == HP5590_OPT_TRAILING_LINES_MODE) + { + switch (scanner->eop_trailing_lines_mode) { + case TRAILING_LINES_MODE_RAW: + memset (value , 0, scanner->opts[option].size); + memcpy (value, TRAILING_LINES_MODE_RAW_KEY, strlen (TRAILING_LINES_MODE_RAW_KEY)); + break; + case TRAILING_LINES_MODE_LAST: + memset (value , 0, scanner->opts[option].size); + memcpy (value, TRAILING_LINES_MODE_LAST_KEY, strlen (TRAILING_LINES_MODE_LAST_KEY)); + break; + case TRAILING_LINES_MODE_RASTER: + memset (value , 0, scanner->opts[option].size); + memcpy (value, TRAILING_LINES_MODE_RASTER_KEY, strlen (TRAILING_LINES_MODE_RASTER_KEY)); + break; + case TRAILING_LINES_MODE_BLACK: + memset (value , 0, scanner->opts[option].size); + memcpy (value, TRAILING_LINES_MODE_BLACK_KEY, strlen (TRAILING_LINES_MODE_BLACK_KEY)); + break; + case TRAILING_LINES_MODE_WHITE: + memset (value , 0, scanner->opts[option].size); + memcpy (value, TRAILING_LINES_MODE_WHITE_KEY, strlen (TRAILING_LINES_MODE_WHITE_KEY)); + break; + case TRAILING_LINES_MODE_COLOR: + memset (value , 0, scanner->opts[option].size); + memcpy (value, TRAILING_LINES_MODE_COLOR_KEY, strlen (TRAILING_LINES_MODE_COLOR_KEY)); + break; + default: + return SANE_STATUS_INVAL; + } + } + + if (option == HP5590_OPT_TRAILING_LINES_COLOR) + { + *(SANE_Int *) value = scanner->eop_trailing_lines_color; + } } - + if (action == SANE_ACTION_SET_VALUE) { if (option == HP5590_OPT_NUM) - return SANE_STATUS_INVAL; + return SANE_STATUS_INVAL; if (option == HP5590_OPT_BR_X) - { - float val = SANE_UNFIX(*(SANE_Fixed *) value) / 25.4; - if (val <= scanner->tl_x) - return SANE_STATUS_GOOD; - scanner->br_x = val; - if (info) - *info = SANE_INFO_RELOAD_PARAMS; - } + { + float val = SANE_UNFIX(*(SANE_Fixed *) value) / 25.4; + if (val <= scanner->tl_x) + return SANE_STATUS_GOOD; + scanner->br_x = val; + if (info) + *info = SANE_INFO_RELOAD_PARAMS; + } if (option == HP5590_OPT_BR_Y) - { - float val = SANE_UNFIX(*(SANE_Fixed *) value) / 25.4; - if (val <= scanner->tl_y) - return SANE_STATUS_GOOD; - scanner->br_y = val; - if (info) - *info = SANE_INFO_RELOAD_PARAMS; - } + { + float val = SANE_UNFIX(*(SANE_Fixed *) value) / 25.4; + if (val <= scanner->tl_y) + return SANE_STATUS_GOOD; + scanner->br_y = val; + if (info) + *info = SANE_INFO_RELOAD_PARAMS; + } if (option == HP5590_OPT_TL_X) - { - float val = SANE_UNFIX(*(SANE_Fixed *) value) / 25.4; - if (val >= scanner->br_x) - return SANE_STATUS_GOOD; - scanner->tl_x = val; - if (info) - *info = SANE_INFO_RELOAD_PARAMS; - } + { + float val = SANE_UNFIX(*(SANE_Fixed *) value) / 25.4; + if (val >= scanner->br_x) + return SANE_STATUS_GOOD; + scanner->tl_x = val; + if (info) + *info = SANE_INFO_RELOAD_PARAMS; + } if (option == HP5590_OPT_TL_Y) - { - float val = SANE_UNFIX(*(SANE_Fixed *) value) / 25.4; - if (val >= scanner->br_y) - return SANE_STATUS_GOOD; - scanner->tl_y = val; - if (info) - *info = SANE_INFO_RELOAD_PARAMS; - } + { + float val = SANE_UNFIX(*(SANE_Fixed *) value) / 25.4; + if (val >= scanner->br_y) + return SANE_STATUS_GOOD; + scanner->tl_y = val; + if (info) + *info = SANE_INFO_RELOAD_PARAMS; + } if (option == HP5590_OPT_MODE) - { - if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_MODE_LINEART) == 0) - { - scanner->depth = DEPTH_BW; - } - - if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_MODE_GRAY) == 0) - { - scanner->depth = DEPTH_GRAY; - } - - if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_MODE_COLOR_24) == 0) - { - scanner->depth = DEPTH_COLOR_24; - } - - if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_MODE_COLOR_48) == 0) - { - scanner->depth = DEPTH_COLOR_48; - } - if (info) - *info = SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; - } + { + if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_MODE_LINEART) == 0) + { + scanner->depth = DEPTH_BW; + } + else if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_MODE_GRAY) == 0) + { + scanner->depth = DEPTH_GRAY; + } + else if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_MODE_COLOR_24) == 0) + { + scanner->depth = DEPTH_COLOR_24; + } + else if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_MODE_COLOR_48) == 0) + { + scanner->depth = DEPTH_COLOR_48; + } + else + { + return SANE_STATUS_INVAL; + } + + if (info) + *info = SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; + } if (option == HP5590_OPT_SOURCE) - { + { range_y.max = SANE_FIX(scanner->info->max_size_y * 25.4); - if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_SOURCE_FLATBED) == 0) - { - scanner->source = SOURCE_FLATBED; - range_x.max = SANE_FIX(scanner->info->max_size_x * 25.4); - range_y.max = SANE_FIX(scanner->info->max_size_y * 25.4); - scanner->br_x = scanner->info->max_size_x; - scanner->br_y = scanner->info->max_size_y; - } - /* In ADF modes the device can scan up to ADF_MAX_Y_INCHES, which is usually - * bigger than what scanner reports back during initialization - */ - if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_SOURCE_ADF) == 0) - { - scanner->source = SOURCE_ADF; - range_x.max = SANE_FIX(scanner->info->max_size_x * 25.4); - range_y.max = SANE_FIX(ADF_MAX_Y_INCHES * 25.4); - scanner->br_x = scanner->info->max_size_x; - scanner->br_y = ADF_MAX_Y_INCHES * 25.4; - } - if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_SOURCE_ADF_DUPLEX) == 0) - { - scanner->source = SOURCE_ADF_DUPLEX; - range_x.max = SANE_FIX(scanner->info->max_size_x * 25.4); - range_y.max = SANE_FIX(ADF_MAX_Y_INCHES * 25.4 * 2); - scanner->br_y = ADF_MAX_Y_INCHES * 25.4 * 2; - scanner->br_x = scanner->info->max_size_x; - } - if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_SOURCE_TMA_SLIDES) == 0) - { - scanner->source = SOURCE_TMA_SLIDES; - range_x.max = SANE_FIX(TMA_MAX_X_INCHES * 25.4); - range_y.max = SANE_FIX(TMA_MAX_Y_INCHES * 25.4); - scanner->br_x = TMA_MAX_X_INCHES * 25.4; - scanner->br_y = TMA_MAX_Y_INCHES * 25.4; - } - if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_SOURCE_TMA_NEGATIVES) == 0) - { - scanner->source = SOURCE_TMA_NEGATIVES; - range_x.max = SANE_FIX(TMA_MAX_X_INCHES * 25.4); - range_y.max = SANE_FIX(TMA_MAX_Y_INCHES * 25.4); - scanner->br_x = TMA_MAX_X_INCHES * 25.4; - scanner->br_y = TMA_MAX_Y_INCHES * 25.4; - } - if (info) - *info = SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; - } + if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_SOURCE_FLATBED) == 0) + { + scanner->source = SOURCE_FLATBED; + range_x.max = SANE_FIX(scanner->info->max_size_x * 25.4); + range_y.max = SANE_FIX(scanner->info->max_size_y * 25.4); + scanner->br_x = scanner->info->max_size_x; + scanner->br_y = scanner->info->max_size_y; + } + else if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_SOURCE_ADF) == 0) + { + /* In ADF modes the device can scan up to ADF_MAX_Y_INCHES, which is usually + * bigger than what scanner reports back during initialization + */ + if (! (scanner->info->features & FEATURE_ADF)) + { + DBG(DBG_err, "ADF feature not available: %s\n", (char *) value); + return SANE_STATUS_UNSUPPORTED; + } + scanner->source = SOURCE_ADF; + range_x.max = SANE_FIX(scanner->info->max_size_x * 25.4); + range_y.max = SANE_FIX(ADF_MAX_Y_INCHES * 25.4); + scanner->br_x = scanner->info->max_size_x; + scanner->br_y = ADF_MAX_Y_INCHES; + } + else if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_SOURCE_ADF_DUPLEX) == 0) + { + if (! (scanner->info->features & FEATURE_ADF)) + { + DBG(DBG_err, "ADF feature not available: %s\n", (char *) value); + return SANE_STATUS_UNSUPPORTED; + } + scanner->source = SOURCE_ADF_DUPLEX; + range_x.max = SANE_FIX(scanner->info->max_size_x * 25.4); + range_y.max = SANE_FIX(ADF_MAX_Y_INCHES * 25.4); + scanner->br_x = scanner->info->max_size_x; + scanner->br_y = ADF_MAX_Y_INCHES; + } + else if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_SOURCE_TMA_SLIDES) == 0) + { + if (! (scanner->info->features & FEATURE_TMA)) + { + DBG(DBG_err, "TMA feature not available: %s\n", (char *) value); + return SANE_STATUS_UNSUPPORTED; + } + scanner->source = SOURCE_TMA_SLIDES; + range_x.max = SANE_FIX(TMA_MAX_X_INCHES * 25.4); + range_y.max = SANE_FIX(TMA_MAX_Y_INCHES * 25.4); + scanner->br_x = TMA_MAX_X_INCHES; + scanner->br_y = TMA_MAX_Y_INCHES; + } + else if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_SOURCE_TMA_NEGATIVES) == 0) + { + if (! (scanner->info->features & FEATURE_TMA)) + { + DBG(DBG_err, "TMA feature not available: %s\n", (char *) value); + return SANE_STATUS_UNSUPPORTED; + } + scanner->source = SOURCE_TMA_NEGATIVES; + range_x.max = SANE_FIX(TMA_MAX_X_INCHES * 25.4); + range_y.max = SANE_FIX(TMA_MAX_Y_INCHES * 25.4); + scanner->br_x = TMA_MAX_X_INCHES; + scanner->br_y = TMA_MAX_Y_INCHES; + } + else + { + return SANE_STATUS_INVAL; + } + if (info) + *info = SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; + } if (option == HP5590_OPT_RESOLUTION) - { - scanner->dpi = *(SANE_Int *) value; - if (info) - *info = SANE_INFO_RELOAD_PARAMS; - } + { + scanner->dpi = *(SANE_Int *) value; + if (info) + *info = SANE_INFO_RELOAD_PARAMS; + } if (option == HP5590_OPT_LAMP_TIMEOUT) - { - scanner->extend_lamp_timeout = *(SANE_Bool *) value; - } + { + scanner->extend_lamp_timeout = *(SANE_Bool *) value; + } if (option == HP5590_OPT_WAIT_FOR_BUTTON) - { - scanner->wait_for_button = *(SANE_Bool *) value; - } + { + scanner->wait_for_button = *(SANE_Bool *) value; + } + + if (option == HP5590_OPT_BUTTON_PRESSED) + { + DBG(DBG_verbose, "State of buttons is read only. Setting of state will be ignored.\n"); + } + + if (option == HP5590_OPT_COLOR_LED) + { + DBG(DBG_verbose, "State of color LED indicator is read only. Setting of state will be ignored.\n"); + } + + if (option == HP5590_OPT_LCD_COUNTER) + { + DBG(DBG_verbose, "Value of LCD counter is read only. Setting of value will be ignored.\n"); + } + + if (option == HP5590_OPT_DOC_IN_ADF) + { + DBG(DBG_verbose, "Value of document-available indicator is read only. Setting of value will be ignored.\n"); + } if (option == HP5590_OPT_PREVIEW) - { - scanner->preview = *(SANE_Bool *) value; - } + { + scanner->preview = *(SANE_Bool *) value; + } + + if (option == HP5590_OPT_OVERWRITE_EOP_PIXEL) + { + scanner->overwrite_eop_pixel = *(SANE_Bool *) value; + } + + if (option == HP5590_OPT_TRAILING_LINES_MODE) + { + if (strcmp ((char *) value, (char *) TRAILING_LINES_MODE_RAW_KEY) == 0) + scanner->eop_trailing_lines_mode = TRAILING_LINES_MODE_RAW; + if (strcmp ((char *) value, (char *) TRAILING_LINES_MODE_LAST_KEY) == 0) + scanner->eop_trailing_lines_mode = TRAILING_LINES_MODE_LAST; + if (strcmp ((char *) value, (char *) TRAILING_LINES_MODE_RASTER_KEY) == 0) + scanner->eop_trailing_lines_mode = TRAILING_LINES_MODE_RASTER; + if (strcmp ((char *) value, (char *) TRAILING_LINES_MODE_BLACK_KEY) == 0) + scanner->eop_trailing_lines_mode = TRAILING_LINES_MODE_BLACK; + if (strcmp ((char *) value, (char *) TRAILING_LINES_MODE_WHITE_KEY) == 0) + scanner->eop_trailing_lines_mode = TRAILING_LINES_MODE_WHITE; + if (strcmp ((char *) value, (char *) TRAILING_LINES_MODE_COLOR_KEY) == 0) + scanner->eop_trailing_lines_mode = TRAILING_LINES_MODE_COLOR; + } + + if (option == HP5590_OPT_TRAILING_LINES_COLOR) + { + scanner->eop_trailing_lines_color = *(SANE_Int *) value; + } } - + return SANE_STATUS_GOOD; } /******************************************************************************/ SANE_Status sane_get_parameters (SANE_Handle handle, - SANE_Parameters * params) + SANE_Parameters * params) { - struct hp5590_scanner *scanner = handle; - SANE_Status ret; - unsigned int pixel_bits; + struct hp5590_scanner *scanner = handle; + SANE_Status ret; + unsigned int pixel_bits; DBG (DBG_proc, "%s\n", __func__); @@ -935,10 +1428,10 @@ SANE_Status sane_get_parameters (SANE_Handle handle, return SANE_STATUS_INVAL; ret = calc_image_params (scanner, - (unsigned int *) &pixel_bits, - (unsigned int *) ¶ms->pixels_per_line, - (unsigned int *) ¶ms->bytes_per_line, - (unsigned int *) ¶ms->lines, NULL); + (unsigned int *) &pixel_bits, + (unsigned int *) ¶ms->pixels_per_line, + (unsigned int *) ¶ms->bytes_per_line, + (unsigned int *) ¶ms->lines, NULL); if (ret != SANE_STATUS_GOOD) return ret; @@ -964,11 +1457,11 @@ SANE_Status sane_get_parameters (SANE_Handle handle, params->format = SANE_FRAME_RGB; break; default: - DBG(0, "%s: Unknown depth\n", __func__); + DBG(DBG_err, "%s: Unknown depth\n", __func__); return SANE_STATUS_INVAL; } - + DBG (DBG_proc, "format: %u, last_frame: %u, bytes_per_line: %u, " "pixels_per_line: %u, lines: %u, depth: %u\n", params->format, params->last_frame, @@ -982,31 +1475,61 @@ SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Status sane_start (SANE_Handle handle) { - struct hp5590_scanner *scanner = handle; - SANE_Status ret; - unsigned int bytes_per_line; + struct hp5590_scanner *scanner = handle; + SANE_Status ret; + unsigned int bytes_per_line; DBG (DBG_proc, "%s\n", __func__); if (!scanner) return SANE_STATUS_INVAL; + /* Cleanup for all pages. */ + if (scanner->eop_last_line_data) + { + /* Release last line data */ + free (scanner->eop_last_line_data); + scanner->eop_last_line_data = NULL; + scanner->eop_last_line_data_rpos = 0; + } + if (scanner->one_line_read_buffer) + { + /* Release temporary line buffer. */ + free (scanner->one_line_read_buffer); + scanner->one_line_read_buffer = NULL; + scanner->one_line_read_buffer_rpos = 0; + } + if (scanner->color_shift_line_buffer1) + { + /* Release line buffer1 for shifting colors. */ + free (scanner->color_shift_line_buffer1); + scanner->color_shift_line_buffer1 = NULL; + scanner->color_shift_buffered_lines1 = 0; + } + if (scanner->color_shift_line_buffer2) + { + /* Release line buffer2 for shifting colors. */ + free (scanner->color_shift_line_buffer2); + scanner->color_shift_line_buffer2 = NULL; + scanner->color_shift_buffered_lines2 = 0; + } + if ( scanner->scanning == SANE_TRUE && ( scanner->source == SOURCE_ADF - || scanner->source == SOURCE_ADF_DUPLEX)) + || scanner->source == SOURCE_ADF_DUPLEX)) { DBG (DBG_verbose, "%s: Scanner is scanning, check if more data is available\n", - __func__); + __func__); ret = hp5590_is_data_available (scanner->dn, scanner->proto_flags); if (ret == SANE_STATUS_GOOD) - { - DBG (DBG_verbose, "%s: More data is available\n", __func__); - scanner->transferred_image_size = scanner->image_size; - return SANE_STATUS_GOOD; - } + { + DBG (DBG_verbose, "%s: More data is available\n", __func__); + scanner->transferred_image_size = scanner->image_size; + return SANE_STATUS_GOOD; + } if (ret != SANE_STATUS_NO_DOCS) - return ret; + return ret; } sane_cancel (handle); @@ -1015,25 +1538,25 @@ sane_start (SANE_Handle handle) { enum button_status status; for (;;) - { - ret = hp5590_read_buttons (scanner->dn, - scanner->proto_flags, - &status); - if (ret != SANE_STATUS_GOOD) - return ret; - - if (status == BUTTON_CANCEL) - return SANE_STATUS_CANCELLED; - - if (status != BUTTON_NONE && status != BUTTON_POWER) - break; - sleep (1); - } + { + ret = hp5590_read_buttons (scanner->dn, + scanner->proto_flags, + &status); + if (ret != SANE_STATUS_GOOD) + return ret; + + if (status == BUTTON_CANCEL) + return SANE_STATUS_CANCELLED; + + if (status != BUTTON_NONE && status != BUTTON_POWER) + break; + usleep (100 * 1000); + } } - DBG (DBG_verbose, "Init scanner\n"); + DBG (DBG_verbose, "Init scanner\n"); ret = hp5590_init_scanner (scanner->dn, scanner->proto_flags, - NULL, SCANNER_NONE); + NULL, SCANNER_NONE); if (ret != SANE_STATUS_GOOD) return ret; @@ -1043,21 +1566,21 @@ sane_start (SANE_Handle handle) DBG (DBG_verbose, "Wakeup\n"); ret = hp5590_select_source_and_wakeup (scanner->dn, scanner->proto_flags, - scanner->source, - scanner->extend_lamp_timeout); + scanner->source, + scanner->extend_lamp_timeout); if (ret != SANE_STATUS_GOOD) return ret; - + ret = hp5590_set_scan_params (scanner->dn, - scanner->proto_flags, - scanner->info, - scanner->tl_x * scanner->dpi, - scanner->tl_y * scanner->dpi, - (scanner->br_x - scanner->tl_x) * scanner->dpi, - (scanner->br_y - scanner->tl_y) * scanner->dpi, - scanner->dpi, - scanner->depth, scanner->preview ? MODE_PREVIEW : MODE_NORMAL, - scanner->source); + scanner->proto_flags, + scanner->info, + scanner->tl_x * scanner->dpi, + scanner->tl_y * scanner->dpi, + (scanner->br_x - scanner->tl_x) * scanner->dpi, + (scanner->br_y - scanner->tl_y) * scanner->dpi, + scanner->dpi, + scanner->depth, scanner->preview ? MODE_PREVIEW : MODE_NORMAL, + scanner->source); if (ret != SANE_STATUS_GOOD) { hp5590_reset_scan_head (scanner->dn, scanner->proto_flags); @@ -1065,8 +1588,8 @@ sane_start (SANE_Handle handle) } ret = calc_image_params (scanner, NULL, NULL, - &bytes_per_line, NULL, - &scanner->image_size); + &bytes_per_line, NULL, + &scanner->image_size); if (ret != SANE_STATUS_GOOD) { hp5590_reset_scan_head (scanner->dn, scanner->proto_flags); @@ -1079,35 +1602,35 @@ sane_start (SANE_Handle handle) || scanner->depth == DEPTH_COLOR_48) { DBG (1, "Color 24/48 bits: checking if image size is correctly " - "aligned on number of colors\n"); + "aligned on number of colors\n"); if (bytes_per_line % 3) - { - DBG (DBG_err, "Color 24/48 bits: image size doesn't lined up on number of colors (3) " - "(image size: %u, bytes per line %u)\n", - scanner->image_size, bytes_per_line); + { + DBG (DBG_err, "Color 24/48 bits: image size doesn't lined up on number of colors (3) " + "(image size: %llu, bytes per line %u)\n", + scanner->image_size, bytes_per_line); hp5590_reset_scan_head (scanner->dn, scanner->proto_flags); - return SANE_STATUS_INVAL; - } + return SANE_STATUS_INVAL; + } DBG (1, "Color 24/48 bits: image size is correctly aligned on number of colors " - "(image size: %u, bytes per line %u)\n", - scanner->image_size, bytes_per_line); + "(image size: %llu, bytes per line %u)\n", + scanner->image_size, bytes_per_line); DBG (1, "Color 24/48 bits: checking if image size is correctly " - "aligned on bytes per line\n"); + "aligned on bytes per line\n"); if (scanner->image_size % bytes_per_line) - { - DBG (DBG_err, "Color 24/48 bits: image size doesn't lined up on bytes per line " - "(image size: %u, bytes per line %u)\n", - scanner->image_size, bytes_per_line); + { + DBG (DBG_err, "Color 24/48 bits: image size doesn't lined up on bytes per line " + "(image size: %llu, bytes per line %u)\n", + scanner->image_size, bytes_per_line); hp5590_reset_scan_head (scanner->dn, scanner->proto_flags); - return SANE_STATUS_INVAL; - } + return SANE_STATUS_INVAL; + } DBG (1, "Color 24/48 bits: image size correctly aligned on bytes per line " - "(images size: %u, bytes per line: %u)\n", - scanner->image_size, bytes_per_line); + "(images size: %llu, bytes per line: %u)\n", + scanner->image_size, bytes_per_line); } - - DBG (DBG_verbose, "Final image size: %u\n", scanner->image_size); + + DBG (DBG_verbose, "Final image size: %llu\n", scanner->image_size); DBG (DBG_verbose, "Reverse calibration maps\n"); ret = hp5590_send_reverse_calibration_map (scanner->dn, scanner->proto_flags); @@ -1125,14 +1648,23 @@ sane_start (SANE_Handle handle) return ret; } + if (scanner->adf_next_page_lines_data) + { + free (scanner->adf_next_page_lines_data); + scanner->adf_next_page_lines_data = NULL; + scanner->adf_next_page_lines_data_size = 0; + scanner->adf_next_page_lines_data_rpos = 0; + scanner->adf_next_page_lines_data_wpos = 0; + } + scanner->scanning = SANE_TRUE; DBG (DBG_verbose, "Starting scan\n"); ret = hp5590_start_scan (scanner->dn, scanner->proto_flags); /* Check for paper jam */ - if ( ret == SANE_STATUS_DEVICE_BUSY + if ( ret == SANE_STATUS_DEVICE_BUSY && ( scanner->source == SOURCE_ADF - || scanner->source == SOURCE_ADF_DUPLEX)) + || scanner->source == SOURCE_ADF_DUPLEX)) return SANE_STATUS_JAMMED; if (ret != SANE_STATUS_GOOD) @@ -1145,44 +1677,218 @@ sane_start (SANE_Handle handle) } /******************************************************************************/ -static SANE_Status -convert_lineart (struct hp5590_scanner *scanner, SANE_Byte *data, SANE_Int size) +static void +invert_negative_colors (unsigned char *buf, unsigned int bytes_per_line, struct hp5590_scanner *scanner) { - SANE_Int i; + /* Invert lineart or negatives. */ + int is_linear = (scanner->depth == DEPTH_BW); + int is_negative = (scanner->source == SOURCE_TMA_NEGATIVES); + if (is_linear ^ is_negative) + { + for (unsigned int k = 0; k < bytes_per_line; k++) + buf[k] ^= 0xff; + } +} - DBG (DBG_proc, "%s\n", __func__); +/******************************************************************************/ +static SANE_Status +convert_gray_and_lineart (struct hp5590_scanner *scanner, SANE_Byte *data, SANE_Int size) +{ + unsigned int pixels_per_line; + unsigned int pixel_bits; + unsigned int bytes_per_line; + unsigned int lines; + unsigned char *buf; + SANE_Status ret; hp5590_assert (scanner != NULL); hp5590_assert (data != NULL); - /* Invert lineart */ - if (scanner->depth == DEPTH_BW) + if ( ! (scanner->depth == DEPTH_BW || scanner->depth == DEPTH_GRAY)) + return SANE_STATUS_GOOD; + + DBG (DBG_proc, "%s\n", __func__); + + ret = calc_image_params (scanner, + &pixel_bits, + &pixels_per_line, &bytes_per_line, + NULL, NULL); + if (ret != SANE_STATUS_GOOD) + return ret; + + lines = size / bytes_per_line; + + buf = data; + for (unsigned int i = 0; i < lines; buf += bytes_per_line, ++i) { - for (i = 0; i < size; i++) - data[i] ^= 0xff; + if (! scanner->eop_last_line_data) + { + if (pixels_per_line > 0) + { + /* Test for last-line indicator pixel. If found, store last line + * and optionally overwrite indicator pixel with neighbor value. + */ + unsigned int j = bytes_per_line - 1; + int eop_found = 0; + if (scanner->depth == DEPTH_GRAY) + { + eop_found = (buf[j] != 0); + if (scanner->overwrite_eop_pixel && (j > 0)) + { + buf[j] = buf[j-1]; + } + } + else if (scanner->depth == DEPTH_BW) + { + eop_found = (buf[j] != 0); + if (scanner->overwrite_eop_pixel && (j > 0)) + { + buf[j] = (buf[j-1] & 0x01) ? 0xff : 0; + } + } + + invert_negative_colors (buf, bytes_per_line, scanner); + + if (eop_found && (! scanner->eop_last_line_data)) + { + DBG (DBG_verbose, "Found end-of-page at line %u in reading block.\n", i); + scanner->eop_last_line_data = malloc(bytes_per_line); + if (! scanner->eop_last_line_data) + return SANE_STATUS_NO_MEM; + + memcpy (scanner->eop_last_line_data, buf, bytes_per_line); + scanner->eop_last_line_data_rpos = 0; + + /* Fill trailing line buffer with requested color. */ + if (scanner->eop_trailing_lines_mode == TRAILING_LINES_MODE_RASTER) + { + /* Black-white raster. */ + if (scanner->depth == DEPTH_BW) + { + memset (scanner->eop_last_line_data, 0xaa, bytes_per_line); + } + else + { + /* Gray. */ + for (unsigned int k = 0; k < bytes_per_line; ++k) + { + scanner->eop_last_line_data[k] = (k & 1 ? 0xff : 0); + } + } + } + else if (scanner->eop_trailing_lines_mode == TRAILING_LINES_MODE_WHITE) + { + /* White. */ + if (scanner->depth == DEPTH_BW) + { + memset (scanner->eop_last_line_data, 0x00, bytes_per_line); + } + else + { + memset (scanner->eop_last_line_data, 0xff, bytes_per_line); + } + } + else if (scanner->eop_trailing_lines_mode == TRAILING_LINES_MODE_BLACK) + { + /* Black. */ + if (scanner->depth == DEPTH_BW) + { + memset (scanner->eop_last_line_data, 0xff, bytes_per_line); + } + else + { + memset (scanner->eop_last_line_data, 0x00, bytes_per_line); + } + } + else if (scanner->eop_trailing_lines_mode == TRAILING_LINES_MODE_COLOR) + { + if (scanner->depth == DEPTH_BW) + { + /* Black or white. */ + memset (scanner->eop_last_line_data, scanner->eop_trailing_lines_color & 0x01 ? 0x00 : 0xff, bytes_per_line); + } + else + { + /* Gray value */ + memset (scanner->eop_last_line_data, scanner->eop_trailing_lines_color & 0xff, bytes_per_line); + } + } + } + } + } + else + { + DBG (DBG_verbose, "Trailing lines mode: line=%u, mode=%d, color=%u\n", + i, scanner->eop_trailing_lines_mode, scanner->eop_trailing_lines_color); + + if ((scanner->source == SOURCE_ADF) || (scanner->source == SOURCE_ADF_DUPLEX)) + { + /* We are in in ADF mode after last-line and store next page data + * to buffer. + */ + if (! scanner->adf_next_page_lines_data) + { + unsigned int n_rest_lines = lines - i; + unsigned int buf_size = n_rest_lines * bytes_per_line; + scanner->adf_next_page_lines_data = malloc(buf_size); + if (! scanner->adf_next_page_lines_data) + return SANE_STATUS_NO_MEM; + scanner->adf_next_page_lines_data_size = buf_size; + scanner->adf_next_page_lines_data_rpos = 0; + scanner->adf_next_page_lines_data_wpos = 0; + DBG (DBG_verbose, "ADF between pages: Save n=%u next page lines in buffer.\n", n_rest_lines); + } + DBG (DBG_verbose, "ADF between pages: Store line %u of %u.\n", i, lines); + invert_negative_colors (buf, bytes_per_line, scanner); + memcpy (scanner->adf_next_page_lines_data + scanner->adf_next_page_lines_data_wpos, buf, bytes_per_line); + scanner->adf_next_page_lines_data_wpos += bytes_per_line; + } + + if (scanner->eop_trailing_lines_mode != TRAILING_LINES_MODE_RAW) + { + /* Copy last line data or corresponding color over trailing lines + * data. + */ + memcpy (buf, scanner->eop_last_line_data, bytes_per_line); + } + } } return SANE_STATUS_GOOD; } /******************************************************************************/ +static unsigned char +get_checked (unsigned char *ptr, unsigned int i, unsigned int length) +{ + if (i < length) + { + return ptr[i]; + } + DBG (DBG_details, "get from array out of range: idx=%u, size=%u\n", i, length); + return 0; +} + +/******************************************************************************/ static SANE_Status convert_to_rgb (struct hp5590_scanner *scanner, SANE_Byte *data, SANE_Int size) { unsigned int pixels_per_line; + unsigned int pixel_bits; unsigned int bytes_per_color; unsigned int bytes_per_line; + unsigned int bytes_per_line_limit; unsigned int lines; unsigned int i, j; unsigned char *buf; + unsigned char *bufptr; unsigned char *ptr; - SANE_Status ret; + SANE_Status ret; hp5590_assert (scanner != NULL); hp5590_assert (data != NULL); - if ( scanner->depth == DEPTH_BW - || scanner->depth == DEPTH_GRAY) + if ( ! (scanner->depth == DEPTH_COLOR_24 || scanner->depth == DEPTH_COLOR_48)) return SANE_STATUS_GOOD; DBG (DBG_proc, "%s\n", __func__); @@ -1193,79 +1899,555 @@ convert_to_rgb (struct hp5590_scanner *scanner, SANE_Byte *data, SANE_Int size) #endif ret = calc_image_params (scanner, - NULL, - &pixels_per_line, &bytes_per_line, - NULL, NULL); + &pixel_bits, + &pixels_per_line, &bytes_per_line, + NULL, NULL); if (ret != SANE_STATUS_GOOD) return ret; lines = size / bytes_per_line; - bytes_per_color = bytes_per_line / 3; - - DBG (DBG_verbose, "Length : %u\n", size); + bytes_per_color = (pixel_bits + 7) / 8; + + bytes_per_line_limit = bytes_per_line; + if ((scanner->depth == DEPTH_COLOR_48) && (bytes_per_line_limit > 3)) + { + /* Last-line indicator pixel has only 3 bytes instead of 6. */ + bytes_per_line_limit -= 3; + } + + DBG (DBG_verbose, "Length : %u\n", size); DBG (DBG_verbose, "Converting row RGB to normal RGB\n"); DBG (DBG_verbose, "Bytes per line %u\n", bytes_per_line); + DBG (DBG_verbose, "Bytes per line limited %u\n", bytes_per_line_limit); DBG (DBG_verbose, "Bytes per color %u\n", bytes_per_color); + DBG (DBG_verbose, "Pixels per line %u\n", pixels_per_line); DBG (DBG_verbose, "Lines %u\n", lines); - buf = malloc (bytes_per_line); - if (!buf) + /* Use working buffer for color mapping. */ + bufptr = malloc (size); + if (! bufptr) return SANE_STATUS_NO_MEM; + memset (bufptr, 0, size); + buf = bufptr; ptr = data; - for (j = 0; j < lines; ptr += bytes_per_line, j++) + for (j = 0; j < lines; ptr += bytes_per_line_limit, buf += bytes_per_line, j++) { - memset (buf, 0, bytes_per_line); for (i = 0; i < pixels_per_line; i++) - { - if (scanner->depth == DEPTH_COLOR_24) - { - /* R */ - buf[i*3] = ptr[i]; - /* G */ - buf[i*3+1] = ptr[i+bytes_per_color]; - /* B */ - buf[i*3+2] = ptr[i+bytes_per_color*2]; - } - else - { - /* R */ - buf[i*6] = ptr[2*i+1]; - buf[i*6+1] = ptr[2*i]; - /* G */ - buf[i*6+2] = ptr[2*i+bytes_per_color+1]; - buf[i*6+3] = ptr[2*i+bytes_per_color]; - /* B */ - buf[i*6+4] = ptr[2*i+bytes_per_color*2+1]; - buf[i*6+5] = ptr[2*i+bytes_per_color*2]; - } - } - - /* Invert pixels in case of TMA Negatives source has been selected */ - if (scanner->source == SOURCE_TMA_NEGATIVES) { - for (i = 0; i < bytes_per_line; i++) - buf[i] ^= 0xff; + /* Color mapping from raw scanner data to RGB buffer. */ + if (scanner->depth == DEPTH_COLOR_24) + { + /* R */ + buf[i*3] = get_checked(ptr, i, bytes_per_line_limit); + /* G */ + buf[i*3+1] = get_checked(ptr, i+pixels_per_line, bytes_per_line_limit);; + /* B */ + buf[i*3+2] = get_checked(ptr, i+pixels_per_line*2, bytes_per_line_limit); + } + else if (scanner->depth == DEPTH_COLOR_48) + { + /* Note: The last-line indicator pixel uses only 24 bits, not 48. + *Blue uses offset of 2 bytes. Green swaps lo and hi. + */ + /* R lo, hi*/ + buf[i*6] = get_checked(ptr, 2*i+(pixels_per_line-1)*0+1, bytes_per_line_limit); + buf[i*6+1] = get_checked(ptr, 2*i+(pixels_per_line-1)*0+0, bytes_per_line_limit); + /* G lo, hi*/ + buf[i*6+2] = get_checked(ptr, 2*i+(pixels_per_line-1)*2+0, bytes_per_line_limit); + buf[i*6+3] = get_checked(ptr, 2*i+(pixels_per_line-1)*2+1, bytes_per_line_limit); + /* B lo, hi*/ + buf[i*6+4] = get_checked(ptr, 2*i+(pixels_per_line-1)*4+1+2, bytes_per_line_limit); + buf[i*6+5] = get_checked(ptr, 2*i+(pixels_per_line-1)*4+0+2, bytes_per_line_limit); + } + } + + if (! scanner->eop_last_line_data) + { + if (pixels_per_line > 0) + { + /* Test for last-line indicator pixel on blue. If found, store + * last line and optionally overwrite indicator pixel with + * neighbor value. + */ + i = pixels_per_line - 1; + int eop_found = 0; + if (scanner->depth == DEPTH_COLOR_24) + { + /* DBG (DBG_details, "BUF24: %u %u %u\n", buf[i*3], buf[i*3+1], buf[i*3+2]); */ + eop_found = (buf[i*3+2] != 0); + if (scanner->overwrite_eop_pixel && (i > 0)) + { + buf[i*3] = buf[(i-1)*3]; + buf[i*3+1] = buf[(i-1)*3+1]; + buf[i*3+2] = buf[(i-1)*3+2]; + } + } + else if (scanner->depth == DEPTH_COLOR_48) + { + /* DBG (DBG_details, "BUF48: %u %u %u\n", buf[i*6+1], buf[i*6+3], buf[i*6+5]); */ + eop_found = (buf[i*6+5] != 0); + if (scanner->overwrite_eop_pixel && (i > 0)) + { + buf[i*6] = buf[(i-1)*6]; + buf[i*6+1] = buf[(i-1)*6+1]; + buf[i*6+2] = buf[(i-1)*6+2]; + buf[i*6+3] = buf[(i-1)*6+3]; + buf[i*6+4] = buf[(i-1)*6+4]; + buf[i*6+5] = buf[(i-1)*6+5]; + } + } + + invert_negative_colors (buf, bytes_per_line, scanner); + + if (eop_found && (! scanner->eop_last_line_data)) + { + DBG (DBG_verbose, "Found end-of-page at line %u in reading block.\n", j); + scanner->eop_last_line_data = malloc(bytes_per_line); + if (! scanner->eop_last_line_data) + return SANE_STATUS_NO_MEM; + + memcpy (scanner->eop_last_line_data, buf, bytes_per_line); + scanner->eop_last_line_data_rpos = 0; + + /* Fill trailing line buffer with requested color. */ + if (scanner->eop_trailing_lines_mode == TRAILING_LINES_MODE_RASTER) + { + /* Black-white raster. */ + if (scanner->depth == DEPTH_COLOR_24) + { + for (unsigned int k = 0; k < bytes_per_line; ++k) + { + scanner->eop_last_line_data[k] = (k % 6 < 3 ? 0xff : 0); + } + } + else + { + /* Color48. */ + for (unsigned int k = 0; k < bytes_per_line; ++k) + { + scanner->eop_last_line_data[k] = (k % 12 < 6 ? 0xff : 0); + } + } + } + else if (scanner->eop_trailing_lines_mode == TRAILING_LINES_MODE_WHITE) + { + memset (scanner->eop_last_line_data, 0xff, bytes_per_line); + } + else if (scanner->eop_trailing_lines_mode == TRAILING_LINES_MODE_BLACK) + { + memset (scanner->eop_last_line_data, 0x00, bytes_per_line); + } + else if (scanner->eop_trailing_lines_mode == TRAILING_LINES_MODE_COLOR) + { + /* RGB color value. */ + int rgb[3]; + rgb[0] = (scanner->eop_trailing_lines_color >> 16) & 0xff; + rgb[1] = (scanner->eop_trailing_lines_color >> 8) & 0xff; + rgb[2] = scanner->eop_trailing_lines_color & 0xff; + if (scanner->depth == DEPTH_COLOR_24) + { + for (unsigned int k = 0; k < bytes_per_line; ++k) + { + scanner->eop_last_line_data[k] = rgb[k % 3]; + } + } + else + { + /* Color48. */ + for (unsigned int k = 0; k < bytes_per_line; ++k) + { + scanner->eop_last_line_data[k] = rgb[(k % 6) >> 1]; + } + } + } + } + } + } + else + { + DBG (DBG_verbose, "Trailing lines mode: line=%u, mode=%d, color=%u\n", + j, scanner->eop_trailing_lines_mode, scanner->eop_trailing_lines_color); + + if ((scanner->source == SOURCE_ADF) || (scanner->source == SOURCE_ADF_DUPLEX)) + { + /* We are in in ADF mode after last-line and store next page data + * to buffer. + */ + if (! scanner->adf_next_page_lines_data) + { + unsigned int n_rest_lines = lines - j; + unsigned int buf_size = n_rest_lines * bytes_per_line; + scanner->adf_next_page_lines_data = malloc(buf_size); + if (! scanner->adf_next_page_lines_data) + return SANE_STATUS_NO_MEM; + scanner->adf_next_page_lines_data_size = buf_size; + scanner->adf_next_page_lines_data_rpos = 0; + scanner->adf_next_page_lines_data_wpos = 0; + DBG (DBG_verbose, "ADF between pages: Save n=%u next page lines in buffer.\n", n_rest_lines); + } + DBG (DBG_verbose, "ADF between pages: Store line %u of %u.\n", j, lines); + invert_negative_colors (buf, bytes_per_line, scanner); + memcpy (scanner->adf_next_page_lines_data + scanner->adf_next_page_lines_data_wpos, buf, bytes_per_line); + scanner->adf_next_page_lines_data_wpos += bytes_per_line; + } + + if (scanner->eop_trailing_lines_mode != TRAILING_LINES_MODE_RAW) + { + /* Copy last line data or corresponding color over trailing lines + * data. + */ + memcpy (buf, scanner->eop_last_line_data, bytes_per_line); + } + } + } + memcpy (data, bufptr, size); + free (bufptr); + + return SANE_STATUS_GOOD; +} + +/******************************************************************************/ +static void +read_data_from_temporary_buffer(struct hp5590_scanner *scanner, + SANE_Byte * data, unsigned int max_length, + unsigned int bytes_per_line, SANE_Int *length) +{ + *length = 0; + if (scanner && scanner->one_line_read_buffer) + { + /* Copy scan data from temporary read buffer and return size copied data. */ + /* Release buffer, when no data left. */ + unsigned int rest_len; + rest_len = bytes_per_line - scanner->one_line_read_buffer_rpos; + rest_len = (rest_len < max_length) ? rest_len : max_length; + if (rest_len > 0) + { + memcpy (data, scanner->one_line_read_buffer + scanner->one_line_read_buffer_rpos, rest_len); + scanner->one_line_read_buffer_rpos += rest_len; + scanner->transferred_image_size -= rest_len; + *length = rest_len; + } + + DBG (DBG_verbose, "Copy scan data from temporary buffer: length = %u, rest in buffer = %u.\n", + *length, bytes_per_line - scanner->one_line_read_buffer_rpos); + + if (scanner->one_line_read_buffer_rpos >= bytes_per_line) + { + DBG (DBG_verbose, "Release temporary buffer.\n"); + free (scanner->one_line_read_buffer); + scanner->one_line_read_buffer = NULL; + scanner->one_line_read_buffer_rpos = 0; + } + } +} + +/******************************************************************************/ +static SANE_Status +sane_read_internal (struct hp5590_scanner * scanner, SANE_Byte * data, + SANE_Int max_length, SANE_Int * length, unsigned int bytes_per_line) +{ + SANE_Status ret; + + DBG (DBG_proc, "%s, length %u, left %llu\n", + __func__, + max_length, + scanner->transferred_image_size); + + SANE_Int length_limited = 0; + *length = max_length; + if ((unsigned long long) *length > scanner->transferred_image_size) + *length = (SANE_Int) scanner->transferred_image_size; + + /* Align reading size to bytes per line. */ + *length -= *length % bytes_per_line; + + if (scanner->depth == DEPTH_COLOR_48) + { + /* Note: The last-line indicator pixel uses only 24 bits (3 bytes), not + * 48 bits (6 bytes). + */ + if (bytes_per_line > 3) + { + length_limited = *length - *length % (bytes_per_line - 3); + } + } + + DBG (DBG_verbose, "Aligning requested size to bytes per line " + "(requested: %d, aligned: %u, limit_for_48bit: %u)\n", + max_length, *length, length_limited); + + if (max_length <= 0) + { + DBG (DBG_verbose, "Buffer too small for one scan line. Need at least %u bytes per line.\n", + bytes_per_line); + scanner->scanning = SANE_FALSE; + return SANE_STATUS_UNSUPPORTED; + } + + if (scanner->one_line_read_buffer) + { + /* Copy scan data from temporary read buffer. */ + read_data_from_temporary_buffer (scanner, data, max_length, bytes_per_line, length); + if (*length > 0) + { + DBG (DBG_verbose, "Return %d bytes, left %llu bytes.\n", *length, scanner->transferred_image_size); + return SANE_STATUS_GOOD; + } + } + + /* Buffer to return scanned data. We need at least space for one line to + * simplify color processing and last-line detection. If call buffer is too + * small, use temporary read buffer for reading one line instead. + */ + SANE_Byte * scan_data; + SANE_Int scan_data_length; + scan_data = data; + scan_data_length = *length; + + /* Note, read length is shorter in 48bit mode. */ + SANE_Int length_for_read = length_limited ? length_limited : scan_data_length; + if (length_for_read == 0) + { + /* Call buffer is too small for one line. Use temporary read buffer + * instead. + */ + if (! scanner->one_line_read_buffer) + { + scanner->one_line_read_buffer = malloc (bytes_per_line); + if (! scanner->one_line_read_buffer) + return SANE_STATUS_NO_MEM; + memset (scanner->one_line_read_buffer, 0, bytes_per_line); } - memcpy (ptr, buf, bytes_per_line); + DBG (DBG_verbose, "Call buffer too small for one scan line. Use temporary read buffer for one line with %u bytes.\n", + bytes_per_line); + + /* Scan and process next line in temporary buffer. */ + scan_data = scanner->one_line_read_buffer; + scan_data_length = bytes_per_line; + length_for_read = bytes_per_line; + if (scanner->depth == DEPTH_COLOR_48) + { + /* The last-line indicator pixel uses only 24 bits (3 bytes), not 48 + * bits (6 bytes). + */ + if (length_for_read > 3) + { + length_for_read -= 3; + } + } } - free (buf); + int read_from_scanner = 1; + if ((scanner->source == SOURCE_ADF) || (scanner->source == SOURCE_ADF_DUPLEX)) + { + if (scanner->eop_last_line_data) + { + /* Scanner is in ADF mode between last-line of previous page and + * start of next page. + * Fill remaining lines with last-line data. + */ + unsigned int wpos = 0; + while (wpos < (unsigned int) scan_data_length) + { + unsigned int n1 = scan_data_length - wpos; + unsigned int n2 = bytes_per_line - scanner->eop_last_line_data_rpos; + n1 = (n1 < n2) ? n1 : n2; + memcpy (scan_data + wpos, scanner->eop_last_line_data + scanner->eop_last_line_data_rpos, n1); + wpos += n1; + scanner->eop_last_line_data_rpos += n1; + if (scanner->eop_last_line_data_rpos >= bytes_per_line) + scanner->eop_last_line_data_rpos = 0; + } + read_from_scanner = (wpos == 0); + DBG (DBG_verbose, "ADF use last-line data, wlength=%u, length=%u\n", wpos, scan_data_length); + } + else if (scanner->adf_next_page_lines_data) + { + /* Scanner is in ADF mode at start of next page and already some next + * page data is available from earlier read operation. Return this + * data. + */ + unsigned int wpos = 0; + while ((wpos < (unsigned int) scan_data_length) && + (scanner->adf_next_page_lines_data_rpos < scanner->adf_next_page_lines_data_size)) + { + unsigned int n1 = scan_data_length - wpos; + unsigned int n2 = scanner->adf_next_page_lines_data_size - scanner->adf_next_page_lines_data_rpos; + n1 = (n1 < n2) ? n1 : n2; + memcpy (scan_data + wpos, scanner->adf_next_page_lines_data + scanner->adf_next_page_lines_data_rpos, n1); + wpos += n1; + scanner->adf_next_page_lines_data_rpos += n1; + if (scanner->adf_next_page_lines_data_rpos >= scanner->adf_next_page_lines_data_size) + { + free (scanner->adf_next_page_lines_data); + scanner->adf_next_page_lines_data = NULL; + scanner->adf_next_page_lines_data_size = 0; + scanner->adf_next_page_lines_data_rpos = 0; + scanner->adf_next_page_lines_data_wpos = 0; + } + } + scan_data_length = wpos; + read_from_scanner = (wpos == 0); + DBG (DBG_verbose, "ADF use next-page data, wlength=%u, length=%u\n", wpos, scan_data_length); + } + } + + if (read_from_scanner) + { + /* Read data from scanner. */ + ret = hp5590_read (scanner->dn, scanner->proto_flags, + scan_data, length_for_read, + scanner->bulk_read_state); + if (ret != SANE_STATUS_GOOD) + { + scanner->scanning = SANE_FALSE; + return ret; + } + + /* Look for last-line indicator pixels in convert functions. + * If found: + * - Overwrite indicator pixel with neighboring color (optional). + * - Save last line data for later use. + */ + ret = convert_to_rgb (scanner, scan_data, scan_data_length); + if (ret != SANE_STATUS_GOOD) + { + scanner->scanning = SANE_FALSE; + return ret; + } + + ret = convert_gray_and_lineart (scanner, scan_data, scan_data_length); + if (ret != SANE_STATUS_GOOD) + return ret; + } + + if (data == scan_data) + { + /* Scanned to call buffer. */ + scanner->transferred_image_size -= scan_data_length; + *length = scan_data_length; + } + else + { + /* Scanned to temporary read buffer. */ + if (scanner->one_line_read_buffer) + { + /* Copy scan data from temporary read buffer. */ + read_data_from_temporary_buffer (scanner, data, max_length, scan_data_length, length); + } + else + { + *length = 0; + } + } + + DBG (DBG_verbose, "Return %d bytes, left %llu bytes\n", *length, scanner->transferred_image_size); return SANE_STATUS_GOOD; } +/****************************************************************************** + * Copy at maximum the last n lines from the src buffer to the begin of the dst + * buffer. + * Return number of lines copied. + */ +static SANE_Int +copy_n_last_lines(SANE_Byte * src, SANE_Int src_len, SANE_Byte * dst, SANE_Int n, unsigned int bytes_per_line) +{ + DBG (DBG_proc, "%s\n", __func__); + SANE_Int n_copy = MY_MIN(src_len, n); + SANE_Byte * src1 = src + (src_len - n_copy) * bytes_per_line; + memcpy (dst, src1, n_copy * bytes_per_line); + return n_copy; +} + +/****************************************************************************** + * Copy the color values from line - delta_lines to line. + * buffer2 : Source and target buffer. + * buffer1 : Only source buffer. Contains lines scanned before lines in buffer1. + * color_idx : Index of color to be copied (0..2). + * delta_lines : color shift. + * color_48 : True = 2 byte , false = 1 byte per color. + */ +static void +shift_color_lines(SANE_Byte * buffer2, SANE_Int n_lines2, SANE_Byte * buffer1, SANE_Int n_lines1, SANE_Int color_idx, SANE_Int delta_lines, SANE_Bool color_48, unsigned int bytes_per_line) +{ + DBG (DBG_proc, "%s\n", __func__); + for (SANE_Int i = n_lines2 - 1; i >= 0; --i) { + SANE_Byte * dst = buffer2 + i * bytes_per_line; + SANE_Int ii = i - delta_lines; + SANE_Byte * src = NULL; + SANE_Int source_color_idx = color_idx; + if (ii >= 0) { + /* Read from source and target buffer. */ + src = buffer2 + ii * bytes_per_line; + } else { + ii += n_lines1; + if (ii >= 0) { + /* Read from source only buffer. */ + src = buffer1 + ii * bytes_per_line; + } else { + /* Read other color from source position. */ + src = dst; + source_color_idx = 2; + } + } + /* Copy selected color values. */ + SANE_Int step = color_48 ? 2 : 1; + SANE_Int stride = 3 * step; + for (unsigned int pos = 0; pos < bytes_per_line; pos += stride) { + SANE_Int p1 = pos + step * source_color_idx; + SANE_Int p2 = pos + step * color_idx; + dst[p2] = src[p1]; + if (color_48) { + dst[p2 + 1] = src[p1 + 1]; + } + } + } +} + +/****************************************************************************** + * Append all lines from buffer2 to the end of buffer1 and keep max_lines last + * lines. + * buffer2 : Source line buffer. + * buffer1 : Target line buffer. Length will be adjusted. + * max_lines : Max number of lines in buffer1. + */ +static void +append_and_move_lines(SANE_Byte * buffer2, SANE_Int n_lines2, SANE_Byte * buffer1, unsigned int * n_lines1_ptr, SANE_Int max_lines, unsigned int bytes_per_line) +{ + DBG (DBG_proc, "%s\n", __func__); + SANE_Int rest1 = max_lines - *n_lines1_ptr; + SANE_Int copy2 = MY_MIN(n_lines2, max_lines); + if (copy2 > rest1) { + SANE_Int shift1 = *n_lines1_ptr + copy2 - max_lines; + SANE_Int blen = MY_MIN(max_lines - shift1, (SANE_Int) *n_lines1_ptr); + SANE_Byte * pdst = buffer1; + SANE_Byte * psrc = pdst + shift1 * bytes_per_line; + for (SANE_Int i = 0; i < blen; ++i) { + memcpy (pdst, psrc, bytes_per_line); + pdst += bytes_per_line; + psrc += bytes_per_line; + } + *n_lines1_ptr -= shift1; + } + SANE_Int n_copied = copy_n_last_lines(buffer2, n_lines2, buffer1 + *n_lines1_ptr * bytes_per_line, copy2, bytes_per_line); + *n_lines1_ptr += n_copied; +} + + /******************************************************************************/ SANE_Status sane_read (SANE_Handle handle, SANE_Byte * data, - SANE_Int max_length, SANE_Int * length) + SANE_Int max_length, SANE_Int * length) { - struct hp5590_scanner *scanner = handle; - SANE_Status ret; + struct hp5590_scanner *scanner = handle; + SANE_Status ret; - DBG (DBG_proc, "%s, length %u, left %u\n", + DBG (DBG_proc, "%s, length %u, left %llu\n", __func__, max_length, scanner->transferred_image_size); @@ -1283,7 +2465,7 @@ sane_read (SANE_Handle handle, SANE_Byte * data, ret = hp5590_inc_scan_count (scanner->dn, scanner->proto_flags); if (ret != SANE_STATUS_GOOD) - return ret; + return ret; /* Don't free bulk read state, some bytes could be left * for the next images from ADF @@ -1295,70 +2477,78 @@ sane_read (SANE_Handle handle, SANE_Byte * data, { ret = hp5590_low_init_bulk_read_state (&scanner->bulk_read_state); if (ret != SANE_STATUS_GOOD) - { - scanner->scanning = SANE_FALSE; - return ret; - } + { + scanner->scanning = SANE_FALSE; + return ret; + } } - *length = max_length; - if (*length > scanner->transferred_image_size) - *length = scanner->transferred_image_size; - - if ( scanner->depth == DEPTH_COLOR_24 - || scanner->depth == DEPTH_COLOR_48) - { - unsigned int bytes_per_line; - ret = calc_image_params (scanner, - NULL, NULL, - &bytes_per_line, - NULL, NULL); - if (ret != SANE_STATUS_GOOD) - return ret; + unsigned int bytes_per_line; + ret = calc_image_params (scanner, + NULL, NULL, + &bytes_per_line, + NULL, NULL); + if (ret != SANE_STATUS_GOOD) + return ret; - *length -= *length % bytes_per_line; - DBG (2, "Aligning requested size to bytes per line " - "(requested: %u, aligned: %u)\n", - max_length, *length); - } + ret = sane_read_internal(scanner, data, max_length, length, bytes_per_line); - ret = hp5590_read (scanner->dn, scanner->proto_flags, - data, *length, scanner->bulk_read_state); - if (ret != SANE_STATUS_GOOD) + if ((ret == SANE_STATUS_GOOD) && (scanner->dpi == 2400) && + ((scanner->depth == DEPTH_COLOR_48) || (scanner->depth == DEPTH_COLOR_24))) { - scanner->scanning = SANE_FALSE; - return ret; - } + /* Correct color shift bug for 2400 dpi. + * Note: 2400 dpi only works in color mode. Grey mode and lineart seem to + * fail. + * Align colors by shifting B channel by 48 lines and G channel by 24 + * lines. + */ + const SANE_Int offset_max = 48; + const SANE_Int offset_part = 24; + SANE_Bool color_48 = (scanner->depth == DEPTH_COLOR_48); - scanner->transferred_image_size -= *length; + if (! scanner->color_shift_line_buffer1) + { + scanner->color_shift_buffered_lines1 = 0; + scanner->color_shift_line_buffer1 = malloc (bytes_per_line * offset_max); + if (! scanner->color_shift_line_buffer1) + return SANE_STATUS_NO_MEM; + memset (scanner->color_shift_line_buffer1, 0, bytes_per_line * offset_max); + } + if (! scanner->color_shift_line_buffer2) + { + scanner->color_shift_buffered_lines2 = 0; + scanner->color_shift_line_buffer2 = malloc (bytes_per_line * offset_max); + if (! scanner->color_shift_line_buffer2) + return SANE_STATUS_NO_MEM; + memset (scanner->color_shift_line_buffer2, 0, bytes_per_line * offset_max); + } - ret = convert_to_rgb (scanner, data, *length); - if (ret != SANE_STATUS_GOOD) - { - scanner->scanning = SANE_FALSE; - return ret; - } + SANE_Int n_lines = *length / bytes_per_line; + scanner->color_shift_buffered_lines2 = MY_MIN(n_lines, offset_max); + copy_n_last_lines(data, n_lines, scanner->color_shift_line_buffer2, scanner->color_shift_buffered_lines2, bytes_per_line); - ret = convert_lineart (scanner, data, *length); - if (ret != SANE_STATUS_GOOD) - return ret; + shift_color_lines(data, n_lines, scanner->color_shift_line_buffer1, scanner->color_shift_buffered_lines1, 1, offset_part, color_48, bytes_per_line); + shift_color_lines(data, n_lines, scanner->color_shift_line_buffer1, scanner->color_shift_buffered_lines1, 0, offset_max, color_48, bytes_per_line); - return SANE_STATUS_GOOD; + append_and_move_lines(scanner->color_shift_line_buffer2, scanner->color_shift_buffered_lines2, scanner->color_shift_line_buffer1, &(scanner->color_shift_buffered_lines1), offset_max, bytes_per_line); + } + + return ret; } /******************************************************************************/ void sane_cancel (SANE_Handle handle) { - struct hp5590_scanner *scanner = handle; - SANE_Status ret; + struct hp5590_scanner *scanner = handle; + SANE_Status ret; DBG (DBG_proc, "%s\n", __func__); scanner->scanning = SANE_FALSE; if (scanner->dn < 0) - return; + return; hp5590_low_free_bulk_read_state (&scanner->bulk_read_state); @@ -1371,7 +2561,7 @@ sane_cancel (SANE_Handle handle) SANE_Status sane_set_io_mode (SANE_Handle __sane_unused__ handle, - SANE_Bool __sane_unused__ non_blocking) + SANE_Bool __sane_unused__ non_blocking) { DBG (DBG_proc, "%s\n", __func__); @@ -1381,7 +2571,7 @@ sane_set_io_mode (SANE_Handle __sane_unused__ handle, /******************************************************************************/ SANE_Status sane_get_select_fd (SANE_Handle __sane_unused__ handle, - SANE_Int __sane_unused__ * fd) + SANE_Int __sane_unused__ * fd) { DBG (DBG_proc, "%s\n", __func__); |