From 974ee17cf49c44ab79decbafbb8c912245d7bed1 Mon Sep 17 00:00:00 2001 From: Rolf Bensch Date: Fri, 3 Feb 2012 22:05:32 +0100 Subject: [PATCH] 1 bit B/W lineart for pixma_mp150 and pixma_810 subdrivers --- ChangeLog | 5 ++ backend/pixma.c | 123 ++++++++++++++++++++++++++++------- backend/pixma.h | 31 ++++++++- backend/pixma_common.c | 114 ++++++++++++++++++++++++++++++++ backend/pixma_common.h | 2 + backend/pixma_mp150.c | 96 +++++++++++++++++---------- backend/pixma_mp810.c | 104 ++++++++++++++++++----------- backend/pixma_sane_options.c | 26 +++++++- backend/pixma_sane_options.h | 2 + 9 files changed, 403 insertions(+), 100 deletions(-) diff --git a/ChangeLog b/ChangeLog index 310b0205f..8aaf53ed8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2012-02-03 Rolf Bensch + * backend/pixma.[ch], backend/pixma_common.[ch], + backend/pixma_sane_options.[ch], backend/pixma_mp{150,810}.c: + 1 bit B/W lineart for pixma_mp150 and pixma_810 subdrivers. + 2012-02-03 Rolf Bensch * backend/pixma.h: set PIXMA_VERSION_{MAJOR,MINOR,BUILD} to 0.16.3. diff --git a/backend/pixma.c b/backend/pixma.c index 91ed45cd1..dc587cd5f 100644 --- a/backend/pixma.c +++ b/backend/pixma.c @@ -84,8 +84,10 @@ #define OVAL(opt) OPT_IN_CTX[opt].val #define AUTO_GAMMA 2.2 -#include "pixma_sane_options.h" /* generated by gen_options.py */ - +/* pixma_sane_options.h generated by + * scripts/pixma_gen_options.py h < pixma.c > pixma_sane_options.h + * *and* formatted */ +#include "pixma_sane_options.h" typedef struct pixma_sane_t { @@ -103,6 +105,7 @@ typedef struct pixma_sane_t SANE_Range xrange, yrange; SANE_Word dpi_list[9]; /* up to 9600 dpi */ SANE_String_Const mode_list[4]; + pixma_scan_mode_t mode_map[4]; uint8_t gamma_table[4096]; SANE_String_Const source_list[4]; pixma_paper_source_t source_map[4]; @@ -324,6 +327,43 @@ clamp_value (pixma_sane_t * ss, SANE_Int n, void *v, SANE_Int * info) } } +/* create dynamic mode_list + * ss: scanner device + * tpu = 0: flatbed or ADF mode + * 1 bit lineart, 8 bit grayscale and 24 bit color scans + * tpu = 1: TPU mode + * 16 bit grayscale and 48 bit color scans */ +static void +create_mode_list (pixma_sane_t * ss, SANE_Bool tpu) +{ + const pixma_config_t *cfg; + int i; + + cfg = pixma_get_config (ss->s); + + /* setup available mode. */ + i = 0; + ss->mode_list[i] = SANE_VALUE_SCAN_MODE_COLOR; + ss->mode_map[i] = PIXMA_SCAN_MODE_COLOR; + i++; + if (cfg->cap & PIXMA_CAP_GRAY) + { + ss->mode_list[i] = SANE_VALUE_SCAN_MODE_GRAY; + ss->mode_map[i] = PIXMA_SCAN_MODE_GRAY; + i++; + } + + if (!tpu && cfg->cap & PIXMA_CAP_LINEART) + { + ss->mode_list[i] = SANE_VALUE_SCAN_MODE_LINEART; + ss->mode_map[i] = PIXMA_SCAN_MODE_LINEART; + i++; + } + /* terminate mode_list and mode_map */ + ss->mode_list[i] = 0; + ss->mode_map[i] = 0; +} + /* create ss->dpi_list * ss: scanner device * ext = 0: min = 75 dpi; max = cfg->xdpi @@ -533,6 +573,8 @@ control_option (pixma_sane_t * ss, SANE_Int n, cfg = pixma_get_config (ss->s); + /* PDBG (pixma_dbg (4, "*control_option***** n = %u, a = %u\n", n, a)); */ + result = SANE_STATUS_UNSUPPORTED; switch (n) { @@ -598,22 +640,46 @@ control_option (pixma_sane_t * ss, SANE_Int n, *info |= SANE_INFO_RELOAD_OPTIONS; } break; + case opt_mode: + if (a == SANE_ACTION_SET_VALUE || a == SANE_ACTION_SET_AUTO) + { + if (ss->mode_map[OVAL (opt_mode).w] == PIXMA_SCAN_MODE_LINEART) + { + enable_option (ss, opt_threshold, SANE_TRUE); + enable_option (ss, opt_custom_gamma, SANE_FALSE); + } + else + { + enable_option (ss, opt_custom_gamma, SANE_TRUE); + enable_option (ss, opt_threshold, SANE_FALSE); + } + *info |= SANE_INFO_RELOAD_OPTIONS; + } + break; case opt_source: if (cfg->cap & (PIXMA_CAP_ADF|PIXMA_CAP_ADFDUP|PIXMA_CAP_TPU) && (a == SANE_ACTION_SET_VALUE || a == SANE_ACTION_SET_AUTO)) { /* new source selected: flatbed, ADF, TPU, ... */ - /* to avoid fatal errors, select 600 dpi for dpi_list */ + /* to avoid fatal errors, select 600 dpi + * and first entry of dynamic mode_list + * available identifiers are unknown here */ OVAL (opt_resolution).w = 600; - if (ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_TPU - || ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_ADF - || ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_ADFDUP) - { - /* create dpi_list for ADF/TPU mode */ + OVAL (opt_mode).w = 0; + /* recreate dynamic lists */ + if (ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_TPU) + { /* TPU mode */ + create_mode_list (ss, SANE_TRUE); + create_dpi_list (ss, SANE_TRUE); + } + else if (ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_ADF + || ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_ADFDUP) + { /* ADF mode */ + create_mode_list (ss, SANE_FALSE); create_dpi_list (ss, SANE_TRUE); } else - { - /* create dpi_list for flatbed mode */ + { /* flatbed mode */ + create_mode_list (ss, SANE_FALSE); create_dpi_list (ss, SANE_FALSE); } *info |= SANE_INFO_RELOAD_OPTIONS; @@ -680,7 +746,9 @@ calc_scan_param (pixma_sane_t * ss, pixma_scan_param_t * sp) sp->gamma_table = (OVAL (opt_custom_gamma).b) ? ss->gamma_table : NULL; sp->source = ss->source_map[OVAL (opt_source).w]; + sp->mode = ss->mode_map[OVAL (opt_mode).w]; sp->adf_pageid = ss->page_count; + sp->threshold = OVAL (opt_threshold).w; error = pixma_check_scan_param (ss->s, sp); if (error < 0) @@ -715,16 +783,7 @@ init_option_descriptors (pixma_sane_t * ss) * because the whole pixma_sane_t was cleared during allocation. */ /* setup available mode. */ - ss->mode_list[0] = SANE_VALUE_SCAN_MODE_COLOR; - if (cfg->cap & PIXMA_CAP_GRAY) - { - ss->mode_list[1] = SANE_VALUE_SCAN_MODE_GRAY; - } - - if (cfg->cap & PIXMA_CAP_LINEART) - { - ss->mode_list[2] = SANE_VALUE_SCAN_MODE_LINEART; - } + create_mode_list (ss, SANE_FALSE); /* setup paper source */ i = 0; @@ -1393,7 +1452,9 @@ sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len) return ss->last_read_status; status = SANE_STATUS_GOOD; - if ((ss->sp.line_size - ss->output_line_size) == 0) + /* CCD scanners use software lineart + * the scanner must scan 24 bit color for one bit lineart */ + if ((ss->sp.line_size - ((ss->sp.software_lineart == 1) ? (ss->output_line_size * 8) : ss->output_line_size)) == 0) { status = read_image (ss, buf, maxlen, &sum); } @@ -1401,6 +1462,7 @@ sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len) { /* FIXME: Because there is no frontend that can cope with padding at the end of line, we've to remove it here in the backend! */ + PDBG (pixma_dbg (1, "*sane_read***** Warning: padding may cause incomplete scan results\n")); sum = 0; while (sum < maxlen) { @@ -1513,8 +1575,8 @@ type int resolution info reload_params type string mode[10] - default Color constraint @string_list = ss->mode_list + default @s = SANE_I18N(ss->mode_list[0]) title @SANE_TITLE_SCAN_MODE desc @SANE_DESC_SCAN_MODE cap soft_select soft_detect automatic @@ -1607,7 +1669,22 @@ type int button-2 title Button 2 cap soft_select soft_detect advanced +rem ------------------------------------------- +type group + title Extras + +type int threshold + unit PERCENT + default 50 + title @SANE_TITLE_THRESHOLD + desc @SANE_DESC_THRESHOLD + cap soft_select soft_detect automatic inactive + rem ------------------------------------------- END SANE_Option_Descriptor */ -#include "pixma_sane_options.c" /* generated by gen_options.py */ + +/* pixma_sane_options.c generated by + * scripts/pixma_gen_options.py < pixma.c > pixma_sane_options.c + * *and* formatted */ +#include "pixma_sane_options.c" diff --git a/backend/pixma.h b/backend/pixma.h index ba3bdedad..a3d620f13 100644 --- a/backend/pixma.h +++ b/backend/pixma.h @@ -198,6 +198,16 @@ typedef enum pixma_paper_source_t PIXMA_SOURCE_ADFDUP /* duplex */ } pixma_paper_source_t; +/** Scan modes */ +typedef enum pixma_scan_mode_t +{ + /* standard scan modes */ + PIXMA_SCAN_MODE_COLOR, + PIXMA_SCAN_MODE_GRAY, + /* 1 bit lineart scan mode */ + PIXMA_SCAN_MODE_LINEART +} pixma_scan_mode_t; + typedef enum pixma_hardware_status_t { PIXMA_HARDWARE_OK, @@ -251,10 +261,15 @@ struct pixma_scan_param_t * This field will be set by pixma_check_scan_param(). */ uint64_t image_size; - /** Channels per pixel. 1 = grayscale, 3 = color */ + /** Channels per pixel. 1 = grayscale and lineart, 3 = color */ unsigned channels; - /** Bits per channels. 0 = default. Currently not used. */ + /** Bits per channels. + * 1 = 1 bit B/W lineart (flatbed) + * 8 = 8 bit grayscale, + * 24 bit color (both flatbed) + * 16 = 16 bit grayscale (TPU, flatbed not implemeted), + * 48 bit color (TPU, flatbed not implemented) */ unsigned depth; /*@{ */ @@ -277,6 +292,15 @@ struct pixma_scan_param_t * Currently only used in pixma_mp810.c sub-driver */ unsigned tpu_offset_added; + /** Flag indicating whether a software-lineart scan is in progress + * 0 = other scan + * 1 = software-lineart scan */ + unsigned software_lineart; + + /** Threshold for software-lineart scans + * Value in percent (0% ... 100%) */ + unsigned threshold; + /** Gamma table. 4096 entries, 12 bit => 8 bit. If \c NULL, default gamma * specified by subdriver will be used. */ const uint8_t *gamma_table; @@ -284,6 +308,9 @@ struct pixma_scan_param_t /** \see #pixma_paper_source_t */ pixma_paper_source_t source; + /** \see #pixma_scan_mode_t */ + pixma_scan_mode_t mode; + /** The current page # in the same ADF scan session, 0 in non ADF */ unsigned adf_pageid; }; diff --git a/backend/pixma_common.c b/backend/pixma_common.c index 3cedbbbfc..ab01ff6a3 100644 --- a/backend/pixma_common.c +++ b/backend/pixma_common.c @@ -304,6 +304,115 @@ pixma_get_time (time_t * sec, uint32_t * usec) *usec = tv.tv_usec; } +/* convert 24/48 bit RGB to 8/16 bit grayscale + * + * Formular: g = (R + G + B) / 3 + * + * sptr: source color scale buffer + * gptr: destination gray scale buffer + * c == 3: 24 bit RGB -> 8 bit gray + * c == 6: 48 bit RGB -> 16 bit gray + */ +uint8_t * +pixma_rgb_to_gray (uint8_t * gptr, uint8_t * sptr, unsigned w, unsigned c) +{ + unsigned i, j, g; + + /* PDBG (pixma_dbg (4, "*pixma_rgb_to_gray*****\n")); */ + + for (i = 0; i < w; i++) + { + for (j = 0, g = 0; j < 3; j++) + { + g += *sptr++; + if (c == 6) g += (*sptr++ << 8); /* 48 bit RGB: high byte */ + } + + g /= 3; /* 8 or 16 bit gray */ + *gptr++ = g; + if (c == 6) *gptr++ = (g >> 8); /* 16 bit gray: high byte */ + } + return gptr; +} + +/** + * This code was taken from the genesys backend + * This sub-driver has no threshold_curve + * uses threshold to control software binarization + * @param sp device set up for the scan + * @param dst pointer where to store result + * @param src pointer to raw data + * @param width width of the processed line + * @param c 3 for 3-channel single-byte data, 6 for double-byte data + * */ +uint8_t * +pixma_binarize_line(pixma_scan_param_t * sp, uint8_t * dst, uint8_t * src, unsigned width, unsigned c) +{ + unsigned j, x, offset; + unsigned char mask; + uint8_t min, max; + unsigned threshold = 0xFF * sp->threshold / 100; /* sp->threshold is 0% ... 100% */ + + /* PDBG (pixma_dbg (4, "*pixma_binarize_line***** src = %u, dst = %u, width = %u, c = %u, threshold = %u *****\n", + src, dst, width, c, sp->threshold)); */ + + /* 16 bit grayscale not supported */ + if (c == 6) + { + PDBG (pixma_dbg (1, "*pixma_binarize_line***** Error: 16 bit grayscale not supported\n")); + return dst; + } + + /* first, color convert to grayscale */ + pixma_rgb_to_gray(dst, src, width, c); + + /* second, normalize line */ + min = 255; + max = 0; + for (x = 0; x < width; x++) + { + if (src[x] > max) + { + max = src[x]; + } + if (src[x] < min) + { + min = src[x]; + } + } + + /* safeguard against dark or white areas */ + if(min>80) + min=0; + if(max<80) + max=255; + for (x = 0; x < width; x++) + { + src[x] = ((src[x] - min) * 255) / (max - min); + } + + /* third, walk the input buffer, output bits */ + for (j = 0; j < width; j++) + { + /* output image location */ + offset = j % 8; + mask = 0x80 >> offset; + + /* lookup threshold */ + if (src[j] > threshold) + *dst &= ~mask; /* white */ + else + *dst |= mask; /* black */ + + if (offset == 7) + dst++; + } + + /* PDBG (pixma_dbg (4, " *pixma_binarize_line***** ready: src = %u, dst = %u *****\n", src, dst)); */ + + return dst; +} + int pixma_map_status_errno (unsigned status) { @@ -574,6 +683,7 @@ pixma_scan (pixma_t * s, pixma_scan_param_t * sp) pixma_dbg (3, " dpi=%ux%u offset=(%u,%u) dimension=%ux%u\n", sp->xdpi, sp->ydpi, sp->x, sp->y, sp->w, sp->h); pixma_dbg (3, " gamma_table=%p source=%d\n", sp->gamma_table, sp->source); + pixma_dbg (3, " threshold=%d\n", sp->threshold); #endif s->param = sp; @@ -843,6 +953,10 @@ pixma_check_scan_param (pixma_t * s, pixma_scan_param_t * sp) if (sp->line_size == 0) sp->line_size = sp->depth / 8 * sp->channels * sp->w; sp->image_size = sp->line_size * sp->h; + + /* image_size for software lineart is counted in bits */ + if (sp->software_lineart == 1) + sp->image_size /= 8; return 0; } diff --git a/backend/pixma_common.h b/backend/pixma_common.h index 6175fd32c..4cdb4a8e8 100644 --- a/backend/pixma_common.h +++ b/backend/pixma_common.h @@ -188,6 +188,8 @@ uint8_t pixma_sum_bytes (const void *data, unsigned len); int pixma_check_dpi (unsigned dpi, unsigned max); void pixma_sleep (unsigned long usec); void pixma_get_time (time_t * sec, uint32_t * usec); +uint8_t * pixma_rgb_to_gray (uint8_t * gptr, uint8_t * sptr, unsigned w, unsigned c); +uint8_t * pixma_binarize_line(pixma_scan_param_t *, uint8_t * dst, uint8_t * src, unsigned width, unsigned c); /**@}*/ /** \name Command related functions */ diff --git a/backend/pixma_mp150.c b/backend/pixma_mp150.c index 7251004af..358317844 100644 --- a/backend/pixma_mp150.c +++ b/backend/pixma_mp150.c @@ -567,15 +567,22 @@ has_ccd_sensor (pixma_t * s) static int is_ccd_grayscale (pixma_t * s) { - return (has_ccd_sensor (s) && (s->param->channels == 1)); + return (has_ccd_sensor (s) && (s->param->channels == 1) && !s->param->software_lineart); } -/* CCD sensors don't have a Grayscale mode, but use color mode instead */ +static int +is_ccd_lineart (pixma_t * s) +{ + return (has_ccd_sensor (s) && s->param->software_lineart); +} + +/* CCD sensors don't have neither a Grayscale mode nor a Lineart mode, + * but use color mode instead */ static unsigned get_cis_ccd_line_size (pixma_t * s) { - return (s->param->wx ? s->param->line_size / s->param->w * s->param->wx - : s->param->line_size) * ((is_ccd_grayscale (s)) ? 3 : 1); + return ((s->param->wx ? s->param->line_size / s->param->w * s->param->wx + : s->param->line_size) * ((is_ccd_grayscale (s) || is_ccd_lineart (s)) ? 3 : 1)); } static unsigned @@ -654,6 +661,12 @@ send_scan_param (pixma_t * s) unsigned h = MIN (s->param->h + calc_shifting (s), s->cfg->height * s->param->ydpi / 75); + /* TPU scan does not support lineart */ + if (is_scanning_from_tpu (s) && is_ccd_lineart (s)) + { + return PIXMA_ENOTSUP; + } + if (mp->generation <= 2) { data = pixma_newcmd (&mp->cb, cmd_scan_param, 0x30, 0); @@ -665,8 +678,8 @@ send_scan_param (pixma_t * s) pixma_set_be32 (s->param->y, data + 0x0c); pixma_set_be32 (raw_width, data + 0x10); pixma_set_be32 (h, data + 0x14); - data[0x18] = ((s->param->channels != 1) || is_ccd_grayscale (s)) ? 0x08 : 0x04; - data[0x19] = s->param->depth * ((is_ccd_grayscale (s)) ? 3 : s->param->channels); /* bits per pixel */ + data[0x18] = ((s->param->channels != 1) || is_ccd_grayscale (s) || is_ccd_lineart (s)) ? 0x08 : 0x04; + data[0x19] = s->param->depth * ((is_ccd_grayscale (s) || is_ccd_lineart (s)) ? 3 : s->param->channels); /* bits per pixel */ data[0x1a] = (is_scanning_from_tpu (s) ? 1 : 0); data[0x20] = 0xff; data[0x23] = 0x81; @@ -699,13 +712,14 @@ send_scan_param (pixma_t * s) pixma_set_be32 (s->param->y, data + 0x10); pixma_set_be32 (raw_width, data + 0x14); pixma_set_be32 (h, data + 0x18); - data[0x1c] = ((s->param->channels != 1) || is_ccd_grayscale (s)) ? 0x08 : 0x04; + data[0x1c] = ((s->param->channels != 1) || is_ccd_grayscale (s) || is_ccd_lineart (s)) ? 0x08 : 0x04; #ifdef DEBUG_TPU_48 data[0x1d] = 24; #else - data[0x1d] = (is_scanning_from_tpu (s)) ? 48 : - s->param->depth * ((is_ccd_grayscale (s)) ? 3 : s->param->channels); /* bits per pixel */ + data[0x1d] = (is_scanning_from_tpu (s)) ? 48 + : (((s->param->depth == 1) ? 8 : s->param->depth) + * ((is_ccd_grayscale (s) || is_ccd_lineart (s)) ? 3 : s->param->channels)); /* bits per pixel */ #endif data[0x1f] = 0x01; @@ -952,26 +966,6 @@ shift_colors (uint8_t * dptr, uint8_t * sptr, return dptr; } -static uint8_t * -rgb_to_gray (uint8_t * gptr, uint8_t * sptr, unsigned w, unsigned c) -{ - unsigned i, j, g; - - for (i = 0; i < w; i++) - { - for (j = 0, g = 0; j < 3; j++) - { - g += *sptr++; - if (c == 6) g += (*sptr++ << 8); - } - - g /= 3; - *gptr++ = g; - if (c == 6) *gptr++ = (g >> 8); - } - return gptr; -} - static void reorder_pixels (uint8_t * linebuf, uint8_t * sptr, unsigned c, unsigned n, unsigned m, unsigned w, unsigned line_size) @@ -1018,7 +1012,8 @@ post_process_image_data (pixma_t * s, pixma_imagebuf_t * ib) unsigned c, lines, i, line_size, n, m, cw, cx; uint8_t *sptr, *dptr, *gptr, *cptr; - c = ((is_ccd_grayscale (s)) ? 3 : s->param->channels) * s->param->depth / 8; + c = ((is_ccd_grayscale (s) || is_ccd_lineart (s)) ? 3 : s->param->channels) + * ((s->param->depth == 1) ? 8 : s->param->depth) / 8; cw = c * s->param->w; cx = c * s->param->xs; @@ -1059,9 +1054,12 @@ post_process_image_data (pixma_t * s, pixma_imagebuf_t * ib) /* Crop line to selected borders */ memmove(cptr, sptr + cx, cw); + /* Color to Lineart convert for CCD sensor */ + if (is_ccd_lineart (s)) + cptr = gptr = pixma_binarize_line (s->param, gptr, cptr, s->param->w, c); /* Color to Grayscale convert for CCD sensor */ - if (is_ccd_grayscale (s)) - cptr = gptr = rgb_to_gray (gptr, cptr, s->param->w, c); + else if (is_ccd_grayscale (s)) + cptr = gptr = pixma_rgb_to_gray (gptr, cptr, s->param->w, c); else cptr += cw; } @@ -1140,14 +1138,39 @@ static int mp150_check_param (pixma_t * s, pixma_scan_param_t * sp) { mp150_t *mp = (mp150_t *) s->subdriver; + unsigned w_max; - sp->depth = 8; /* MP150 only supports 8 bit per channel. */ + /* MP150 only supports 8 bit per channel in color and grayscale mode */ + if (sp->depth != 1) + { + sp->software_lineart = 0; + sp->depth = 8; #ifdef TPU_48 #ifndef DEBUG_TPU_48 - if (sp->source == PIXMA_SOURCE_TPU) + if (sp->source == PIXMA_SOURCE_TPU) #endif - sp->depth = 16; /* TPU in 16 bits mode */ + sp->depth = 16; /* TPU in 16 bits mode */ #endif + } + else + { + /* software lineart */ + sp->software_lineart = 1; + sp->depth = 1; + sp->channels = 1; + } + + /* for software lineart w must be a multiple of 8 */ + if (sp->software_lineart == 1 && sp->w % 8) + { + sp->w += 8 - (sp->w % 8); + + /* do not exceed the scanner capability */ + w_max = s->cfg->width * s->cfg->xdpi / 75; + w_max -= w_max % 8; + if (sp->w > w_max) + sp->w = w_max; + } if (mp->generation >= 2) { @@ -1159,7 +1182,7 @@ mp150_check_param (pixma_t * s, pixma_scan_param_t * sp) sp->xs = 0; /*PDBG (pixma_dbg (4, "*mp150_check_param***** Selected origin, origin shift: %i, %i *****\n", sp->x, sp->xs));*/ sp->wx = calc_raw_width (mp, sp); - sp->line_size = sp->w * sp->channels * (sp->depth / 8); /* bytes per line per color after cropping */ + sp->line_size = sp->w * sp->channels * (((sp->depth == 1) ? 8 : sp->depth) / 8); /* bytes per line per color after cropping */ /*PDBG (pixma_dbg (4, "*mp150_check_param***** Final scan width and line-size: %i, %i *****\n", sp->wx, sp->line_size));*/ /* Some exceptions here for particular devices */ @@ -1504,6 +1527,7 @@ static const pixma_scan_ops_t pixma_mp150_ops = { ext_min_dpi, ext_max_dpi, /* ext_min_dpi, ext_max_dpi */ \ w, h, /* width, height */ \ PIXMA_CAP_EASY_RGB|PIXMA_CAP_GRAY| \ + PIXMA_CAP_LINEART| \ PIXMA_CAP_GAMMA_TABLE|PIXMA_CAP_EVENTS|cap \ } diff --git a/backend/pixma_mp810.c b/backend/pixma_mp810.c index 9b37c8619..7e8a69440 100644 --- a/backend/pixma_mp810.c +++ b/backend/pixma_mp810.c @@ -489,7 +489,7 @@ static unsigned calc_raw_width (const mp810_t * mp, const pixma_scan_param_t * param) { unsigned raw_width; - /* NOTE: Actually, we can send arbitary width to MP150. Lines returned + /* NOTE: Actually, we can send arbitary width to MP810. Lines returned are always padded to multiple of 4 or 12 pixels. Is this valid for other models, too? */ if (mp->generation >= 2) @@ -517,15 +517,22 @@ has_ccd_sensor (pixma_t * s) static int is_ccd_grayscale (pixma_t * s) { - return (has_ccd_sensor (s) && (s->param->channels == 1)); + return (has_ccd_sensor (s) && (s->param->channels == 1) && !s->param->software_lineart); } -/* CCD sensors don't have a Grayscale mode, but use color mode instead */ +static int +is_ccd_lineart (pixma_t * s) +{ + return (has_ccd_sensor (s) && s->param->software_lineart); +} + +/* CCD sensors don't have neither a Grayscale mode nor a Lineart mode, + * but use color mode instead */ static unsigned get_cis_ccd_line_size (pixma_t * s) { - return (s->param->wx ? s->param->line_size / s->param->w * s->param->wx - : s->param->line_size) * ((is_ccd_grayscale (s)) ? 3 : 1); + return ((s->param->wx ? s->param->line_size / s->param->w * s->param->wx + : s->param->line_size) * ((is_ccd_grayscale (s) || is_ccd_lineart (s)) ? 3 : 1)); } static unsigned @@ -679,6 +686,12 @@ send_scan_param (pixma_t * s) unsigned raw_width = calc_raw_width (mp, s->param); unsigned h, h1, h2, shifting; + /* TPU scan does not support lineart */ + if (is_scanning_from_tpu (s) && is_ccd_lineart (s)) + { + return PIXMA_ENOTSUP; + } + shifting = calc_shifting (s); h1 = s->param->h + shifting; /* add lines for color shifting */ /* PDBG (pixma_dbg (4, "* send_scan_param: height calc (choose lesser) 1 %u \n", h1 )); */ @@ -710,8 +723,8 @@ send_scan_param (pixma_t * s) pixma_set_be32 (s->param->y, data + 0x0c); pixma_set_be32 (raw_width, data + 0x10); pixma_set_be32 (h, data + 0x14); - data[0x18] = ((s->param->channels != 1) || is_ccd_grayscale (s)) ? 0x08 : 0x04; - data[0x19] = s->param->depth * ((is_ccd_grayscale (s)) ? 3 : s->param->channels); /* bits per pixel */ + data[0x18] = ((s->param->channels != 1) || is_ccd_grayscale (s) || is_ccd_lineart (s)) ? 0x08 : 0x04; + data[0x19] = s->param->depth * ((is_ccd_grayscale (s) || is_ccd_lineart (s)) ? 3 : s->param->channels); /* bits per pixel */ data[0x1a] = (is_scanning_from_tpu (s) ? 1 : 0); data[0x20] = 0xff; data[0x23] = 0x81; @@ -752,13 +765,14 @@ send_scan_param (pixma_t * s) pixma_set_be32 (s->param->y, data + 0x10); pixma_set_be32 (raw_width, data + 0x14); pixma_set_be32 (h, data + 0x18); - data[0x1c] = ((s->param->channels != 1) || is_ccd_grayscale (s)) ? 0x08 : 0x04; + data[0x1c] = ((s->param->channels != 1) || is_ccd_grayscale (s) || is_ccd_lineart (s)) ? 0x08 : 0x04; #ifdef DEBUG_TPU_48 data[0x1d] = 24; #else - data[0x1d] = (is_scanning_from_tpu (s)) ? 48 : - s->param->depth * ((is_ccd_grayscale (s)) ? 3 : s->param->channels); /* bits per pixel */ + data[0x1d] = (is_scanning_from_tpu (s)) ? 48 + : (((s->param->depth == 1) ? 8 : s->param->depth) + * ((is_ccd_grayscale (s) || is_ccd_lineart (s)) ? 3 : s->param->channels)); /* bits per pixel */ #endif data[0x1f] = 0x01; /* for 9000F this appears to be 0x00, not sure if that is because of positives */ @@ -1080,26 +1094,6 @@ shift_colorsCS9000 (uint8_t * dptr, uint8_t * sptr, return dptr; } -static uint8_t * -rgb_to_gray (uint8_t * gptr, uint8_t * sptr, unsigned w, unsigned c) -{ - unsigned i, j, g; - - for (i = 0; i < w; i++) - { - for (j = 0, g = 0; j < 3; j++) - { - g += *sptr++; - if (c == 6) g += (*sptr++ << 8); - } - - g /= 3; - *gptr++ = g; - if (c == 6) *gptr++ = (g >> 8); - } - return gptr; -} - /* under some conditions some scanners have sub images in one line */ /* e.g. doubled image, line size = 8 */ /* line before reordering: px1 px3 px5 px7 px2 px4 px6 px8 */ @@ -1292,7 +1286,8 @@ post_process_image_data (pixma_t * s, pixma_imagebuf_t * ib) test=0; jumplines=0; - c = ((is_ccd_grayscale (s)) ? 3 : s->param->channels) * s->param->depth / 8; + c = ((is_ccd_grayscale (s) || is_ccd_lineart (s)) ? 3 : s->param->channels) + * ((s->param->depth == 1) ? 8 : s->param->depth) / 8; cw = c * s->param->w; cx = c * s->param->xs; @@ -1440,9 +1435,12 @@ post_process_image_data (pixma_t * s, pixma_imagebuf_t * ib) memmove(cptr, sptr + cx, cw); /* PDBG (pixma_dbg (4, "*post_process_image_data***** crop line: cx=%u, cw=%u ***** \n", cx, cw)); */ + /* Color to Lineart convert for CCD sensor */ + if (is_ccd_lineart (s)) + cptr = gptr = pixma_binarize_line (s->param, gptr, cptr, s->param->w, c); /* Color to Grayscale convert for CCD sensor */ - if (is_ccd_grayscale (s)) - cptr = gptr = rgb_to_gray (gptr, cptr, s->param->w, c); + else if (is_ccd_grayscale (s)) + cptr = gptr = pixma_rgb_to_gray (gptr, cptr, s->param->w, c); else cptr += cw; } @@ -1533,14 +1531,42 @@ static int mp810_check_param (pixma_t * s, pixma_scan_param_t * sp) { mp810_t *mp = (mp810_t *) s->subdriver; + unsigned w_max; - sp->depth = 8; /* MP150 only supports 8 bit per channel. */ + /* PDBG (pixma_dbg (4, "*mp810_check_param***** Initially: channels=%u, depth=%u, x=%u, y=%u, w=%u, h=%u, xs=%u, wx=%u *****\n", + sp->channels, sp->depth, sp->x, sp->y, sp->w, sp->h, sp->xs, sp->wx)); */ + + /* MP810 only supports 8 bit per channel in color and grayscale mode */ + if (sp->depth != 1) + { + sp->software_lineart = 0; + sp->depth = 8; #ifdef TPU_48 #ifndef DEBUG_TPU_48 - if (sp->source == PIXMA_SOURCE_TPU) + if (sp->source == PIXMA_SOURCE_TPU) #endif - sp->depth = 16; /* TPU in 16 bits mode */ + sp->depth = 16; /* TPU in 16 bits mode */ #endif + } + else + { + /* software lineart */ + sp->software_lineart = 1; + sp->depth = 1; + sp->channels = 1; + } + + /* for software lineart w must be a multiple of 8 */ + if (sp->software_lineart == 1 && sp->w % 8) + { + sp->w += 8 - (sp->w % 8); + + /* do not exceed the scanner capability */ + w_max = s->cfg->width * s->cfg->xdpi / 75; + w_max -= w_max % 8; + if (sp->w > w_max) + sp->w = w_max; + } if (sp->source == PIXMA_SOURCE_TPU && !sp->tpu_offset_added) { @@ -1620,7 +1646,7 @@ mp810_check_param (pixma_t * s, pixma_scan_param_t * sp) /* PDBG (pixma_dbg (4, "*mp810_check_param***** (else) xs=0 Selected origin, origin shift: %u, %u *****\n", sp->x, sp->xs)); */ } sp->wx = calc_raw_width (mp, sp); - sp->line_size = sp->w * sp->channels * (sp->depth / 8); /* bytes per line per color after cropping */ + sp->line_size = sp->w * sp->channels * (((sp->depth == 1) ? 8 : sp->depth) / 8); /* bytes per line per color after cropping */ /* PDBG (pixma_dbg (4, "*mp810_check_param***** (else) Final scan width and line-size: %u, %"PRIu64" *****\n", sp->wx, sp->line_size)); */ if (sp->source == PIXMA_SOURCE_FLATBED) @@ -1674,6 +1700,9 @@ mp810_check_param (pixma_t * s, pixma_scan_param_t * sp) sp->ydpi = sp->xdpi; } + /* PDBG (pixma_dbg (4, "*mp810_check_param***** Finally: channels=%u, depth=%u, x=%u, y=%u, w=%u, h=%u, xs=%u, wx=%u *****\n", + sp->channels, sp->depth, sp->x, sp->y, sp->w, sp->h, sp->xs, sp->wx)); */ + return 0; } @@ -1971,6 +2000,7 @@ static const pixma_scan_ops_t pixma_mp810_ops = { ext_min_dpi, ext_max_dpi, /* ext_min_dpi, ext_max_dpi */ \ w, h, /* width, height */ \ PIXMA_CAP_EASY_RGB|PIXMA_CAP_GRAY| \ + PIXMA_CAP_LINEART| \ PIXMA_CAP_GAMMA_TABLE|PIXMA_CAP_EVENTS|cap \ } diff --git a/backend/pixma_sane_options.c b/backend/pixma_sane_options.c index 45051e671..eb4453381 100644 --- a/backend/pixma_sane_options.c +++ b/backend/pixma_sane_options.c @@ -1,7 +1,6 @@ /* Automatically generated from pixma_sane.c */ static const SANE_Range constraint_gamma_table = { 0, 255, 0 }; - static int find_string_in_list (SANE_String_Const str, const SANE_String_Const * list) { @@ -67,7 +66,7 @@ build_option_descriptors (struct pixma_sane_t *ss) sod->constraint_type = SANE_CONSTRAINT_STRING_LIST; sod->constraint.string_list = ss->mode_list; OPT_IN_CTX[opt_mode].info = SANE_INFO_RELOAD_PARAMS; - opt->def.s = SANE_VALUE_SCAN_MODE_COLOR; + opt->def.s = SANE_I18N (ss->mode_list[0]); opt->val.w = find_string_in_list (opt->def.s, sod->constraint.string_list); opt = &(OPT_IN_CTX[opt_source]); @@ -250,6 +249,29 @@ build_option_descriptors (struct pixma_sane_t *ss) opt->def.w = 0; opt->val.w = 0; + opt = &(OPT_IN_CTX[opt__group_5]); + sod = &opt->sod; + sod->type = SANE_TYPE_GROUP; + sod->title = SANE_I18N ("Extras"); + sod->desc = sod->title; + + opt = &(OPT_IN_CTX[opt_threshold]); + sod = &opt->sod; + sod->type = SANE_TYPE_INT; + sod->title = SANE_TITLE_THRESHOLD; + sod->desc = SANE_DESC_THRESHOLD; + sod->name = "threshold"; + sod->unit = SANE_UNIT_PERCENT; + sod->size = 1 * sizeof (SANE_Word); + sod->cap = + SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC | + SANE_CAP_INACTIVE; + sod->constraint_type = SANE_CONSTRAINT_NONE; + OPT_IN_CTX[opt_threshold].info = 0; + opt->def.w = 50; + opt->val.w = 50; + return 0; } + diff --git a/backend/pixma_sane_options.h b/backend/pixma_sane_options.h index 81ede7713..aca2e0842 100644 --- a/backend/pixma_sane_options.h +++ b/backend/pixma_sane_options.h @@ -30,6 +30,8 @@ typedef enum opt_button_update, opt_button_1, opt_button_2, + opt__group_5, + opt_threshold, opt_last } option_t;