kopia lustrzana https://gitlab.com/sane-project/backends
* backend/epjitsu.[ch]: backend v16:
- split fill_frontback_buffers_S300 into 3 functions - enable threshold_curve option - add 1-D dynamic binary thresholding code - remove y-resolution option - pad 225x200 data to 255x225merge-requests/1/head
rodzic
98de411d6c
commit
7274bfc500
|
@ -1,3 +1,11 @@
|
|||
2008-10-01 m. allan noah <kitno455 a t gmail d o t com>
|
||||
* backend/epjitsu.[ch]: backend v16:
|
||||
- split fill_frontback_buffers_S300 into 3 functions
|
||||
- enable threshold_curve option
|
||||
- add 1-D dynamic binary thresholding code
|
||||
- remove y-resolution option
|
||||
- pad 225x200 data to 255x225
|
||||
|
||||
2008-10-01 Gerhard Jaeger <gerhard@gjaeger.de>
|
||||
* doc/descriptions/plustek.desc: Added entry for UT12 devid 0x0013
|
||||
* doc/descriptions/unsupported.desc: Removed entry for UT12 devid 0x0013
|
||||
|
|
|
@ -109,6 +109,12 @@
|
|||
- support for automatic paper length detection (parm.lines = -1)
|
||||
v15 2008-09-24, MAN
|
||||
- expose hardware buttons/sensors as options for S300
|
||||
v16 2008-10-01, MAN
|
||||
- split fill_frontback_buffers_S300 into 3 functions
|
||||
- enable threshold_curve option
|
||||
- add 1-D dynamic binary thresholding code
|
||||
- remove y-resolution option
|
||||
- pad 225x200 data to 255x225
|
||||
|
||||
SANE FLOW DIAGRAM
|
||||
|
||||
|
@ -168,7 +174,7 @@
|
|||
#include "epjitsu-cmd.h"
|
||||
|
||||
#define DEBUG 1
|
||||
#define BUILD 15
|
||||
#define BUILD 16
|
||||
|
||||
unsigned char global_firmware_filename[PATH_MAX];
|
||||
|
||||
|
@ -467,7 +473,6 @@ attach_one (const char *name)
|
|||
s->source = SOURCE_ADF_FRONT;
|
||||
s->mode = MODE_LINEART;
|
||||
s->resolution_x = 300;
|
||||
s->resolution_y = 300;
|
||||
s->page_height = 11.5 * 1200;
|
||||
|
||||
s->threshold = 120;
|
||||
|
@ -490,7 +495,6 @@ attach_one (const char *name)
|
|||
s->source = SOURCE_FLATBED;
|
||||
s->mode = MODE_COLOR;
|
||||
s->resolution_x = 300;
|
||||
s->resolution_y = 300;
|
||||
s->page_height = 5.83 * 1200;
|
||||
|
||||
s->threshold = 120;
|
||||
|
@ -997,33 +1001,6 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
|
|||
opt->constraint.word_list = s->x_res_list;
|
||||
}
|
||||
|
||||
else if(option==OPT_Y_RES){
|
||||
i=0;
|
||||
if(s->y_res_150){
|
||||
s->y_res_list[++i] = 150;
|
||||
}
|
||||
if(s->y_res_225){
|
||||
s->y_res_list[++i] = 225;
|
||||
}
|
||||
if(s->y_res_300){
|
||||
s->y_res_list[++i] = 300;
|
||||
}
|
||||
if(s->y_res_600){
|
||||
s->y_res_list[++i] = 600;
|
||||
}
|
||||
s->y_res_list[0] = i;
|
||||
|
||||
opt->name = SANE_NAME_SCAN_Y_RESOLUTION;
|
||||
opt->title = SANE_TITLE_SCAN_Y_RESOLUTION;
|
||||
opt->desc = SANE_DESC_SCAN_Y_RESOLUTION;
|
||||
opt->type = SANE_TYPE_INT;
|
||||
opt->unit = SANE_UNIT_DPI;
|
||||
opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
||||
|
||||
opt->constraint_type = SANE_CONSTRAINT_WORD_LIST;
|
||||
opt->constraint.word_list = s->y_res_list;
|
||||
}
|
||||
|
||||
/* "Geometry" group ---------------------------------------------------- */
|
||||
if(option==OPT_GEOMETRY_GROUP){
|
||||
opt->name = SANE_NAME_GEOMETRY;
|
||||
|
@ -1252,7 +1229,7 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
|
|||
if(option==OPT_THRESHOLD_CURVE){
|
||||
opt->name = "threshold-curve";
|
||||
opt->title = "Threshold curve";
|
||||
opt->desc = "Dynamic hreshold curve, from light to dark, normally 50-65";
|
||||
opt->desc = "Dynamic threshold curve, from light to dark, normally 50-65";
|
||||
opt->type = SANE_TYPE_INT;
|
||||
opt->unit = SANE_UNIT_NONE;
|
||||
|
||||
|
@ -1266,7 +1243,6 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
|
|||
if(s->mode != MODE_LINEART){
|
||||
opt->cap |= SANE_CAP_INACTIVE;
|
||||
}
|
||||
opt->cap = SANE_CAP_INACTIVE;
|
||||
}
|
||||
|
||||
/* "Sensor" group ------------------------------------------------------ */
|
||||
|
@ -1276,6 +1252,10 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
|
|||
opt->desc = SANE_DESC_SENSORS;
|
||||
opt->type = SANE_TYPE_GROUP;
|
||||
opt->constraint_type = SANE_CONSTRAINT_NONE;
|
||||
|
||||
/*flaming hack to get scanimage to hide group*/
|
||||
if (!s->has_adf)
|
||||
opt->type = SANE_TYPE_BOOL;
|
||||
}
|
||||
|
||||
if(option==OPT_SCAN_SW){
|
||||
|
@ -1434,10 +1414,6 @@ sane_control_option (SANE_Handle handle, SANE_Int option,
|
|||
*val_p = s->resolution_x;
|
||||
return SANE_STATUS_GOOD;
|
||||
|
||||
case OPT_Y_RES:
|
||||
*val_p = s->resolution_y;
|
||||
return SANE_STATUS_GOOD;
|
||||
|
||||
case OPT_TL_X:
|
||||
*val_p = SCANNER_UNIT_TO_FIXED_MM(s->tl_x);
|
||||
return SANE_STATUS_GOOD;
|
||||
|
@ -1589,27 +1565,11 @@ sane_control_option (SANE_Handle handle, SANE_Int option,
|
|||
if (s->resolution_x == val_c)
|
||||
return SANE_STATUS_GOOD;
|
||||
|
||||
/* currently the same? move y too */
|
||||
if (s->resolution_x == s->resolution_y){
|
||||
s->resolution_y = val_c;
|
||||
/*sanei_constrain_value (s->opt + OPT_Y_RES, (void *) &val_c, 0) == SANE_STATUS_GOOD*/
|
||||
}
|
||||
|
||||
s->resolution_x = val_c;
|
||||
|
||||
*info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
|
||||
return change_params(s);
|
||||
|
||||
case OPT_Y_RES:
|
||||
|
||||
if (s->resolution_y == val_c)
|
||||
return SANE_STATUS_GOOD;
|
||||
|
||||
s->resolution_y = val_c;
|
||||
|
||||
*info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
|
||||
return change_params(s);
|
||||
|
||||
/* Geometry Group */
|
||||
case OPT_TL_X:
|
||||
if (s->tl_x == FIXED_MM_TO_SCANNER_UNIT(val_c))
|
||||
|
@ -1706,7 +1666,8 @@ void update_transfer_totals(struct transfer * t)
|
|||
/* we hard-code the list (determined from usb snoops) here */
|
||||
struct model_res {
|
||||
int model;
|
||||
int resolution;
|
||||
int x_res;
|
||||
int y_res;
|
||||
int usb_power;
|
||||
|
||||
int max_x;
|
||||
|
@ -1736,67 +1697,67 @@ struct model_res {
|
|||
struct model_res settings[] = {
|
||||
|
||||
/*S300 AC*/
|
||||
/* model res u mxx mnx mxy mny actw reqw hedw padw bh calw */
|
||||
{ MODEL_S300, 150, 0, 1296, 32, 2662, 32, 4256, 1480, 1296, 184, 41, 8512,
|
||||
/* model xres yres u mxx mnx mxy mny actw reqw hedw padw bh calw */
|
||||
{ MODEL_S300, 150, 150, 0, 1296, 32, 2662, 32, 4256, 1480, 1296, 184, 41, 8512,
|
||||
setWindowCoarseCal_S300_150, setWindowFineCal_S300_150,
|
||||
setWindowSendCal_S300_150, sendCal1Header_S300_150,
|
||||
sendCal2Header_S300_150, setWindowScan_S300_150 },
|
||||
|
||||
{ MODEL_S300, 225, 0, 1944, 32, 3993, 32, 6144, 2100, 1944, 156, 28, 8192,
|
||||
{ MODEL_S300, 225, 200, 0, 1944, 32, 3993, 32, 6144, 2100, 1944, 156, 28, 8192,
|
||||
setWindowCoarseCal_S300_225, setWindowFineCal_S300_225,
|
||||
setWindowSendCal_S300_225, sendCal1Header_S300_225,
|
||||
sendCal2Header_S300_225, setWindowScan_S300_225 },
|
||||
|
||||
{ MODEL_S300, 300, 0, 2592, 32, 5324, 32, 8192, 2800, 2592, 208, 21, 8192,
|
||||
{ MODEL_S300, 300, 300, 0, 2592, 32, 5324, 32, 8192, 2800, 2592, 208, 21, 8192,
|
||||
setWindowCoarseCal_S300_300, setWindowFineCal_S300_300,
|
||||
setWindowSendCal_S300_300, sendCal1Header_S300_300,
|
||||
sendCal2Header_S300_300, setWindowScan_S300_300 },
|
||||
|
||||
{ MODEL_S300, 600, 0, 5184, 32, 10648, 32, 16064, 5440, 5184, 256, 10, 16064,
|
||||
{ MODEL_S300, 600, 600, 0, 5184, 32, 10648, 32, 16064, 5440, 5184, 256, 10, 16064,
|
||||
setWindowCoarseCal_S300_600, setWindowFineCal_S300_600,
|
||||
setWindowSendCal_S300_600, sendCal1Header_S300_600,
|
||||
sendCal2Header_S300_600, setWindowScan_S300_600 },
|
||||
|
||||
/*S300 USB*/
|
||||
/* model res u mxx mnx mxy mny actw reqw hedw padw bh calw */
|
||||
{ MODEL_S300, 150, 1, 1296, 32, 2662, 32, 7216, 2960, 1296, 1664, 24, 14432,
|
||||
/* model xres yres u mxx mnx mxy mny actw reqw hedw padw bh calw */
|
||||
{ MODEL_S300, 150, 150, 1, 1296, 32, 2662, 32, 7216, 2960, 1296, 1664, 24, 14432,
|
||||
setWindowCoarseCal_S300_150_U, setWindowFineCal_S300_150_U,
|
||||
setWindowSendCal_S300_150_U, sendCal1Header_S300_150_U,
|
||||
sendCal2Header_S300_150_U, setWindowScan_S300_150_U },
|
||||
|
||||
{ MODEL_S300, 225, 1, 1944, 32, 3993, 32, 10584, 4320, 1944, 2376, 16, 14112,
|
||||
{ MODEL_S300, 225, 200, 1, 1944, 32, 3993, 32, 10584, 4320, 1944, 2376, 16, 14112,
|
||||
setWindowCoarseCal_S300_225_U, setWindowFineCal_S300_225_U,
|
||||
setWindowSendCal_S300_225_U, sendCal1Header_S300_225_U,
|
||||
sendCal2Header_S300_225_U, setWindowScan_S300_225_U },
|
||||
|
||||
{ MODEL_S300, 300, 1, 2592, 32, 5324, 32, 15872, 6640, 2592, 4048, 11, 15872,
|
||||
{ MODEL_S300, 300, 300, 1, 2592, 32, 5324, 32, 15872, 6640, 2592, 4048, 11, 15872,
|
||||
setWindowCoarseCal_S300_300_U, setWindowFineCal_S300_300_U,
|
||||
setWindowSendCal_S300_300_U, sendCal1Header_S300_300_U,
|
||||
sendCal2Header_S300_300_U, setWindowScan_S300_300_U },
|
||||
|
||||
{ MODEL_S300, 600, 1, 5184, 32, 10648, 32, 16064, 5440, 5184, 256, 10, 16064,
|
||||
{ MODEL_S300, 600, 600, 1, 5184, 32, 10648, 32, 16064, 5440, 5184, 256, 10, 16064,
|
||||
setWindowCoarseCal_S300_600, setWindowFineCal_S300_600,
|
||||
setWindowSendCal_S300_600, sendCal1Header_S300_600,
|
||||
sendCal2Header_S300_600, setWindowScan_S300_600 },
|
||||
|
||||
/*fi-60F*/
|
||||
/* model res u mxx mnx mxy mny actw reqw hedw padw bh calw */
|
||||
{ MODEL_FI60F, 150, 0, 600, 32, 875, 32, 1480, 1480, 216, 1114, 41, 1480,
|
||||
/* model xres yres u mxx mnx mxy mny actw reqw hedw padw bh calw */
|
||||
{ MODEL_FI60F, 150, 150, 0, 600, 32, 875, 32, 1480, 1480, 216, 1114, 41, 1480,
|
||||
setWindowCoarseCal_FI60F_150, setWindowFineCal_FI60F_150,
|
||||
setWindowSendCal_FI60F_150, sendCal1Header_FI60F_150,
|
||||
sendCal2Header_FI60F_150, setWindowScan_FI60F_150 },
|
||||
|
||||
{ MODEL_FI60F, 300, 0, 1296, 32, 1749, 32, 2400, 2400, 432, 526, 72, 2400,
|
||||
{ MODEL_FI60F, 300, 300, 0, 1296, 32, 1749, 32, 2400, 2400, 432, 526, 72, 2400,
|
||||
setWindowCoarseCal_FI60F_300, setWindowFineCal_FI60F_300,
|
||||
setWindowSendCal_FI60F_300, sendCal1Header_FI60F_300,
|
||||
sendCal2Header_FI60F_300, setWindowScan_FI60F_300 },
|
||||
|
||||
{ MODEL_FI60F, 600, 0, 2592, 32, 3498, 32, 2848, 2848, 864, 114, 61, 2848,
|
||||
{ MODEL_FI60F, 600, 600, 0, 2592, 32, 3498, 32, 2848, 2848, 864, 114, 61, 2848,
|
||||
setWindowCoarseCal_FI60F_600, setWindowFineCal_FI60F_600,
|
||||
setWindowSendCal_FI60F_600, sendCal1Header_FI60F_600,
|
||||
sendCal2Header_FI60F_600, setWindowScan_FI60F_600 },
|
||||
|
||||
{ MODEL_NONE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
{ MODEL_NONE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL },
|
||||
|
||||
};
|
||||
|
@ -1815,9 +1776,12 @@ change_params(struct scanner *s)
|
|||
|
||||
do {
|
||||
if(settings[i].model == s->model
|
||||
&& settings[i].resolution == s->resolution_x
|
||||
&& settings[i].x_res == s->resolution_x
|
||||
&& settings[i].usb_power == s->usb_power){
|
||||
|
||||
/*pull in closest y resolution*/
|
||||
s->resolution_y = settings[i].y_res;
|
||||
|
||||
/*1200 dpi*/
|
||||
s->max_x = settings[i].max_x * 1200/s->resolution_x;
|
||||
s->min_x = settings[i].min_x * 1200/s->resolution_x;
|
||||
|
@ -1917,23 +1881,111 @@ change_params(struct scanner *s)
|
|||
s->front.width_bytes = s->front.width_pix/8;
|
||||
break;
|
||||
}
|
||||
s->front.height = s->scan.height;
|
||||
/*output image might be taller than scan due to interpolation*/
|
||||
s->front.height = s->scan.height * s->resolution_x / s->resolution_y;
|
||||
update_transfer_totals(&s->front);
|
||||
|
||||
/* dynamic threshold temp buffer, in gray */
|
||||
s->dt.width_pix = s->front.width_pix;
|
||||
s->dt.width_bytes = s->front.width_pix;
|
||||
s->dt.height = 1;
|
||||
update_transfer_totals(&s->dt);
|
||||
|
||||
/* back settings always same as front settings */
|
||||
if(s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_ADF_BACK){
|
||||
s->back.height = s->front.height;
|
||||
s->back.width_pix = s->front.width_pix;
|
||||
s->back.width_bytes = s->front.width_bytes;
|
||||
s->back.total_pix = s->front.total_pix;
|
||||
s->back.total_bytes = s->front.total_bytes;
|
||||
}
|
||||
update_transfer_totals(&s->back);
|
||||
|
||||
DBG (10, "change_params: finish\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Function to build a lookup table (LUT), often
|
||||
used by scanners to implement brightness/contrast/gamma
|
||||
or by backends to speed binarization/thresholding
|
||||
|
||||
offset and slope inputs are -127 to +127
|
||||
|
||||
slope rotates line around central input/output val,
|
||||
0 makes horizontal line
|
||||
|
||||
pos zero neg
|
||||
. x . . x
|
||||
. x . . x
|
||||
out . x .xxxxxxxxxxx . x
|
||||
. x . . x
|
||||
....x....... ............ .......x....
|
||||
in in in
|
||||
|
||||
offset moves line vertically, and clamps to output range
|
||||
0 keeps the line crossing the center of the table
|
||||
|
||||
high low
|
||||
. xxxxxxxx .
|
||||
. x .
|
||||
out x . x
|
||||
. . x
|
||||
............ xxxxxxxx....
|
||||
in in
|
||||
|
||||
out_min/max provide bounds on output values,
|
||||
useful when building thresholding lut.
|
||||
0 and 255 are good defaults otherwise.
|
||||
*/
|
||||
static SANE_Status
|
||||
load_lut (unsigned char * lut,
|
||||
int in_bits, int out_bits,
|
||||
int out_min, int out_max,
|
||||
int slope, int offset)
|
||||
{
|
||||
SANE_Status ret = SANE_STATUS_GOOD;
|
||||
int i, j;
|
||||
double shift, rise;
|
||||
int max_in_val = (1 << in_bits) - 1;
|
||||
int max_out_val = (1 << out_bits) - 1;
|
||||
unsigned char * lut_p = lut;
|
||||
|
||||
DBG (10, "load_lut: start\n");
|
||||
|
||||
/* slope is converted to rise per unit run:
|
||||
* first [-127,127] to [-1,1]
|
||||
* then multiply by PI/2 to convert to radians
|
||||
* then take the tangent (T.O.A)
|
||||
* then multiply by the normal linear slope
|
||||
* because the table may not be square, i.e. 1024x256*/
|
||||
rise = tan((double)slope/127 * M_PI/2) * max_out_val / max_in_val;
|
||||
|
||||
/* line must stay vertically centered, so figure
|
||||
* out vertical offset at central input value */
|
||||
shift = (double)max_out_val/2 - (rise*max_in_val/2);
|
||||
|
||||
/* convert the user offset setting to scale of output
|
||||
* first [-127,127] to [-1,1]
|
||||
* then to [-max_out_val/2,max_out_val/2]*/
|
||||
shift += (double)offset / 127 * max_out_val / 2;
|
||||
|
||||
for(i=0;i<=max_in_val;i++){
|
||||
j = rise*i + shift;
|
||||
|
||||
if(j<out_min){
|
||||
j=out_min;
|
||||
}
|
||||
else if(j>out_max){
|
||||
j=out_max;
|
||||
}
|
||||
|
||||
*lut_p=j;
|
||||
lut_p++;
|
||||
}
|
||||
|
||||
hexdump(5, "load_lut: ", lut, max_in_val+1);
|
||||
|
||||
DBG (10, "load_lut: finish\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* @@ Section 4 - SANE scanning functions
|
||||
*/
|
||||
|
@ -1968,7 +2020,7 @@ sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
|
|||
params->lines = -1;
|
||||
}
|
||||
else{
|
||||
params->lines = s->scan.height;
|
||||
params->lines = s->front.height;
|
||||
}
|
||||
params->last_frame = 1;
|
||||
|
||||
|
@ -2060,6 +2112,14 @@ sane_start (SANE_Handle handle)
|
|||
return SANE_STATUS_NO_MEM;
|
||||
}
|
||||
|
||||
ret = load_lut(s->dt_lut, 8, 8, 50, 205,
|
||||
s->threshold_curve, s->threshold-127);
|
||||
if (ret != SANE_STATUS_GOOD) {
|
||||
DBG (5, "sane_start: ERROR: failed to load_lut for dt\n");
|
||||
sane_cancel((SANE_Handle)s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = coarsecal(s);
|
||||
if (ret != SANE_STATUS_GOOD) {
|
||||
DBG (5, "sane_start: ERROR: failed to coarsecal\n");
|
||||
|
@ -2171,8 +2231,16 @@ setup_buffers(struct scanner *s)
|
|||
return SANE_STATUS_NO_MEM;
|
||||
}
|
||||
|
||||
/* one grayscale line for dynamic threshold */
|
||||
s->dt.buffer = calloc (1,s->dt.total_bytes);
|
||||
if(!s->dt.buffer){
|
||||
DBG (5, "setup_buffers: ERROR: failed to setup dt buffer\n");
|
||||
return SANE_STATUS_NO_MEM;
|
||||
}
|
||||
|
||||
/* make image buffer to hold frontside data */
|
||||
if(s->source != SOURCE_ADF_BACK){
|
||||
|
||||
s->front.buffer = calloc (1,s->front.total_bytes+8);
|
||||
if(!s->front.buffer){
|
||||
DBG (5, "setup_buffers: ERROR: failed to setup front buffer\n");
|
||||
|
@ -2182,6 +2250,7 @@ setup_buffers(struct scanner *s)
|
|||
|
||||
/* make image buffer to hold backside data */
|
||||
if(s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_ADF_BACK){
|
||||
|
||||
s->back.buffer = calloc (1,s->back.total_bytes+8);
|
||||
if(!s->back.buffer){
|
||||
DBG (5, "setup_buffers: ERROR: failed to setup back buffer\n");
|
||||
|
@ -2240,7 +2309,11 @@ coarsecal(struct scanner *s)
|
|||
}
|
||||
|
||||
/* dark cal, lamp off */
|
||||
lamp(s,0);
|
||||
ret = lamp(s,0);
|
||||
if(ret){
|
||||
DBG (5, "coarsecal: error lamp off\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
while(try++ < 1){
|
||||
|
||||
|
@ -2332,7 +2405,11 @@ coarsecal(struct scanner *s)
|
|||
}
|
||||
|
||||
/* light cal, lamp on */
|
||||
lamp(s,1);
|
||||
ret = lamp(s,1);
|
||||
if(ret){
|
||||
DBG (5, "coarsecal: error lamp on\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
try = 0;
|
||||
direction = -1;
|
||||
|
@ -2453,7 +2530,11 @@ finecal(struct scanner *s)
|
|||
}
|
||||
|
||||
/* dark cal, lamp off */
|
||||
lamp(s,0);
|
||||
ret = lamp(s,0);
|
||||
if(ret){
|
||||
DBG (5, "finecal: error lamp off\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* send scan d2 command */
|
||||
cmd[0] = 0x1b;
|
||||
|
@ -2489,7 +2570,11 @@ finecal(struct scanner *s)
|
|||
s->darkcal.done = 0;
|
||||
|
||||
/* grab rows with lamp on */
|
||||
lamp(s,1);
|
||||
ret = lamp(s,1);
|
||||
if(ret){
|
||||
DBG (5, "finecal: error lamp on\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* send scan d2 command */
|
||||
cmd[0] = 0x1b;
|
||||
|
@ -3194,6 +3279,7 @@ sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len
|
|||
}
|
||||
}
|
||||
|
||||
/*while(!s->block.done){*/
|
||||
ret = read_from_scanner(s, &s->block);
|
||||
if(ret){
|
||||
DBG (5, "sane_read: cant read from scanner\n");
|
||||
|
@ -3228,7 +3314,43 @@ sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = fill_frontback_buffers_S300(s);
|
||||
/*copy backside data into buffer*/
|
||||
if( s->source == SOURCE_ADF_DUPLEX
|
||||
|| s->source == SOURCE_ADF_BACK ){
|
||||
|
||||
switch (s->mode) {
|
||||
case MODE_COLOR:
|
||||
ret = copy_S300_color(s,SIDE_BACK);
|
||||
break;
|
||||
|
||||
case MODE_GRAYSCALE:
|
||||
ret = copy_S300_gray(s,SIDE_BACK);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = copy_S300_binary(s,SIDE_BACK);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*copy frontside data into buffer*/
|
||||
if( s->source != SOURCE_ADF_BACK ){
|
||||
|
||||
switch (s->mode) {
|
||||
case MODE_COLOR:
|
||||
ret = copy_S300_color(s,SIDE_FRONT);
|
||||
break;
|
||||
|
||||
case MODE_GRAYSCALE:
|
||||
ret = copy_S300_gray(s,SIDE_FRONT);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = copy_S300_binary(s,SIDE_FRONT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(ret){
|
||||
DBG (5, "sane_read: cant copy to front/back\n");
|
||||
return ret;
|
||||
|
@ -3346,152 +3468,286 @@ read_from_scanner(struct scanner *s, struct transfer * tp)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* copies block buffer into front and back buffers */
|
||||
/* moves front/back rx_bytes forward */
|
||||
/* copies block buffer into front or back buffer */
|
||||
/* moves rx_bytes forward */
|
||||
static SANE_Status
|
||||
fill_frontback_buffers_S300(struct scanner *s)
|
||||
copy_S300_color(struct scanner *s,int side)
|
||||
{
|
||||
SANE_Status ret=SANE_STATUS_GOOD;
|
||||
struct transfer * tp;
|
||||
int i,j;
|
||||
int thresh = s->threshold * 3;
|
||||
|
||||
DBG (10, "fill_frontback_buffers_S300: start\n");
|
||||
DBG (10, "copy_S300_color: start\n");
|
||||
|
||||
switch (s->mode) {
|
||||
case MODE_COLOR:
|
||||
/* put frontside data into buffer */
|
||||
if(s->source != SOURCE_ADF_BACK){
|
||||
/*front side data*/
|
||||
if(side == SIDE_FRONT){
|
||||
tp = &s->front;
|
||||
|
||||
for(i=0; i<s->block.rx_bytes-8; i+=s->block.width_bytes){
|
||||
for(j=0; j<s->front.width_pix; j++){
|
||||
|
||||
int lineStart = tp->rx_bytes;
|
||||
|
||||
for(j=0; j<tp->width_pix; j++){
|
||||
|
||||
/*red*/
|
||||
s->front.buffer[s->front.rx_bytes++] =
|
||||
s->block.buffer[i + s->req_width*3 + j*3];
|
||||
tp->buffer[tp->rx_bytes++]
|
||||
= s->block.buffer[i + s->req_width*3 + j*3];
|
||||
|
||||
/*green*/
|
||||
s->front.buffer[s->front.rx_bytes++] =
|
||||
s->block.buffer[i + s->req_width*6 + j*3];
|
||||
tp->buffer[tp->rx_bytes++]
|
||||
= s->block.buffer[i + s->req_width*6 + j*3];
|
||||
|
||||
/*blue*/
|
||||
s->front.buffer[s->front.rx_bytes++] =
|
||||
s->block.buffer[i + j*3];
|
||||
tp->buffer[tp->rx_bytes++]
|
||||
= s->block.buffer[i + j*3];
|
||||
}
|
||||
|
||||
/*add a periodic row because of non-square pixels*/
|
||||
/*FIXME: only works with 225x200*/
|
||||
if(s->resolution_x > s->resolution_y
|
||||
&& tp->rx_bytes != tp->total_bytes
|
||||
&& tp->rx_bytes/tp->width_bytes % 9 == 8
|
||||
){
|
||||
memcpy(tp->buffer+tp->rx_bytes,tp->buffer+lineStart,tp->width_bytes);
|
||||
tp->rx_bytes+=tp->width_bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* put backside data into buffer */
|
||||
if(s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_ADF_BACK){
|
||||
/*back side data*/
|
||||
else{
|
||||
tp = &s->back;
|
||||
|
||||
for(i=0; i<s->block.rx_bytes-8; i+=s->block.width_bytes){
|
||||
for(j=0; j<s->back.width_pix; j++){
|
||||
|
||||
int lineStart = tp->rx_bytes;
|
||||
|
||||
for(j=0; j<tp->width_pix; j++){
|
||||
|
||||
/*red*/
|
||||
s->back.buffer[s->back.rx_bytes++] =
|
||||
s->block.buffer[i + s->req_width*3 + (s->back.width_pix-1)*3 - j*3 + 1];
|
||||
tp->buffer[tp->rx_bytes++] =
|
||||
s->block.buffer[i + s->req_width*3 + (tp->width_pix-1)*3 - j*3 + 1];
|
||||
|
||||
/*green*/
|
||||
s->back.buffer[s->back.rx_bytes++] =
|
||||
s->block.buffer[i + s->req_width*6 + (s->back.width_pix-1)*3 - j*3 + 1];
|
||||
tp->buffer[tp->rx_bytes++] =
|
||||
s->block.buffer[i + s->req_width*6 + (tp->width_pix-1)*3 - j*3 + 1];
|
||||
|
||||
/*blue*/
|
||||
s->back.buffer[s->back.rx_bytes++] =
|
||||
s->block.buffer[i + (s->back.width_pix-1)*3 - j*3 + 1];
|
||||
tp->buffer[tp->rx_bytes++] =
|
||||
s->block.buffer[i + (tp->width_pix-1)*3 - j*3 + 1];
|
||||
}
|
||||
|
||||
/*add a periodic row because of non-square pixels*/
|
||||
/*FIXME: only works with 225x200*/
|
||||
if(s->resolution_x > s->resolution_y
|
||||
&& tp->rx_bytes != tp->total_bytes
|
||||
&& tp->rx_bytes/tp->width_bytes % 9 == 8
|
||||
){
|
||||
memcpy(tp->buffer+tp->rx_bytes,tp->buffer+lineStart,tp->width_bytes);
|
||||
tp->rx_bytes+=tp->width_bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
DBG (10, "copy_S300_color: finish\n");
|
||||
|
||||
case MODE_GRAYSCALE:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SANE_Status
|
||||
copy_S300_gray(struct scanner *s,int side)
|
||||
{
|
||||
SANE_Status ret=SANE_STATUS_GOOD;
|
||||
struct transfer * tp;
|
||||
int i,j;
|
||||
|
||||
DBG (10, "copy_S300_gray: start\n");
|
||||
|
||||
/*front side data*/
|
||||
if(side == SIDE_FRONT){
|
||||
tp = &s->front;
|
||||
|
||||
/* put frontside data into buffer */
|
||||
if(s->source != SOURCE_ADF_BACK){
|
||||
for(i=0; i<s->block.rx_bytes-8; i+=s->block.width_bytes){
|
||||
for(j=0; j<s->front.width_pix; j++){
|
||||
|
||||
/* GS is (red+green+blue)/3 */
|
||||
s->front.buffer[s->front.rx_bytes++] =
|
||||
int lineStart = tp->rx_bytes;
|
||||
|
||||
for(j=0; j<tp->width_pix; j++){
|
||||
|
||||
tp->buffer[tp->rx_bytes++] =
|
||||
|
||||
/*red*/
|
||||
( s->block.buffer[i + s->req_width*3 + j*3]
|
||||
|
||||
/*green*/
|
||||
+ s->block.buffer[i + s->req_width*6 + j*3]
|
||||
|
||||
/*blue*/
|
||||
+ s->block.buffer[i + j*3]) / 3;
|
||||
}
|
||||
|
||||
/*add a periodic row because of non-square pixels*/
|
||||
/*FIXME: only works with 225x200*/
|
||||
if(s->resolution_x > s->resolution_y
|
||||
&& tp->rx_bytes != tp->total_bytes
|
||||
&& tp->rx_bytes/tp->width_bytes % 9 == 8
|
||||
){
|
||||
memcpy(tp->buffer+tp->rx_bytes,tp->buffer+lineStart,tp->width_bytes);
|
||||
tp->rx_bytes+=tp->width_bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*back side data*/
|
||||
else{
|
||||
tp = &s->back;
|
||||
|
||||
for(i=0; i<s->block.rx_bytes-8; i+=s->block.width_bytes){
|
||||
|
||||
int lineStart = tp->rx_bytes;
|
||||
|
||||
for(j=0; j<tp->width_pix; j++){
|
||||
|
||||
tp->buffer[tp->rx_bytes++] =
|
||||
/*red*/
|
||||
(s->block.buffer[i + s->req_width*3 + (tp->width_pix-1)*3 - j*3 + 1]
|
||||
|
||||
/*green*/
|
||||
+s->block.buffer[i + s->req_width*6 + (tp->width_pix-1)*3 - j*3 + 1]
|
||||
|
||||
/*blue*/
|
||||
+s->block.buffer[i + (tp->width_pix-1)*3 - j*3 + 1]) / 3;
|
||||
}
|
||||
|
||||
/*add a periodic row because of non-square pixels*/
|
||||
/*FIXME: only works with 225x200*/
|
||||
if(s->resolution_x > s->resolution_y
|
||||
&& tp->rx_bytes != tp->total_bytes
|
||||
&& tp->rx_bytes/tp->width_bytes % 9 == 8
|
||||
){
|
||||
memcpy(tp->buffer+tp->rx_bytes,tp->buffer+lineStart,tp->width_bytes);
|
||||
tp->rx_bytes+=tp->width_bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DBG (10, "copy_S300_gray: finish\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*uses the threshold/threshold_curve to control binarization*/
|
||||
static SANE_Status
|
||||
copy_S300_binary(struct scanner *s,int side)
|
||||
{
|
||||
SANE_Status ret=SANE_STATUS_GOOD;
|
||||
struct transfer * tp;
|
||||
int i,j,windowX;
|
||||
|
||||
DBG (10, "copy_S300_binary: start\n");
|
||||
|
||||
/* ~1mm works best, but the window needs to have odd # of pixels */
|
||||
windowX = 6 * s->resolution_x/150;
|
||||
if(!(windowX % 2)){
|
||||
windowX++;
|
||||
}
|
||||
|
||||
for(i=0; i<s->block.rx_bytes-8; i+=s->block.width_bytes){
|
||||
|
||||
int sum = 0;
|
||||
int lineStart = 0;
|
||||
|
||||
/*first, load a gray line into the dt buffer*/
|
||||
if(side == SIDE_FRONT){
|
||||
tp = &s->front;
|
||||
|
||||
for(j=0; j<tp->width_pix; j++){
|
||||
|
||||
s->dt.buffer[j] =
|
||||
|
||||
/*red*/
|
||||
( s->block.buffer[i + s->req_width*3 + j*3]
|
||||
|
||||
/*green*/
|
||||
+ s->block.buffer[i + s->req_width*6 + j*3]
|
||||
|
||||
/*blue*/
|
||||
+ s->block.buffer[i + j*3]) / 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
tp = &s->back;
|
||||
|
||||
/* put backside data into buffer */
|
||||
if(s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_ADF_BACK){
|
||||
for(i=0; i<s->block.rx_bytes-8; i+=s->block.width_bytes){
|
||||
for(j=0; j<s->back.width_pix; j++){
|
||||
for(j=0; j<tp->width_pix; j++){
|
||||
|
||||
/* GS is (red+green+blue)/3 */
|
||||
s->back.buffer[s->back.rx_bytes++] =
|
||||
( (int)s->block.buffer[i + s->req_width*3 + (s->back.width_pix-1)*3 - j*3 + 1]
|
||||
+ s->block.buffer[i + s->req_width*6 + (s->back.width_pix-1)*3 - j*3 + 1]
|
||||
+ s->block.buffer[i + (s->back.width_pix-1)*3 - j*3 + 1]) / 3;
|
||||
s->dt.buffer[j] =
|
||||
|
||||
/*red*/
|
||||
(s->block.buffer[i + s->req_width*3 + (tp->width_pix-1)*3 - j*3 + 1]
|
||||
|
||||
/*green*/
|
||||
+s->block.buffer[i + s->req_width*6 + (tp->width_pix-1)*3 - j*3 + 1]
|
||||
|
||||
/*blue*/
|
||||
+s->block.buffer[i + (tp->width_pix-1)*3 - j*3 + 1]) / 3;
|
||||
}
|
||||
}
|
||||
|
||||
/*second, prefill the sliding sum*/
|
||||
for(j=0; j<windowX; j++){
|
||||
sum += s->dt.buffer[j];
|
||||
}
|
||||
break;
|
||||
|
||||
default: /* binary */
|
||||
lineStart = tp->rx_bytes;
|
||||
|
||||
/* put frontside data into buffer */
|
||||
if(s->source != SOURCE_ADF_BACK){
|
||||
|
||||
for(i=0; i<s->block.rx_bytes-8; i+=s->block.width_bytes){
|
||||
for(j=0; j<s->front.width_pix; j++){
|
||||
/* third, walk the dt buffer, update the sliding sum, */
|
||||
/* determine threshold, output bits */
|
||||
for(j=0; j<tp->width_pix; j++){
|
||||
|
||||
/*output image location*/
|
||||
int offset = j%8;
|
||||
unsigned char mask = 0x80 >> offset;
|
||||
int curr = s->block.buffer[i+j*3] + /*blue*/
|
||||
s->block.buffer[i+(s->req_width+j)*3] + /*green*/
|
||||
s->block.buffer[i+(s->req_width*2+j)*3]; /*red*/
|
||||
int thresh = s->threshold;
|
||||
|
||||
/* looks white */
|
||||
if(curr > thresh){
|
||||
s->front.buffer[s->front.rx_bytes] &= ~mask;
|
||||
/* move sum/update threshold only if there is a curve*/
|
||||
if(s->threshold_curve){
|
||||
int addCol = j + windowX/2;
|
||||
int dropCol = addCol - windowX;
|
||||
|
||||
if(dropCol >= 0 && addCol < tp->width_pix){
|
||||
sum -= s->dt.buffer[dropCol];
|
||||
sum += s->dt.buffer[addCol];
|
||||
}
|
||||
|
||||
thresh = s->dt_lut[sum/windowX];
|
||||
}
|
||||
|
||||
/*use average to lookup threshold*/
|
||||
/* white */
|
||||
if(s->dt.buffer[j] > thresh){
|
||||
tp->buffer[tp->rx_bytes] &= ~mask;
|
||||
}
|
||||
/* black */
|
||||
else{
|
||||
s->front.buffer[s->front.rx_bytes] |= mask;
|
||||
tp->buffer[tp->rx_bytes] |= mask;
|
||||
}
|
||||
|
||||
if(offset == 7){
|
||||
s->front.rx_bytes++;
|
||||
}
|
||||
}
|
||||
tp->rx_bytes++;
|
||||
}
|
||||
}
|
||||
|
||||
/* put backside data into buffer */
|
||||
if(s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_ADF_BACK){
|
||||
|
||||
for(i=0; i<s->block.rx_bytes-8; i+=s->block.width_bytes){
|
||||
for(j=0; j<s->back.width_pix; j++){
|
||||
|
||||
int offset = j%8;
|
||||
unsigned char mask = 0x80 >> offset;
|
||||
int curr = s->block.buffer[i+(s->back.width_pix-1-j)*3+1] +
|
||||
s->block.buffer[i+(s->req_width+s->back.width_pix-1-j)*3+1] +
|
||||
s->block.buffer[i+(s->req_width*2+s->back.width_pix-1-j)*3+1];
|
||||
|
||||
/* looks white */
|
||||
if(curr > thresh){
|
||||
s->back.buffer[s->back.rx_bytes] &= ~mask;
|
||||
/*add a periodic row because of non-square pixels*/
|
||||
/*FIXME: only works with 225x200*/
|
||||
if(s->resolution_x > s->resolution_y
|
||||
&& tp->rx_bytes != tp->total_bytes
|
||||
&& tp->rx_bytes/tp->width_bytes % 9 == 8
|
||||
){
|
||||
memcpy(tp->buffer+tp->rx_bytes,tp->buffer+lineStart,tp->width_bytes);
|
||||
tp->rx_bytes+=tp->width_bytes;
|
||||
}
|
||||
else{
|
||||
s->back.buffer[s->back.rx_bytes] |= mask;
|
||||
}
|
||||
|
||||
if(offset == 7){
|
||||
s->back.rx_bytes++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
DBG (10, "fill_frontback_buffers_S300: finish\n");
|
||||
DBG (10, "copy_S300_binary: finish\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -3725,13 +3981,19 @@ teardown_buffers(struct scanner *s)
|
|||
s->block.buffer = NULL;
|
||||
}
|
||||
|
||||
/* make image buffer to hold frontside data */
|
||||
/* dynamic thresh slice */
|
||||
if(s->dt.buffer){
|
||||
free(s->dt.buffer);
|
||||
s->dt.buffer = NULL;
|
||||
}
|
||||
|
||||
/* image buffer to hold frontside data */
|
||||
if(s->front.buffer){
|
||||
free(s->front.buffer);
|
||||
s->front.buffer = NULL;
|
||||
}
|
||||
|
||||
/* make image buffer to hold backside data */
|
||||
/* image buffer to hold backside data */
|
||||
if(s->back.buffer){
|
||||
free(s->back.buffer);
|
||||
s->back.buffer = NULL;
|
||||
|
|
|
@ -213,6 +213,10 @@ struct scanner
|
|||
/* the block struct holds the most recent buffer */
|
||||
struct transfer block;
|
||||
|
||||
/* temporary buffers used by dynamic threshold code */
|
||||
struct transfer dt;
|
||||
unsigned char dt_lut[256];
|
||||
|
||||
/* final-sized front image, always used */
|
||||
struct transfer front;
|
||||
|
||||
|
@ -352,11 +356,16 @@ static SANE_Status set_window(struct scanner *s, int window);
|
|||
static SANE_Status scan(struct scanner *s);
|
||||
|
||||
static SANE_Status read_from_scanner(struct scanner *s, struct transfer *tp);
|
||||
static SANE_Status fill_frontback_buffers_S300(struct scanner *s);
|
||||
static SANE_Status copy_S300_color(struct scanner *s, int side);
|
||||
static SANE_Status copy_S300_gray(struct scanner *s, int side);
|
||||
static SANE_Status copy_S300_binary(struct scanner *s, int side);
|
||||
static SANE_Status fill_frontback_buffers_FI60F(struct scanner *s);
|
||||
|
||||
static SANE_Status get_hardware_status (struct scanner *s);
|
||||
|
||||
static SANE_Status load_lut (unsigned char * lut, int in_bits, int out_bits,
|
||||
int out_min, int out_max, int slope, int offset);
|
||||
|
||||
int get_page_width (struct scanner *s);
|
||||
int get_page_height (struct scanner *s);
|
||||
unsigned char get_stat(struct scanner *s);
|
||||
|
|
Ładowanie…
Reference in New Issue