1 /* sane - Scanner Access Now Easy.
2
3 Copyright (C) 2003, 2004 Henning Meier-Geinitz <henning@meier-geinitz.de>
4 Copyright (C) 2004, 2005 Gerhard Jaeger <gerhard@gjaeger.de>
5 Copyright (C) 2004-2016 Stéphane Voltz <stef.dev@free.fr>
6 Copyright (C) 2005-2009 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
7 Copyright (C) 2006 Laurent Charpentier <laurent_pubs@yahoo.com>
8 Copyright (C) 2007 Luke <iceyfor@gmail.com>
9 Copyright (C) 2010 Chris Berry <s0457957@sms.ed.ac.uk> and Michael Rickmann <mrickma@gwdg.de>
10 for Plustek Opticbook 3600 support
11
12 Dynamic rasterization code was taken from the epjistsu backend by
13 m. allan noah <kitno455 at gmail dot com>
14
15 Software processing for deskew, crop and dspeckle are inspired by allan's
16 noah work in the fujitsu backend
17
18 This file is part of the SANE package.
19
20 This program is free software; you can redistribute it and/or
21 modify it under the terms of the GNU General Public License as
22 published by the Free Software Foundation; either version 2 of the
23 License, or (at your option) any later version.
24
25 This program is distributed in the hope that it will be useful, but
26 WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 General Public License for more details.
29
30 You should have received a copy of the GNU General Public License
31 along with this program. If not, see <https://www.gnu.org/licenses/>.
32 */
33
34 /*
35 * SANE backend for Genesys Logic GL646/GL841/GL842/GL843/GL846/GL847/GL124 based scanners
36 */
37
38 #define DEBUG_NOT_STATIC
39
40 #include "genesys.h"
41 #include "gl124_registers.h"
42 #include "gl841_registers.h"
43 #include "gl842_registers.h"
44 #include "gl843_registers.h"
45 #include "gl846_registers.h"
46 #include "gl847_registers.h"
47 #include "usb_device.h"
48 #include "utilities.h"
49 #include "scanner_interface_usb.h"
50 #include "test_scanner_interface.h"
51 #include "test_settings.h"
52 #include "../include/sane/sanei_config.h"
53
54 #include <array>
55 #include <cmath>
56 #include <cstring>
57 #include <fstream>
58 #include <iterator>
59 #include <list>
60 #include <numeric>
61 #include <exception>
62 #include <vector>
63
64 #ifndef SANE_GENESYS_API_LINKAGE
65 #define SANE_GENESYS_API_LINKAGE extern "C"
66 #endif
67
68 namespace genesys {
69
70 // Data that we allocate to back SANE_Device objects in s_sane_devices
71 struct SANE_Device_Data
72 {
73 std::string name;
74 };
75
76 namespace {
77 StaticInit<std::list<Genesys_Scanner>> s_scanners;
78 StaticInit<std::vector<SANE_Device>> s_sane_devices;
79 StaticInit<std::vector<SANE_Device_Data>> s_sane_devices_data;
80 StaticInit<std::vector<SANE_Device*>> s_sane_devices_ptrs;
81 StaticInit<std::list<Genesys_Device>> s_devices;
82
83 // Maximum time for lamp warm-up
84 constexpr unsigned WARMUP_TIME = 65;
85 } // namespace
86
87 static SANE_String_Const mode_list[] = {
88 SANE_VALUE_SCAN_MODE_COLOR,
89 SANE_VALUE_SCAN_MODE_GRAY,
90 // SANE_TITLE_HALFTONE, not used
91 // SANE_VALUE_SCAN_MODE_LINEART, not used
92 nullptr
93 };
94
95 static SANE_String_Const color_filter_list[] = {
96 SANE_I18N ("Red"),
97 SANE_I18N ("Green"),
98 SANE_I18N ("Blue"),
99 nullptr
100 };
101
102 static SANE_String_Const cis_color_filter_list[] = {
103 SANE_I18N ("Red"),
104 SANE_I18N ("Green"),
105 SANE_I18N ("Blue"),
106 SANE_I18N ("None"),
107 nullptr
108 };
109
110 static SANE_Range time_range = {
111 0, /* minimum */
112 60, /* maximum */
113 0 /* quantization */
114 };
115
116 static const SANE_Range u12_range = {
117 0, /* minimum */
118 4095, /* maximum */
119 0 /* quantization */
120 };
121
122 static const SANE_Range u14_range = {
123 0, /* minimum */
124 16383, /* maximum */
125 0 /* quantization */
126 };
127
128 static const SANE_Range u16_range = {
129 0, /* minimum */
130 65535, /* maximum */
131 0 /* quantization */
132 };
133
134 static const SANE_Range percentage_range = {
135 float_to_fixed(0), // minimum
136 float_to_fixed(100), // maximum
137 float_to_fixed(1) // quantization
138 };
139
140 /**
141 * range for brightness and contrast
142 */
143 static const SANE_Range enhance_range = {
144 -100, /* minimum */
145 100, /* maximum */
146 1 /* quantization */
147 };
148
149 /**
150 * range for expiration time
151 */
152 static const SANE_Range expiration_range = {
153 -1, /* minimum */
154 30000, /* maximum */
155 1 /* quantization */
156 };
157
sanei_genesys_find_sensor_any(const Genesys_Device * dev)158 const Genesys_Sensor& sanei_genesys_find_sensor_any(const Genesys_Device* dev)
159 {
160 DBG_HELPER(dbg);
161 for (const auto& sensor : *s_sensors) {
162 if (dev->model->sensor_id == sensor.sensor_id) {
163 return sensor;
164 }
165 }
166 throw std::runtime_error("Given device does not have sensor defined");
167 }
168
find_sensor_impl(const Genesys_Device * dev,unsigned dpi,unsigned channels,ScanMethod scan_method)169 Genesys_Sensor* find_sensor_impl(const Genesys_Device* dev, unsigned dpi, unsigned channels,
170 ScanMethod scan_method)
171 {
172 DBG_HELPER_ARGS(dbg, "dpi: %d, channels: %d, scan_method: %d", dpi, channels,
173 static_cast<unsigned>(scan_method));
174 for (auto& sensor : *s_sensors) {
175 if (dev->model->sensor_id == sensor.sensor_id && sensor.resolutions.matches(dpi) &&
176 sensor.matches_channel_count(channels) && sensor.method == scan_method)
177 {
178 return &sensor;
179 }
180 }
181 return nullptr;
182 }
183
sanei_genesys_has_sensor(const Genesys_Device * dev,unsigned dpi,unsigned channels,ScanMethod scan_method)184 bool sanei_genesys_has_sensor(const Genesys_Device* dev, unsigned dpi, unsigned channels,
185 ScanMethod scan_method)
186 {
187 DBG_HELPER_ARGS(dbg, "dpi: %d, channels: %d, scan_method: %d", dpi, channels,
188 static_cast<unsigned>(scan_method));
189 return find_sensor_impl(dev, dpi, channels, scan_method) != nullptr;
190 }
191
sanei_genesys_find_sensor(const Genesys_Device * dev,unsigned dpi,unsigned channels,ScanMethod scan_method)192 const Genesys_Sensor& sanei_genesys_find_sensor(const Genesys_Device* dev, unsigned dpi,
193 unsigned channels, ScanMethod scan_method)
194 {
195 DBG_HELPER_ARGS(dbg, "dpi: %d, channels: %d, scan_method: %d", dpi, channels,
196 static_cast<unsigned>(scan_method));
197 const auto* sensor = find_sensor_impl(dev, dpi, channels, scan_method);
198 if (sensor)
199 return *sensor;
200 throw std::runtime_error("Given device does not have sensor defined");
201 }
202
sanei_genesys_find_sensor_for_write(Genesys_Device * dev,unsigned dpi,unsigned channels,ScanMethod scan_method)203 Genesys_Sensor& sanei_genesys_find_sensor_for_write(Genesys_Device* dev, unsigned dpi,
204 unsigned channels,
205 ScanMethod scan_method)
206 {
207 DBG_HELPER_ARGS(dbg, "dpi: %d, channels: %d, scan_method: %d", dpi, channels,
208 static_cast<unsigned>(scan_method));
209 auto* sensor = find_sensor_impl(dev, dpi, channels, scan_method);
210 if (sensor)
211 return *sensor;
212 throw std::runtime_error("Given device does not have sensor defined");
213 }
214
215
216 std::vector<std::reference_wrapper<const Genesys_Sensor>>
sanei_genesys_find_sensors_all(const Genesys_Device * dev,ScanMethod scan_method)217 sanei_genesys_find_sensors_all(const Genesys_Device* dev, ScanMethod scan_method)
218 {
219 DBG_HELPER_ARGS(dbg, "scan_method: %d", static_cast<unsigned>(scan_method));
220 std::vector<std::reference_wrapper<const Genesys_Sensor>> ret;
221 for (auto& sensor : *s_sensors) {
222 if (dev->model->sensor_id == sensor.sensor_id && sensor.method == scan_method) {
223 ret.push_back(sensor);
224 }
225 }
226 return ret;
227 }
228
229 std::vector<std::reference_wrapper<Genesys_Sensor>>
sanei_genesys_find_sensors_all_for_write(Genesys_Device * dev,ScanMethod scan_method)230 sanei_genesys_find_sensors_all_for_write(Genesys_Device* dev, ScanMethod scan_method)
231 {
232 DBG_HELPER_ARGS(dbg, "scan_method: %d", static_cast<unsigned>(scan_method));
233 std::vector<std::reference_wrapper<Genesys_Sensor>> ret;
234 for (auto& sensor : *s_sensors) {
235 if (dev->model->sensor_id == sensor.sensor_id && sensor.method == scan_method) {
236 ret.push_back(sensor);
237 }
238 }
239 return ret;
240 }
241
sanei_genesys_init_structs(Genesys_Device * dev)242 void sanei_genesys_init_structs (Genesys_Device * dev)
243 {
244 DBG_HELPER(dbg);
245
246 bool gpo_ok = false;
247 bool motor_ok = false;
248 bool fe_ok = false;
249
250 /* initialize the GPO data stuff */
251 for (const auto& gpo : *s_gpo) {
252 if (dev->model->gpio_id == gpo.id) {
253 dev->gpo = gpo;
254 gpo_ok = true;
255 break;
256 }
257 }
258
259 // initialize the motor data stuff
260 for (const auto& motor : *s_motors) {
261 if (dev->model->motor_id == motor.id) {
262 dev->motor = motor;
263 motor_ok = true;
264 break;
265 }
266 }
267
268 for (const auto& frontend : *s_frontends) {
269 if (dev->model->adc_id == frontend.id) {
270 dev->frontend_initial = frontend;
271 dev->frontend = frontend;
272 fe_ok = true;
273 break;
274 }
275 }
276
277 if (dev->model->asic_type == AsicType::GL845 ||
278 dev->model->asic_type == AsicType::GL846 ||
279 dev->model->asic_type == AsicType::GL847 ||
280 dev->model->asic_type == AsicType::GL124)
281 {
282 bool memory_layout_found = false;
283 for (const auto& memory_layout : *s_memory_layout) {
284 if (memory_layout.models.matches(dev->model->model_id)) {
285 dev->memory_layout = memory_layout;
286 memory_layout_found = true;
287 break;
288 }
289 }
290 if (!memory_layout_found) {
291 throw SaneException("Could not find memory layout");
292 }
293 }
294
295 if (!motor_ok || !gpo_ok || !fe_ok) {
296 throw SaneException("bad description(s) for fe/gpo/motor=%d/%d/%d\n",
297 static_cast<unsigned>(dev->model->sensor_id),
298 static_cast<unsigned>(dev->model->gpio_id),
299 static_cast<unsigned>(dev->model->motor_id));
300 }
301 }
302
303 /** @brief computes gamma table
304 * Generates a gamma table of the given length within 0 and the given
305 * maximum value
306 * @param gamma_table gamma table to fill
307 * @param size size of the table
308 * @param maximum value allowed for gamma
309 * @param gamma_max maximum gamma value
310 * @param gamma gamma to compute values
311 * @return a gamma table filled with the computed values
312 * */
sanei_genesys_create_gamma_table(std::vector<std::uint16_t> & gamma_table,int size,float maximum,float gamma_max,float gamma)313 void sanei_genesys_create_gamma_table(std::vector<std::uint16_t>& gamma_table, int size,
314 float maximum, float gamma_max, float gamma)
315 {
316 gamma_table.clear();
317 gamma_table.resize(size, 0);
318
319 int i;
320 float value;
321
322 DBG(DBG_proc, "%s: size = %d, ""maximum = %g, gamma_max = %g, gamma = %g\n", __func__, size,
323 maximum, gamma_max, gamma);
324 for (i = 0; i < size; i++)
325 {
326 value = static_cast<float>(gamma_max * std::pow(static_cast<double>(i) / size, 1.0 / gamma));
327 if (value > maximum) {
328 value = maximum;
329 }
330 gamma_table[i] = static_cast<std::uint16_t>(value);
331 }
332 DBG(DBG_proc, "%s: completed\n", __func__);
333 }
334
sanei_genesys_create_default_gamma_table(Genesys_Device * dev,std::vector<std::uint16_t> & gamma_table,float gamma)335 void sanei_genesys_create_default_gamma_table(Genesys_Device* dev,
336 std::vector<std::uint16_t>& gamma_table, float gamma)
337 {
338 int size = 0;
339 int max = 0;
340 if (dev->model->asic_type == AsicType::GL646) {
341 if (has_flag(dev->model->flags, ModelFlag::GAMMA_14BIT)) {
342 size = 16384;
343 } else {
344 size = 4096;
345 }
346 max = size - 1;
347 } else if (dev->model->asic_type == AsicType::GL124 ||
348 dev->model->asic_type == AsicType::GL845 ||
349 dev->model->asic_type == AsicType::GL846 ||
350 dev->model->asic_type == AsicType::GL847) {
351 size = 257;
352 max = 65535;
353 } else {
354 size = 256;
355 max = 65535;
356 }
357 sanei_genesys_create_gamma_table(gamma_table, size, max, max, gamma);
358 }
359
360 /* computes the exposure_time on the basis of the given vertical dpi,
361 the number of pixels the ccd needs to send,
362 the step_type and the corresponding maximum speed from the motor struct */
363 /*
364 Currently considers maximum motor speed at given step_type, minimum
365 line exposure needed for conversion and led exposure time.
366
367 TODO: Should also consider maximum transfer rate: ~6.5MB/s.
368 Note: The enhance option of the scanners does _not_ help. It only halves
369 the amount of pixels transferred.
370 */
sanei_genesys_exposure_time2(Genesys_Device * dev,const MotorProfile & profile,float ydpi,int endpixel,int exposure_by_led)371 SANE_Int sanei_genesys_exposure_time2(Genesys_Device * dev, const MotorProfile& profile, float ydpi,
372 int endpixel, int exposure_by_led)
373 {
374 int exposure_by_ccd = endpixel + 32;
375 unsigned max_speed_motor_w = profile.slope.max_speed_w;
376 int exposure_by_motor = static_cast<int>((max_speed_motor_w * dev->motor.base_ydpi) / ydpi);
377
378 int exposure = exposure_by_ccd;
379
380 if (exposure < exposure_by_motor) {
381 exposure = exposure_by_motor;
382 }
383
384 if (exposure < exposure_by_led && dev->model->is_cis) {
385 exposure = exposure_by_led;
386 }
387
388 return exposure;
389 }
390
391
392 /* Sends a block of shading information to the scanner.
393 The data is placed at address 0x0000 for color mode, gray mode and
394 unconditionally for the following CCD chips: HP2300, HP2400 and HP5345
395
396 The data needs to be of size "size", and in little endian byte order.
397 */
genesys_send_offset_and_shading(Genesys_Device * dev,const Genesys_Sensor & sensor,std::uint8_t * data,int size)398 static void genesys_send_offset_and_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,
399 std::uint8_t* data, int size)
400 {
401 DBG_HELPER_ARGS(dbg, "(size = %d)", size);
402 int start_address;
403
404 /* ASIC higher than gl843 doesn't have register 2A/2B, so we route to
405 * a per ASIC shading data loading function if available.
406 * It is also used for scanners using SHDAREA */
407 if (dev->cmd_set->has_send_shading_data()) {
408 dev->cmd_set->send_shading_data(dev, sensor, data, size);
409 return;
410 }
411
412 start_address = 0x00;
413
414 dev->interface->write_buffer(0x3c, start_address, data, size);
415 }
416
sanei_genesys_init_shading_data(Genesys_Device * dev,const Genesys_Sensor & sensor,int pixels_per_line)417 void sanei_genesys_init_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor,
418 int pixels_per_line)
419 {
420 DBG_HELPER_ARGS(dbg, "pixels_per_line: %d", pixels_per_line);
421
422 if (dev->cmd_set->has_send_shading_data()) {
423 return;
424 }
425
426 DBG(DBG_proc, "%s (pixels_per_line = %d)\n", __func__, pixels_per_line);
427
428 unsigned channels = dev->settings.get_channels();
429
430 // 16 bit black, 16 bit white
431 std::vector<std::uint8_t> shading_data(pixels_per_line * 4 * channels, 0);
432
433 std::uint8_t* shading_data_ptr = shading_data.data();
434
435 for (unsigned i = 0; i < pixels_per_line * channels; i++) {
436 *shading_data_ptr++ = 0x00; /* dark lo */
437 *shading_data_ptr++ = 0x00; /* dark hi */
438 *shading_data_ptr++ = 0x00; /* white lo */
439 *shading_data_ptr++ = 0x40; /* white hi -> 0x4000 */
440 }
441
442 genesys_send_offset_and_shading(dev, sensor, shading_data.data(),
443 pixels_per_line * 4 * channels);
444 }
445
446 namespace gl124 {
447 void gl124_setup_scan_gpio(Genesys_Device* dev, int resolution);
448 } // namespace gl124
449
scanner_clear_scan_and_feed_counts(Genesys_Device & dev)450 void scanner_clear_scan_and_feed_counts(Genesys_Device& dev)
451 {
452 switch (dev.model->asic_type) {
453 case AsicType::GL841: {
454 dev.interface->write_register(gl841::REG_0x0D,
455 gl841::REG_0x0D_CLRLNCNT);
456 break;
457 }
458 case AsicType::GL842: {
459 dev.interface->write_register(gl842::REG_0x0D,
460 gl842::REG_0x0D_CLRLNCNT);
461 break;
462 }
463 case AsicType::GL843: {
464 dev.interface->write_register(gl843::REG_0x0D,
465 gl843::REG_0x0D_CLRLNCNT | gl843::REG_0x0D_CLRMCNT);
466 break;
467 }
468 case AsicType::GL845:
469 case AsicType::GL846: {
470 dev.interface->write_register(gl846::REG_0x0D,
471 gl846::REG_0x0D_CLRLNCNT | gl846::REG_0x0D_CLRMCNT);
472 break;
473 }
474 case AsicType::GL847:{
475 dev.interface->write_register(gl847::REG_0x0D,
476 gl847::REG_0x0D_CLRLNCNT | gl847::REG_0x0D_CLRMCNT);
477 break;
478 }
479 case AsicType::GL124:{
480 dev.interface->write_register(gl124::REG_0x0D,
481 gl124::REG_0x0D_CLRLNCNT | gl124::REG_0x0D_CLRMCNT);
482 break;
483 }
484 default:
485 throw SaneException("Unsupported asic type");
486 }
487 }
488
scanner_send_slope_table(Genesys_Device * dev,const Genesys_Sensor & sensor,unsigned table_nr,const std::vector<std::uint16_t> & slope_table)489 void scanner_send_slope_table(Genesys_Device* dev, const Genesys_Sensor& sensor, unsigned table_nr,
490 const std::vector<std::uint16_t>& slope_table)
491 {
492 DBG_HELPER_ARGS(dbg, "table_nr = %d, steps = %zu", table_nr, slope_table.size());
493
494 unsigned max_table_nr = 0;
495 switch (dev->model->asic_type) {
496 case AsicType::GL646: {
497 max_table_nr = 2;
498 break;
499 }
500 case AsicType::GL841:
501 case AsicType::GL842:
502 case AsicType::GL843:
503 case AsicType::GL845:
504 case AsicType::GL846:
505 case AsicType::GL847:
506 case AsicType::GL124: {
507 max_table_nr = 4;
508 break;
509 }
510 default:
511 throw SaneException("Unsupported ASIC type");
512 }
513
514 if (table_nr > max_table_nr) {
515 throw SaneException("invalid table number %d", table_nr);
516 }
517
518 std::vector<std::uint8_t> table;
519 table.reserve(slope_table.size() * 2);
520 for (std::size_t i = 0; i < slope_table.size(); i++) {
521 table.push_back(slope_table[i] & 0xff);
522 table.push_back(slope_table[i] >> 8);
523 }
524 if (dev->model->asic_type == AsicType::GL841 ||
525 dev->model->model_id == ModelId::CANON_LIDE_90)
526 {
527 // BUG: do this on all gl842 scanners
528 auto max_table_size = get_slope_table_max_size(dev->model->asic_type);
529 table.reserve(max_table_size * 2);
530 while (table.size() < max_table_size * 2) {
531 table.push_back(slope_table.back() & 0xff);
532 table.push_back(slope_table.back() >> 8);
533 }
534 }
535
536 if (dev->interface->is_mock()) {
537 dev->interface->record_slope_table(table_nr, slope_table);
538 }
539
540 switch (dev->model->asic_type) {
541 case AsicType::GL646: {
542 unsigned dpihw = dev->reg.find_reg(0x05).value >> 6;
543 unsigned start_address = 0;
544 if (dpihw == 0) { // 600 dpi
545 start_address = 0x08000;
546 } else if (dpihw == 1) { // 1200 dpi
547 start_address = 0x10000;
548 } else if (dpihw == 2) { // 2400 dpi
549 start_address = 0x1f800;
550 } else {
551 throw SaneException("Unexpected dpihw");
552 }
553 dev->interface->write_buffer(0x3c, start_address + table_nr * 0x100, table.data(),
554 table.size());
555 break;
556 }
557 case AsicType::GL841:
558 case AsicType::GL842: {
559 unsigned start_address = 0;
560 switch (sensor.register_dpihw) {
561 case 600: start_address = 0x08000; break;
562 case 1200: start_address = 0x10000; break;
563 case 2400: start_address = 0x20000; break;
564 default: throw SaneException("Unexpected dpihw");
565 }
566 dev->interface->write_buffer(0x3c, start_address + table_nr * 0x200, table.data(),
567 table.size());
568 break;
569 }
570 case AsicType::GL843: {
571 // slope table addresses are fixed : 0x40000, 0x48000, 0x50000, 0x58000, 0x60000
572 // XXX STEF XXX USB 1.1 ? sanei_genesys_write_0x8c (dev, 0x0f, 0x14);
573 dev->interface->write_gamma(0x28, 0x40000 + 0x8000 * table_nr, table.data(),
574 table.size());
575 break;
576 }
577 case AsicType::GL845:
578 case AsicType::GL846:
579 case AsicType::GL847:
580 case AsicType::GL124: {
581 // slope table addresses are fixed
582 dev->interface->write_ahb(0x10000000 + 0x4000 * table_nr, table.size(),
583 table.data());
584 break;
585 }
586 default:
587 throw SaneException("Unsupported ASIC type");
588 }
589
590 }
591
scanner_is_motor_stopped(Genesys_Device & dev)592 bool scanner_is_motor_stopped(Genesys_Device& dev)
593 {
594 switch (dev.model->asic_type) {
595 case AsicType::GL646: {
596 auto status = scanner_read_status(dev);
597 return !status.is_motor_enabled && status.is_feeding_finished;
598 }
599 case AsicType::GL841: {
600 auto status = scanner_read_status(dev);
601 auto reg = dev.interface->read_register(gl841::REG_0x40);
602
603 return (!(reg & gl841::REG_0x40_DATAENB) && !(reg & gl841::REG_0x40_MOTMFLG) &&
604 !status.is_motor_enabled);
605 }
606 case AsicType::GL842: {
607 auto status = scanner_read_status(dev);
608 auto reg = dev.interface->read_register(gl842::REG_0x40);
609
610 return (!(reg & gl842::REG_0x40_DATAENB) && !(reg & gl842::REG_0x40_MOTMFLG) &&
611 !status.is_motor_enabled);
612 }
613 case AsicType::GL843: {
614 auto status = scanner_read_status(dev);
615 auto reg = dev.interface->read_register(gl843::REG_0x40);
616
617 return (!(reg & gl843::REG_0x40_DATAENB) && !(reg & gl843::REG_0x40_MOTMFLG) &&
618 !status.is_motor_enabled);
619 }
620 case AsicType::GL845:
621 case AsicType::GL846: {
622 auto status = scanner_read_status(dev);
623 auto reg = dev.interface->read_register(gl846::REG_0x40);
624
625 return (!(reg & gl846::REG_0x40_DATAENB) && !(reg & gl846::REG_0x40_MOTMFLG) &&
626 !status.is_motor_enabled);
627 }
628 case AsicType::GL847: {
629 auto status = scanner_read_status(dev);
630 auto reg = dev.interface->read_register(gl847::REG_0x40);
631
632 return (!(reg & gl847::REG_0x40_DATAENB) && !(reg & gl847::REG_0x40_MOTMFLG) &&
633 !status.is_motor_enabled);
634 }
635 case AsicType::GL124: {
636 auto status = scanner_read_status(dev);
637 auto reg = dev.interface->read_register(gl124::REG_0x100);
638
639 return (!(reg & gl124::REG_0x100_DATAENB) && !(reg & gl124::REG_0x100_MOTMFLG) &&
640 !status.is_motor_enabled);
641 }
642 default:
643 throw SaneException("Unsupported asic type");
644 }
645 }
646
scanner_setup_sensor(Genesys_Device & dev,const Genesys_Sensor & sensor,Genesys_Register_Set & regs)647 void scanner_setup_sensor(Genesys_Device& dev, const Genesys_Sensor& sensor,
648 Genesys_Register_Set& regs)
649 {
650 DBG_HELPER(dbg);
651
652 for (const auto& custom_reg : sensor.custom_regs) {
653 regs.set8(custom_reg.address, custom_reg.value);
654 }
655
656 if (dev.model->asic_type != AsicType::GL843)
657 {
658 // FIXME: remove the above check
659 regs_set_exposure(dev.model->asic_type, regs, sensor.exposure);
660 }
661
662 dev.segment_order = sensor.segment_order;
663 }
664
scanner_stop_action(Genesys_Device & dev)665 void scanner_stop_action(Genesys_Device& dev)
666 {
667 DBG_HELPER(dbg);
668
669 switch (dev.model->asic_type) {
670 case AsicType::GL841:
671 case AsicType::GL842:
672 case AsicType::GL843:
673 case AsicType::GL845:
674 case AsicType::GL846:
675 case AsicType::GL847:
676 case AsicType::GL124:
677 break;
678 default:
679 throw SaneException("Unsupported asic type");
680 }
681
682 dev.cmd_set->update_home_sensor_gpio(dev);
683
684 if (scanner_is_motor_stopped(dev)) {
685 DBG(DBG_info, "%s: already stopped\n", __func__);
686 return;
687 }
688
689 scanner_stop_action_no_move(dev, dev.reg);
690
691 if (is_testing_mode()) {
692 return;
693 }
694
695 for (unsigned i = 0; i < 10; ++i) {
696 if (scanner_is_motor_stopped(dev)) {
697 return;
698 }
699
700 dev.interface->sleep_ms(100);
701 }
702
703 throw SaneException(SANE_STATUS_IO_ERROR, "could not stop motor");
704 }
705
scanner_stop_action_no_move(Genesys_Device & dev,genesys::Genesys_Register_Set & regs)706 void scanner_stop_action_no_move(Genesys_Device& dev, genesys::Genesys_Register_Set& regs)
707 {
708 switch (dev.model->asic_type) {
709 case AsicType::GL646:
710 case AsicType::GL841:
711 case AsicType::GL842:
712 case AsicType::GL843:
713 case AsicType::GL845:
714 case AsicType::GL846:
715 case AsicType::GL847:
716 case AsicType::GL124:
717 break;
718 default:
719 throw SaneException("Unsupported asic type");
720 }
721
722 regs_set_optical_off(dev.model->asic_type, regs);
723 // same across all supported ASICs
724 dev.interface->write_register(0x01, regs.get8(0x01));
725
726 // looks like certain scanners lock up if we try to scan immediately after stopping previous
727 // action.
728 dev.interface->sleep_ms(100);
729 }
730
scanner_move(Genesys_Device & dev,ScanMethod scan_method,unsigned steps,Direction direction)731 void scanner_move(Genesys_Device& dev, ScanMethod scan_method, unsigned steps, Direction direction)
732 {
733 DBG_HELPER_ARGS(dbg, "steps=%d direction=%d", steps, static_cast<unsigned>(direction));
734
735 auto local_reg = dev.reg;
736
737 unsigned resolution = dev.model->get_resolution_settings(scan_method).get_min_resolution_y();
738
739 const auto& sensor = sanei_genesys_find_sensor(&dev, resolution, 3, scan_method);
740
741 bool uses_secondary_head = (scan_method == ScanMethod::TRANSPARENCY ||
742 scan_method == ScanMethod::TRANSPARENCY_INFRARED) &&
743 (!has_flag(dev.model->flags, ModelFlag::UTA_NO_SECONDARY_MOTOR));
744
745 bool uses_secondary_pos = uses_secondary_head &&
746 dev.model->default_method == ScanMethod::FLATBED;
747
748 if (!dev.is_head_pos_known(ScanHeadId::PRIMARY)) {
749 throw SaneException("Unknown head position");
750 }
751 if (uses_secondary_pos && !dev.is_head_pos_known(ScanHeadId::SECONDARY)) {
752 throw SaneException("Unknown head position");
753 }
754 if (direction == Direction::BACKWARD && steps > dev.head_pos(ScanHeadId::PRIMARY)) {
755 throw SaneException("Trying to feed behind the home position %d %d",
756 steps, dev.head_pos(ScanHeadId::PRIMARY));
757 }
758 if (uses_secondary_pos && direction == Direction::BACKWARD &&
759 steps > dev.head_pos(ScanHeadId::SECONDARY))
760 {
761 throw SaneException("Trying to feed behind the home position %d %d",
762 steps, dev.head_pos(ScanHeadId::SECONDARY));
763 }
764
765 ScanSession session;
766 session.params.xres = resolution;
767 session.params.yres = resolution;
768 session.params.startx = 0;
769 session.params.starty = steps;
770 session.params.pixels = 50;
771 session.params.lines = 3;
772 session.params.depth = 8;
773 session.params.channels = 1;
774 session.params.scan_method = scan_method;
775 session.params.scan_mode = ScanColorMode::GRAY;
776 session.params.color_filter = ColorFilter::GREEN;
777 session.params.contrast_adjustment = dev.settings.contrast;
778 session.params.brightness_adjustment = dev.settings.brightness;
779
780 session.params.flags = ScanFlag::DISABLE_SHADING |
781 ScanFlag::DISABLE_GAMMA |
782 ScanFlag::FEEDING |
783 ScanFlag::IGNORE_STAGGER_OFFSET |
784 ScanFlag::IGNORE_COLOR_OFFSET;
785
786 if (dev.model->asic_type == AsicType::GL124) {
787 session.params.flags |= ScanFlag::DISABLE_BUFFER_FULL_MOVE;
788 }
789
790 if (direction == Direction::BACKWARD) {
791 session.params.flags |= ScanFlag::REVERSE;
792 }
793
794 compute_session(&dev, session, sensor);
795
796 dev.cmd_set->init_regs_for_scan_session(&dev, sensor, &local_reg, session);
797
798 if (dev.model->asic_type != AsicType::GL843) {
799 regs_set_exposure(dev.model->asic_type, local_reg,
800 sanei_genesys_fixup_exposure({0, 0, 0}));
801 }
802 scanner_clear_scan_and_feed_counts(dev);
803
804 dev.interface->write_registers(local_reg);
805 if (uses_secondary_head) {
806 dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY_AND_SECONDARY);
807 }
808
809 try {
810 scanner_start_action(dev, true);
811 } catch (...) {
812 catch_all_exceptions(__func__, [&]() {
813 dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY);
814 });
815 catch_all_exceptions(__func__, [&]() { scanner_stop_action(dev); });
816 // restore original registers
817 catch_all_exceptions(__func__, [&]() { dev.interface->write_registers(dev.reg); });
818 throw;
819 }
820
821 if (is_testing_mode()) {
822 dev.interface->test_checkpoint("feed");
823
824 dev.advance_head_pos_by_steps(ScanHeadId::PRIMARY, direction, steps);
825 if (uses_secondary_pos) {
826 dev.advance_head_pos_by_steps(ScanHeadId::SECONDARY, direction, steps);
827 }
828
829 scanner_stop_action(dev);
830 if (uses_secondary_head) {
831 dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY);
832 }
833 return;
834 }
835
836 // wait until feed count reaches the required value
837 if (dev.model->model_id == ModelId::CANON_LIDE_700F) {
838 dev.cmd_set->update_home_sensor_gpio(dev);
839 }
840
841 // FIXME: should porbably wait for some timeout
842 Status status;
843 for (unsigned i = 0;; ++i) {
844 status = scanner_read_status(dev);
845 if (status.is_feeding_finished || (
846 direction == Direction::BACKWARD && status.is_at_home))
847 {
848 break;
849 }
850 dev.interface->sleep_ms(10);
851 }
852
853 scanner_stop_action(dev);
854 if (uses_secondary_head) {
855 dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY);
856 }
857
858 dev.advance_head_pos_by_steps(ScanHeadId::PRIMARY, direction, steps);
859 if (uses_secondary_pos) {
860 dev.advance_head_pos_by_steps(ScanHeadId::SECONDARY, direction, steps);
861 }
862
863 // looks like certain scanners lock up if we scan immediately after feeding
864 dev.interface->sleep_ms(100);
865 }
866
scanner_move_to_ta(Genesys_Device & dev)867 void scanner_move_to_ta(Genesys_Device& dev)
868 {
869 DBG_HELPER(dbg);
870
871 unsigned feed = static_cast<unsigned>((dev.model->y_offset_sensor_to_ta * dev.motor.base_ydpi) /
872 MM_PER_INCH);
873 scanner_move(dev, dev.model->default_method, feed, Direction::FORWARD);
874 }
875
scanner_move_back_home(Genesys_Device & dev,bool wait_until_home)876 void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home)
877 {
878 DBG_HELPER_ARGS(dbg, "wait_until_home = %d", wait_until_home);
879
880 switch (dev.model->asic_type) {
881 case AsicType::GL841:
882 case AsicType::GL842:
883 case AsicType::GL843:
884 case AsicType::GL845:
885 case AsicType::GL846:
886 case AsicType::GL847:
887 case AsicType::GL124:
888 break;
889 default:
890 throw SaneException("Unsupported asic type");
891 }
892
893 if (dev.model->is_sheetfed) {
894 dbg.vlog(DBG_proc, "sheetfed scanner, skipping going back home");
895 return;
896 }
897
898 // FIXME: also check whether the scanner actually has a secondary head
899 if ((!dev.is_head_pos_known(ScanHeadId::SECONDARY) ||
900 dev.head_pos(ScanHeadId::SECONDARY) > 0 ||
901 dev.settings.scan_method == ScanMethod::TRANSPARENCY ||
902 dev.settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) &&
903 (!has_flag(dev.model->flags, ModelFlag::UTA_NO_SECONDARY_MOTOR)))
904 {
905 scanner_move_back_home_ta(dev);
906 }
907
908 if (dev.is_head_pos_known(ScanHeadId::PRIMARY) &&
909 dev.head_pos(ScanHeadId::PRIMARY) > 1000)
910 {
911 // leave 500 steps for regular slow back home
912 scanner_move(dev, dev.model->default_method, dev.head_pos(ScanHeadId::PRIMARY) - 500,
913 Direction::BACKWARD);
914 }
915
916 dev.cmd_set->update_home_sensor_gpio(dev);
917
918 auto status = scanner_read_reliable_status(dev);
919
920 if (status.is_at_home) {
921 dbg.log(DBG_info, "already at home");
922 dev.set_head_pos_zero(ScanHeadId::PRIMARY);
923 return;
924 }
925
926 Genesys_Register_Set local_reg = dev.reg;
927 unsigned resolution = sanei_genesys_get_lowest_ydpi(&dev);
928
929 const auto& sensor = sanei_genesys_find_sensor(&dev, resolution, 1, dev.model->default_method);
930
931 ScanSession session;
932 session.params.xres = resolution;
933 session.params.yres = resolution;
934 session.params.startx = 0;
935 session.params.starty = 40000;
936 session.params.pixels = 50;
937 session.params.lines = 3;
938 session.params.depth = 8;
939 session.params.channels = 1;
940 session.params.scan_method = dev.settings.scan_method;
941 session.params.scan_mode = ScanColorMode::GRAY;
942 session.params.color_filter = ColorFilter::GREEN;
943 session.params.contrast_adjustment = dev.settings.contrast;
944 session.params.brightness_adjustment = dev.settings.brightness;
945
946 session.params.flags = ScanFlag::DISABLE_SHADING |
947 ScanFlag::DISABLE_GAMMA |
948 ScanFlag::IGNORE_STAGGER_OFFSET |
949 ScanFlag::IGNORE_COLOR_OFFSET |
950 ScanFlag::REVERSE;
951
952 if (dev.model->asic_type == AsicType::GL843) {
953 session.params.flags |= ScanFlag::DISABLE_BUFFER_FULL_MOVE;
954 }
955
956 compute_session(&dev, session, sensor);
957
958 dev.cmd_set->init_regs_for_scan_session(&dev, sensor, &local_reg, session);
959
960 scanner_clear_scan_and_feed_counts(dev);
961
962 dev.interface->write_registers(local_reg);
963
964 if (dev.model->asic_type == AsicType::GL124) {
965 gl124::gl124_setup_scan_gpio(&dev, resolution);
966 }
967
968 try {
969 scanner_start_action(dev, true);
970 } catch (...) {
971 catch_all_exceptions(__func__, [&]() { scanner_stop_action(dev); });
972 // restore original registers
973 catch_all_exceptions(__func__, [&]()
974 {
975 dev.interface->write_registers(dev.reg);
976 });
977 throw;
978 }
979
980 dev.cmd_set->update_home_sensor_gpio(dev);
981
982 if (is_testing_mode()) {
983 dev.interface->test_checkpoint("move_back_home");
984 dev.set_head_pos_zero(ScanHeadId::PRIMARY);
985 return;
986 }
987
988 if (wait_until_home) {
989 for (unsigned i = 0; i < 300; ++i) {
990 auto status = scanner_read_status(dev);
991
992 if (status.is_at_home) {
993 dbg.log(DBG_info, "reached home position");
994 if (dev.model->asic_type == AsicType::GL846 ||
995 dev.model->asic_type == AsicType::GL847)
996 {
997 scanner_stop_action(dev);
998 }
999 dev.set_head_pos_zero(ScanHeadId::PRIMARY);
1000 return;
1001 }
1002
1003 dev.interface->sleep_ms(100);
1004 }
1005
1006 // when we come here then the scanner needed too much time for this, so we better stop
1007 // the motor
1008 catch_all_exceptions(__func__, [&](){ scanner_stop_action(dev); });
1009 dev.set_head_pos_unknown(ScanHeadId::PRIMARY | ScanHeadId::SECONDARY);
1010 throw SaneException(SANE_STATUS_IO_ERROR, "timeout while waiting for scanhead to go home");
1011 }
1012 dbg.log(DBG_info, "scanhead is still moving");
1013 }
1014
1015 namespace {
should_use_secondary_motor_mode(Genesys_Device & dev)1016 bool should_use_secondary_motor_mode(Genesys_Device& dev)
1017 {
1018 bool should_use = !dev.is_head_pos_known(ScanHeadId::SECONDARY) ||
1019 !dev.is_head_pos_known(ScanHeadId::PRIMARY) ||
1020 dev.head_pos(ScanHeadId::SECONDARY) > dev.head_pos(ScanHeadId::PRIMARY);
1021 bool supports = dev.model->model_id == ModelId::CANON_8600F;
1022 return should_use && supports;
1023 }
1024
handle_motor_position_after_move_back_home_ta(Genesys_Device & dev,MotorMode motor_mode)1025 void handle_motor_position_after_move_back_home_ta(Genesys_Device& dev, MotorMode motor_mode)
1026 {
1027 if (motor_mode == MotorMode::SECONDARY) {
1028 dev.set_head_pos_zero(ScanHeadId::SECONDARY);
1029 return;
1030 }
1031
1032 if (dev.is_head_pos_known(ScanHeadId::PRIMARY)) {
1033 if (dev.head_pos(ScanHeadId::PRIMARY) > dev.head_pos(ScanHeadId::SECONDARY)) {
1034 dev.advance_head_pos_by_steps(ScanHeadId::PRIMARY, Direction::BACKWARD,
1035 dev.head_pos(ScanHeadId::SECONDARY));
1036 } else {
1037 dev.set_head_pos_zero(ScanHeadId::PRIMARY);
1038 }
1039 dev.set_head_pos_zero(ScanHeadId::SECONDARY);
1040 }
1041 }
1042 } // namespace
1043
scanner_move_back_home_ta(Genesys_Device & dev)1044 void scanner_move_back_home_ta(Genesys_Device& dev)
1045 {
1046 DBG_HELPER(dbg);
1047
1048 switch (dev.model->asic_type) {
1049 case AsicType::GL842:
1050 case AsicType::GL843:
1051 case AsicType::GL845:
1052 break;
1053 default:
1054 throw SaneException("Unsupported asic type");
1055 }
1056
1057 Genesys_Register_Set local_reg = dev.reg;
1058
1059 auto scan_method = ScanMethod::TRANSPARENCY;
1060 unsigned resolution = dev.model->get_resolution_settings(scan_method).get_min_resolution_y();
1061
1062 const auto& sensor = sanei_genesys_find_sensor(&dev, resolution, 1, scan_method);
1063
1064 if (dev.is_head_pos_known(ScanHeadId::SECONDARY) &&
1065 dev.is_head_pos_known(ScanHeadId::PRIMARY) &&
1066 dev.head_pos(ScanHeadId::SECONDARY) > 1000 &&
1067 dev.head_pos(ScanHeadId::SECONDARY) <= dev.head_pos(ScanHeadId::PRIMARY))
1068 {
1069 // leave 500 steps for regular slow back home
1070 scanner_move(dev, scan_method, dev.head_pos(ScanHeadId::SECONDARY) - 500,
1071 Direction::BACKWARD);
1072 }
1073
1074 ScanSession session;
1075 session.params.xres = resolution;
1076 session.params.yres = resolution;
1077 session.params.startx = 0;
1078 session.params.starty = 40000;
1079 session.params.pixels = 50;
1080 session.params.lines = 3;
1081 session.params.depth = 8;
1082 session.params.channels = 1;
1083 session.params.scan_method = scan_method;
1084 session.params.scan_mode = ScanColorMode::GRAY;
1085 session.params.color_filter = ColorFilter::GREEN;
1086 session.params.contrast_adjustment = dev.settings.contrast;
1087 session.params.brightness_adjustment = dev.settings.brightness;
1088
1089 session.params.flags = ScanFlag::DISABLE_SHADING |
1090 ScanFlag::DISABLE_GAMMA |
1091 ScanFlag::IGNORE_STAGGER_OFFSET |
1092 ScanFlag::IGNORE_COLOR_OFFSET |
1093 ScanFlag::REVERSE;
1094
1095 compute_session(&dev, session, sensor);
1096
1097 dev.cmd_set->init_regs_for_scan_session(&dev, sensor, &local_reg, session);
1098
1099 scanner_clear_scan_and_feed_counts(dev);
1100
1101 dev.interface->write_registers(local_reg);
1102
1103 auto motor_mode = should_use_secondary_motor_mode(dev) ? MotorMode::SECONDARY
1104 : MotorMode::PRIMARY_AND_SECONDARY;
1105
1106 dev.cmd_set->set_motor_mode(dev, local_reg, motor_mode);
1107
1108 try {
1109 scanner_start_action(dev, true);
1110 } catch (...) {
1111 catch_all_exceptions(__func__, [&]() { scanner_stop_action(dev); });
1112 // restore original registers
1113 catch_all_exceptions(__func__, [&]() { dev.interface->write_registers(dev.reg); });
1114 throw;
1115 }
1116
1117 if (is_testing_mode()) {
1118 dev.interface->test_checkpoint("move_back_home_ta");
1119
1120 handle_motor_position_after_move_back_home_ta(dev, motor_mode);
1121
1122 scanner_stop_action(dev);
1123 dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY);
1124 return;
1125 }
1126
1127 for (unsigned i = 0; i < 1200; ++i) {
1128
1129 auto status = scanner_read_status(dev);
1130
1131 if (status.is_at_home) {
1132 dbg.log(DBG_info, "TA reached home position");
1133
1134 handle_motor_position_after_move_back_home_ta(dev, motor_mode);
1135
1136 scanner_stop_action(dev);
1137 dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY);
1138 return;
1139 }
1140
1141 dev.interface->sleep_ms(100);
1142 }
1143
1144 throw SaneException("Timeout waiting for XPA lamp to park");
1145 }
1146
scanner_search_strip(Genesys_Device & dev,bool forward,bool black)1147 void scanner_search_strip(Genesys_Device& dev, bool forward, bool black)
1148 {
1149 DBG_HELPER_ARGS(dbg, "%s %s", black ? "black" : "white", forward ? "forward" : "reverse");
1150
1151 if (dev.model->asic_type == AsicType::GL841 && !black && forward) {
1152 dev.frontend.set_gain(0, 0xff);
1153 dev.frontend.set_gain(1, 0xff);
1154 dev.frontend.set_gain(2, 0xff);
1155 }
1156
1157 // set up for a gray scan at lowest dpi
1158 const auto& resolution_settings = dev.model->get_resolution_settings(dev.settings.scan_method);
1159 unsigned dpi = resolution_settings.get_min_resolution_x();
1160 unsigned channels = 1;
1161
1162 auto& sensor = sanei_genesys_find_sensor(&dev, dpi, channels, dev.settings.scan_method);
1163 dev.cmd_set->set_fe(&dev, sensor, AFE_SET);
1164 scanner_stop_action(dev);
1165
1166
1167 // shading calibration is done with dev.motor.base_ydpi
1168 unsigned lines = static_cast<unsigned>(dev.model->y_size_calib_mm * dpi / MM_PER_INCH);
1169 if (dev.model->asic_type == AsicType::GL841) {
1170 lines = 10; // TODO: use dev.model->search_lines
1171 lines = static_cast<unsigned>((lines * dpi) / MM_PER_INCH);
1172 }
1173
1174 unsigned pixels = dev.model->x_size_calib_mm * dpi / MM_PER_INCH;
1175
1176 dev.set_head_pos_zero(ScanHeadId::PRIMARY);
1177
1178 unsigned length = 20;
1179 if (dev.model->asic_type == AsicType::GL841) {
1180 // 20 cm max length for calibration sheet
1181 length = static_cast<unsigned>(((200 * dpi) / MM_PER_INCH) / lines);
1182 }
1183
1184 auto local_reg = dev.reg;
1185
1186 ScanSession session;
1187 session.params.xres = dpi;
1188 session.params.yres = dpi;
1189 session.params.startx = 0;
1190 session.params.starty = 0;
1191 session.params.pixels = pixels;
1192 session.params.lines = lines;
1193 session.params.depth = 8;
1194 session.params.channels = channels;
1195 session.params.scan_method = dev.settings.scan_method;
1196 session.params.scan_mode = ScanColorMode::GRAY;
1197 session.params.color_filter = ColorFilter::RED;
1198 session.params.contrast_adjustment = dev.settings.contrast;
1199 session.params.brightness_adjustment = dev.settings.brightness;
1200 session.params.flags = ScanFlag::DISABLE_SHADING |
1201 ScanFlag::DISABLE_GAMMA;
1202 if (dev.model->asic_type != AsicType::GL841 && !forward) {
1203 session.params.flags |= ScanFlag::REVERSE;
1204 }
1205 compute_session(&dev, session, sensor);
1206
1207 dev.cmd_set->init_regs_for_scan_session(&dev, sensor, &local_reg, session);
1208
1209 dev.interface->write_registers(local_reg);
1210
1211 dev.cmd_set->begin_scan(&dev, sensor, &local_reg, true);
1212
1213 if (is_testing_mode()) {
1214 dev.interface->test_checkpoint("search_strip");
1215 scanner_stop_action(dev);
1216 return;
1217 }
1218
1219 wait_until_buffer_non_empty(&dev);
1220
1221 // now we're on target, we can read data
1222 auto image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes);
1223
1224 scanner_stop_action(dev);
1225
1226 unsigned pass = 0;
1227 if (dbg_log_image_data()) {
1228 char title[80];
1229 std::sprintf(title, "gl_search_strip_%s_%s%02d.tiff",
1230 black ? "black" : "white", forward ? "fwd" : "bwd", pass);
1231 write_tiff_file(title, image);
1232 }
1233
1234 // loop until strip is found or maximum pass number done
1235 bool found = false;
1236 while (pass < length && !found) {
1237 dev.interface->write_registers(local_reg);
1238
1239 // now start scan
1240 dev.cmd_set->begin_scan(&dev, sensor, &local_reg, true);
1241
1242 wait_until_buffer_non_empty(&dev);
1243
1244 // now we're on target, we can read data
1245 image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes);
1246
1247 scanner_stop_action(dev);
1248
1249 if (dbg_log_image_data()) {
1250 char title[80];
1251 std::sprintf(title, "gl_search_strip_%s_%s%02d.tiff",
1252 black ? "black" : "white",
1253 forward ? "fwd" : "bwd", static_cast<int>(pass));
1254 write_tiff_file(title, image);
1255 }
1256
1257 unsigned white_level = 90;
1258 unsigned black_level = 60;
1259
1260 std::size_t count = 0;
1261 // Search data to find black strip
1262 // When searching forward, we only need one line of the searched color since we
1263 // will scan forward. But when doing backward search, we need all the area of the ame color
1264 if (forward) {
1265
1266 for (std::size_t y = 0; y < image.get_height() && !found; y++) {
1267 count = 0;
1268
1269 // count of white/black pixels depending on the color searched
1270 for (std::size_t x = 0; x < image.get_width(); x++) {
1271
1272 // when searching for black, detect white pixels
1273 if (black && image.get_raw_channel(x, y, 0) > white_level) {
1274 count++;
1275 }
1276
1277 // when searching for white, detect black pixels
1278 if (!black && image.get_raw_channel(x, y, 0) < black_level) {
1279 count++;
1280 }
1281 }
1282
1283 // at end of line, if count >= 3%, line is not fully of the desired color
1284 // so we must go to next line of the buffer */
1285 // count*100/pixels < 3
1286
1287 auto found_percentage = (count * 100 / image.get_width());
1288 if (found_percentage < 3) {
1289 found = 1;
1290 DBG(DBG_data, "%s: strip found forward during pass %d at line %zu\n", __func__,
1291 pass, y);
1292 } else {
1293 DBG(DBG_data, "%s: pixels=%zu, count=%zu (%zu%%)\n", __func__,
1294 image.get_width(), count, found_percentage);
1295 }
1296 }
1297 } else {
1298 /* since calibration scans are done forward, we need the whole area
1299 to be of the required color when searching backward
1300 */
1301 count = 0;
1302 for (std::size_t y = 0; y < image.get_height(); y++) {
1303 // count of white/black pixels depending on the color searched
1304 for (std::size_t x = 0; x < image.get_width(); x++) {
1305 // when searching for black, detect white pixels
1306 if (black && image.get_raw_channel(x, y, 0) > white_level) {
1307 count++;
1308 }
1309 // when searching for white, detect black pixels
1310 if (!black && image.get_raw_channel(x, y, 0) < black_level) {
1311 count++;
1312 }
1313 }
1314 }
1315
1316 // at end of area, if count >= 3%, area is not fully of the desired color
1317 // so we must go to next buffer
1318 auto found_percentage = count * 100 / (image.get_width() * image.get_height());
1319 if (found_percentage < 3) {
1320 found = 1;
1321 DBG(DBG_data, "%s: strip found backward during pass %d \n", __func__, pass);
1322 } else {
1323 DBG(DBG_data, "%s: pixels=%zu, count=%zu (%zu%%)\n", __func__, image.get_width(),
1324 count, found_percentage);
1325 }
1326 }
1327 pass++;
1328 }
1329
1330 if (found) {
1331 DBG(DBG_info, "%s: %s strip found\n", __func__, black ? "black" : "white");
1332 } else {
1333 throw SaneException(SANE_STATUS_UNSUPPORTED, "%s strip not found",
1334 black ? "black" : "white");
1335 }
1336 }
1337
dark_average_channel(const Image & image,unsigned black,unsigned channel)1338 static int dark_average_channel(const Image& image, unsigned black, unsigned channel)
1339 {
1340 auto channels = get_pixel_channels(image.get_format());
1341
1342 unsigned avg[3];
1343
1344 // computes average values on black margin
1345 for (unsigned ch = 0; ch < channels; ch++) {
1346 avg[ch] = 0;
1347 unsigned count = 0;
1348 // FIXME: start with the second line because the black pixels often have noise on the first
1349 // line; the cause is probably incorrectly cleaned up previous scan
1350 for (std::size_t y = 1; y < image.get_height(); y++) {
1351 for (unsigned j = 0; j < black; j++) {
1352 avg[ch] += image.get_raw_channel(j, y, ch);
1353 count++;
1354 }
1355 }
1356 if (count > 0) {
1357 avg[ch] /= count;
1358 }
1359 DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, ch, avg[ch]);
1360 }
1361 DBG(DBG_info, "%s: average = %d\n", __func__, avg[channel]);
1362 return avg[channel];
1363 }
1364
should_calibrate_only_active_area(const Genesys_Device & dev,const Genesys_Settings & settings)1365 bool should_calibrate_only_active_area(const Genesys_Device& dev,
1366 const Genesys_Settings& settings)
1367 {
1368 if (settings.scan_method == ScanMethod::TRANSPARENCY ||
1369 settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
1370 {
1371 if (dev.model->model_id == ModelId::CANON_4400F && settings.xres >= 4800) {
1372 return true;
1373 }
1374 if (dev.model->model_id == ModelId::CANON_8600F && settings.xres == 4800) {
1375 return true;
1376 }
1377 }
1378 return false;
1379 }
1380
scanner_offset_calibration(Genesys_Device & dev,const Genesys_Sensor & sensor,Genesys_Register_Set & regs)1381 void scanner_offset_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor,
1382 Genesys_Register_Set& regs)
1383 {
1384 DBG_HELPER(dbg);
1385
1386 if (dev.model->asic_type == AsicType::GL842 &&
1387 dev.frontend.layout.type != FrontendType::WOLFSON)
1388 {
1389 return;
1390 }
1391
1392 if (dev.model->asic_type == AsicType::GL843 &&
1393 dev.frontend.layout.type != FrontendType::WOLFSON)
1394 {
1395 return;
1396 }
1397
1398 if (dev.model->asic_type == AsicType::GL845 ||
1399 dev.model->asic_type == AsicType::GL846)
1400 {
1401 // no gain nor offset for AKM AFE
1402 std::uint8_t reg04 = dev.interface->read_register(gl846::REG_0x04);
1403 if ((reg04 & gl846::REG_0x04_FESET) == 0x02) {
1404 return;
1405 }
1406 }
1407 if (dev.model->asic_type == AsicType::GL847) {
1408 // no gain nor offset for AKM AFE
1409 std::uint8_t reg04 = dev.interface->read_register(gl847::REG_0x04);
1410 if ((reg04 & gl847::REG_0x04_FESET) == 0x02) {
1411 return;
1412 }
1413 }
1414
1415 if (dev.model->asic_type == AsicType::GL124) {
1416 std::uint8_t reg0a = dev.interface->read_register(gl124::REG_0x0A);
1417 if (((reg0a & gl124::REG_0x0A_SIFSEL) >> gl124::REG_0x0AS_SIFSEL) == 3) {
1418 return;
1419 }
1420 }
1421
1422 unsigned target_pixels = dev.model->x_size_calib_mm * sensor.full_resolution / MM_PER_INCH;
1423 unsigned start_pixel = 0;
1424 unsigned black_pixels = (sensor.black_pixels * sensor.full_resolution) / sensor.full_resolution;
1425
1426 unsigned channels = 3;
1427 unsigned lines = 1;
1428 unsigned resolution = sensor.full_resolution;
1429
1430 const Genesys_Sensor* calib_sensor = &sensor;
1431 if (dev.model->asic_type == AsicType::GL843) {
1432 lines = 8;
1433
1434 // compute divider factor to compute final pixels number
1435 const auto& dpihw_sensor = sanei_genesys_find_sensor(&dev, dev.settings.xres, channels,
1436 dev.settings.scan_method);
1437 resolution = dpihw_sensor.shading_resolution;
1438 unsigned factor = sensor.full_resolution / resolution;
1439
1440 calib_sensor = &sanei_genesys_find_sensor(&dev, resolution, channels,
1441 dev.settings.scan_method);
1442
1443 target_pixels = dev.model->x_size_calib_mm * resolution / MM_PER_INCH;
1444 black_pixels = calib_sensor->black_pixels / factor;
1445
1446 if (should_calibrate_only_active_area(dev, dev.settings)) {
1447 float offset = dev.model->x_offset_ta;
1448 start_pixel = static_cast<int>((offset * calib_sensor->get_optical_resolution()) / MM_PER_INCH);
1449
1450 float size = dev.model->x_size_ta;
1451 target_pixels = static_cast<int>((size * calib_sensor->get_optical_resolution()) / MM_PER_INCH);
1452 }
1453
1454 if (dev.model->model_id == ModelId::CANON_4400F &&
1455 dev.settings.scan_method == ScanMethod::FLATBED)
1456 {
1457 return;
1458 }
1459 }
1460
1461 if (dev.model->model_id == ModelId::CANON_5600F) {
1462 // FIXME: use same approach as for GL843 scanners
1463 lines = 8;
1464 }
1465
1466 if (dev.model->asic_type == AsicType::GL847) {
1467 calib_sensor = &sanei_genesys_find_sensor(&dev, resolution, channels,
1468 dev.settings.scan_method);
1469 }
1470
1471 ScanFlag flags = ScanFlag::DISABLE_SHADING |
1472 ScanFlag::DISABLE_GAMMA |
1473 ScanFlag::SINGLE_LINE |
1474 ScanFlag::IGNORE_STAGGER_OFFSET |
1475 ScanFlag::IGNORE_COLOR_OFFSET;
1476
1477 if (dev.settings.scan_method == ScanMethod::TRANSPARENCY ||
1478 dev.settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
1479 {
1480 flags |= ScanFlag::USE_XPA;
1481 }
1482
1483 ScanSession session;
1484 session.params.xres = resolution;
1485 session.params.yres = resolution;
1486 session.params.startx = start_pixel;
1487 session.params.starty = 0;
1488 session.params.pixels = target_pixels;
1489 session.params.lines = lines;
1490 session.params.depth = 8;
1491 session.params.channels = channels;
1492 session.params.scan_method = dev.settings.scan_method;
1493 session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
1494 session.params.color_filter = dev.model->asic_type == AsicType::GL843 ? ColorFilter::RED
1495 : dev.settings.color_filter;
1496 session.params.contrast_adjustment = dev.settings.contrast;
1497 session.params.brightness_adjustment = dev.settings.brightness;
1498 session.params.flags = flags;
1499 compute_session(&dev, session, *calib_sensor);
1500
1501 dev.cmd_set->init_regs_for_scan_session(&dev, *calib_sensor, ®s, session);
1502
1503 unsigned output_pixels = session.output_pixels;
1504
1505 sanei_genesys_set_motor_power(regs, false);
1506
1507 int top[3], bottom[3];
1508 int topavg[3], bottomavg[3], avg[3];
1509
1510 // init gain and offset
1511 for (unsigned ch = 0; ch < 3; ch++)
1512 {
1513 bottom[ch] = 10;
1514 dev.frontend.set_offset(ch, bottom[ch]);
1515 dev.frontend.set_gain(ch, 0);
1516 }
1517 dev.cmd_set->set_fe(&dev, *calib_sensor, AFE_SET);
1518
1519 // scan with bottom AFE settings
1520 dev.interface->write_registers(regs);
1521 DBG(DBG_info, "%s: starting first line reading\n", __func__);
1522
1523 dev.cmd_set->begin_scan(&dev, *calib_sensor, ®s, true);
1524
1525 if (is_testing_mode()) {
1526 dev.interface->test_checkpoint("offset_calibration");
1527 if (dev.model->asic_type == AsicType::GL842 ||
1528 dev.model->asic_type == AsicType::GL843)
1529 {
1530 scanner_stop_action_no_move(dev, regs);
1531 }
1532 return;
1533 }
1534
1535 Image first_line;
1536 if (dev.model->asic_type == AsicType::GL842 ||
1537 dev.model->asic_type == AsicType::GL843)
1538 {
1539 first_line = read_unshuffled_image_from_scanner(&dev, session,
1540 session.output_total_bytes_raw);
1541 scanner_stop_action_no_move(dev, regs);
1542 } else {
1543 first_line = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes);
1544
1545 if (dev.model->model_id == ModelId::CANON_5600F) {
1546 scanner_stop_action_no_move(dev, regs);
1547 }
1548 }
1549
1550 if (dbg_log_image_data()) {
1551 char fn[40];
1552 std::snprintf(fn, 40, "gl843_bottom_offset_%03d_%03d_%03d.tiff",
1553 bottom[0], bottom[1], bottom[2]);
1554 write_tiff_file(fn, first_line);
1555 }
1556
1557 for (unsigned ch = 0; ch < 3; ch++) {
1558 bottomavg[ch] = dark_average_channel(first_line, black_pixels, ch);
1559 DBG(DBG_info, "%s: bottom avg %d=%d\n", __func__, ch, bottomavg[ch]);
1560 }
1561
1562 // now top value
1563 for (unsigned ch = 0; ch < 3; ch++) {
1564 top[ch] = 255;
1565 dev.frontend.set_offset(ch, top[ch]);
1566 }
1567 dev.cmd_set->set_fe(&dev, *calib_sensor, AFE_SET);
1568
1569 // scan with top AFE values
1570 dev.interface->write_registers(regs);
1571 DBG(DBG_info, "%s: starting second line reading\n", __func__);
1572
1573 dev.cmd_set->begin_scan(&dev, *calib_sensor, ®s, true);
1574
1575 Image second_line;
1576 if (dev.model->asic_type == AsicType::GL842 ||
1577 dev.model->asic_type == AsicType::GL843)
1578 {
1579 second_line = read_unshuffled_image_from_scanner(&dev, session,
1580 session.output_total_bytes_raw);
1581 scanner_stop_action_no_move(dev, regs);
1582 } else {
1583 second_line = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes);
1584
1585 if (dev.model->model_id == ModelId::CANON_5600F) {
1586 scanner_stop_action_no_move(dev, regs);
1587 }
1588 }
1589
1590 for (unsigned ch = 0; ch < 3; ch++){
1591 topavg[ch] = dark_average_channel(second_line, black_pixels, ch);
1592 DBG(DBG_info, "%s: top avg %d=%d\n", __func__, ch, topavg[ch]);
1593 }
1594
1595 unsigned pass = 0;
1596
1597 std::vector<std::uint8_t> debug_image;
1598 std::size_t debug_image_lines = 0;
1599 std::string debug_image_info;
1600
1601 // loop until acceptable level
1602 while ((pass < 32) && ((top[0] - bottom[0] > 1) ||
1603 (top[1] - bottom[1] > 1) ||
1604 (top[2] - bottom[2] > 1)))
1605 {
1606 pass++;
1607
1608 for (unsigned ch = 0; ch < 3; ch++) {
1609 if (top[ch] - bottom[ch] > 1) {
1610 dev.frontend.set_offset(ch, (top[ch] + bottom[ch]) / 2);
1611 }
1612 }
1613 dev.cmd_set->set_fe(&dev, *calib_sensor, AFE_SET);
1614
1615 // scan with no move
1616 dev.interface->write_registers(regs);
1617 DBG(DBG_info, "%s: starting second line reading\n", __func__);
1618 dev.cmd_set->begin_scan(&dev, *calib_sensor, ®s, true);
1619
1620 if (dev.model->asic_type == AsicType::GL842 ||
1621 dev.model->asic_type == AsicType::GL843)
1622 {
1623 second_line = read_unshuffled_image_from_scanner(&dev, session,
1624 session.output_total_bytes_raw);
1625 scanner_stop_action_no_move(dev, regs);
1626 } else {
1627 second_line = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes);
1628
1629 if (dev.model->model_id == ModelId::CANON_5600F) {
1630 scanner_stop_action_no_move(dev, regs);
1631 }
1632 }
1633
1634 if (dbg_log_image_data()) {
1635 char title[100];
1636 std::snprintf(title, 100, "lines: %d pixels_per_line: %d offsets[0..2]: %d %d %d\n",
1637 lines, output_pixels,
1638 dev.frontend.get_offset(0),
1639 dev.frontend.get_offset(1),
1640 dev.frontend.get_offset(2));
1641 debug_image_info += title;
1642 std::copy(second_line.get_row_ptr(0),
1643 second_line.get_row_ptr(0) + second_line.get_row_bytes() * second_line.get_height(),
1644 std::back_inserter(debug_image));
1645 debug_image_lines += lines;
1646 }
1647
1648 for (unsigned ch = 0; ch < 3; ch++) {
1649 avg[ch] = dark_average_channel(second_line, black_pixels, ch);
1650 DBG(DBG_info, "%s: avg[%d]=%d offset=%d\n", __func__, ch, avg[ch],
1651 dev.frontend.get_offset(ch));
1652 }
1653
1654 // compute new boundaries
1655 for (unsigned ch = 0; ch < 3; ch++) {
1656 if (topavg[ch] >= avg[ch]) {
1657 topavg[ch] = avg[ch];
1658 top[ch] = dev.frontend.get_offset(ch);
1659 } else {
1660 bottomavg[ch] = avg[ch];
1661 bottom[ch] = dev.frontend.get_offset(ch);
1662 }
1663 }
1664 }
1665
1666 if (dbg_log_image_data()) {
1667 sanei_genesys_write_file("gl_offset_all_desc.txt",
1668 reinterpret_cast<const std::uint8_t*>(debug_image_info.data()),
1669 debug_image_info.size());
1670 write_tiff_file("gl_offset_all.tiff", debug_image.data(), session.params.depth, channels,
1671 output_pixels, debug_image_lines);
1672 }
1673
1674 DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
1675 dev.frontend.get_offset(0),
1676 dev.frontend.get_offset(1),
1677 dev.frontend.get_offset(2));
1678 }
1679
1680 /* With offset and coarse calibration we only want to get our input range into
1681 a reasonable shape. the fine calibration of the upper and lower bounds will
1682 be done with shading.
1683 */
scanner_coarse_gain_calibration(Genesys_Device & dev,const Genesys_Sensor & sensor,Genesys_Register_Set & regs,unsigned dpi)1684 void scanner_coarse_gain_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor,
1685 Genesys_Register_Set& regs, unsigned dpi)
1686 {
1687 DBG_HELPER_ARGS(dbg, "dpi = %d", dpi);
1688
1689 if (dev.model->asic_type == AsicType::GL842 &&
1690 dev.frontend.layout.type != FrontendType::WOLFSON)
1691 {
1692 return;
1693 }
1694
1695 if (dev.model->asic_type == AsicType::GL843 &&
1696 dev.frontend.layout.type != FrontendType::WOLFSON)
1697 {
1698 return;
1699 }
1700
1701 if (dev.model->asic_type == AsicType::GL845 ||
1702 dev.model->asic_type == AsicType::GL846)
1703 {
1704 // no gain nor offset for AKM AFE
1705 std::uint8_t reg04 = dev.interface->read_register(gl846::REG_0x04);
1706 if ((reg04 & gl846::REG_0x04_FESET) == 0x02) {
1707 return;
1708 }
1709 }
1710
1711 if (dev.model->asic_type == AsicType::GL847) {
1712 // no gain nor offset for AKM AFE
1713 std::uint8_t reg04 = dev.interface->read_register(gl847::REG_0x04);
1714 if ((reg04 & gl847::REG_0x04_FESET) == 0x02) {
1715 return;
1716 }
1717 }
1718
1719 if (dev.model->asic_type == AsicType::GL124) {
1720 // no gain nor offset for TI AFE
1721 std::uint8_t reg0a = dev.interface->read_register(gl124::REG_0x0A);
1722 if (((reg0a & gl124::REG_0x0A_SIFSEL) >> gl124::REG_0x0AS_SIFSEL) == 3) {
1723 return;
1724 }
1725 }
1726
1727 if (dev.model->asic_type == AsicType::GL841) {
1728 // feed to white strip if needed
1729 if (dev.model->y_offset_calib_white > 0) {
1730 unsigned move = static_cast<unsigned>(
1731 (dev.model->y_offset_calib_white * (dev.motor.base_ydpi)) / MM_PER_INCH);
1732 scanner_move(dev, dev.model->default_method, move, Direction::FORWARD);
1733 }
1734 }
1735
1736 // coarse gain calibration is always done in color mode
1737 unsigned channels = 3;
1738
1739 unsigned resolution = sensor.full_resolution;
1740 if (dev.model->asic_type == AsicType::GL841) {
1741 const auto& dpihw_sensor = sanei_genesys_find_sensor(&dev, dev.settings.xres, channels,
1742 dev.settings.scan_method);
1743 resolution = dpihw_sensor.shading_resolution;
1744 }
1745
1746 if (dev.model->asic_type == AsicType::GL842 ||
1747 dev.model->asic_type == AsicType::GL843)
1748 {
1749 const auto& dpihw_sensor = sanei_genesys_find_sensor(&dev, dpi, channels,
1750 dev.settings.scan_method);
1751 resolution = dpihw_sensor.shading_resolution;
1752 }
1753
1754 float coeff = 1;
1755
1756 // Follow CKSEL
1757 if (dev.model->sensor_id == SensorId::CCD_KVSS080 ||
1758 dev.model->asic_type == AsicType::GL845 ||
1759 dev.model->asic_type == AsicType::GL846 ||
1760 dev.model->asic_type == AsicType::GL847 ||
1761 dev.model->asic_type == AsicType::GL124)
1762 {
1763 if (dev.settings.xres < sensor.full_resolution) {
1764 coeff = 0.9f;
1765 }
1766 }
1767
1768 unsigned lines = 10;
1769 if (dev.model->asic_type == AsicType::GL841) {
1770 lines = 1;
1771 }
1772
1773 const Genesys_Sensor* calib_sensor = &sensor;
1774 if (dev.model->asic_type == AsicType::GL841 ||
1775 dev.model->asic_type == AsicType::GL842 ||
1776 dev.model->asic_type == AsicType::GL843 ||
1777 dev.model->asic_type == AsicType::GL847)
1778 {
1779 calib_sensor = &sanei_genesys_find_sensor(&dev, resolution, channels,
1780 dev.settings.scan_method);
1781 }
1782
1783 ScanFlag flags = ScanFlag::DISABLE_SHADING |
1784 ScanFlag::DISABLE_GAMMA |
1785 ScanFlag::SINGLE_LINE |
1786 ScanFlag::IGNORE_STAGGER_OFFSET |
1787 ScanFlag::IGNORE_COLOR_OFFSET;
1788
1789 if (dev.settings.scan_method == ScanMethod::TRANSPARENCY ||
1790 dev.settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
1791 {
1792 flags |= ScanFlag::USE_XPA;
1793 }
1794
1795 ScanSession session;
1796 session.params.xres = resolution;
1797 session.params.yres = dev.model->asic_type == AsicType::GL841 ? dev.settings.yres : resolution;
1798 session.params.startx = 0;
1799 session.params.starty = 0;
1800 session.params.pixels = dev.model->x_size_calib_mm * resolution / MM_PER_INCH;
1801 session.params.lines = lines;
1802 session.params.depth = dev.model->asic_type == AsicType::GL841 ? 16 : 8;
1803 session.params.channels = channels;
1804 session.params.scan_method = dev.settings.scan_method;
1805 session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
1806 session.params.color_filter = dev.settings.color_filter;
1807 session.params.contrast_adjustment = dev.settings.contrast;
1808 session.params.brightness_adjustment = dev.settings.brightness;
1809 session.params.flags = flags;
1810 compute_session(&dev, session, *calib_sensor);
1811
1812 std::size_t pixels = session.output_pixels;
1813
1814 try {
1815 dev.cmd_set->init_regs_for_scan_session(&dev, *calib_sensor, ®s, session);
1816 } catch (...) {
1817 if (dev.model->asic_type != AsicType::GL841) {
1818 catch_all_exceptions(__func__, [&](){ sanei_genesys_set_motor_power(regs, false); });
1819 }
1820 throw;
1821 }
1822
1823 if (dev.model->asic_type != AsicType::GL841) {
1824 sanei_genesys_set_motor_power(regs, false);
1825 }
1826
1827 dev.interface->write_registers(regs);
1828
1829 if (dev.model->asic_type != AsicType::GL841) {
1830 dev.cmd_set->set_fe(&dev, *calib_sensor, AFE_SET);
1831 }
1832 dev.cmd_set->begin_scan(&dev, *calib_sensor, ®s, true);
1833
1834 if (is_testing_mode()) {
1835 dev.interface->test_checkpoint("coarse_gain_calibration");
1836 scanner_stop_action(dev);
1837 dev.cmd_set->move_back_home(&dev, true);
1838 return;
1839 }
1840
1841 Image image;
1842 if (dev.model->asic_type == AsicType::GL842 ||
1843 dev.model->asic_type == AsicType::GL843)
1844 {
1845 image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes_raw);
1846 } else if (dev.model->asic_type == AsicType::GL124) {
1847 // BUG: we probably want to read whole image, not just first line
1848 image = read_unshuffled_image_from_scanner(&dev, session, session.output_line_bytes);
1849 } else {
1850 image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes);
1851 }
1852
1853 if (dev.model->asic_type == AsicType::GL842 ||
1854 dev.model->asic_type == AsicType::GL843)
1855 {
1856 scanner_stop_action_no_move(dev, regs);
1857 }
1858
1859 if (dbg_log_image_data()) {
1860 write_tiff_file("gl_coarse_gain.tiff", image);
1861 }
1862
1863 for (unsigned ch = 0; ch < channels; ch++) {
1864 float curr_output = 0;
1865 float target_value = 0;
1866
1867 if (dev.model->asic_type == AsicType::GL841 ||
1868 dev.model->asic_type == AsicType::GL842 ||
1869 dev.model->asic_type == AsicType::GL843)
1870 {
1871 std::vector<std::uint16_t> values;
1872 // FIXME: start from the second line because the first line often has artifacts. Probably
1873 // caused by unclean cleanup of previous scan
1874 for (std::size_t x = pixels / 4; x < (pixels * 3 / 4); x++) {
1875 values.push_back(image.get_raw_channel(x, 1, ch));
1876 }
1877
1878 // pick target value at 95th percentile of all values. There may be a lot of black values
1879 // in transparency scans for example
1880 std::sort(values.begin(), values.end());
1881 curr_output = static_cast<float>(values[unsigned((values.size() - 1) * 0.95)]);
1882 target_value = calib_sensor->gain_white_ref * coeff;
1883
1884 } else {
1885 // FIXME: use the GL843 approach
1886 auto width = image.get_width();
1887
1888 std::uint64_t total = 0;
1889 for (std::size_t x = width / 4; x < (width * 3 / 4); x++) {
1890 total += image.get_raw_channel(x, 0, ch);
1891 }
1892
1893 curr_output = total / (width / 2);
1894 target_value = calib_sensor->gain_white_ref * coeff;
1895 }
1896
1897 std::uint8_t out_gain = compute_frontend_gain(curr_output, target_value,
1898 dev.frontend.layout.type);
1899 dev.frontend.set_gain(ch, out_gain);
1900
1901 DBG(DBG_proc, "%s: channel %d, curr=%f, target=%f, out_gain:%d\n", __func__, ch,
1902 curr_output, target_value, out_gain);
1903
1904 if (dev.model->asic_type == AsicType::GL841 &&
1905 target_value / curr_output > 30)
1906 {
1907 DBG(DBG_error0, "****************************************\n");
1908 DBG(DBG_error0, "* *\n");
1909 DBG(DBG_error0, "* Extremely low Brightness detected. *\n");
1910 DBG(DBG_error0, "* Check the scanning head is *\n");
1911 DBG(DBG_error0, "* unlocked and moving. *\n");
1912 DBG(DBG_error0, "* *\n");
1913 DBG(DBG_error0, "****************************************\n");
1914 throw SaneException(SANE_STATUS_JAMMED, "scanning head is locked");
1915 }
1916
1917 dbg.vlog(DBG_info, "gain=(%d, %d, %d)", dev.frontend.get_gain(0), dev.frontend.get_gain(1),
1918 dev.frontend.get_gain(2));
1919 }
1920
1921 if (dev.model->is_cis) {
1922 std::uint8_t min_gain = std::min({dev.frontend.get_gain(0),
1923 dev.frontend.get_gain(1),
1924 dev.frontend.get_gain(2)});
1925
1926 dev.frontend.set_gain(0, min_gain);
1927 dev.frontend.set_gain(1, min_gain);
1928 dev.frontend.set_gain(2, min_gain);
1929 }
1930
1931 dbg.vlog(DBG_info, "final gain=(%d, %d, %d)", dev.frontend.get_gain(0),
1932 dev.frontend.get_gain(1), dev.frontend.get_gain(2));
1933
1934 scanner_stop_action(dev);
1935
1936 dev.cmd_set->move_back_home(&dev, true);
1937 }
1938
1939 namespace gl124 {
1940 void move_to_calibration_area(Genesys_Device* dev, const Genesys_Sensor& sensor,
1941 Genesys_Register_Set& regs);
1942 } // namespace gl124
1943
scanner_led_calibration(Genesys_Device & dev,const Genesys_Sensor & sensor,Genesys_Register_Set & regs)1944 SensorExposure scanner_led_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor,
1945 Genesys_Register_Set& regs)
1946 {
1947 DBG_HELPER(dbg);
1948
1949 float move = 0;
1950
1951 if (dev.model->asic_type == AsicType::GL841) {
1952 if (dev.model->y_offset_calib_white > 0) {
1953 move = (dev.model->y_offset_calib_white * (dev.motor.base_ydpi)) / MM_PER_INCH;
1954 scanner_move(dev, dev.model->default_method, static_cast<unsigned>(move),
1955 Direction::FORWARD);
1956 }
1957 } else if (dev.model->asic_type == AsicType::GL842 ||
1958 dev.model->asic_type == AsicType::GL843)
1959 {
1960 // do nothing
1961 } else if (dev.model->asic_type == AsicType::GL845 ||
1962 dev.model->asic_type == AsicType::GL846 ||
1963 dev.model->asic_type == AsicType::GL847)
1964 {
1965 move = dev.model->y_offset_calib_white;
1966 move = static_cast<float>((move * (dev.motor.base_ydpi / 4)) / MM_PER_INCH);
1967 if (move > 20) {
1968 scanner_move(dev, dev.model->default_method, static_cast<unsigned>(move),
1969 Direction::FORWARD);
1970 }
1971 } else if (dev.model->asic_type == AsicType::GL124) {
1972 gl124::move_to_calibration_area(&dev, sensor, regs);
1973 }
1974
1975
1976 unsigned channels = 3;
1977 unsigned resolution = sensor.shading_resolution;
1978 const auto& calib_sensor = sanei_genesys_find_sensor(&dev, resolution, channels,
1979 dev.settings.scan_method);
1980
1981 if (dev.model->asic_type == AsicType::GL841 ||
1982 dev.model->asic_type == AsicType::GL845 ||
1983 dev.model->asic_type == AsicType::GL846 ||
1984 dev.model->asic_type == AsicType::GL847 ||
1985 dev.model->asic_type == AsicType::GL124)
1986 {
1987 regs = dev.reg; // FIXME: apply this to all ASICs
1988 }
1989
1990 ScanSession session;
1991 session.params.xres = resolution;
1992 session.params.yres = resolution;
1993 session.params.startx = 0;
1994 session.params.starty = 0;
1995 session.params.pixels = dev.model->x_size_calib_mm * resolution / MM_PER_INCH;
1996 session.params.lines = 1;
1997 session.params.depth = 16;
1998 session.params.channels = channels;
1999 session.params.scan_method = dev.settings.scan_method;
2000 session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
2001 session.params.color_filter = dev.settings.color_filter;
2002 session.params.contrast_adjustment = dev.settings.contrast;
2003 session.params.brightness_adjustment = dev.settings.brightness;
2004 session.params.flags = ScanFlag::DISABLE_SHADING |
2005 ScanFlag::DISABLE_GAMMA |
2006 ScanFlag::SINGLE_LINE |
2007 ScanFlag::IGNORE_STAGGER_OFFSET |
2008 ScanFlag::IGNORE_COLOR_OFFSET;
2009 compute_session(&dev, session, calib_sensor);
2010
2011 dev.cmd_set->init_regs_for_scan_session(&dev, calib_sensor, ®s, session);
2012
2013 std::uint16_t exp[3];
2014
2015 exp[0] = calib_sensor.exposure.red;
2016 exp[1] = calib_sensor.exposure.green;
2017 exp[2] = calib_sensor.exposure.blue;
2018
2019 std::uint16_t target = sensor.gain_white_ref * 256;
2020
2021 std::uint16_t top[3] = {};
2022 std::uint16_t bottom[3] = {};
2023
2024 if (dev.model->asic_type == AsicType::GL845 ||
2025 dev.model->asic_type == AsicType::GL846)
2026 {
2027 bottom[0] = 29000;
2028 bottom[1] = 29000;
2029 bottom[2] = 29000;
2030
2031 top[0] = 41000;
2032 top[1] = 51000;
2033 top[2] = 51000;
2034 } else if (dev.model->asic_type == AsicType::GL847) {
2035 bottom[0] = 28000;
2036 bottom[1] = 28000;
2037 bottom[2] = 28000;
2038
2039 top[0] = 32000;
2040 top[1] = 32000;
2041 top[2] = 32000;
2042 }
2043
2044 if (dev.model->asic_type == AsicType::GL845 ||
2045 dev.model->asic_type == AsicType::GL846 ||
2046 dev.model->asic_type == AsicType::GL847 ||
2047 dev.model->asic_type == AsicType::GL124)
2048 {
2049 sanei_genesys_set_motor_power(regs, false);
2050 }
2051
2052 bool acceptable = false;
2053 for (unsigned i_test = 0; i_test < 100 && !acceptable; ++i_test) {
2054 regs_set_exposure(dev.model->asic_type, regs, { exp[0], exp[1], exp[2] });
2055
2056 dev.interface->write_registers(regs);
2057
2058 dbg.log(DBG_info, "starting line reading");
2059 dev.cmd_set->begin_scan(&dev, calib_sensor, ®s, true);
2060
2061 if (is_testing_mode()) {
2062 dev.interface->test_checkpoint("led_calibration");
2063 if (dev.model->asic_type == AsicType::GL841) {
2064 scanner_stop_action(dev);
2065 dev.cmd_set->move_back_home(&dev, true);
2066 } else if (dev.model->asic_type == AsicType::GL124) {
2067 scanner_stop_action(dev);
2068 } else {
2069 scanner_stop_action(dev);
2070 dev.cmd_set->move_back_home(&dev, true);
2071 }
2072 return { exp[0], exp[1], exp[2] };
2073 }
2074
2075 auto image = read_unshuffled_image_from_scanner(&dev, session, session.output_line_bytes);
2076
2077 scanner_stop_action(dev);
2078
2079 if (dbg_log_image_data()) {
2080 char fn[30];
2081 std::snprintf(fn, 30, "gl_led_%02d.tiff", i_test);
2082 write_tiff_file(fn, image);
2083 }
2084
2085 int avg[3];
2086 for (unsigned ch = 0; ch < channels; ch++) {
2087 avg[ch] = 0;
2088 for (std::size_t x = 0; x < image.get_width(); x++) {
2089 avg[ch] += image.get_raw_channel(x, 0, ch);
2090 }
2091 avg[ch] /= image.get_width();
2092 }
2093
2094 dbg.vlog(DBG_info, "average: %d, %d, %d", avg[0], avg[1], avg[2]);
2095
2096 acceptable = true;
2097
2098 if (dev.model->asic_type == AsicType::GL845 ||
2099 dev.model->asic_type == AsicType::GL846)
2100 {
2101 for (unsigned i = 0; i < 3; i++) {
2102 if (avg[i] < bottom[i]) {
2103 if (avg[i] != 0) {
2104 exp[i] = (exp[i] * bottom[i]) / avg[i];
2105 } else {
2106 exp[i] *= 10;
2107 }
2108 acceptable = false;
2109 }
2110 if (avg[i] > top[i]) {
2111 if (avg[i] != 0) {
2112 exp[i] = (exp[i] * top[i]) / avg[i];
2113 } else {
2114 exp[i] *= 10;
2115 }
2116 acceptable = false;
2117 }
2118 }
2119 } else if (dev.model->asic_type == AsicType::GL847) {
2120 for (unsigned i = 0; i < 3; i++) {
2121 if (avg[i] < bottom[i] || avg[i] > top[i]) {
2122 auto target = (bottom[i] + top[i]) / 2;
2123 if (avg[i] != 0) {
2124 exp[i] = (exp[i] * target) / avg[i];
2125 } else {
2126 exp[i] *= 10;
2127 }
2128
2129 acceptable = false;
2130 }
2131 }
2132 } else if (dev.model->asic_type == AsicType::GL841 ||
2133 dev.model->asic_type == AsicType::GL124)
2134 {
2135 for (unsigned i = 0; i < 3; i++) {
2136 // we accept +- 2% delta from target
2137 if (std::abs(avg[i] - target) > target / 50) {
2138 float prev_weight = 0.5;
2139 if (avg[i] != 0) {
2140 exp[i] = exp[i] * prev_weight + ((exp[i] * target) / avg[i]) * (1 - prev_weight);
2141 } else {
2142 exp[i] = exp[i] * prev_weight + (exp[i] * 10) * (1 - prev_weight);
2143 }
2144 acceptable = false;
2145 }
2146 }
2147 }
2148 }
2149
2150 if (dev.model->asic_type == AsicType::GL845 ||
2151 dev.model->asic_type == AsicType::GL846 ||
2152 dev.model->asic_type == AsicType::GL847 ||
2153 dev.model->asic_type == AsicType::GL124)
2154 {
2155 // set these values as final ones for scan
2156 regs_set_exposure(dev.model->asic_type, dev.reg, { exp[0], exp[1], exp[2] });
2157 }
2158
2159 if (dev.model->asic_type == AsicType::GL841 ||
2160 dev.model->asic_type == AsicType::GL842 ||
2161 dev.model->asic_type == AsicType::GL843)
2162 {
2163 dev.cmd_set->move_back_home(&dev, true);
2164 }
2165
2166 if (dev.model->asic_type == AsicType::GL845 ||
2167 dev.model->asic_type == AsicType::GL846 ||
2168 dev.model->asic_type == AsicType::GL847)
2169 {
2170 if (move > 20) {
2171 dev.cmd_set->move_back_home(&dev, true);
2172 }
2173 }
2174
2175 dbg.vlog(DBG_info,"acceptable exposure: %d, %d, %d\n", exp[0], exp[1], exp[2]);
2176
2177 return { exp[0], exp[1], exp[2] };
2178 }
2179
sanei_genesys_calculate_zmod(bool two_table,std::uint32_t exposure_time,const std::vector<std::uint16_t> & slope_table,unsigned acceleration_steps,unsigned move_steps,unsigned buffer_acceleration_steps,std::uint32_t * out_z1,std::uint32_t * out_z2)2180 void sanei_genesys_calculate_zmod(bool two_table,
2181 std::uint32_t exposure_time,
2182 const std::vector<std::uint16_t>& slope_table,
2183 unsigned acceleration_steps,
2184 unsigned move_steps,
2185 unsigned buffer_acceleration_steps,
2186 std::uint32_t* out_z1, std::uint32_t* out_z2)
2187 {
2188 // acceleration total time
2189 unsigned sum = std::accumulate(slope_table.begin(), slope_table.begin() + acceleration_steps,
2190 0, std::plus<unsigned>());
2191
2192 /* Z1MOD:
2193 c = sum(slope_table; reg_stepno)
2194 d = reg_fwdstep * <cruising speed>
2195 Z1MOD = (c+d) % exposure_time
2196 */
2197 *out_z1 = (sum + buffer_acceleration_steps * slope_table[acceleration_steps - 1]) % exposure_time;
2198
2199 /* Z2MOD:
2200 a = sum(slope_table; reg_stepno)
2201 b = move_steps or 1 if 2 tables
2202 Z1MOD = (a+b) % exposure_time
2203 */
2204 if (!two_table) {
2205 sum = sum + (move_steps * slope_table[acceleration_steps - 1]);
2206 } else {
2207 sum = sum + slope_table[acceleration_steps - 1];
2208 }
2209 *out_z2 = sum % exposure_time;
2210 }
2211
2212 /**
2213 * scans a white area with motor and lamp off to get the per CCD pixel offset
2214 * that will be used to compute shading coefficient
2215 * @param dev scanner's device
2216 */
genesys_shading_calibration_impl(Genesys_Device * dev,const Genesys_Sensor & sensor,Genesys_Register_Set & local_reg,std::vector<std::uint16_t> & out_average_data,bool is_dark,const std::string & log_filename_prefix)2217 static void genesys_shading_calibration_impl(Genesys_Device* dev, const Genesys_Sensor& sensor,
2218 Genesys_Register_Set& local_reg,
2219 std::vector<std::uint16_t>& out_average_data,
2220 bool is_dark, const std::string& log_filename_prefix)
2221 {
2222 DBG_HELPER(dbg);
2223
2224 if (dev->model->asic_type == AsicType::GL646) {
2225 dev->cmd_set->init_regs_for_shading(dev, sensor, local_reg);
2226 local_reg = dev->reg;
2227 } else {
2228 local_reg = dev->reg;
2229 dev->cmd_set->init_regs_for_shading(dev, sensor, local_reg);
2230 dev->interface->write_registers(local_reg);
2231 }
2232
2233 debug_dump(DBG_info, dev->calib_session);
2234
2235 size_t size;
2236 std::uint32_t pixels_per_line;
2237
2238 if (dev->model->asic_type == AsicType::GL842 ||
2239 dev->model->asic_type == AsicType::GL843 ||
2240 dev->model->model_id == ModelId::CANON_5600F)
2241 {
2242 pixels_per_line = dev->calib_session.output_pixels;
2243 } else {
2244 // BUG: this selects incorrect pixel number
2245 pixels_per_line = dev->calib_session.params.pixels;
2246 }
2247 unsigned channels = dev->calib_session.params.channels;
2248
2249 // BUG: we are using wrong pixel number here
2250 unsigned start_offset =
2251 dev->calib_session.params.startx * sensor.full_resolution / dev->calib_session.params.xres;
2252 unsigned out_pixels_per_line = pixels_per_line + start_offset;
2253
2254 // FIXME: we set this during both dark and white calibration. A cleaner approach should
2255 // probably be used
2256 dev->average_size = channels * out_pixels_per_line;
2257
2258 out_average_data.clear();
2259 out_average_data.resize(dev->average_size);
2260
2261 if (is_dark && dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) {
2262 // FIXME: dark shading currently not supported on infrared transparency scans
2263 return;
2264 }
2265
2266 // FIXME: the current calculation is likely incorrect on non-GL843 implementations,
2267 // but this needs checking. Note the extra line when computing size.
2268 if (dev->model->asic_type == AsicType::GL842 ||
2269 dev->model->asic_type == AsicType::GL843 ||
2270 dev->model->model_id == ModelId::CANON_5600F)
2271 {
2272 size = dev->calib_session.output_total_bytes_raw;
2273 } else {
2274 size = channels * 2 * pixels_per_line * (dev->calib_session.params.lines + 1);
2275 }
2276
2277 std::vector<std::uint16_t> calibration_data(size / 2);
2278
2279 // turn off motor and lamp power for flatbed scanners, but not for sheetfed scanners
2280 // because they have a calibration sheet with a sufficient black strip
2281 if (is_dark && !dev->model->is_sheetfed) {
2282 sanei_genesys_set_lamp_power(dev, sensor, local_reg, false);
2283 } else {
2284 sanei_genesys_set_lamp_power(dev, sensor, local_reg, true);
2285 }
2286 sanei_genesys_set_motor_power(local_reg, true);
2287
2288 dev->interface->write_registers(local_reg);
2289
2290 if (is_dark) {
2291 // wait some time to let lamp to get dark
2292 dev->interface->sleep_ms(200);
2293 } else if (has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) {
2294 // make sure lamp is bright again
2295 // FIXME: what about scanners that take a long time to warm the lamp?
2296 dev->interface->sleep_ms(500);
2297 }
2298
2299 bool start_motor = !is_dark;
2300 dev->cmd_set->begin_scan(dev, sensor, &local_reg, start_motor);
2301
2302
2303 if (is_testing_mode()) {
2304 dev->interface->test_checkpoint(is_dark ? "dark_shading_calibration"
2305 : "white_shading_calibration");
2306 dev->cmd_set->end_scan(dev, &local_reg, true);
2307 return;
2308 }
2309
2310 sanei_genesys_read_data_from_scanner(dev, reinterpret_cast<std::uint8_t*>(calibration_data.data()),
2311 size);
2312
2313 dev->cmd_set->end_scan(dev, &local_reg, true);
2314
2315 if (has_flag(dev->model->flags, ModelFlag::SWAP_16BIT_DATA)) {
2316 for (std::size_t i = 0; i < size / 2; ++i) {
2317 auto value = calibration_data[i];
2318 value = ((value >> 8) & 0xff) | ((value << 8) & 0xff00);
2319 calibration_data[i] = value;
2320 }
2321 }
2322
2323 if (has_flag(dev->model->flags, ModelFlag::INVERT_PIXEL_DATA)) {
2324 for (std::size_t i = 0; i < size / 2; ++i) {
2325 calibration_data[i] = 0xffff - calibration_data[i];
2326 }
2327 }
2328
2329 std::fill(out_average_data.begin(),
2330 out_average_data.begin() + start_offset * channels, 0);
2331
2332 compute_array_percentile_approx(out_average_data.data() +
2333 start_offset * channels,
2334 calibration_data.data(),
2335 dev->calib_session.params.lines, pixels_per_line * channels,
2336 0.5f);
2337
2338 if (dbg_log_image_data()) {
2339 write_tiff_file(log_filename_prefix + "_shading.tiff", calibration_data.data(), 16,
2340 channels, pixels_per_line, dev->calib_session.params.lines);
2341 write_tiff_file(log_filename_prefix + "_average.tiff", out_average_data.data(), 16,
2342 channels, out_pixels_per_line, 1);
2343 }
2344 }
2345
2346 /*
2347 * this function builds dummy dark calibration data so that we can
2348 * compute shading coefficient in a clean way
2349 * todo: current values are hardcoded, we have to find if they
2350 * can be computed from previous calibration data (when doing offset
2351 * calibration ?)
2352 */
genesys_dark_shading_by_dummy_pixel(Genesys_Device * dev,const Genesys_Sensor & sensor)2353 static void genesys_dark_shading_by_dummy_pixel(Genesys_Device* dev, const Genesys_Sensor& sensor)
2354 {
2355 DBG_HELPER(dbg);
2356 std::uint32_t pixels_per_line;
2357 std::uint32_t skip, xend;
2358 int dummy1, dummy2, dummy3; /* dummy black average per channel */
2359
2360 if (dev->model->asic_type == AsicType::GL842 ||
2361 dev->model->asic_type == AsicType::GL843)
2362 {
2363 pixels_per_line = dev->calib_session.output_pixels;
2364 } else {
2365 pixels_per_line = dev->calib_session.params.pixels;
2366 }
2367
2368 unsigned channels = dev->calib_session.params.channels;
2369
2370 // BUG: we are using wrong pixel number here
2371 unsigned start_offset =
2372 dev->calib_session.params.startx * sensor.full_resolution / dev->calib_session.params.xres;
2373
2374 unsigned out_pixels_per_line = pixels_per_line + start_offset;
2375
2376 dev->average_size = channels * out_pixels_per_line;
2377 dev->dark_average_data.clear();
2378 dev->dark_average_data.resize(dev->average_size, 0);
2379
2380 /* we average values on 'the left' where CCD pixels are under casing and
2381 give darkest values. We then use these as dummy dark calibration */
2382 if (dev->settings.xres <= sensor.full_resolution / 2) {
2383 skip = 4;
2384 xend = 36;
2385 }
2386 else
2387 {
2388 skip = 4;
2389 xend = 68;
2390 }
2391 if (dev->model->sensor_id==SensorId::CCD_G4050 ||
2392 dev->model->sensor_id==SensorId::CCD_HP_4850C
2393 || dev->model->sensor_id==SensorId::CCD_CANON_4400F
2394 || dev->model->sensor_id==SensorId::CCD_CANON_8400F
2395 || dev->model->sensor_id==SensorId::CCD_KVSS080)
2396 {
2397 skip = 2;
2398 xend = sensor.black_pixels;
2399 }
2400
2401 /* average each channels on half left margin */
2402 dummy1 = 0;
2403 dummy2 = 0;
2404 dummy3 = 0;
2405
2406 for (unsigned x = skip + 1; x <= xend; x++) {
2407 dummy1 += dev->white_average_data[channels * x];
2408 if (channels > 1) {
2409 dummy2 += dev->white_average_data[channels * x + 1];
2410 dummy3 += dev->white_average_data[channels * x + 2];
2411 }
2412 }
2413
2414 dummy1 /= (xend - skip);
2415 if (channels > 1)
2416 {
2417 dummy2 /= (xend - skip);
2418 dummy3 /= (xend - skip);
2419 }
2420 DBG(DBG_proc, "%s: dummy1=%d, dummy2=%d, dummy3=%d \n", __func__, dummy1, dummy2, dummy3);
2421
2422 /* fill dark_average */
2423 for (unsigned x = 0; x < out_pixels_per_line; x++) {
2424 dev->dark_average_data[channels * x] = dummy1;
2425 if (channels > 1) {
2426 dev->dark_average_data[channels * x + 1] = dummy2;
2427 dev->dark_average_data[channels * x + 2] = dummy3;
2428 }
2429 }
2430 }
2431
genesys_dark_shading_by_constant(Genesys_Device & dev)2432 static void genesys_dark_shading_by_constant(Genesys_Device& dev)
2433 {
2434 dev.dark_average_data.clear();
2435 dev.dark_average_data.resize(dev.average_size, 0x0101);
2436 }
2437
genesys_repark_sensor_before_shading(Genesys_Device * dev)2438 static void genesys_repark_sensor_before_shading(Genesys_Device* dev)
2439 {
2440 DBG_HELPER(dbg);
2441 if (has_flag(dev->model->flags, ModelFlag::SHADING_REPARK)) {
2442 dev->cmd_set->move_back_home(dev, true);
2443
2444 if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
2445 dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
2446 {
2447 scanner_move_to_ta(*dev);
2448 }
2449 }
2450 }
2451
genesys_repark_sensor_after_white_shading(Genesys_Device * dev)2452 static void genesys_repark_sensor_after_white_shading(Genesys_Device* dev)
2453 {
2454 DBG_HELPER(dbg);
2455 if (has_flag(dev->model->flags, ModelFlag::SHADING_REPARK)) {
2456 dev->cmd_set->move_back_home(dev, true);
2457 }
2458 }
2459
genesys_host_shading_calibration_impl(Genesys_Device & dev,const Genesys_Sensor & sensor,std::vector<std::uint16_t> & out_average_data,bool is_dark,const std::string & log_filename_prefix)2460 static void genesys_host_shading_calibration_impl(Genesys_Device& dev, const Genesys_Sensor& sensor,
2461 std::vector<std::uint16_t>& out_average_data,
2462 bool is_dark,
2463 const std::string& log_filename_prefix)
2464 {
2465 DBG_HELPER(dbg);
2466
2467 if (is_dark && dev.settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) {
2468 // FIXME: dark shading currently not supported on infrared transparency scans
2469 return;
2470 }
2471
2472 auto local_reg = dev.reg;
2473 dev.cmd_set->init_regs_for_shading(&dev, sensor, local_reg);
2474
2475 auto& session = dev.calib_session;
2476 debug_dump(DBG_info, session);
2477
2478 // turn off motor and lamp power for flatbed scanners, but not for sheetfed scanners
2479 // because they have a calibration sheet with a sufficient black strip
2480 if (is_dark && !dev.model->is_sheetfed) {
2481 sanei_genesys_set_lamp_power(&dev, sensor, local_reg, false);
2482 } else {
2483 sanei_genesys_set_lamp_power(&dev, sensor, local_reg, true);
2484 }
2485 sanei_genesys_set_motor_power(local_reg, true);
2486
2487 dev.interface->write_registers(local_reg);
2488
2489 if (is_dark) {
2490 // wait some time to let lamp to get dark
2491 dev.interface->sleep_ms(200);
2492 } else if (has_flag(dev.model->flags, ModelFlag::DARK_CALIBRATION)) {
2493 // make sure lamp is bright again
2494 // FIXME: what about scanners that take a long time to warm the lamp?
2495 dev.interface->sleep_ms(500);
2496 }
2497
2498 bool start_motor = !is_dark;
2499 dev.cmd_set->begin_scan(&dev, sensor, &local_reg, start_motor);
2500
2501 if (is_testing_mode()) {
2502 dev.interface->test_checkpoint(is_dark ? "host_dark_shading_calibration"
2503 : "host_white_shading_calibration");
2504 dev.cmd_set->end_scan(&dev, &local_reg, true);
2505 return;
2506 }
2507
2508 Image image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes_raw);
2509 scanner_stop_action(dev);
2510
2511 auto start_offset = session.params.startx;
2512 auto out_pixels_per_line = start_offset + session.output_pixels;
2513
2514 // FIXME: we set this during both dark and white calibration. A cleaner approach should
2515 // probably be used
2516 dev.average_size = session.params.channels * out_pixels_per_line;
2517
2518 out_average_data.clear();
2519 out_average_data.resize(dev.average_size);
2520
2521 std::fill(out_average_data.begin(),
2522 out_average_data.begin() + start_offset * session.params.channels, 0);
2523
2524 compute_array_percentile_approx(out_average_data.data() +
2525 start_offset * session.params.channels,
2526 reinterpret_cast<std::uint16_t*>(image.get_row_ptr(0)),
2527 session.params.lines,
2528 session.output_pixels * session.params.channels,
2529 0.5f);
2530
2531 if (dbg_log_image_data()) {
2532 write_tiff_file(log_filename_prefix + "_host_shading.tiff", image);
2533 write_tiff_file(log_filename_prefix + "_host_average.tiff", out_average_data.data(), 16,
2534 session.params.channels, out_pixels_per_line, 1);
2535 }
2536 }
2537
genesys_dark_shading_calibration(Genesys_Device * dev,const Genesys_Sensor & sensor,Genesys_Register_Set & local_reg)2538 static void genesys_dark_shading_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
2539 Genesys_Register_Set& local_reg)
2540 {
2541 DBG_HELPER(dbg);
2542 if (has_flag(dev->model->flags, ModelFlag::HOST_SIDE_CALIBRATION_COMPLETE_SCAN)) {
2543 genesys_host_shading_calibration_impl(*dev, sensor, dev->dark_average_data, true,
2544 "gl_black");
2545 } else {
2546 genesys_shading_calibration_impl(dev, sensor, local_reg, dev->dark_average_data, true,
2547 "gl_black");
2548 }
2549 }
2550
genesys_white_shading_calibration(Genesys_Device * dev,const Genesys_Sensor & sensor,Genesys_Register_Set & local_reg)2551 static void genesys_white_shading_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
2552 Genesys_Register_Set& local_reg)
2553 {
2554 DBG_HELPER(dbg);
2555 if (has_flag(dev->model->flags, ModelFlag::HOST_SIDE_CALIBRATION_COMPLETE_SCAN)) {
2556 genesys_host_shading_calibration_impl(*dev, sensor, dev->white_average_data, false,
2557 "gl_white");
2558 } else {
2559 genesys_shading_calibration_impl(dev, sensor, local_reg, dev->white_average_data, false,
2560 "gl_white");
2561 }
2562 }
2563
2564 // This calibration uses a scan over the calibration target, comprising a black and a white strip.
2565 // (So the motor must be on.)
genesys_dark_white_shading_calibration(Genesys_Device * dev,const Genesys_Sensor & sensor,Genesys_Register_Set & local_reg)2566 static void genesys_dark_white_shading_calibration(Genesys_Device* dev,
2567 const Genesys_Sensor& sensor,
2568 Genesys_Register_Set& local_reg)
2569 {
2570 DBG_HELPER(dbg);
2571
2572 if (dev->model->asic_type == AsicType::GL646) {
2573 dev->cmd_set->init_regs_for_shading(dev, sensor, local_reg);
2574 local_reg = dev->reg;
2575 } else {
2576 local_reg = dev->reg;
2577 dev->cmd_set->init_regs_for_shading(dev, sensor, local_reg);
2578 dev->interface->write_registers(local_reg);
2579 }
2580
2581 std::size_t size;
2582 std::uint32_t pixels_per_line;
2583 unsigned int x;
2584
2585 if (dev->model->asic_type == AsicType::GL842 ||
2586 dev->model->asic_type == AsicType::GL843)
2587 {
2588 pixels_per_line = dev->calib_session.output_pixels;
2589 } else {
2590 pixels_per_line = dev->calib_session.params.pixels;
2591 }
2592
2593 unsigned channels = dev->calib_session.params.channels;
2594
2595 // BUG: we are using wrong pixel number here
2596 unsigned start_offset =
2597 dev->calib_session.params.startx * sensor.full_resolution / dev->calib_session.params.xres;
2598
2599 unsigned out_pixels_per_line = pixels_per_line + start_offset;
2600
2601 dev->average_size = channels * out_pixels_per_line;
2602
2603 dev->white_average_data.clear();
2604 dev->white_average_data.resize(dev->average_size);
2605
2606 dev->dark_average_data.clear();
2607 dev->dark_average_data.resize(dev->average_size);
2608
2609 if (dev->model->asic_type == AsicType::GL842 ||
2610 dev->model->asic_type == AsicType::GL843)
2611 {
2612 size = dev->calib_session.output_total_bytes_raw;
2613 } else {
2614 // FIXME: on GL841 this is different than dev->calib_session.output_total_bytes_raw,
2615 // needs checking
2616 size = channels * 2 * pixels_per_line * dev->calib_session.params.lines;
2617 }
2618
2619 std::vector<std::uint8_t> calibration_data(size);
2620
2621 // turn on motor and lamp power
2622 sanei_genesys_set_lamp_power(dev, sensor, local_reg, true);
2623 sanei_genesys_set_motor_power(local_reg, true);
2624
2625 dev->interface->write_registers(local_reg);
2626
2627 dev->cmd_set->begin_scan(dev, sensor, &local_reg, false);
2628
2629 if (is_testing_mode()) {
2630 dev->interface->test_checkpoint("dark_white_shading_calibration");
2631 dev->cmd_set->end_scan(dev, &local_reg, true);
2632 return;
2633 }
2634
2635 sanei_genesys_read_data_from_scanner(dev, calibration_data.data(), size);
2636
2637 dev->cmd_set->end_scan(dev, &local_reg, true);
2638
2639 if (dbg_log_image_data()) {
2640 if (dev->model->is_cis) {
2641 write_tiff_file("gl_black_white_shading.tiff", calibration_data.data(),
2642 16, 1, pixels_per_line*channels,
2643 dev->calib_session.params.lines);
2644 } else {
2645 write_tiff_file("gl_black_white_shading.tiff", calibration_data.data(),
2646 16, channels, pixels_per_line,
2647 dev->calib_session.params.lines);
2648 }
2649 }
2650
2651
2652 std::fill(dev->dark_average_data.begin(),
2653 dev->dark_average_data.begin() + start_offset * channels, 0);
2654 std::fill(dev->white_average_data.begin(),
2655 dev->white_average_data.begin() + start_offset * channels, 0);
2656
2657 std::uint16_t* average_white = dev->white_average_data.data() + start_offset * channels;
2658 std::uint16_t* average_dark = dev->dark_average_data.data() + start_offset * channels;
2659
2660 for (x = 0; x < pixels_per_line * channels; x++)
2661 {
2662 std::uint32_t dark = 0xffff;
2663 std::uint32_t white = 0;
2664
2665 for (std::size_t y = 0; y < dev->calib_session.params.lines; y++)
2666 {
2667 std::uint32_t col = calibration_data[(x + y * pixels_per_line * channels) * 2];
2668 col |=
2669 calibration_data[(x + y * pixels_per_line * channels) * 2 +
2670 1] << 8;
2671
2672 if (col > white)
2673 white = col;
2674 if (col < dark)
2675 dark = col;
2676 }
2677
2678 std::uint32_t dif = white - dark;
2679
2680 dark = dark + dif / 8;
2681 white = white - dif / 8;
2682
2683 std::uint32_t dark_count = 0;
2684 std::uint32_t dark_sum = 0;
2685
2686 std::uint32_t white_count = 0;
2687 std::uint32_t white_sum = 0;
2688
2689 for (std::size_t y = 0; y < dev->calib_session.params.lines; y++)
2690 {
2691 std::uint32_t col = calibration_data[(x + y * pixels_per_line * channels) * 2];
2692 col |=
2693 calibration_data[(x + y * pixels_per_line * channels) * 2 +
2694 1] << 8;
2695
2696 if (col >= white)
2697 {
2698 white_sum += col;
2699 white_count++;
2700 }
2701 if (col <= dark)
2702 {
2703 dark_sum += col;
2704 dark_count++;
2705 }
2706
2707 }
2708
2709 dark_sum /= dark_count;
2710 white_sum /= white_count;
2711
2712 *average_dark++ = dark_sum;
2713 *average_white++ = white_sum;
2714 }
2715
2716 if (dbg_log_image_data()) {
2717 write_tiff_file("gl_white_average.tiff", dev->white_average_data.data(), 16, channels,
2718 out_pixels_per_line, 1);
2719 write_tiff_file("gl_dark_average.tiff", dev->dark_average_data.data(), 16, channels,
2720 out_pixels_per_line, 1);
2721 }
2722 }
2723
2724 /* computes one coefficient given bright-dark value
2725 * @param coeff factor giving 1.00 gain
2726 * @param target desired target code
2727 * @param value brght-dark value
2728 * */
2729 static unsigned int
compute_coefficient(unsigned int coeff,unsigned int target,unsigned int value)2730 compute_coefficient (unsigned int coeff, unsigned int target, unsigned int value)
2731 {
2732 int result;
2733
2734 if (value > 0)
2735 {
2736 result = (coeff * target) / value;
2737 if (result >= 65535)
2738 {
2739 result = 65535;
2740 }
2741 }
2742 else
2743 {
2744 result = coeff;
2745 }
2746 return result;
2747 }
2748
2749 /** @brief compute shading coefficients for LiDE scanners
2750 * The dark/white shading is actually performed _after_ reducing
2751 * resolution via averaging. only dark/white shading data for what would be
2752 * first pixel at full resolution is used.
2753 *
2754 * scanner raw input to output value calculation:
2755 * o=(i-off)*(gain/coeff)
2756 *
2757 * from datasheet:
2758 * off=dark_average
2759 * gain=coeff*bright_target/(bright_average-dark_average)
2760 * works for dark_target==0
2761 *
2762 * what we want is these:
2763 * bright_target=(bright_average-off)*(gain/coeff)
2764 * dark_target=(dark_average-off)*(gain/coeff)
2765 * leading to
2766 * off = (dark_average*bright_target - bright_average*dark_target)/(bright_target - dark_target)
2767 * gain = (bright_target - dark_target)/(bright_average - dark_average)*coeff
2768 *
2769 * @param dev scanner's device
2770 * @param shading_data memory area where to store the computed shading coefficients
2771 * @param pixels_per_line number of pixels per line
2772 * @param words_per_color memory words per color channel
2773 * @param channels number of color channels (actually 1 or 3)
2774 * @param o shading coefficients left offset
2775 * @param coeff 4000h or 2000h depending on fast scan mode or not (GAIN4 bit)
2776 * @param target_bright value of the white target code
2777 * @param target_dark value of the black target code
2778 */
compute_averaged_planar(Genesys_Device * dev,const Genesys_Sensor & sensor,std::uint8_t * shading_data,unsigned int pixels_per_line,unsigned int words_per_color,unsigned int channels,unsigned int o,unsigned int coeff,unsigned int target_bright,unsigned int target_dark)2779 static void compute_averaged_planar(Genesys_Device * dev, const Genesys_Sensor& sensor,
2780 std::uint8_t* shading_data,
2781 unsigned int pixels_per_line,
2782 unsigned int words_per_color,
2783 unsigned int channels,
2784 unsigned int o,
2785 unsigned int coeff,
2786 unsigned int target_bright,
2787 unsigned int target_dark)
2788 {
2789 unsigned int x, i, j, br, dk, res, avgpixels, basepixels, val;
2790 unsigned int fill,factor;
2791
2792 DBG(DBG_info, "%s: pixels=%d, offset=%d\n", __func__, pixels_per_line, o);
2793
2794 /* initialize result */
2795 memset (shading_data, 0xff, words_per_color * 3 * 2);
2796
2797 /*
2798 strangely i can write 0x20000 bytes beginning at 0x00000 without overwriting
2799 slope tables - which begin at address 0x10000(for 1200dpi hw mode):
2800 memory is organized in words(2 bytes) instead of single bytes. explains
2801 quite some things
2802 */
2803 /*
2804 another one: the dark/white shading is actually performed _after_ reducing
2805 resolution via averaging. only dark/white shading data for what would be
2806 first pixel at full resolution is used.
2807 */
2808 /*
2809 scanner raw input to output value calculation:
2810 o=(i-off)*(gain/coeff)
2811
2812 from datasheet:
2813 off=dark_average
2814 gain=coeff*bright_target/(bright_average-dark_average)
2815 works for dark_target==0
2816
2817 what we want is these:
2818 bright_target=(bright_average-off)*(gain/coeff)
2819 dark_target=(dark_average-off)*(gain/coeff)
2820 leading to
2821 off = (dark_average*bright_target - bright_average*dark_target)/(bright_target - dark_target)
2822 gain = (bright_target - dark_target)/(bright_average - dark_average)*coeff
2823 */
2824 res = dev->settings.xres;
2825
2826 if (sensor.full_resolution > sensor.get_optical_resolution()) {
2827 res *= 2;
2828 }
2829
2830 // this should be evenly dividable
2831 basepixels = sensor.full_resolution / res;
2832
2833 /* gl841 supports 1/1 1/2 1/3 1/4 1/5 1/6 1/8 1/10 1/12 1/15 averaging */
2834 if (basepixels < 1)
2835 avgpixels = 1;
2836 else if (basepixels < 6)
2837 avgpixels = basepixels;
2838 else if (basepixels < 8)
2839 avgpixels = 6;
2840 else if (basepixels < 10)
2841 avgpixels = 8;
2842 else if (basepixels < 12)
2843 avgpixels = 10;
2844 else if (basepixels < 15)
2845 avgpixels = 12;
2846 else
2847 avgpixels = 15;
2848
2849 /* LiDE80 packs shading data */
2850 if (dev->model->sensor_id != SensorId::CIS_CANON_LIDE_80)
2851 {
2852 factor=1;
2853 fill=avgpixels;
2854 }
2855 else
2856 {
2857 factor=avgpixels;
2858 fill=1;
2859 }
2860
2861 DBG(DBG_info, "%s: averaging over %d pixels\n", __func__, avgpixels);
2862 DBG(DBG_info, "%s: packing factor is %d\n", __func__, factor);
2863 DBG(DBG_info, "%s: fill length is %d\n", __func__, fill);
2864
2865 for (x = 0; x <= pixels_per_line - avgpixels; x += avgpixels)
2866 {
2867 if ((x + o) * 2 * 2 + 3 > words_per_color * 2)
2868 break;
2869
2870 for (j = 0; j < channels; j++)
2871 {
2872
2873 dk = 0;
2874 br = 0;
2875 for (i = 0; i < avgpixels; i++)
2876 {
2877 // dark data
2878 dk += dev->dark_average_data[(x + i + pixels_per_line * j)];
2879 // white data
2880 br += dev->white_average_data[(x + i + pixels_per_line * j)];
2881 }
2882
2883 br /= avgpixels;
2884 dk /= avgpixels;
2885
2886 if (br * target_dark > dk * target_bright)
2887 val = 0;
2888 else if (dk * target_bright - br * target_dark >
2889 65535 * (target_bright - target_dark))
2890 val = 65535;
2891 else
2892 {
2893 val = (dk * target_bright - br * target_dark) / (target_bright - target_dark);
2894 }
2895
2896 /*fill all pixels, even if only the last one is relevant*/
2897 for (i = 0; i < fill; i++)
2898 {
2899 shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j] = val & 0xff;
2900 shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j + 1] = val >> 8;
2901 }
2902
2903 val = br - dk;
2904
2905 if (65535 * val > (target_bright - target_dark) * coeff)
2906 {
2907 val = (coeff * (target_bright - target_dark)) / val;
2908 }
2909 else
2910 {
2911 val = 65535;
2912 }
2913
2914 /*fill all pixels, even if only the last one is relevant*/
2915 for (i = 0; i < fill; i++)
2916 {
2917 shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j + 2] = val & 0xff;
2918 shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j + 3] = val >> 8;
2919 }
2920 }
2921
2922 /* fill remaining channels */
2923 for (j = channels; j < 3; j++)
2924 {
2925 for (i = 0; i < fill; i++)
2926 {
2927 shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j ] = shading_data[(x/factor + o + i) * 2 * 2 ];
2928 shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j + 1] = shading_data[(x/factor + o + i) * 2 * 2 + 1];
2929 shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j + 2] = shading_data[(x/factor + o + i) * 2 * 2 + 2];
2930 shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j + 3] = shading_data[(x/factor + o + i) * 2 * 2 + 3];
2931 }
2932 }
2933 }
2934 }
2935
color_order_to_cmat(ColorOrder color_order)2936 static std::array<unsigned, 3> color_order_to_cmat(ColorOrder color_order)
2937 {
2938 switch (color_order) {
2939 case ColorOrder::RGB: return {0, 1, 2};
2940 case ColorOrder::GBR: return {2, 0, 1};
2941 default:
2942 throw std::logic_error("Unknown color order");
2943 }
2944 }
2945
2946 /**
2947 * Computes shading coefficient using formula in data sheet. 16bit data values
2948 * manipulated here are little endian. For now we assume deletion scanning type
2949 * and that there is always 3 channels.
2950 * @param dev scanner's device
2951 * @param shading_data memory area where to store the computed shading coefficients
2952 * @param pixels_per_line number of pixels per line
2953 * @param channels number of color channels (actually 1 or 3)
2954 * @param cmat color transposition matrix
2955 * @param offset shading coefficients left offset
2956 * @param coeff 4000h or 2000h depending on fast scan mode or not
2957 * @param target value of the target code
2958 */
compute_coefficients(Genesys_Device * dev,std::uint8_t * shading_data,unsigned int pixels_per_line,unsigned int channels,ColorOrder color_order,int offset,unsigned int coeff,unsigned int target)2959 static void compute_coefficients(Genesys_Device * dev,
2960 std::uint8_t* shading_data,
2961 unsigned int pixels_per_line,
2962 unsigned int channels,
2963 ColorOrder color_order,
2964 int offset,
2965 unsigned int coeff,
2966 unsigned int target)
2967 {
2968 unsigned int x, c;
2969 unsigned int val, br, dk;
2970 unsigned int start, end;
2971
2972 DBG(DBG_io, "%s: pixels_per_line=%d, coeff=0x%04x\n", __func__, pixels_per_line, coeff);
2973
2974 auto cmat = color_order_to_cmat(color_order);
2975
2976 /* compute start & end values depending of the offset */
2977 if (offset < 0)
2978 {
2979 start = -1 * offset;
2980 end = pixels_per_line;
2981 }
2982 else
2983 {
2984 start = 0;
2985 end = pixels_per_line - offset;
2986 }
2987
2988 for (c = 0; c < channels; c++)
2989 {
2990 for (x = start; x < end; x++)
2991 {
2992 /* TODO if channels=1 , use filter to know the base addr */
2993 // contain 16bit words in little endian
2994 std::uint8_t* ptr = shading_data + 4 * ((x + offset) * channels + cmat[c]);
2995
2996 // dark data
2997 dk = dev->dark_average_data[x * channels + c];
2998
2999 // white data
3000 br = dev->white_average_data[x * channels + c];
3001
3002 /* compute coeff */
3003 val=compute_coefficient(coeff,target,br-dk);
3004
3005 /* assign it */
3006 ptr[0] = dk & 255;
3007 ptr[1] = dk / 256;
3008 ptr[2] = val & 0xff;
3009 ptr[3] = val / 256;
3010
3011 }
3012 }
3013 }
3014
3015 /**
3016 * Computes shading coefficient using formula in data sheet. 16bit data values
3017 * manipulated here are little endian. Data is in planar form, ie grouped by
3018 * lines of the same color component.
3019 * @param dev scanner's device
3020 * @param shading_data memory area where to store the computed shading coefficients
3021 * @param factor averaging factor when the calibration scan is done at a higher resolution
3022 * than the final scan
3023 * @param pixels_per_line number of pixels per line
3024 * @param words_per_color total number of shading data words for one color element
3025 * @param channels number of color channels (actually 1 or 3)
3026 * @param cmat transcoding matrix for color channel order
3027 * @param offset shading coefficients left offset
3028 * @param coeff 4000h or 2000h depending on fast scan mode or not
3029 * @param target white target value
3030 */
compute_planar_coefficients(Genesys_Device * dev,std::uint8_t * shading_data,unsigned int factor,unsigned int pixels_per_line,unsigned int words_per_color,unsigned int channels,ColorOrder color_order,unsigned int offset,unsigned int coeff,unsigned int target)3031 static void compute_planar_coefficients(Genesys_Device * dev,
3032 std::uint8_t* shading_data,
3033 unsigned int factor,
3034 unsigned int pixels_per_line,
3035 unsigned int words_per_color,
3036 unsigned int channels,
3037 ColorOrder color_order,
3038 unsigned int offset,
3039 unsigned int coeff,
3040 unsigned int target)
3041 {
3042 std::uint32_t i;
3043 std::uint32_t val, dk, br;
3044
3045 auto cmat = color_order_to_cmat(color_order);
3046
3047 DBG(DBG_io, "%s: factor=%d, pixels_per_line=%d, words=0x%X, coeff=0x%04x\n", __func__, factor,
3048 pixels_per_line, words_per_color, coeff);
3049 for (unsigned c = 0; c < channels; c++) {
3050 /* shading data is larger than pixels_per_line so offset can be neglected */
3051 for (unsigned x = 0; x < pixels_per_line; x += factor) {
3052 /* x2 because of 16 bit values, and x2 since one coeff for dark
3053 * and another for white */
3054 // contains 16bit words in little endian
3055 std::uint8_t* ptr = shading_data + words_per_color * cmat[c] * 2 + (x + offset) * 4;
3056
3057 dk = 0;
3058 br = 0;
3059
3060 /* average case */
3061 for(i=0;i<factor;i++)
3062 {
3063 dk += dev->dark_average_data[((x+i) + pixels_per_line * c)];
3064 br += dev->white_average_data[((x+i) + pixels_per_line * c)];
3065 }
3066 dk /= factor;
3067 br /= factor;
3068
3069 val = compute_coefficient (coeff, target, br - dk);
3070
3071 // we duplicate the information to have calibration data at optical resolution
3072 for (unsigned i = 0; i < factor; i++) {
3073 ptr[0 + 4 * i] = dk & 255;
3074 ptr[1 + 4 * i] = dk / 256;
3075 ptr[2 + 4 * i] = val & 0xff;
3076 ptr[3 + 4 * i] = val / 256;
3077 }
3078 }
3079 }
3080 /* in case of gray level scan, we duplicate shading information on all
3081 * three color channels */
3082 if(channels==1)
3083 {
3084 memcpy(shading_data+cmat[1]*2*words_per_color,
3085 shading_data+cmat[0]*2*words_per_color,
3086 words_per_color*2);
3087 memcpy(shading_data+cmat[2]*2*words_per_color,
3088 shading_data+cmat[0]*2*words_per_color,
3089 words_per_color*2);
3090 }
3091 }
3092
compute_shifted_coefficients(Genesys_Device * dev,const Genesys_Sensor & sensor,std::uint8_t * shading_data,unsigned int pixels_per_line,unsigned int channels,ColorOrder color_order,int offset,unsigned int coeff,unsigned int target_dark,unsigned int target_bright,unsigned int patch_size)3093 static void compute_shifted_coefficients(Genesys_Device * dev,
3094 const Genesys_Sensor& sensor,
3095 std::uint8_t* shading_data,
3096 unsigned int pixels_per_line,
3097 unsigned int channels,
3098 ColorOrder color_order,
3099 int offset,
3100 unsigned int coeff,
3101 unsigned int target_dark,
3102 unsigned int target_bright,
3103 unsigned int patch_size) /* contiguous extent */
3104 {
3105 unsigned int x, avgpixels, basepixels, i, j, val1, val2;
3106 unsigned int br_tmp [3], dk_tmp [3];
3107 std::uint8_t* ptr = shading_data + offset * 3 * 4; // contain 16bit words in little endian
3108 unsigned int patch_cnt = offset * 3; /* at start, offset of first patch */
3109
3110 auto cmat = color_order_to_cmat(color_order);
3111
3112 x = dev->settings.xres;
3113 if (sensor.full_resolution > sensor.get_optical_resolution()) {
3114 x *= 2; // scanner is using half-ccd mode
3115 }
3116 basepixels = sensor.full_resolution / x; // this should be evenly dividable
3117
3118 /* gl841 supports 1/1 1/2 1/3 1/4 1/5 1/6 1/8 1/10 1/12 1/15 averaging */
3119 if (basepixels < 1)
3120 avgpixels = 1;
3121 else if (basepixels < 6)
3122 avgpixels = basepixels;
3123 else if (basepixels < 8)
3124 avgpixels = 6;
3125 else if (basepixels < 10)
3126 avgpixels = 8;
3127 else if (basepixels < 12)
3128 avgpixels = 10;
3129 else if (basepixels < 15)
3130 avgpixels = 12;
3131 else
3132 avgpixels = 15;
3133 DBG(DBG_info, "%s: pixels_per_line=%d, coeff=0x%04x, averaging over %d pixels\n", __func__,
3134 pixels_per_line, coeff, avgpixels);
3135
3136 for (x = 0; x <= pixels_per_line - avgpixels; x += avgpixels) {
3137 memset (&br_tmp, 0, sizeof(br_tmp));
3138 memset (&dk_tmp, 0, sizeof(dk_tmp));
3139
3140 for (i = 0; i < avgpixels; i++) {
3141 for (j = 0; j < channels; j++) {
3142 br_tmp[j] += dev->white_average_data[((x + i) * channels + j)];
3143 dk_tmp[i] += dev->dark_average_data[((x + i) * channels + j)];
3144 }
3145 }
3146 for (j = 0; j < channels; j++) {
3147 br_tmp[j] /= avgpixels;
3148 dk_tmp[j] /= avgpixels;
3149
3150 if (br_tmp[j] * target_dark > dk_tmp[j] * target_bright)
3151 val1 = 0;
3152 else if (dk_tmp[j] * target_bright - br_tmp[j] * target_dark > 65535 * (target_bright - target_dark))
3153 val1 = 65535;
3154 else
3155 val1 = (dk_tmp[j] * target_bright - br_tmp[j] * target_dark) / (target_bright - target_dark);
3156
3157 val2 = br_tmp[j] - dk_tmp[j];
3158 if (65535 * val2 > (target_bright - target_dark) * coeff)
3159 val2 = (coeff * (target_bright - target_dark)) / val2;
3160 else
3161 val2 = 65535;
3162
3163 br_tmp[j] = val1;
3164 dk_tmp[j] = val2;
3165 }
3166 for (i = 0; i < avgpixels; i++) {
3167 for (j = 0; j < channels; j++) {
3168 * ptr++ = br_tmp[ cmat[j] ] & 0xff;
3169 * ptr++ = br_tmp[ cmat[j] ] >> 8;
3170 * ptr++ = dk_tmp[ cmat[j] ] & 0xff;
3171 * ptr++ = dk_tmp[ cmat[j] ] >> 8;
3172 patch_cnt++;
3173 if (patch_cnt == patch_size) {
3174 patch_cnt = 0;
3175 val1 = cmat[2];
3176 cmat[2] = cmat[1];
3177 cmat[1] = cmat[0];
3178 cmat[0] = val1;
3179 }
3180 }
3181 }
3182 }
3183 }
3184
genesys_send_shading_coefficient(Genesys_Device * dev,const Genesys_Sensor & sensor)3185 static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_Sensor& sensor)
3186 {
3187 DBG_HELPER(dbg);
3188
3189 if (sensor.use_host_side_calib) {
3190 return;
3191 }
3192
3193 std::uint32_t pixels_per_line;
3194 int o;
3195 unsigned int length; /**> number of shading calibration data words */
3196 unsigned int factor;
3197 unsigned int coeff, target_code, words_per_color = 0;
3198
3199
3200 // BUG: we are using wrong pixel number here
3201 unsigned start_offset =
3202 dev->calib_session.params.startx * sensor.full_resolution / dev->calib_session.params.xres;
3203
3204 if (dev->model->asic_type == AsicType::GL842 ||
3205 dev->model->asic_type == AsicType::GL843)
3206 {
3207 pixels_per_line = dev->calib_session.output_pixels + start_offset;
3208 } else {
3209 pixels_per_line = dev->calib_session.params.pixels + start_offset;
3210 }
3211
3212 unsigned channels = dev->calib_session.params.channels;
3213
3214 /* we always build data for three channels, even for gray
3215 * we make the shading data such that each color channel data line is contiguous
3216 * to the next one, which allow to write the 3 channels in 1 write
3217 * during genesys_send_shading_coefficient, some values are words, other bytes
3218 * hence the x2 factor */
3219 switch (dev->reg.get8(0x05) >> 6)
3220 {
3221 /* 600 dpi */
3222 case 0:
3223 words_per_color = 0x2a00;
3224 break;
3225 /* 1200 dpi */
3226 case 1:
3227 words_per_color = 0x5500;
3228 break;
3229 /* 2400 dpi */
3230 case 2:
3231 words_per_color = 0xa800;
3232 break;
3233 /* 4800 dpi */
3234 case 3:
3235 words_per_color = 0x15000;
3236 break;
3237 }
3238
3239 /* special case, memory is aligned on 0x5400, this has yet to be explained */
3240 /* could be 0xa800 because sensor is truly 2400 dpi, then halved because
3241 * we only set 1200 dpi */
3242 if(dev->model->sensor_id==SensorId::CIS_CANON_LIDE_80)
3243 {
3244 words_per_color = 0x5400;
3245 }
3246
3247 length = words_per_color * 3 * 2;
3248
3249 /* allocate computed size */
3250 // contains 16bit words in little endian
3251 std::vector<std::uint8_t> shading_data(length, 0);
3252
3253 if (!dev->calib_session.computed) {
3254 genesys_send_offset_and_shading(dev, sensor, shading_data.data(), length);
3255 return;
3256 }
3257
3258 /* TARGET/(Wn-Dn) = white gain -> ~1.xxx then it is multiplied by 0x2000
3259 or 0x4000 to give an integer
3260 Wn = white average for column n
3261 Dn = dark average for column n
3262 */
3263 if (get_registers_gain4_bit(dev->model->asic_type, dev->reg)) {
3264 coeff = 0x4000;
3265 } else {
3266 coeff = 0x2000;
3267 }
3268
3269 /* compute avg factor */
3270 if (dev->settings.xres > sensor.full_resolution) {
3271 factor = 1;
3272 } else {
3273 factor = sensor.full_resolution / dev->settings.xres;
3274 }
3275
3276 /* for GL646, shading data is planar if REG_0x01_FASTMOD is set and
3277 * chunky if not. For now we rely on the fact that we know that
3278 * each sensor is used only in one mode. Currently only the CIS_XP200
3279 * sets REG_0x01_FASTMOD.
3280 */
3281
3282 /* TODO setup a struct in genesys_devices that
3283 * will handle these settings instead of having this switch growing up */
3284 switch (dev->model->sensor_id)
3285 {
3286 case SensorId::CCD_XP300:
3287 case SensorId::CCD_DOCKETPORT_487:
3288 case SensorId::CCD_ROADWARRIOR:
3289 case SensorId::CCD_DP665:
3290 case SensorId::CCD_DP685:
3291 case SensorId::CCD_DSMOBILE600:
3292 target_code = 0xdc00;
3293 o = 4;
3294 compute_planar_coefficients (dev,
3295 shading_data.data(),
3296 factor,
3297 pixels_per_line,
3298 words_per_color,
3299 channels,
3300 ColorOrder::RGB,
3301 o,
3302 coeff,
3303 target_code);
3304 break;
3305 case SensorId::CIS_XP200:
3306 target_code = 0xdc00;
3307 o = 2;
3308 compute_planar_coefficients (dev,
3309 shading_data.data(),
3310 1,
3311 pixels_per_line,
3312 words_per_color,
3313 channels,
3314 ColorOrder::GBR,
3315 o,
3316 coeff,
3317 target_code);
3318 break;
3319 case SensorId::CCD_HP2300:
3320 target_code = 0xdc00;
3321 o = 2;
3322 if (dev->settings.xres <= sensor.full_resolution / 2) {
3323 o = o - sensor.dummy_pixel / 2;
3324 }
3325 compute_coefficients (dev,
3326 shading_data.data(),
3327 pixels_per_line,
3328 3,
3329 ColorOrder::RGB,
3330 o,
3331 coeff,
3332 target_code);
3333 break;
3334 case SensorId::CCD_5345:
3335 target_code = 0xe000;
3336 o = 4;
3337 if(dev->settings.xres<=sensor.full_resolution/2)
3338 {
3339 o = o - sensor.dummy_pixel;
3340 }
3341 compute_coefficients (dev,
3342 shading_data.data(),
3343 pixels_per_line,
3344 3,
3345 ColorOrder::RGB,
3346 o,
3347 coeff,
3348 target_code);
3349 break;
3350 case SensorId::CCD_HP3670:
3351 case SensorId::CCD_HP2400:
3352 target_code = 0xe000;
3353 // offset is dependent on ccd_pixels_per_system_pixel(), but we couldn't use this in
3354 // common code previously.
3355 // FIXME: use sensor.ccd_pixels_per_system_pixel()
3356 if(dev->settings.xres<=300)
3357 {
3358 o = -10;
3359 }
3360 else if(dev->settings.xres<=600)
3361 {
3362 o = -6;
3363 }
3364 else
3365 {
3366 o = +2;
3367 }
3368 compute_coefficients (dev,
3369 shading_data.data(),
3370 pixels_per_line,
3371 3,
3372 ColorOrder::RGB,
3373 o,
3374 coeff,
3375 target_code);
3376 break;
3377 case SensorId::CCD_KVSS080:
3378 case SensorId::CCD_PLUSTEK_OPTICBOOK_3800:
3379 case SensorId::CCD_G4050:
3380 case SensorId::CCD_HP_4850C:
3381 case SensorId::CCD_CANON_4400F:
3382 case SensorId::CCD_CANON_8400F:
3383 case SensorId::CCD_CANON_8600F:
3384 case SensorId::CCD_PLUSTEK_OPTICFILM_7200:
3385 case SensorId::CCD_PLUSTEK_OPTICFILM_7200I:
3386 case SensorId::CCD_PLUSTEK_OPTICFILM_7300:
3387 case SensorId::CCD_PLUSTEK_OPTICFILM_7400:
3388 case SensorId::CCD_PLUSTEK_OPTICFILM_7500I:
3389 case SensorId::CCD_PLUSTEK_OPTICFILM_8200I:
3390 target_code = 0xe000;
3391 o = 0;
3392 compute_coefficients (dev,
3393 shading_data.data(),
3394 pixels_per_line,
3395 3,
3396 ColorOrder::RGB,
3397 o,
3398 coeff,
3399 target_code);
3400 break;
3401 case SensorId::CIS_CANON_LIDE_700F:
3402 case SensorId::CIS_CANON_LIDE_100:
3403 case SensorId::CIS_CANON_LIDE_200:
3404 case SensorId::CIS_CANON_LIDE_110:
3405 case SensorId::CIS_CANON_LIDE_120:
3406 case SensorId::CIS_CANON_LIDE_210:
3407 case SensorId::CIS_CANON_LIDE_220:
3408 case SensorId::CCD_CANON_5600F:
3409 /* TODO store this in a data struct so we avoid
3410 * growing this switch */
3411 switch(dev->model->sensor_id)
3412 {
3413 case SensorId::CIS_CANON_LIDE_110:
3414 case SensorId::CIS_CANON_LIDE_120:
3415 case SensorId::CIS_CANON_LIDE_210:
3416 case SensorId::CIS_CANON_LIDE_220:
3417 case SensorId::CIS_CANON_LIDE_700F:
3418 target_code = 0xc000;
3419 break;
3420 default:
3421 target_code = 0xdc00;
3422 }
3423 words_per_color=pixels_per_line*2;
3424 length = words_per_color * 3 * 2;
3425 shading_data.clear();
3426 shading_data.resize(length, 0);
3427 compute_planar_coefficients (dev,
3428 shading_data.data(),
3429 1,
3430 pixels_per_line,
3431 words_per_color,
3432 channels,
3433 ColorOrder::RGB,
3434 0,
3435 coeff,
3436 target_code);
3437 break;
3438 case SensorId::CIS_CANON_LIDE_35:
3439 case SensorId::CIS_CANON_LIDE_60:
3440 case SensorId::CIS_CANON_LIDE_90:
3441 compute_averaged_planar (dev, sensor,
3442 shading_data.data(),
3443 pixels_per_line,
3444 words_per_color,
3445 channels,
3446 4,
3447 coeff,
3448 0xe000,
3449 0x0a00);
3450 break;
3451 case SensorId::CIS_CANON_LIDE_80:
3452 compute_averaged_planar (dev, sensor,
3453 shading_data.data(),
3454 pixels_per_line,
3455 words_per_color,
3456 channels,
3457 0,
3458 coeff,
3459 0xe000,
3460 0x0800);
3461 break;
3462 case SensorId::CCD_PLUSTEK_OPTICPRO_3600:
3463 compute_shifted_coefficients (dev, sensor,
3464 shading_data.data(),
3465 pixels_per_line,
3466 channels,
3467 ColorOrder::RGB,
3468 12, /* offset */
3469 coeff,
3470 0x0001, /* target_dark */
3471 0xf900, /* target_bright */
3472 256); /* patch_size: contiguous extent */
3473 break;
3474 default:
3475 throw SaneException(SANE_STATUS_UNSUPPORTED, "sensor %d not supported",
3476 static_cast<unsigned>(dev->model->sensor_id));
3477 break;
3478 }
3479
3480 // do the actual write of shading calibration data to the scanner
3481 genesys_send_offset_and_shading(dev, sensor, shading_data.data(), length);
3482 }
3483
3484
3485 /**
3486 * search calibration cache list for an entry matching required scan.
3487 * If one is found, set device calibration with it
3488 * @param dev scanner's device
3489 * @return false if no matching cache entry has been
3490 * found, true if one has been found and used.
3491 */
3492 static bool
genesys_restore_calibration(Genesys_Device * dev,Genesys_Sensor & sensor)3493 genesys_restore_calibration(Genesys_Device * dev, Genesys_Sensor& sensor)
3494 {
3495 DBG_HELPER(dbg);
3496
3497 // if no cache or no function to evaluate cache entry there can be no match/
3498 if (dev->calibration_cache.empty()) {
3499 return false;
3500 }
3501
3502 auto session = dev->cmd_set->calculate_scan_session(dev, sensor, dev->settings);
3503
3504 /* we walk the link list of calibration cache in search for a
3505 * matching one */
3506 for (auto& cache : dev->calibration_cache)
3507 {
3508 if (sanei_genesys_is_compatible_calibration(dev, session, &cache, false)) {
3509 dev->frontend = cache.frontend;
3510 /* we don't restore the gamma fields */
3511 sensor.exposure = cache.sensor.exposure;
3512
3513 dev->calib_session = cache.session;
3514 dev->average_size = cache.average_size;
3515
3516 dev->dark_average_data = cache.dark_average_data;
3517 dev->white_average_data = cache.white_average_data;
3518
3519 if (!dev->cmd_set->has_send_shading_data()) {
3520 genesys_send_shading_coefficient(dev, sensor);
3521 }
3522
3523 DBG(DBG_proc, "%s: restored\n", __func__);
3524 return true;
3525 }
3526 }
3527 DBG(DBG_proc, "%s: completed(nothing found)\n", __func__);
3528 return false;
3529 }
3530
3531
genesys_save_calibration(Genesys_Device * dev,const Genesys_Sensor & sensor)3532 static void genesys_save_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor)
3533 {
3534 DBG_HELPER(dbg);
3535 #ifdef HAVE_SYS_TIME_H
3536 struct timeval time;
3537 #endif
3538
3539 auto session = dev->cmd_set->calculate_scan_session(dev, sensor, dev->settings);
3540
3541 auto found_cache_it = dev->calibration_cache.end();
3542 for (auto cache_it = dev->calibration_cache.begin(); cache_it != dev->calibration_cache.end();
3543 cache_it++)
3544 {
3545 if (sanei_genesys_is_compatible_calibration(dev, session, &*cache_it, true)) {
3546 found_cache_it = cache_it;
3547 break;
3548 }
3549 }
3550
3551 /* if we found on overridable cache, we reuse it */
3552 if (found_cache_it == dev->calibration_cache.end())
3553 {
3554 /* create a new cache entry and insert it in the linked list */
3555 dev->calibration_cache.push_back(Genesys_Calibration_Cache());
3556 found_cache_it = std::prev(dev->calibration_cache.end());
3557 }
3558
3559 found_cache_it->average_size = dev->average_size;
3560
3561 found_cache_it->dark_average_data = dev->dark_average_data;
3562 found_cache_it->white_average_data = dev->white_average_data;
3563
3564 found_cache_it->params = session.params;
3565 found_cache_it->frontend = dev->frontend;
3566 found_cache_it->sensor = sensor;
3567
3568 found_cache_it->session = dev->calib_session;
3569
3570 #ifdef HAVE_SYS_TIME_H
3571 gettimeofday(&time, nullptr);
3572 found_cache_it->last_calibration = time.tv_sec;
3573 #endif
3574 }
3575
genesys_flatbed_calibration(Genesys_Device * dev,Genesys_Sensor & sensor)3576 static void genesys_flatbed_calibration(Genesys_Device* dev, Genesys_Sensor& sensor)
3577 {
3578 DBG_HELPER(dbg);
3579 std::uint32_t pixels_per_line;
3580
3581 unsigned coarse_res = sensor.full_resolution;
3582 if (dev->settings.yres <= sensor.full_resolution / 2) {
3583 coarse_res /= 2;
3584 }
3585
3586 if (dev->model->model_id == ModelId::CANON_8400F) {
3587 coarse_res = 1600;
3588 }
3589
3590 if (dev->model->model_id == ModelId::CANON_4400F ||
3591 dev->model->model_id == ModelId::CANON_8600F)
3592 {
3593 coarse_res = 1200;
3594 }
3595
3596 auto local_reg = dev->initial_regs;
3597
3598 if (!has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION)) {
3599 // do ADC calibration first.
3600 dev->interface->record_progress_message("offset_calibration");
3601 dev->cmd_set->offset_calibration(dev, sensor, local_reg);
3602
3603 dev->interface->record_progress_message("coarse_gain_calibration");
3604 dev->cmd_set->coarse_gain_calibration(dev, sensor, local_reg, coarse_res);
3605 }
3606
3607 if (dev->model->is_cis &&
3608 !has_flag(dev->model->flags, ModelFlag::DISABLE_EXPOSURE_CALIBRATION))
3609 {
3610 // ADC now sends correct data, we can configure the exposure for the LEDs
3611 dev->interface->record_progress_message("led_calibration");
3612 switch (dev->model->asic_type) {
3613 case AsicType::GL124:
3614 case AsicType::GL841:
3615 case AsicType::GL845:
3616 case AsicType::GL846:
3617 case AsicType::GL847: {
3618 auto calib_exposure = dev->cmd_set->led_calibration(dev, sensor, local_reg);
3619 for (auto& sensor_update :
3620 sanei_genesys_find_sensors_all_for_write(dev, sensor.method)) {
3621 sensor_update.get().exposure = calib_exposure;
3622 }
3623 sensor.exposure = calib_exposure;
3624 break;
3625 }
3626 default: {
3627 sensor.exposure = dev->cmd_set->led_calibration(dev, sensor, local_reg);
3628 }
3629 }
3630
3631 if (!has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION)) {
3632 // recalibrate ADC again for the new LED exposure
3633 dev->interface->record_progress_message("offset_calibration");
3634 dev->cmd_set->offset_calibration(dev, sensor, local_reg);
3635
3636 dev->interface->record_progress_message("coarse_gain_calibration");
3637 dev->cmd_set->coarse_gain_calibration(dev, sensor, local_reg, coarse_res);
3638 }
3639 }
3640
3641 /* we always use sensor pixel number when the ASIC can't handle multi-segments sensor */
3642 if (!has_flag(dev->model->flags, ModelFlag::SIS_SENSOR)) {
3643 pixels_per_line = static_cast<std::uint32_t>((dev->model->x_size * dev->settings.xres) /
3644 MM_PER_INCH);
3645 } else {
3646 pixels_per_line = static_cast<std::uint32_t>((dev->model->x_size_calib_mm * dev->settings.xres)
3647 / MM_PER_INCH);
3648 }
3649
3650 // send default shading data
3651 dev->interface->record_progress_message("sanei_genesys_init_shading_data");
3652 sanei_genesys_init_shading_data(dev, sensor, pixels_per_line);
3653
3654 if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
3655 dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
3656 {
3657 scanner_move_to_ta(*dev);
3658 }
3659
3660 // shading calibration
3661 if (!has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION)) {
3662 if (has_flag(dev->model->flags, ModelFlag::DARK_WHITE_CALIBRATION)) {
3663 dev->interface->record_progress_message("genesys_dark_white_shading_calibration");
3664 genesys_dark_white_shading_calibration(dev, sensor, local_reg);
3665 } else {
3666 DBG(DBG_proc, "%s : genesys_dark_shading_calibration local_reg ", __func__);
3667 debug_dump(DBG_proc, local_reg);
3668
3669 if (has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) {
3670 dev->interface->record_progress_message("genesys_dark_shading_calibration");
3671 genesys_dark_shading_calibration(dev, sensor, local_reg);
3672 genesys_repark_sensor_before_shading(dev);
3673 }
3674
3675 dev->interface->record_progress_message("genesys_white_shading_calibration");
3676 genesys_white_shading_calibration(dev, sensor, local_reg);
3677
3678 genesys_repark_sensor_after_white_shading(dev);
3679
3680 if (!has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) {
3681 if (has_flag(dev->model->flags, ModelFlag::USE_CONSTANT_FOR_DARK_CALIBRATION)) {
3682 genesys_dark_shading_by_constant(*dev);
3683 } else {
3684 genesys_dark_shading_by_dummy_pixel(dev, sensor);
3685 }
3686 }
3687 }
3688 }
3689
3690 if (!dev->cmd_set->has_send_shading_data()) {
3691 dev->interface->record_progress_message("genesys_send_shading_coefficient");
3692 genesys_send_shading_coefficient(dev, sensor);
3693 }
3694 }
3695
3696 /**
3697 * Does the calibration process for a sheetfed scanner
3698 * - offset calibration
3699 * - gain calibration
3700 * - shading calibration
3701 * During calibration a predefined calibration sheet with specific black and white
3702 * areas is used.
3703 * @param dev device to calibrate
3704 */
genesys_sheetfed_calibration(Genesys_Device * dev,Genesys_Sensor & sensor)3705 static void genesys_sheetfed_calibration(Genesys_Device* dev, Genesys_Sensor& sensor)
3706 {
3707 DBG_HELPER(dbg);
3708 bool forward = true;
3709
3710 auto local_reg = dev->initial_regs;
3711
3712 // first step, load document
3713 dev->cmd_set->load_document(dev);
3714
3715 unsigned coarse_res = sensor.full_resolution;
3716
3717 /* the afe needs to sends valid data even before calibration */
3718
3719 /* go to a white area */
3720 try {
3721 scanner_search_strip(*dev, forward, false);
3722 } catch (...) {
3723 catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });
3724 throw;
3725 }
3726
3727 if (!has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION)) {
3728 // do ADC calibration first.
3729 dev->interface->record_progress_message("offset_calibration");
3730 dev->cmd_set->offset_calibration(dev, sensor, local_reg);
3731
3732 dev->interface->record_progress_message("coarse_gain_calibration");
3733 dev->cmd_set->coarse_gain_calibration(dev, sensor, local_reg, coarse_res);
3734 }
3735
3736 if (dev->model->is_cis &&
3737 !has_flag(dev->model->flags, ModelFlag::DISABLE_EXPOSURE_CALIBRATION))
3738 {
3739 // ADC now sends correct data, we can configure the exposure for the LEDs
3740 dev->interface->record_progress_message("led_calibration");
3741 dev->cmd_set->led_calibration(dev, sensor, local_reg);
3742
3743 if (!has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION)) {
3744 // recalibrate ADC again for the new LED exposure
3745 dev->interface->record_progress_message("offset_calibration");
3746 dev->cmd_set->offset_calibration(dev, sensor, local_reg);
3747
3748 dev->interface->record_progress_message("coarse_gain_calibration");
3749 dev->cmd_set->coarse_gain_calibration(dev, sensor, local_reg, coarse_res);
3750 }
3751 }
3752
3753 /* search for a full width black strip and then do a 16 bit scan to
3754 * gather black shading data */
3755 if (has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) {
3756 // seek black/white reverse/forward
3757 try {
3758 scanner_search_strip(*dev, forward, true);
3759 } catch (...) {
3760 catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });
3761 throw;
3762 }
3763
3764 try {
3765 genesys_dark_shading_calibration(dev, sensor, local_reg);
3766 } catch (...) {
3767 catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });
3768 throw;
3769 }
3770 forward = false;
3771 }
3772
3773
3774 /* go to a white area */
3775 try {
3776 scanner_search_strip(*dev, forward, false);
3777 } catch (...) {
3778 catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });
3779 throw;
3780 }
3781
3782 genesys_repark_sensor_before_shading(dev);
3783
3784 try {
3785 genesys_white_shading_calibration(dev, sensor, local_reg);
3786 genesys_repark_sensor_after_white_shading(dev);
3787 } catch (...) {
3788 catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });
3789 throw;
3790 }
3791
3792 // in case we haven't black shading data, build it from black pixels of white calibration
3793 // FIXME: shouldn't we use genesys_dark_shading_by_dummy_pixel() ?
3794 if (!has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) {
3795 genesys_dark_shading_by_constant(*dev);
3796 }
3797
3798 /* send the shading coefficient when doing whole line shading
3799 * but not when using SHDAREA like GL124 */
3800 if (!dev->cmd_set->has_send_shading_data()) {
3801 genesys_send_shading_coefficient(dev, sensor);
3802 }
3803
3804 // save the calibration data
3805 genesys_save_calibration(dev, sensor);
3806
3807 // and finally eject calibration sheet
3808 dev->cmd_set->eject_document(dev);
3809
3810 // restore settings
3811 dev->settings.xres = sensor.full_resolution;
3812 }
3813
3814 /**
3815 * does the calibration process for a device
3816 * @param dev device to calibrate
3817 */
genesys_scanner_calibration(Genesys_Device * dev,Genesys_Sensor & sensor)3818 static void genesys_scanner_calibration(Genesys_Device* dev, Genesys_Sensor& sensor)
3819 {
3820 DBG_HELPER(dbg);
3821 if (!dev->model->is_sheetfed) {
3822 genesys_flatbed_calibration(dev, sensor);
3823 return;
3824 }
3825 genesys_sheetfed_calibration(dev, sensor);
3826 }
3827
3828
3829 /* ------------------------------------------------------------------------ */
3830 /* High level (exported) functions */
3831 /* ------------------------------------------------------------------------ */
3832
3833 /*
3834 * wait lamp to be warm enough by scanning the same line until
3835 * differences between two scans are below a threshold
3836 */
genesys_warmup_lamp(Genesys_Device * dev)3837 static void genesys_warmup_lamp(Genesys_Device* dev)
3838 {
3839 DBG_HELPER(dbg);
3840 unsigned seconds = 0;
3841
3842 const auto& sensor = sanei_genesys_find_sensor_any(dev);
3843
3844 dev->cmd_set->init_regs_for_warmup(dev, sensor, &dev->reg);
3845 dev->interface->write_registers(dev->reg);
3846
3847 auto total_pixels = dev->session.output_pixels;
3848 auto total_size = dev->session.output_line_bytes;
3849 auto channels = dev->session.params.channels;
3850 auto lines = dev->session.output_line_count;
3851
3852 std::vector<std::uint8_t> first_line(total_size);
3853 std::vector<std::uint8_t> second_line(total_size);
3854
3855 do {
3856 first_line = second_line;
3857
3858 dev->cmd_set->begin_scan(dev, sensor, &dev->reg, false);
3859
3860 if (is_testing_mode()) {
3861 dev->interface->test_checkpoint("warmup_lamp");
3862 dev->cmd_set->end_scan(dev, &dev->reg, true);
3863 return;
3864 }
3865
3866 wait_until_buffer_non_empty(dev);
3867
3868 sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size);
3869 dev->cmd_set->end_scan(dev, &dev->reg, true);
3870
3871 // compute difference between the two scans
3872 double first_average = 0;
3873 double second_average = 0;
3874 for (unsigned pixel = 0; pixel < total_size; pixel++) {
3875 // 16 bit data
3876 if (dev->session.params.depth == 16) {
3877 first_average += (first_line[pixel] + first_line[pixel + 1] * 256);
3878 second_average += (second_line[pixel] + second_line[pixel + 1] * 256);
3879 pixel++;
3880 } else {
3881 first_average += first_line[pixel];
3882 second_average += second_line[pixel];
3883 }
3884 }
3885
3886 first_average /= total_pixels;
3887 second_average /= total_pixels;
3888
3889 if (dbg_log_image_data()) {
3890 write_tiff_file("gl_warmup1.tiff", first_line.data(), dev->session.params.depth,
3891 channels, total_size / (lines * channels), lines);
3892 write_tiff_file("gl_warmup2.tiff", second_line.data(), dev->session.params.depth,
3893 channels, total_size / (lines * channels), lines);
3894 }
3895
3896 DBG(DBG_info, "%s: average 1 = %.2f, average 2 = %.2f\n", __func__, first_average,
3897 second_average);
3898
3899 float average_difference = std::fabs(first_average - second_average) / second_average;
3900 if (second_average > 0 && average_difference < 0.005)
3901 {
3902 dbg.vlog(DBG_info, "difference: %f, exiting", average_difference);
3903 break;
3904 }
3905
3906 dev->interface->sleep_ms(1000);
3907 seconds++;
3908 } while (seconds < WARMUP_TIME);
3909
3910 if (seconds >= WARMUP_TIME)
3911 {
3912 throw SaneException(SANE_STATUS_IO_ERROR,
3913 "warmup timed out after %d seconds. Lamp defective?", seconds);
3914 }
3915 else
3916 {
3917 DBG(DBG_info, "%s: warmup succeeded after %d seconds\n", __func__, seconds);
3918 }
3919 }
3920
init_regs_for_scan(Genesys_Device & dev,const Genesys_Sensor & sensor,Genesys_Register_Set & regs)3921 static void init_regs_for_scan(Genesys_Device& dev, const Genesys_Sensor& sensor,
3922 Genesys_Register_Set& regs)
3923 {
3924 DBG_HELPER(dbg);
3925 debug_dump(DBG_info, dev.settings);
3926
3927 auto session = dev.cmd_set->calculate_scan_session(&dev, sensor, dev.settings);
3928
3929 if (dev.model->asic_type == AsicType::GL124 ||
3930 dev.model->asic_type == AsicType::GL845 ||
3931 dev.model->asic_type == AsicType::GL846 ||
3932 dev.model->asic_type == AsicType::GL847)
3933 {
3934 /* Fast move to scan area:
3935
3936 We don't move fast the whole distance since it would involve computing
3937 acceleration/deceleration distance for scan resolution. So leave a remainder for it so
3938 scan makes the final move tuning
3939 */
3940
3941 if (dev.settings.get_channels() * dev.settings.yres >= 600 && session.params.starty > 700) {
3942 scanner_move(dev, dev.model->default_method,
3943 static_cast<unsigned>(session.params.starty - 500),
3944 Direction::FORWARD);
3945 session.params.starty = 500;
3946 }
3947 compute_session(&dev, session, sensor);
3948 }
3949
3950 dev.cmd_set->init_regs_for_scan_session(&dev, sensor, ®s, session);
3951 }
3952
3953 // High-level start of scanning
genesys_start_scan(Genesys_Device * dev,bool lamp_off)3954 static void genesys_start_scan(Genesys_Device* dev, bool lamp_off)
3955 {
3956 DBG_HELPER(dbg);
3957 unsigned int steps, expected;
3958
3959
3960 /* since not all scanners are set to wait for head to park
3961 * we check we are not still parking before starting a new scan */
3962 if (dev->parking) {
3963 sanei_genesys_wait_for_home(dev);
3964 }
3965
3966 // disable power saving
3967 dev->cmd_set->save_power(dev, false);
3968
3969 /* wait for lamp warmup : until a warmup for TRANSPARENCY is designed, skip
3970 * it when scanning from XPA. */
3971 if (has_flag(dev->model->flags, ModelFlag::WARMUP) &&
3972 (dev->settings.scan_method != ScanMethod::TRANSPARENCY_INFRARED))
3973 {
3974 if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
3975 dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
3976 {
3977 scanner_move_to_ta(*dev);
3978 }
3979
3980 genesys_warmup_lamp(dev);
3981 }
3982
3983 /* set top left x and y values by scanning the internals if flatbed scanners */
3984 if (!dev->model->is_sheetfed) {
3985 // TODO: check we can drop this since we cannot have the scanner's head wandering here
3986 dev->parking = false;
3987 dev->cmd_set->move_back_home(dev, true);
3988 }
3989
3990 /* move to calibration area for transparency adapter */
3991 if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
3992 dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
3993 {
3994 scanner_move_to_ta(*dev);
3995 }
3996
3997 /* load document if needed (for sheetfed scanner for instance) */
3998 if (dev->model->is_sheetfed) {
3999 dev->cmd_set->load_document(dev);
4000 }
4001
4002 auto& sensor = sanei_genesys_find_sensor_for_write(dev, dev->settings.xres,
4003 dev->settings.get_channels(),
4004 dev->settings.scan_method);
4005
4006 // send gamma tables. They have been set to device or user value
4007 // when setting option value */
4008 dev->cmd_set->send_gamma_table(dev, sensor);
4009
4010 /* try to use cached calibration first */
4011 if (!genesys_restore_calibration (dev, sensor))
4012 {
4013 // calibration : sheetfed scanners can't calibrate before each scan.
4014 // also don't run calibration for those scanners where all passes are disabled
4015 bool shading_disabled =
4016 has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION) &&
4017 has_flag(dev->model->flags, ModelFlag::DISABLE_EXPOSURE_CALIBRATION) &&
4018 has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION);
4019 if (!shading_disabled && !dev->model->is_sheetfed) {
4020 genesys_scanner_calibration(dev, sensor);
4021 genesys_save_calibration(dev, sensor);
4022 } else {
4023 DBG(DBG_warn, "%s: no calibration done\n", __func__);
4024 }
4025 }
4026
4027 dev->cmd_set->wait_for_motor_stop(dev);
4028
4029 if (dev->cmd_set->needs_home_before_init_regs_for_scan(dev)) {
4030 dev->cmd_set->move_back_home(dev, true);
4031 }
4032
4033 if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
4034 dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
4035 {
4036 scanner_move_to_ta(*dev);
4037 }
4038
4039 init_regs_for_scan(*dev, sensor, dev->reg);
4040
4041 /* no lamp during scan */
4042 if (lamp_off) {
4043 sanei_genesys_set_lamp_power(dev, sensor, dev->reg, false);
4044 }
4045
4046 /* GL124 is using SHDAREA, so we have to wait for scan to be set up before
4047 * sending shading data */
4048 if (dev->cmd_set->has_send_shading_data() &&
4049 !has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION))
4050 {
4051 genesys_send_shading_coefficient(dev, sensor);
4052 }
4053
4054 // now send registers for scan
4055 dev->interface->write_registers(dev->reg);
4056
4057 // start effective scan
4058 dev->cmd_set->begin_scan(dev, sensor, &dev->reg, true);
4059
4060 if (is_testing_mode()) {
4061 dev->interface->test_checkpoint("start_scan");
4062 return;
4063 }
4064
4065 /*do we really need this? the valid data check should be sufficient -- pierre*/
4066 /* waits for head to reach scanning position */
4067 expected = dev->reg.get8(0x3d) * 65536
4068 + dev->reg.get8(0x3e) * 256
4069 + dev->reg.get8(0x3f);
4070 do
4071 {
4072 // wait some time between each test to avoid overloading USB and CPU
4073 dev->interface->sleep_ms(100);
4074 sanei_genesys_read_feed_steps (dev, &steps);
4075 }
4076 while (steps < expected);
4077
4078 wait_until_buffer_non_empty(dev);
4079
4080 // we wait for at least one word of valid scan data
4081 // this is also done in sanei_genesys_read_data_from_scanner -- pierre
4082 if (!dev->model->is_sheetfed) {
4083 do {
4084 dev->interface->sleep_ms(100);
4085 sanei_genesys_read_valid_words(dev, &steps);
4086 }
4087 while (steps < 1);
4088 }
4089 }
4090
4091 /* this function does the effective data read in a manner that suits
4092 the scanner. It does data reordering and resizing if need.
4093 It also manages EOF and I/O errors, and line distance correction.
4094 Returns true on success, false on end-of-file.
4095 */
genesys_read_ordered_data(Genesys_Device * dev,SANE_Byte * destination,size_t * len)4096 static void genesys_read_ordered_data(Genesys_Device* dev, SANE_Byte* destination, size_t* len)
4097 {
4098 DBG_HELPER(dbg);
4099 size_t bytes = 0;
4100
4101 if (!dev->read_active) {
4102 *len = 0;
4103 throw SaneException("read is not active");
4104 }
4105
4106 DBG(DBG_info, "%s: frontend requested %zu bytes\n", __func__, *len);
4107 DBG(DBG_info, "%s: bytes_to_read=%zu, total_bytes_read=%zu\n", __func__,
4108 dev->total_bytes_to_read, dev->total_bytes_read);
4109
4110 /* is there data left to scan */
4111 if (dev->total_bytes_read >= dev->total_bytes_to_read)
4112 {
4113 /* issue park command immediately in case scanner can handle it
4114 * so we save time */
4115 if (!dev->model->is_sheetfed && !has_flag(dev->model->flags, ModelFlag::MUST_WAIT) &&
4116 !dev->parking)
4117 {
4118 dev->cmd_set->move_back_home(dev, false);
4119 dev->parking = true;
4120 }
4121 throw SaneException(SANE_STATUS_EOF, "nothing more to scan: EOF");
4122 }
4123
4124 if (is_testing_mode()) {
4125 if (dev->total_bytes_read + *len > dev->total_bytes_to_read) {
4126 *len = dev->total_bytes_to_read - dev->total_bytes_read;
4127 }
4128 dev->total_bytes_read += *len;
4129 } else {
4130 if (dev->model->is_sheetfed) {
4131 dev->cmd_set->detect_document_end(dev);
4132 }
4133
4134 if (dev->total_bytes_read + *len > dev->total_bytes_to_read) {
4135 *len = dev->total_bytes_to_read - dev->total_bytes_read;
4136 }
4137
4138 dev->pipeline_buffer.get_data(*len, destination);
4139 dev->total_bytes_read += *len;
4140 }
4141
4142 /* end scan if all needed data have been read */
4143 if(dev->total_bytes_read >= dev->total_bytes_to_read)
4144 {
4145 dev->cmd_set->end_scan(dev, &dev->reg, true);
4146 if (dev->model->is_sheetfed) {
4147 dev->cmd_set->eject_document (dev);
4148 }
4149 }
4150
4151 DBG(DBG_proc, "%s: completed, %zu bytes read\n", __func__, bytes);
4152 }
4153
4154
4155
4156 /* ------------------------------------------------------------------------ */
4157 /* Start of higher level functions */
4158 /* ------------------------------------------------------------------------ */
4159
4160 static size_t
max_string_size(const SANE_String_Const strings[])4161 max_string_size (const SANE_String_Const strings[])
4162 {
4163 size_t size, max_size = 0;
4164 SANE_Int i;
4165
4166 for (i = 0; strings[i]; ++i)
4167 {
4168 size = strlen (strings[i]) + 1;
4169 if (size > max_size)
4170 max_size = size;
4171 }
4172 return max_size;
4173 }
4174
max_string_size(const std::vector<const char * > & strings)4175 static std::size_t max_string_size(const std::vector<const char*>& strings)
4176 {
4177 std::size_t max_size = 0;
4178 for (const auto& s : strings) {
4179 if (!s) {
4180 continue;
4181 }
4182 max_size = std::max(max_size, std::strlen(s));
4183 }
4184 return max_size;
4185 }
4186
pick_resolution(const std::vector<unsigned> & resolutions,unsigned resolution,const char * direction)4187 static unsigned pick_resolution(const std::vector<unsigned>& resolutions, unsigned resolution,
4188 const char* direction)
4189 {
4190 DBG_HELPER(dbg);
4191
4192 if (resolutions.empty())
4193 throw SaneException("Empty resolution list");
4194
4195 unsigned best_res = resolutions.front();
4196 unsigned min_diff = abs_diff(best_res, resolution);
4197
4198 for (auto it = std::next(resolutions.begin()); it != resolutions.end(); ++it) {
4199 unsigned curr_diff = abs_diff(*it, resolution);
4200 if (curr_diff < min_diff) {
4201 min_diff = curr_diff;
4202 best_res = *it;
4203 }
4204 }
4205
4206 if (best_res != resolution) {
4207 DBG(DBG_warn, "%s: using resolution %d that is nearest to %d for direction %s\n",
4208 __func__, best_res, resolution, direction);
4209 }
4210 return best_res;
4211 }
4212
calculate_scan_settings(Genesys_Scanner * s)4213 static Genesys_Settings calculate_scan_settings(Genesys_Scanner* s)
4214 {
4215 DBG_HELPER(dbg);
4216
4217 const auto* dev = s->dev;
4218 Genesys_Settings settings;
4219 settings.scan_method = s->scan_method;
4220 settings.scan_mode = option_string_to_scan_color_mode(s->mode);
4221
4222 settings.depth = s->bit_depth;
4223
4224 if (settings.depth > 8) {
4225 settings.depth = 16;
4226 } else if (settings.depth < 8) {
4227 settings.depth = 1;
4228 }
4229
4230 const auto& resolutions = dev->model->get_resolution_settings(settings.scan_method);
4231
4232 settings.xres = pick_resolution(resolutions.resolutions_x, s->resolution, "X");
4233 settings.yres = pick_resolution(resolutions.resolutions_y, s->resolution, "Y");
4234
4235 settings.tl_x = fixed_to_float(s->pos_top_left_x);
4236 settings.tl_y = fixed_to_float(s->pos_top_left_y);
4237 float br_x = fixed_to_float(s->pos_bottom_right_x);
4238 float br_y = fixed_to_float(s->pos_bottom_right_y);
4239
4240 settings.lines = static_cast<unsigned>(((br_y - settings.tl_y) * settings.yres) /
4241 MM_PER_INCH);
4242
4243
4244 unsigned pixels_per_line = static_cast<unsigned>(((br_x - settings.tl_x) * settings.xres) /
4245 MM_PER_INCH);
4246
4247 const auto& sensor = sanei_genesys_find_sensor(dev, settings.xres, settings.get_channels(),
4248 settings.scan_method);
4249
4250 pixels_per_line = session_adjust_output_pixels(pixels_per_line, *dev, sensor,
4251 settings.xres, settings.yres, true);
4252
4253 unsigned xres_factor = s->resolution / settings.xres;
4254 settings.pixels = pixels_per_line;
4255 settings.requested_pixels = pixels_per_line * xres_factor;
4256
4257 if (s->color_filter == "Red") {
4258 settings.color_filter = ColorFilter::RED;
4259 } else if (s->color_filter == "Green") {
4260 settings.color_filter = ColorFilter::GREEN;
4261 } else if (s->color_filter == "Blue") {
4262 settings.color_filter = ColorFilter::BLUE;
4263 } else {
4264 settings.color_filter = ColorFilter::NONE;
4265 }
4266
4267 // brightness and contrast only for for 8 bit scans
4268 if (s->bit_depth == 8) {
4269 settings.contrast = (s->contrast * 127) / 100;
4270 settings.brightness = (s->brightness * 127) / 100;
4271 } else {
4272 settings.contrast = 0;
4273 settings.brightness = 0;
4274 }
4275
4276 settings.expiration_time = s->expiration_time;
4277
4278 return settings;
4279 }
4280
calculate_scan_parameters(const Genesys_Device & dev,const Genesys_Settings & settings)4281 static SANE_Parameters calculate_scan_parameters(const Genesys_Device& dev,
4282 const Genesys_Settings& settings)
4283 {
4284 DBG_HELPER(dbg);
4285
4286 auto sensor = sanei_genesys_find_sensor(&dev, settings.xres, settings.get_channels(),
4287 settings.scan_method);
4288 auto session = dev.cmd_set->calculate_scan_session(&dev, sensor, settings);
4289 auto pipeline = build_image_pipeline(dev, session, 0, false);
4290
4291 SANE_Parameters params;
4292 if (settings.scan_mode == ScanColorMode::GRAY) {
4293 params.format = SANE_FRAME_GRAY;
4294 } else {
4295 params.format = SANE_FRAME_RGB;
4296 }
4297 // only single-pass scanning supported
4298 params.last_frame = true;
4299 params.depth = settings.depth;
4300 params.lines = pipeline.get_output_height();
4301 params.pixels_per_line = pipeline.get_output_width();
4302 params.bytes_per_line = pipeline.get_output_row_bytes();
4303
4304 return params;
4305 }
4306
calc_parameters(Genesys_Scanner * s)4307 static void calc_parameters(Genesys_Scanner* s)
4308 {
4309 DBG_HELPER(dbg);
4310
4311 s->dev->settings = calculate_scan_settings(s);
4312 s->params = calculate_scan_parameters(*s->dev, s->dev->settings);
4313 }
4314
create_bpp_list(Genesys_Scanner * s,const std::vector<unsigned> & bpp)4315 static void create_bpp_list (Genesys_Scanner * s, const std::vector<unsigned>& bpp)
4316 {
4317 s->bpp_list[0] = bpp.size();
4318 std::reverse_copy(bpp.begin(), bpp.end(), s->bpp_list + 1);
4319 }
4320
4321 /** @brief this function initialize a gamma vector based on the ASIC:
4322 * Set up a default gamma table vector based on device description
4323 * gl646: 12 or 14 bits gamma table depending on ModelFlag::GAMMA_14BIT
4324 * gl84x: 16 bits
4325 * gl12x: 16 bits
4326 * @param scanner pointer to scanner session to get options
4327 * @param option option number of the gamma table to set
4328 */
4329 static void
init_gamma_vector_option(Genesys_Scanner * scanner,int option)4330 init_gamma_vector_option (Genesys_Scanner * scanner, int option)
4331 {
4332 /* the option is inactive until the custom gamma control
4333 * is enabled */
4334 scanner->opt[option].type = SANE_TYPE_INT;
4335 scanner->opt[option].cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED;
4336 scanner->opt[option].unit = SANE_UNIT_NONE;
4337 scanner->opt[option].constraint_type = SANE_CONSTRAINT_RANGE;
4338 if (scanner->dev->model->asic_type == AsicType::GL646) {
4339 if (has_flag(scanner->dev->model->flags, ModelFlag::GAMMA_14BIT)) {
4340 scanner->opt[option].size = 16384 * sizeof (SANE_Word);
4341 scanner->opt[option].constraint.range = &u14_range;
4342 }
4343 else
4344 { /* 12 bits gamma tables */
4345 scanner->opt[option].size = 4096 * sizeof (SANE_Word);
4346 scanner->opt[option].constraint.range = &u12_range;
4347 }
4348 }
4349 else
4350 { /* other asics have 16 bits words gamma table */
4351 scanner->opt[option].size = 256 * sizeof (SANE_Word);
4352 scanner->opt[option].constraint.range = &u16_range;
4353 }
4354 }
4355
4356 /**
4357 * allocate a geometry range
4358 * @param size maximum size of the range
4359 * @return a pointer to a valid range or nullptr
4360 */
create_range(float size)4361 static SANE_Range create_range(float size)
4362 {
4363 SANE_Range range;
4364 range.min = float_to_fixed(0.0);
4365 range.max = float_to_fixed(size);
4366 range.quant = float_to_fixed(0.0);
4367 return range;
4368 }
4369
4370 /** @brief generate calibration cache file nam
4371 * Generates the calibration cache file name to use.
4372 * Tries to store the cache in $HOME/.sane or
4373 * then fallbacks to $TMPDIR or TMP. The filename
4374 * uses the model name if only one scanner is plugged
4375 * else is uses the device name when several identical
4376 * scanners are in use.
4377 * @param currdev current scanner device
4378 * @return an allocated string containing a file name
4379 */
calibration_filename(Genesys_Device * currdev)4380 static std::string calibration_filename(Genesys_Device *currdev)
4381 {
4382 std::string ret;
4383 ret.resize(PATH_MAX);
4384
4385 char filename[80];
4386 unsigned int count;
4387 unsigned int i;
4388
4389 /* first compute the DIR where we can store cache:
4390 * 1 - home dir
4391 * 2 - $TMPDIR
4392 * 3 - $TMP
4393 * 4 - tmp dir
4394 * 5 - temp dir
4395 * 6 - then resort to current dir
4396 */
4397 char* ptr = std::getenv("HOME");
4398 if (ptr == nullptr) {
4399 ptr = std::getenv("USERPROFILE");
4400 }
4401 if (ptr == nullptr) {
4402 ptr = std::getenv("TMPDIR");
4403 }
4404 if (ptr == nullptr) {
4405 ptr = std::getenv("TMP");
4406 }
4407
4408 /* now choose filename:
4409 * 1 - if only one scanner, name of the model
4410 * 2 - if several scanners of the same model, use device name,
4411 * replacing special chars
4412 */
4413 count=0;
4414 /* count models of the same names if several scanners attached */
4415 if(s_devices->size() > 1) {
4416 for (const auto& dev : *s_devices) {
4417 if (dev.vendorId == currdev->vendorId && dev.productId == currdev->productId) {
4418 count++;
4419 }
4420 }
4421 }
4422 if(count>1)
4423 {
4424 std::snprintf(filename, sizeof(filename), "%s.cal", currdev->file_name.c_str());
4425 for(i=0;i<strlen(filename);i++)
4426 {
4427 if(filename[i]==':'||filename[i]==PATH_SEP)
4428 {
4429 filename[i]='_';
4430 }
4431 }
4432 }
4433 else
4434 {
4435 snprintf(filename,sizeof(filename),"%s.cal",currdev->model->name);
4436 }
4437
4438 /* build final final name : store dir + filename */
4439 if (ptr == nullptr) {
4440 int size = std::snprintf(&ret.front(), ret.size(), "%s", filename);
4441 ret.resize(size);
4442 }
4443 else
4444 {
4445 int size = 0;
4446 #ifdef HAVE_MKDIR
4447 /* make sure .sane directory exists in existing store dir */
4448 size = std::snprintf(&ret.front(), ret.size(), "%s%c.sane", ptr, PATH_SEP);
4449 ret.resize(size);
4450 mkdir(ret.c_str(), 0700);
4451
4452 ret.resize(PATH_MAX);
4453 #endif
4454 size = std::snprintf(&ret.front(), ret.size(), "%s%c.sane%c%s",
4455 ptr, PATH_SEP, PATH_SEP, filename);
4456 ret.resize(size);
4457 }
4458
4459 DBG(DBG_info, "%s: calibration filename >%s<\n", __func__, ret.c_str());
4460
4461 return ret;
4462 }
4463
set_resolution_option_values(Genesys_Scanner & s,bool reset_resolution_value)4464 static void set_resolution_option_values(Genesys_Scanner& s, bool reset_resolution_value)
4465 {
4466 auto resolutions = s.dev->model->get_resolutions(s.scan_method);
4467
4468 s.opt_resolution_values.resize(resolutions.size() + 1, 0);
4469 s.opt_resolution_values[0] = resolutions.size();
4470 std::copy(resolutions.begin(), resolutions.end(), s.opt_resolution_values.begin() + 1);
4471
4472 s.opt[OPT_RESOLUTION].constraint.word_list = s.opt_resolution_values.data();
4473
4474 if (reset_resolution_value) {
4475 s.resolution = *std::min_element(resolutions.begin(), resolutions.end());
4476 }
4477 }
4478
set_xy_range_option_values(Genesys_Scanner & s)4479 static void set_xy_range_option_values(Genesys_Scanner& s)
4480 {
4481 if (s.scan_method == ScanMethod::FLATBED)
4482 {
4483 s.opt_x_range = create_range(s.dev->model->x_size);
4484 s.opt_y_range = create_range(s.dev->model->y_size);
4485 }
4486 else
4487 {
4488 s.opt_x_range = create_range(s.dev->model->x_size_ta);
4489 s.opt_y_range = create_range(s.dev->model->y_size_ta);
4490 }
4491
4492 s.opt[OPT_TL_X].constraint.range = &s.opt_x_range;
4493 s.opt[OPT_TL_Y].constraint.range = &s.opt_y_range;
4494 s.opt[OPT_BR_X].constraint.range = &s.opt_x_range;
4495 s.opt[OPT_BR_Y].constraint.range = &s.opt_y_range;
4496
4497 s.pos_top_left_x = 0;
4498 s.pos_top_left_y = 0;
4499 s.pos_bottom_right_x = s.opt_x_range.max;
4500 s.pos_bottom_right_y = s.opt_y_range.max;
4501 }
4502
init_options(Genesys_Scanner * s)4503 static void init_options(Genesys_Scanner* s)
4504 {
4505 DBG_HELPER(dbg);
4506 SANE_Int option;
4507 const Genesys_Model* model = s->dev->model;
4508
4509 memset (s->opt, 0, sizeof (s->opt));
4510
4511 for (option = 0; option < NUM_OPTIONS; ++option)
4512 {
4513 s->opt[option].size = sizeof (SANE_Word);
4514 s->opt[option].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
4515 }
4516 s->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
4517 s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
4518 s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
4519 s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
4520 s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
4521
4522 /* "Mode" group: */
4523 s->opt[OPT_MODE_GROUP].name = "scanmode-group";
4524 s->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode");
4525 s->opt[OPT_MODE_GROUP].desc = "";
4526 s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
4527 s->opt[OPT_MODE_GROUP].size = 0;
4528 s->opt[OPT_MODE_GROUP].cap = 0;
4529 s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
4530
4531 /* scan mode */
4532 s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
4533 s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
4534 s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
4535 s->opt[OPT_MODE].type = SANE_TYPE_STRING;
4536 s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
4537 s->opt[OPT_MODE].size = max_string_size (mode_list);
4538 s->opt[OPT_MODE].constraint.string_list = mode_list;
4539 s->mode = SANE_VALUE_SCAN_MODE_GRAY;
4540
4541 /* scan source */
4542 s->opt_source_values.clear();
4543 for (const auto& resolution_setting : model->resolutions) {
4544 for (auto method : resolution_setting.methods) {
4545 s->opt_source_values.push_back(scan_method_to_option_string(method));
4546 }
4547 }
4548 s->opt_source_values.push_back(nullptr);
4549
4550 s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
4551 s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
4552 s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
4553 s->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
4554 s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
4555 s->opt[OPT_SOURCE].size = max_string_size(s->opt_source_values);
4556 s->opt[OPT_SOURCE].constraint.string_list = s->opt_source_values.data();
4557 if (s->opt_source_values.size() < 2) {
4558 throw SaneException("No scan methods specified for scanner");
4559 }
4560 s->scan_method = model->default_method;
4561
4562 /* preview */
4563 s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
4564 s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
4565 s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
4566 s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
4567 s->opt[OPT_PREVIEW].unit = SANE_UNIT_NONE;
4568 s->opt[OPT_PREVIEW].constraint_type = SANE_CONSTRAINT_NONE;
4569 s->preview = false;
4570
4571 /* bit depth */
4572 s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH;
4573 s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
4574 s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
4575 s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT;
4576 s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
4577 s->opt[OPT_BIT_DEPTH].size = sizeof (SANE_Word);
4578 s->opt[OPT_BIT_DEPTH].constraint.word_list = s->bpp_list;
4579 create_bpp_list (s, model->bpp_gray_values);
4580 s->bit_depth = model->bpp_gray_values[0];
4581
4582 // resolution
4583 s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
4584 s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
4585 s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
4586 s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
4587 s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
4588 s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
4589 set_resolution_option_values(*s, true);
4590
4591 /* "Geometry" group: */
4592 s->opt[OPT_GEOMETRY_GROUP].name = SANE_NAME_GEOMETRY;
4593 s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry");
4594 s->opt[OPT_GEOMETRY_GROUP].desc = "";
4595 s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
4596 s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
4597 s->opt[OPT_GEOMETRY_GROUP].size = 0;
4598 s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
4599
4600 s->opt_x_range = create_range(model->x_size);
4601 s->opt_y_range = create_range(model->y_size);
4602
4603 // scan area
4604 s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
4605 s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
4606 s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
4607 s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
4608 s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
4609 s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
4610
4611 s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
4612 s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
4613 s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
4614 s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
4615 s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
4616 s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
4617
4618 s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
4619 s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
4620 s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
4621 s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
4622 s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
4623 s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
4624
4625 s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
4626 s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
4627 s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
4628 s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
4629 s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
4630 s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
4631
4632 set_xy_range_option_values(*s);
4633
4634 /* "Enhancement" group: */
4635 s->opt[OPT_ENHANCEMENT_GROUP].name = SANE_NAME_ENHANCEMENT;
4636 s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement");
4637 s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
4638 s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
4639 s->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED;
4640 s->opt[OPT_ENHANCEMENT_GROUP].size = 0;
4641 s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
4642
4643 /* custom-gamma table */
4644 s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
4645 s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
4646 s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
4647 s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
4648 s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_ADVANCED;
4649 s->custom_gamma = false;
4650
4651 /* grayscale gamma vector */
4652 s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
4653 s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
4654 s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
4655 init_gamma_vector_option (s, OPT_GAMMA_VECTOR);
4656
4657 /* red gamma vector */
4658 s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
4659 s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
4660 s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
4661 init_gamma_vector_option (s, OPT_GAMMA_VECTOR_R);
4662
4663 /* green gamma vector */
4664 s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
4665 s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
4666 s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
4667 init_gamma_vector_option (s, OPT_GAMMA_VECTOR_G);
4668
4669 /* blue gamma vector */
4670 s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
4671 s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
4672 s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
4673 init_gamma_vector_option (s, OPT_GAMMA_VECTOR_B);
4674
4675 /* currently, there are only gamma table options in this group,
4676 * so if the scanner doesn't support gamma table, disable the
4677 * whole group */
4678 if (!has_flag(model->flags, ModelFlag::CUSTOM_GAMMA)) {
4679 s->opt[OPT_ENHANCEMENT_GROUP].cap |= SANE_CAP_INACTIVE;
4680 s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
4681 DBG(DBG_info, "%s: custom gamma disabled\n", __func__);
4682 }
4683
4684 /* software base image enhancements, these are consuming as many
4685 * memory than used by the full scanned image and may fail at high
4686 * resolution
4687 */
4688
4689 /* Software brightness */
4690 s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
4691 s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
4692 s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
4693 s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
4694 s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
4695 s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
4696 s->opt[OPT_BRIGHTNESS].constraint.range = &(enhance_range);
4697 s->opt[OPT_BRIGHTNESS].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
4698 s->brightness = 0; // disable by default
4699
4700 /* Sowftware contrast */
4701 s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
4702 s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
4703 s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
4704 s->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
4705 s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
4706 s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
4707 s->opt[OPT_CONTRAST].constraint.range = &(enhance_range);
4708 s->opt[OPT_CONTRAST].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
4709 s->contrast = 0; // disable by default
4710
4711 /* "Extras" group: */
4712 s->opt[OPT_EXTRAS_GROUP].name = "extras-group";
4713 s->opt[OPT_EXTRAS_GROUP].title = SANE_I18N ("Extras");
4714 s->opt[OPT_EXTRAS_GROUP].desc = "";
4715 s->opt[OPT_EXTRAS_GROUP].type = SANE_TYPE_GROUP;
4716 s->opt[OPT_EXTRAS_GROUP].cap = SANE_CAP_ADVANCED;
4717 s->opt[OPT_EXTRAS_GROUP].size = 0;
4718 s->opt[OPT_EXTRAS_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
4719
4720 /* color filter */
4721 s->opt[OPT_COLOR_FILTER].name = "color-filter";
4722 s->opt[OPT_COLOR_FILTER].title = SANE_I18N ("Color filter");
4723 s->opt[OPT_COLOR_FILTER].desc =
4724 SANE_I18N
4725 ("When using gray or lineart this option selects the used color.");
4726 s->opt[OPT_COLOR_FILTER].type = SANE_TYPE_STRING;
4727 s->opt[OPT_COLOR_FILTER].constraint_type = SANE_CONSTRAINT_STRING_LIST;
4728 /* true gray not yet supported for GL847 and GL124 scanners */
4729 if (!model->is_cis || model->asic_type==AsicType::GL847 || model->asic_type==AsicType::GL124) {
4730 s->opt[OPT_COLOR_FILTER].size = max_string_size (color_filter_list);
4731 s->opt[OPT_COLOR_FILTER].constraint.string_list = color_filter_list;
4732 s->color_filter = s->opt[OPT_COLOR_FILTER].constraint.string_list[1];
4733 }
4734 else
4735 {
4736 s->opt[OPT_COLOR_FILTER].size = max_string_size (cis_color_filter_list);
4737 s->opt[OPT_COLOR_FILTER].constraint.string_list = cis_color_filter_list;
4738 /* default to "None" ie true gray */
4739 s->color_filter = s->opt[OPT_COLOR_FILTER].constraint.string_list[3];
4740 }
4741
4742 // no support for color filter for cis+gl646 scanners
4743 if (model->asic_type == AsicType::GL646 && model->is_cis) {
4744 DISABLE (OPT_COLOR_FILTER);
4745 }
4746
4747 /* calibration store file name */
4748 s->opt[OPT_CALIBRATION_FILE].name = "calibration-file";
4749 s->opt[OPT_CALIBRATION_FILE].title = SANE_I18N ("Calibration file");
4750 s->opt[OPT_CALIBRATION_FILE].desc = SANE_I18N ("Specify the calibration file to use");
4751 s->opt[OPT_CALIBRATION_FILE].type = SANE_TYPE_STRING;
4752 s->opt[OPT_CALIBRATION_FILE].unit = SANE_UNIT_NONE;
4753 s->opt[OPT_CALIBRATION_FILE].size = PATH_MAX;
4754 s->opt[OPT_CALIBRATION_FILE].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
4755 s->opt[OPT_CALIBRATION_FILE].constraint_type = SANE_CONSTRAINT_NONE;
4756 s->calibration_file.clear();
4757 /* disable option if run as root */
4758 #ifdef HAVE_GETUID
4759 if(geteuid()==0)
4760 {
4761 DISABLE (OPT_CALIBRATION_FILE);
4762 }
4763 #endif
4764
4765 /* expiration time for calibration cache entries */
4766 s->opt[OPT_EXPIRATION_TIME].name = "expiration-time";
4767 s->opt[OPT_EXPIRATION_TIME].title = SANE_I18N ("Calibration cache expiration time");
4768 s->opt[OPT_EXPIRATION_TIME].desc = SANE_I18N ("Time (in minutes) before a cached calibration expires. "
4769 "A value of 0 means cache is not used. A negative value means cache never expires.");
4770 s->opt[OPT_EXPIRATION_TIME].type = SANE_TYPE_INT;
4771 s->opt[OPT_EXPIRATION_TIME].unit = SANE_UNIT_NONE;
4772 s->opt[OPT_EXPIRATION_TIME].constraint_type = SANE_CONSTRAINT_RANGE;
4773 s->opt[OPT_EXPIRATION_TIME].constraint.range = &expiration_range;
4774 s->expiration_time = 60; // 60 minutes by default
4775
4776 /* Powersave time (turn lamp off) */
4777 s->opt[OPT_LAMP_OFF_TIME].name = "lamp-off-time";
4778 s->opt[OPT_LAMP_OFF_TIME].title = SANE_I18N ("Lamp off time");
4779 s->opt[OPT_LAMP_OFF_TIME].desc =
4780 SANE_I18N
4781 ("The lamp will be turned off after the given time (in minutes). "
4782 "A value of 0 means, that the lamp won't be turned off.");
4783 s->opt[OPT_LAMP_OFF_TIME].type = SANE_TYPE_INT;
4784 s->opt[OPT_LAMP_OFF_TIME].unit = SANE_UNIT_NONE;
4785 s->opt[OPT_LAMP_OFF_TIME].constraint_type = SANE_CONSTRAINT_RANGE;
4786 s->opt[OPT_LAMP_OFF_TIME].constraint.range = &time_range;
4787 s->lamp_off_time = 15; // 15 minutes
4788
4789 /* turn lamp off during scan */
4790 s->opt[OPT_LAMP_OFF].name = "lamp-off-scan";
4791 s->opt[OPT_LAMP_OFF].title = SANE_I18N ("Lamp off during scan");
4792 s->opt[OPT_LAMP_OFF].desc = SANE_I18N ("The lamp will be turned off during scan. ");
4793 s->opt[OPT_LAMP_OFF].type = SANE_TYPE_BOOL;
4794 s->opt[OPT_LAMP_OFF].unit = SANE_UNIT_NONE;
4795 s->opt[OPT_LAMP_OFF].constraint_type = SANE_CONSTRAINT_NONE;
4796 s->lamp_off = false;
4797
4798 s->opt[OPT_SENSOR_GROUP].name = SANE_NAME_SENSORS;
4799 s->opt[OPT_SENSOR_GROUP].title = SANE_TITLE_SENSORS;
4800 s->opt[OPT_SENSOR_GROUP].desc = SANE_DESC_SENSORS;
4801 s->opt[OPT_SENSOR_GROUP].type = SANE_TYPE_GROUP;
4802 s->opt[OPT_SENSOR_GROUP].cap = SANE_CAP_ADVANCED;
4803 s->opt[OPT_SENSOR_GROUP].size = 0;
4804 s->opt[OPT_SENSOR_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
4805
4806 s->opt[OPT_SCAN_SW].name = SANE_NAME_SCAN;
4807 s->opt[OPT_SCAN_SW].title = SANE_TITLE_SCAN;
4808 s->opt[OPT_SCAN_SW].desc = SANE_DESC_SCAN;
4809 s->opt[OPT_SCAN_SW].type = SANE_TYPE_BOOL;
4810 s->opt[OPT_SCAN_SW].unit = SANE_UNIT_NONE;
4811 if (model->buttons & GENESYS_HAS_SCAN_SW)
4812 s->opt[OPT_SCAN_SW].cap =
4813 SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
4814 else
4815 s->opt[OPT_SCAN_SW].cap = SANE_CAP_INACTIVE;
4816
4817 /* SANE_NAME_FILE is not for buttons */
4818 s->opt[OPT_FILE_SW].name = "file";
4819 s->opt[OPT_FILE_SW].title = SANE_I18N ("File button");
4820 s->opt[OPT_FILE_SW].desc = SANE_I18N ("File button");
4821 s->opt[OPT_FILE_SW].type = SANE_TYPE_BOOL;
4822 s->opt[OPT_FILE_SW].unit = SANE_UNIT_NONE;
4823 if (model->buttons & GENESYS_HAS_FILE_SW)
4824 s->opt[OPT_FILE_SW].cap =
4825 SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
4826 else
4827 s->opt[OPT_FILE_SW].cap = SANE_CAP_INACTIVE;
4828
4829 s->opt[OPT_EMAIL_SW].name = SANE_NAME_EMAIL;
4830 s->opt[OPT_EMAIL_SW].title = SANE_TITLE_EMAIL;
4831 s->opt[OPT_EMAIL_SW].desc = SANE_DESC_EMAIL;
4832 s->opt[OPT_EMAIL_SW].type = SANE_TYPE_BOOL;
4833 s->opt[OPT_EMAIL_SW].unit = SANE_UNIT_NONE;
4834 if (model->buttons & GENESYS_HAS_EMAIL_SW)
4835 s->opt[OPT_EMAIL_SW].cap =
4836 SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
4837 else
4838 s->opt[OPT_EMAIL_SW].cap = SANE_CAP_INACTIVE;
4839
4840 s->opt[OPT_COPY_SW].name = SANE_NAME_COPY;
4841 s->opt[OPT_COPY_SW].title = SANE_TITLE_COPY;
4842 s->opt[OPT_COPY_SW].desc = SANE_DESC_COPY;
4843 s->opt[OPT_COPY_SW].type = SANE_TYPE_BOOL;
4844 s->opt[OPT_COPY_SW].unit = SANE_UNIT_NONE;
4845 if (model->buttons & GENESYS_HAS_COPY_SW)
4846 s->opt[OPT_COPY_SW].cap =
4847 SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
4848 else
4849 s->opt[OPT_COPY_SW].cap = SANE_CAP_INACTIVE;
4850
4851 s->opt[OPT_PAGE_LOADED_SW].name = SANE_NAME_PAGE_LOADED;
4852 s->opt[OPT_PAGE_LOADED_SW].title = SANE_TITLE_PAGE_LOADED;
4853 s->opt[OPT_PAGE_LOADED_SW].desc = SANE_DESC_PAGE_LOADED;
4854 s->opt[OPT_PAGE_LOADED_SW].type = SANE_TYPE_BOOL;
4855 s->opt[OPT_PAGE_LOADED_SW].unit = SANE_UNIT_NONE;
4856 if (model->buttons & GENESYS_HAS_PAGE_LOADED_SW)
4857 s->opt[OPT_PAGE_LOADED_SW].cap =
4858 SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
4859 else
4860 s->opt[OPT_PAGE_LOADED_SW].cap = SANE_CAP_INACTIVE;
4861
4862 /* OCR button */
4863 s->opt[OPT_OCR_SW].name = "ocr";
4864 s->opt[OPT_OCR_SW].title = SANE_I18N ("OCR button");
4865 s->opt[OPT_OCR_SW].desc = SANE_I18N ("OCR button");
4866 s->opt[OPT_OCR_SW].type = SANE_TYPE_BOOL;
4867 s->opt[OPT_OCR_SW].unit = SANE_UNIT_NONE;
4868 if (model->buttons & GENESYS_HAS_OCR_SW)
4869 s->opt[OPT_OCR_SW].cap =
4870 SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
4871 else
4872 s->opt[OPT_OCR_SW].cap = SANE_CAP_INACTIVE;
4873
4874 /* power button */
4875 s->opt[OPT_POWER_SW].name = "power";
4876 s->opt[OPT_POWER_SW].title = SANE_I18N ("Power button");
4877 s->opt[OPT_POWER_SW].desc = SANE_I18N ("Power button");
4878 s->opt[OPT_POWER_SW].type = SANE_TYPE_BOOL;
4879 s->opt[OPT_POWER_SW].unit = SANE_UNIT_NONE;
4880 if (model->buttons & GENESYS_HAS_POWER_SW)
4881 s->opt[OPT_POWER_SW].cap =
4882 SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
4883 else
4884 s->opt[OPT_POWER_SW].cap = SANE_CAP_INACTIVE;
4885
4886 /* extra button */
4887 s->opt[OPT_EXTRA_SW].name = "extra";
4888 s->opt[OPT_EXTRA_SW].title = SANE_I18N("Extra button");
4889 s->opt[OPT_EXTRA_SW].desc = SANE_I18N("Extra button");
4890 s->opt[OPT_EXTRA_SW].type = SANE_TYPE_BOOL;
4891 s->opt[OPT_EXTRA_SW].unit = SANE_UNIT_NONE;
4892 if (model->buttons & GENESYS_HAS_EXTRA_SW)
4893 s->opt[OPT_EXTRA_SW].cap =
4894 SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
4895 else
4896 s->opt[OPT_EXTRA_SW].cap = SANE_CAP_INACTIVE;
4897
4898 /* transparency/scan_film button */
4899 s->opt[OPT_TRANSP_SW].name = "transparency";
4900 s->opt[OPT_TRANSP_SW].title = SANE_I18N ("Transparency button");
4901 s->opt[OPT_TRANSP_SW].desc = SANE_I18N ("Transparency button");
4902 s->opt[OPT_TRANSP_SW].type = SANE_TYPE_BOOL;
4903 s->opt[OPT_TRANSP_SW].unit = SANE_UNIT_NONE;
4904 if (model->buttons & GENESYS_HAS_TRANSP_SW)
4905 s->opt[OPT_TRANSP_SW].cap =
4906 SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
4907 else
4908 s->opt[OPT_TRANSP_SW].cap = SANE_CAP_INACTIVE;
4909
4910 /* PDF special function button 1 */
4911 s->opt[OPT_PDF1_SW].name = "pdf1";
4912 s->opt[OPT_PDF1_SW].title = SANE_I18N ("PDF function button 1");
4913 s->opt[OPT_PDF1_SW].desc = SANE_I18N ("PDF function button 1");
4914 s->opt[OPT_PDF1_SW].type = SANE_TYPE_BOOL;
4915 s->opt[OPT_PDF1_SW].unit = SANE_UNIT_NONE;
4916 if (model->buttons & GENESYS_HAS_PDF1_SW)
4917 s->opt[OPT_PDF1_SW].cap =
4918 SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
4919 else
4920 s->opt[OPT_PDF1_SW].cap = SANE_CAP_INACTIVE;
4921
4922 /* PDF special function button 2 */
4923 s->opt[OPT_PDF2_SW].name = "pdf2";
4924 s->opt[OPT_PDF2_SW].title = SANE_I18N ("PDF function button 2");
4925 s->opt[OPT_PDF2_SW].desc = SANE_I18N ("PDF function button 2");
4926 s->opt[OPT_PDF2_SW].type = SANE_TYPE_BOOL;
4927 s->opt[OPT_PDF2_SW].unit = SANE_UNIT_NONE;
4928 if (model->buttons & GENESYS_HAS_PDF2_SW)
4929 s->opt[OPT_PDF2_SW].cap =
4930 SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
4931 else
4932 s->opt[OPT_PDF2_SW].cap = SANE_CAP_INACTIVE;
4933
4934 /* PDF special function button 3 */
4935 s->opt[OPT_PDF3_SW].name = "pdf3";
4936 s->opt[OPT_PDF3_SW].title = SANE_I18N ("PDF function button 3");
4937 s->opt[OPT_PDF3_SW].desc = SANE_I18N ("PDF function button 3");
4938 s->opt[OPT_PDF3_SW].type = SANE_TYPE_BOOL;
4939 s->opt[OPT_PDF3_SW].unit = SANE_UNIT_NONE;
4940 if (model->buttons & GENESYS_HAS_PDF3_SW)
4941 s->opt[OPT_PDF3_SW].cap =
4942 SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
4943 else
4944 s->opt[OPT_PDF3_SW].cap = SANE_CAP_INACTIVE;
4945
4946 /* PDF special function button 4 */
4947 s->opt[OPT_PDF4_SW].name = "pdf4";
4948 s->opt[OPT_PDF4_SW].title = SANE_I18N ("PDF function button 4");
4949 s->opt[OPT_PDF4_SW].desc = SANE_I18N ("PDF function button 4");
4950 s->opt[OPT_PDF4_SW].type = SANE_TYPE_BOOL;
4951 s->opt[OPT_PDF4_SW].unit = SANE_UNIT_NONE;
4952 if (model->buttons & GENESYS_HAS_PDF4_SW)
4953 s->opt[OPT_PDF4_SW].cap =
4954 SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
4955 else
4956 s->opt[OPT_PDF4_SW].cap = SANE_CAP_INACTIVE;
4957
4958 /* calibration needed */
4959 s->opt[OPT_NEED_CALIBRATION_SW].name = "need-calibration";
4960 s->opt[OPT_NEED_CALIBRATION_SW].title = SANE_I18N ("Needs calibration");
4961 s->opt[OPT_NEED_CALIBRATION_SW].desc = SANE_I18N ("The scanner needs calibration for the current settings");
4962 s->opt[OPT_NEED_CALIBRATION_SW].type = SANE_TYPE_BOOL;
4963 s->opt[OPT_NEED_CALIBRATION_SW].unit = SANE_UNIT_NONE;
4964 if (model->buttons & GENESYS_HAS_CALIBRATE)
4965 s->opt[OPT_NEED_CALIBRATION_SW].cap =
4966 SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
4967 else
4968 s->opt[OPT_NEED_CALIBRATION_SW].cap = SANE_CAP_INACTIVE;
4969
4970 /* button group */
4971 s->opt[OPT_BUTTON_GROUP].name = "buttons";
4972 s->opt[OPT_BUTTON_GROUP].title = SANE_I18N ("Buttons");
4973 s->opt[OPT_BUTTON_GROUP].desc = "";
4974 s->opt[OPT_BUTTON_GROUP].type = SANE_TYPE_GROUP;
4975 s->opt[OPT_BUTTON_GROUP].cap = SANE_CAP_ADVANCED;
4976 s->opt[OPT_BUTTON_GROUP].size = 0;
4977 s->opt[OPT_BUTTON_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
4978
4979 /* calibrate button */
4980 s->opt[OPT_CALIBRATE].name = "calibrate";
4981 s->opt[OPT_CALIBRATE].title = SANE_I18N ("Calibrate");
4982 s->opt[OPT_CALIBRATE].desc =
4983 SANE_I18N ("Start calibration using special sheet");
4984 s->opt[OPT_CALIBRATE].type = SANE_TYPE_BUTTON;
4985 s->opt[OPT_CALIBRATE].unit = SANE_UNIT_NONE;
4986 if (model->buttons & GENESYS_HAS_CALIBRATE)
4987 s->opt[OPT_CALIBRATE].cap =
4988 SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED |
4989 SANE_CAP_AUTOMATIC;
4990 else
4991 s->opt[OPT_CALIBRATE].cap = SANE_CAP_INACTIVE;
4992
4993 /* clear calibration cache button */
4994 s->opt[OPT_CLEAR_CALIBRATION].name = "clear-calibration";
4995 s->opt[OPT_CLEAR_CALIBRATION].title = SANE_I18N ("Clear calibration");
4996 s->opt[OPT_CLEAR_CALIBRATION].desc = SANE_I18N ("Clear calibration cache");
4997 s->opt[OPT_CLEAR_CALIBRATION].type = SANE_TYPE_BUTTON;
4998 s->opt[OPT_CLEAR_CALIBRATION].unit = SANE_UNIT_NONE;
4999 s->opt[OPT_CLEAR_CALIBRATION].size = 0;
5000 s->opt[OPT_CLEAR_CALIBRATION].constraint_type = SANE_CONSTRAINT_NONE;
5001 s->opt[OPT_CLEAR_CALIBRATION].cap =
5002 SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
5003
5004 /* force calibration cache button */
5005 s->opt[OPT_FORCE_CALIBRATION].name = "force-calibration";
5006 s->opt[OPT_FORCE_CALIBRATION].title = SANE_I18N("Force calibration");
5007 s->opt[OPT_FORCE_CALIBRATION].desc = SANE_I18N("Force calibration ignoring all and any calibration caches");
5008 s->opt[OPT_FORCE_CALIBRATION].type = SANE_TYPE_BUTTON;
5009 s->opt[OPT_FORCE_CALIBRATION].unit = SANE_UNIT_NONE;
5010 s->opt[OPT_FORCE_CALIBRATION].size = 0;
5011 s->opt[OPT_FORCE_CALIBRATION].constraint_type = SANE_CONSTRAINT_NONE;
5012 s->opt[OPT_FORCE_CALIBRATION].cap =
5013 SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
5014
5015 // ignore offsets option
5016 s->opt[OPT_IGNORE_OFFSETS].name = "ignore-internal-offsets";
5017 s->opt[OPT_IGNORE_OFFSETS].title = SANE_I18N("Ignore internal offsets");
5018 s->opt[OPT_IGNORE_OFFSETS].desc =
5019 SANE_I18N("Acquires the image including the internal calibration areas of the scanner");
5020 s->opt[OPT_IGNORE_OFFSETS].type = SANE_TYPE_BUTTON;
5021 s->opt[OPT_IGNORE_OFFSETS].unit = SANE_UNIT_NONE;
5022 s->opt[OPT_IGNORE_OFFSETS].size = 0;
5023 s->opt[OPT_IGNORE_OFFSETS].constraint_type = SANE_CONSTRAINT_NONE;
5024 s->opt[OPT_IGNORE_OFFSETS].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT |
5025 SANE_CAP_ADVANCED;
5026
5027 calc_parameters(s);
5028 }
5029
5030 static bool present;
5031
5032 // this function is passed to C API, it must not throw
5033 static SANE_Status
check_present(SANE_String_Const devname)5034 check_present (SANE_String_Const devname) noexcept
5035 {
5036 DBG_HELPER_ARGS(dbg, "%s detected.", devname);
5037 present = true;
5038 return SANE_STATUS_GOOD;
5039 }
5040
get_matching_usb_dev(std::uint16_t vendor_id,std::uint16_t product_id,std::uint16_t bcd_device)5041 const UsbDeviceEntry& get_matching_usb_dev(std::uint16_t vendor_id, std::uint16_t product_id,
5042 std::uint16_t bcd_device)
5043 {
5044 for (auto& usb_dev : *s_usb_devices) {
5045 if (usb_dev.matches(vendor_id, product_id, bcd_device)) {
5046 return usb_dev;
5047 }
5048 }
5049
5050 throw SaneException("vendor 0x%x product 0x%x (bcdDevice 0x%x) "
5051 "is not supported by this backend",
5052 vendor_id, product_id, bcd_device);
5053 }
5054
attach_usb_device(const char * devname,std::uint16_t vendor_id,std::uint16_t product_id,std::uint16_t bcd_device)5055 static Genesys_Device* attach_usb_device(const char* devname,
5056 std::uint16_t vendor_id, std::uint16_t product_id,
5057 std::uint16_t bcd_device)
5058 {
5059 const auto& usb_dev = get_matching_usb_dev(vendor_id, product_id, bcd_device);
5060
5061 s_devices->emplace_back();
5062 Genesys_Device* dev = &s_devices->back();
5063 dev->file_name = devname;
5064 dev->vendorId = vendor_id;
5065 dev->productId = product_id;
5066 dev->model = &usb_dev.model();
5067 dev->usb_mode = 0; // i.e. unset
5068 dev->already_initialized = false;
5069 return dev;
5070 }
5071
5072 static bool s_attach_device_by_name_evaluate_bcd_device = false;
5073
attach_device_by_name(SANE_String_Const devname,bool may_wait)5074 static Genesys_Device* attach_device_by_name(SANE_String_Const devname, bool may_wait)
5075 {
5076 DBG_HELPER_ARGS(dbg, " devname: %s, may_wait = %d", devname, may_wait);
5077
5078 if (!devname) {
5079 throw SaneException("devname must not be nullptr");
5080 }
5081
5082 for (auto& dev : *s_devices) {
5083 if (dev.file_name == devname) {
5084 DBG(DBG_info, "%s: device `%s' was already in device list\n", __func__, devname);
5085 return &dev;
5086 }
5087 }
5088
5089 DBG(DBG_info, "%s: trying to open device `%s'\n", __func__, devname);
5090
5091 UsbDevice usb_dev;
5092
5093 usb_dev.open(devname);
5094 DBG(DBG_info, "%s: device `%s' successfully opened\n", __func__, devname);
5095
5096 auto vendor_id = usb_dev.get_vendor_id();
5097 auto product_id = usb_dev.get_product_id();
5098 auto bcd_device = UsbDeviceEntry::BCD_DEVICE_NOT_SET;
5099 if (s_attach_device_by_name_evaluate_bcd_device) {
5100 // when the device is already known before scanning, we don't want to call get_bcd_device()
5101 // when iterating devices, as that will interfere with record/replay during testing.
5102 bcd_device = usb_dev.get_bcd_device();
5103 }
5104 usb_dev.close();
5105
5106 /* KV-SS080 is an auxiliary device which requires a master device to be here */
5107 if (vendor_id == 0x04da && product_id == 0x100f) {
5108 present = false;
5109 sanei_usb_find_devices(vendor_id, 0x1006, check_present);
5110 sanei_usb_find_devices(vendor_id, 0x1007, check_present);
5111 sanei_usb_find_devices(vendor_id, 0x1010, check_present);
5112 if (present == false) {
5113 throw SaneException("master device not present");
5114 }
5115 }
5116
5117 Genesys_Device* dev = attach_usb_device(devname, vendor_id, product_id, bcd_device);
5118
5119 DBG(DBG_info, "%s: found %u flatbed scanner %u at %s\n", __func__, vendor_id, product_id,
5120 dev->file_name.c_str());
5121
5122 return dev;
5123 }
5124
5125 // this function is passed to C API and must not throw
attach_one_device(SANE_String_Const devname)5126 static SANE_Status attach_one_device(SANE_String_Const devname) noexcept
5127 {
5128 DBG_HELPER(dbg);
5129 return wrap_exceptions_to_status_code(__func__, [=]()
5130 {
5131 attach_device_by_name(devname, false);
5132 });
5133 }
5134
5135 /* configuration framework functions */
5136
5137 // this function is passed to C API, it must not throw
5138 static SANE_Status
config_attach_genesys(SANEI_Config __sane_unused__ * config,const char * devname,void __sane_unused__ * data)5139 config_attach_genesys(SANEI_Config __sane_unused__ *config, const char *devname,
5140 void __sane_unused__ *data) noexcept
5141 {
5142 /* the devname has been processed and is ready to be used
5143 * directly. Since the backend is an USB only one, we can
5144 * call sanei_usb_attach_matching_devices straight */
5145 sanei_usb_attach_matching_devices (devname, attach_one_device);
5146
5147 return SANE_STATUS_GOOD;
5148 }
5149
5150 /* probes for scanner to attach to the backend */
probe_genesys_devices()5151 static void probe_genesys_devices()
5152 {
5153 DBG_HELPER(dbg);
5154 if (is_testing_mode()) {
5155 attach_usb_device(get_testing_device_name().c_str(),
5156 get_testing_vendor_id(), get_testing_product_id(),
5157 get_testing_bcd_device());
5158 return;
5159 }
5160
5161 SANEI_Config config;
5162
5163 // set configuration options structure : no option for this backend
5164 config.descriptors = nullptr;
5165 config.values = nullptr;
5166 config.count = 0;
5167
5168 auto status = sanei_configure_attach(GENESYS_CONFIG_FILE, &config,
5169 config_attach_genesys, NULL);
5170 if (status == SANE_STATUS_ACCESS_DENIED) {
5171 dbg.vlog(DBG_error0, "Critical error: Couldn't access configuration file '%s'",
5172 GENESYS_CONFIG_FILE);
5173 }
5174 TIE(status);
5175
5176 DBG(DBG_info, "%s: %zu devices currently attached\n", __func__, s_devices->size());
5177 }
5178
5179 /**
5180 * This should be changed if one of the substructures of
5181 Genesys_Calibration_Cache change, but it must be changed if there are
5182 changes that don't change size -- at least for now, as we store most
5183 of Genesys_Calibration_Cache as is.
5184 */
5185 static const char* CALIBRATION_IDENT = "sane_genesys";
5186 static const int CALIBRATION_VERSION = 32;
5187
read_calibration(std::istream & str,Genesys_Device::Calibration & calibration,const std::string & path)5188 bool read_calibration(std::istream& str, Genesys_Device::Calibration& calibration,
5189 const std::string& path)
5190 {
5191 DBG_HELPER(dbg);
5192
5193 std::string ident;
5194 serialize(str, ident);
5195
5196 if (ident != CALIBRATION_IDENT) {
5197 DBG(DBG_info, "%s: Incorrect calibration file '%s' header\n", __func__, path.c_str());
5198 return false;
5199 }
5200
5201 size_t version;
5202 serialize(str, version);
5203
5204 if (version != CALIBRATION_VERSION) {
5205 DBG(DBG_info, "%s: Incorrect calibration file '%s' version\n", __func__, path.c_str());
5206 return false;
5207 }
5208
5209 calibration.clear();
5210 serialize(str, calibration);
5211 return true;
5212 }
5213
5214 /**
5215 * reads previously cached calibration data
5216 * from file defined in dev->calib_file
5217 */
sanei_genesys_read_calibration(Genesys_Device::Calibration & calibration,const std::string & path)5218 static bool sanei_genesys_read_calibration(Genesys_Device::Calibration& calibration,
5219 const std::string& path)
5220 {
5221 DBG_HELPER(dbg);
5222
5223 std::ifstream str;
5224 str.open(path);
5225 if (!str.is_open()) {
5226 DBG(DBG_info, "%s: Cannot open %s\n", __func__, path.c_str());
5227 return false;
5228 }
5229
5230 return read_calibration(str, calibration, path);
5231 }
5232
write_calibration(std::ostream & str,Genesys_Device::Calibration & calibration)5233 void write_calibration(std::ostream& str, Genesys_Device::Calibration& calibration)
5234 {
5235 std::string ident = CALIBRATION_IDENT;
5236 serialize(str, ident);
5237 size_t version = CALIBRATION_VERSION;
5238 serialize(str, version);
5239 serialize_newline(str);
5240 serialize(str, calibration);
5241 }
5242
write_calibration(Genesys_Device::Calibration & calibration,const std::string & path)5243 static void write_calibration(Genesys_Device::Calibration& calibration, const std::string& path)
5244 {
5245 DBG_HELPER(dbg);
5246
5247 std::ofstream str;
5248 str.open(path);
5249 if (!str.is_open()) {
5250 throw SaneException("Cannot open calibration for writing");
5251 }
5252 write_calibration(str, calibration);
5253 }
5254
5255 /* -------------------------- SANE API functions ------------------------- */
5256
sane_init_impl(SANE_Int * version_code,SANE_Auth_Callback authorize)5257 void sane_init_impl(SANE_Int * version_code, SANE_Auth_Callback authorize)
5258 {
5259 DBG_INIT ();
5260 DBG_HELPER_ARGS(dbg, "authorize %s null", authorize ? "!=" : "==");
5261 DBG(DBG_init, "SANE Genesys backend from %s\n", PACKAGE_STRING);
5262
5263 if (!is_testing_mode()) {
5264 #ifdef HAVE_LIBUSB
5265 DBG(DBG_init, "SANE Genesys backend built with libusb-1.0\n");
5266 #endif
5267 #ifdef HAVE_LIBUSB_LEGACY
5268 DBG(DBG_init, "SANE Genesys backend built with libusb\n");
5269 #endif
5270 }
5271
5272 if (version_code) {
5273 *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0);
5274 }
5275
5276 if (!is_testing_mode()) {
5277 sanei_usb_init();
5278 }
5279
5280 s_scanners.init();
5281 s_devices.init();
5282 s_sane_devices.init();
5283 s_sane_devices_data.init();
5284 s_sane_devices_ptrs.init();
5285 genesys_init_sensor_tables();
5286 genesys_init_frontend_tables();
5287 genesys_init_gpo_tables();
5288 genesys_init_memory_layout_tables();
5289 genesys_init_motor_tables();
5290 genesys_init_usb_device_tables();
5291
5292
5293 DBG(DBG_info, "%s: %s endian machine\n", __func__,
5294 #ifdef WORDS_BIGENDIAN
5295 "big"
5296 #else
5297 "little"
5298 #endif
5299 );
5300
5301 // cold-plug case :detection of already connected scanners
5302 s_attach_device_by_name_evaluate_bcd_device = false;
5303 probe_genesys_devices();
5304 }
5305
5306
5307 SANE_GENESYS_API_LINKAGE
sane_init(SANE_Int * version_code,SANE_Auth_Callback authorize)5308 SANE_Status sane_init(SANE_Int * version_code, SANE_Auth_Callback authorize)
5309 {
5310 return wrap_exceptions_to_status_code(__func__, [=]()
5311 {
5312 sane_init_impl(version_code, authorize);
5313 });
5314 }
5315
5316 void
sane_exit_impl(void)5317 sane_exit_impl(void)
5318 {
5319 DBG_HELPER(dbg);
5320
5321 if (!is_testing_mode()) {
5322 sanei_usb_exit();
5323 }
5324
5325 run_functions_at_backend_exit();
5326 }
5327
5328 SANE_GENESYS_API_LINKAGE
sane_exit()5329 void sane_exit()
5330 {
5331 catch_all_exceptions(__func__, [](){ sane_exit_impl(); });
5332 }
5333
sane_get_devices_impl(const SANE_Device *** device_list,SANE_Bool local_only)5334 void sane_get_devices_impl(const SANE_Device *** device_list, SANE_Bool local_only)
5335 {
5336 DBG_HELPER_ARGS(dbg, "local_only = %s", local_only ? "true" : "false");
5337
5338 if (!is_testing_mode()) {
5339 // hot-plug case : detection of newly connected scanners */
5340 sanei_usb_scan_devices();
5341 }
5342 s_attach_device_by_name_evaluate_bcd_device = true;
5343 probe_genesys_devices();
5344
5345 s_sane_devices->clear();
5346 s_sane_devices_data->clear();
5347 s_sane_devices_ptrs->clear();
5348 s_sane_devices->reserve(s_devices->size());
5349 s_sane_devices_data->reserve(s_devices->size());
5350 s_sane_devices_ptrs->reserve(s_devices->size() + 1);
5351
5352 for (auto dev_it = s_devices->begin(); dev_it != s_devices->end();) {
5353
5354 if (is_testing_mode()) {
5355 present = true;
5356 } else {
5357 present = false;
5358 sanei_usb_find_devices(dev_it->vendorId, dev_it->productId, check_present);
5359 }
5360
5361 if (present) {
5362 s_sane_devices->emplace_back();
5363 s_sane_devices_data->emplace_back();
5364 auto& sane_device = s_sane_devices->back();
5365 auto& sane_device_data = s_sane_devices_data->back();
5366 sane_device_data.name = dev_it->file_name;
5367 sane_device.name = sane_device_data.name.c_str();
5368 sane_device.vendor = dev_it->model->vendor;
5369 sane_device.model = dev_it->model->model;
5370 sane_device.type = "flatbed scanner";
5371 s_sane_devices_ptrs->push_back(&sane_device);
5372 dev_it++;
5373 } else {
5374 dev_it = s_devices->erase(dev_it);
5375 }
5376 }
5377 s_sane_devices_ptrs->push_back(nullptr);
5378
5379 *const_cast<SANE_Device***>(device_list) = s_sane_devices_ptrs->data();
5380 }
5381
5382 SANE_GENESYS_API_LINKAGE
sane_get_devices(const SANE_Device *** device_list,SANE_Bool local_only)5383 SANE_Status sane_get_devices(const SANE_Device *** device_list, SANE_Bool local_only)
5384 {
5385 return wrap_exceptions_to_status_code(__func__, [=]()
5386 {
5387 sane_get_devices_impl(device_list, local_only);
5388 });
5389 }
5390
sane_open_impl(SANE_String_Const devicename,SANE_Handle * handle)5391 static void sane_open_impl(SANE_String_Const devicename, SANE_Handle * handle)
5392 {
5393 DBG_HELPER_ARGS(dbg, "devicename = %s", devicename);
5394 Genesys_Device* dev = nullptr;
5395
5396 /* devicename="" or devicename="genesys" are default values that use
5397 * first available device
5398 */
5399 if (devicename[0] && strcmp ("genesys", devicename) != 0) {
5400 /* search for the given devicename in the device list */
5401 for (auto& d : *s_devices) {
5402 if (d.file_name == devicename) {
5403 dev = &d;
5404 break;
5405 }
5406 }
5407
5408 if (dev) {
5409 DBG(DBG_info, "%s: found `%s' in devlist\n", __func__, dev->file_name.c_str());
5410 } else if (is_testing_mode()) {
5411 DBG(DBG_info, "%s: couldn't find `%s' in devlist, not attaching", __func__, devicename);
5412 } else {
5413 DBG(DBG_info, "%s: couldn't find `%s' in devlist, trying attach\n", __func__,
5414 devicename);
5415 dbg.status("attach_device_by_name");
5416 dev = attach_device_by_name(devicename, true);
5417 dbg.clear();
5418 }
5419 } else {
5420 // empty devicename or "genesys" -> use first device
5421 if (!s_devices->empty()) {
5422 dev = &s_devices->front();
5423 DBG(DBG_info, "%s: empty devicename, trying `%s'\n", __func__, dev->file_name.c_str());
5424 }
5425 }
5426
5427 if (!dev) {
5428 throw SaneException("could not find the device to open: %s", devicename);
5429 }
5430
5431 if (is_testing_mode()) {
5432 // during testing we need to initialize dev->model before test scanner interface is created
5433 // as that it needs to know what type of chip it needs to mimic.
5434 auto vendor_id = get_testing_vendor_id();
5435 auto product_id = get_testing_product_id();
5436 auto bcd_device = get_testing_bcd_device();
5437
5438 dev->model = &get_matching_usb_dev(vendor_id, product_id, bcd_device).model();
5439
5440 auto interface = std::unique_ptr<TestScannerInterface>{
5441 new TestScannerInterface{dev, vendor_id, product_id, bcd_device}};
5442 interface->set_checkpoint_callback(get_testing_checkpoint_callback());
5443 dev->interface = std::move(interface);
5444
5445 dev->interface->get_usb_device().open(dev->file_name.c_str());
5446 } else {
5447 dev->interface = std::unique_ptr<ScannerInterfaceUsb>{new ScannerInterfaceUsb{dev}};
5448
5449 dbg.vstatus("open device '%s'", dev->file_name.c_str());
5450 dev->interface->get_usb_device().open(dev->file_name.c_str());
5451 dbg.clear();
5452
5453 auto bcd_device = dev->interface->get_usb_device().get_bcd_device();
5454
5455 dev->model = &get_matching_usb_dev(dev->vendorId, dev->productId, bcd_device).model();
5456 }
5457
5458 dbg.vlog(DBG_info, "Opened device %s", dev->model->name);
5459
5460 if (has_flag(dev->model->flags, ModelFlag::UNTESTED)) {
5461 DBG(DBG_error0, "WARNING: Your scanner is not fully supported or at least \n");
5462 DBG(DBG_error0, " had only limited testing. Please be careful and \n");
5463 DBG(DBG_error0, " report any failure/success to \n");
5464 DBG(DBG_error0, " sane-devel@alioth-lists.debian.net. Please provide as many\n");
5465 DBG(DBG_error0, " details as possible, e.g. the exact name of your\n");
5466 DBG(DBG_error0, " scanner and what does (not) work.\n");
5467 }
5468
5469 s_scanners->push_back(Genesys_Scanner());
5470 auto* s = &s_scanners->back();
5471
5472 s->dev = dev;
5473 s->scanning = false;
5474 dev->parking = false;
5475 dev->read_active = false;
5476 dev->force_calibration = 0;
5477 dev->line_count = 0;
5478
5479 *handle = s;
5480
5481 if (!dev->already_initialized) {
5482 sanei_genesys_init_structs (dev);
5483 }
5484
5485 dev->cmd_set = create_cmd_set(dev->model->asic_type);
5486
5487 init_options(s);
5488
5489 DBG_INIT();
5490
5491 // FIXME: we create sensor tables for the sensor, this should happen when we know which sensor
5492 // we will select
5493 dev->cmd_set->init(dev);
5494
5495 // some hardware capabilities are detected through sensors
5496 dev->cmd_set->update_hardware_sensors (s);
5497 }
5498
5499 SANE_GENESYS_API_LINKAGE
sane_open(SANE_String_Const devicename,SANE_Handle * handle)5500 SANE_Status sane_open(SANE_String_Const devicename, SANE_Handle* handle)
5501 {
5502 return wrap_exceptions_to_status_code(__func__, [=]()
5503 {
5504 sane_open_impl(devicename, handle);
5505 });
5506 }
5507
5508 void
sane_close_impl(SANE_Handle handle)5509 sane_close_impl(SANE_Handle handle)
5510 {
5511 DBG_HELPER(dbg);
5512
5513 /* remove handle from list of open handles: */
5514 auto it = s_scanners->end();
5515 for (auto it2 = s_scanners->begin(); it2 != s_scanners->end(); it2++)
5516 {
5517 if (&*it2 == handle) {
5518 it = it2;
5519 break;
5520 }
5521 }
5522 if (it == s_scanners->end())
5523 {
5524 DBG(DBG_error, "%s: invalid handle %p\n", __func__, handle);
5525 return; /* oops, not a handle we know about */
5526 }
5527
5528 auto* dev = it->dev;
5529
5530 // eject document for sheetfed scanners
5531 if (dev->model->is_sheetfed) {
5532 catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });
5533 } else {
5534 // in case scanner is parking, wait for the head to reach home position
5535 if (dev->parking) {
5536 sanei_genesys_wait_for_home(dev);
5537 }
5538 }
5539
5540 // enable power saving before leaving
5541 dev->cmd_set->save_power(dev, true);
5542
5543 // here is the place to store calibration cache
5544 if (dev->force_calibration == 0 && !is_testing_mode()) {
5545 catch_all_exceptions(__func__, [&](){ write_calibration(dev->calibration_cache,
5546 dev->calib_file); });
5547 }
5548
5549 dev->already_initialized = false;
5550 dev->clear();
5551
5552 // LAMP OFF : same register across all the ASICs */
5553 dev->interface->write_register(0x03, 0x00);
5554
5555 catch_all_exceptions(__func__, [&](){ dev->interface->get_usb_device().clear_halt(); });
5556
5557 // we need this to avoid these ASIC getting stuck in bulk writes
5558 catch_all_exceptions(__func__, [&](){ dev->interface->get_usb_device().reset(); });
5559
5560 // not freeing dev because it's in the dev list
5561 catch_all_exceptions(__func__, [&](){ dev->interface->get_usb_device().close(); });
5562
5563 s_scanners->erase(it);
5564 }
5565
5566 SANE_GENESYS_API_LINKAGE
sane_close(SANE_Handle handle)5567 void sane_close(SANE_Handle handle)
5568 {
5569 catch_all_exceptions(__func__, [=]()
5570 {
5571 sane_close_impl(handle);
5572 });
5573 }
5574
5575 const SANE_Option_Descriptor *
sane_get_option_descriptor_impl(SANE_Handle handle,SANE_Int option)5576 sane_get_option_descriptor_impl(SANE_Handle handle, SANE_Int option)
5577 {
5578 DBG_HELPER(dbg);
5579 Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
5580
5581 if (static_cast<unsigned>(option) >= NUM_OPTIONS) {
5582 return nullptr;
5583 }
5584
5585 DBG(DBG_io2, "%s: option = %s (%d)\n", __func__, s->opt[option].name, option);
5586 return s->opt + option;
5587 }
5588
5589
5590 SANE_GENESYS_API_LINKAGE
sane_get_option_descriptor(SANE_Handle handle,SANE_Int option)5591 const SANE_Option_Descriptor* sane_get_option_descriptor(SANE_Handle handle, SANE_Int option)
5592 {
5593 const SANE_Option_Descriptor* ret = nullptr;
5594 catch_all_exceptions(__func__, [&]()
5595 {
5596 ret = sane_get_option_descriptor_impl(handle, option);
5597 });
5598 return ret;
5599 }
5600
print_option(DebugMessageHelper & dbg,const Genesys_Scanner & s,int option,void * val)5601 static void print_option(DebugMessageHelper& dbg, const Genesys_Scanner& s, int option, void* val)
5602 {
5603 switch (s.opt[option].type) {
5604 case SANE_TYPE_INT: {
5605 dbg.vlog(DBG_proc, "value: %d", *reinterpret_cast<SANE_Word*>(val));
5606 return;
5607 }
5608 case SANE_TYPE_BOOL: {
5609 dbg.vlog(DBG_proc, "value: %s", *reinterpret_cast<SANE_Bool*>(val) ? "true" : "false");
5610 return;
5611 }
5612 case SANE_TYPE_FIXED: {
5613 dbg.vlog(DBG_proc, "value: %f", fixed_to_float(*reinterpret_cast<SANE_Word*>(val)));
5614 return;
5615 }
5616 case SANE_TYPE_STRING: {
5617 dbg.vlog(DBG_proc, "value: %s", reinterpret_cast<char*>(val));
5618 return;
5619 }
5620 default: break;
5621 }
5622 dbg.log(DBG_proc, "value: (non-printable)");
5623 }
5624
get_option_value(Genesys_Scanner * s,int option,void * val)5625 static void get_option_value(Genesys_Scanner* s, int option, void* val)
5626 {
5627 DBG_HELPER_ARGS(dbg, "option: %s (%d)", s->opt[option].name, option);
5628 auto* dev = s->dev;
5629 unsigned int i;
5630 SANE_Word* table = nullptr;
5631 std::vector<std::uint16_t> gamma_table;
5632 unsigned option_size = 0;
5633
5634 const Genesys_Sensor* sensor = nullptr;
5635 if (sanei_genesys_has_sensor(dev, dev->settings.xres, dev->settings.get_channels(),
5636 dev->settings.scan_method))
5637 {
5638 sensor = &sanei_genesys_find_sensor(dev, dev->settings.xres,
5639 dev->settings.get_channels(),
5640 dev->settings.scan_method);
5641 }
5642
5643 switch (option)
5644 {
5645 /* geometry */
5646 case OPT_TL_X:
5647 *reinterpret_cast<SANE_Word*>(val) = s->pos_top_left_x;
5648 break;
5649 case OPT_TL_Y:
5650 *reinterpret_cast<SANE_Word*>(val) = s->pos_top_left_y;
5651 break;
5652 case OPT_BR_X:
5653 *reinterpret_cast<SANE_Word*>(val) = s->pos_bottom_right_x;
5654 break;
5655 case OPT_BR_Y:
5656 *reinterpret_cast<SANE_Word*>(val) = s->pos_bottom_right_y;
5657 break;
5658 /* word options: */
5659 case OPT_NUM_OPTS:
5660 *reinterpret_cast<SANE_Word*>(val) = NUM_OPTIONS;
5661 break;
5662 case OPT_RESOLUTION:
5663 *reinterpret_cast<SANE_Word*>(val) = s->resolution;
5664 break;
5665 case OPT_BIT_DEPTH:
5666 *reinterpret_cast<SANE_Word*>(val) = s->bit_depth;
5667 break;
5668 case OPT_PREVIEW:
5669 *reinterpret_cast<SANE_Word*>(val) = s->preview;
5670 break;
5671 case OPT_LAMP_OFF:
5672 *reinterpret_cast<SANE_Word*>(val) = s->lamp_off;
5673 break;
5674 case OPT_LAMP_OFF_TIME:
5675 *reinterpret_cast<SANE_Word*>(val) = s->lamp_off_time;
5676 break;
5677 case OPT_CONTRAST:
5678 *reinterpret_cast<SANE_Word*>(val) = s->contrast;
5679 break;
5680 case OPT_BRIGHTNESS:
5681 *reinterpret_cast<SANE_Word*>(val) = s->brightness;
5682 break;
5683 case OPT_EXPIRATION_TIME:
5684 *reinterpret_cast<SANE_Word*>(val) = s->expiration_time;
5685 break;
5686 case OPT_CUSTOM_GAMMA:
5687 *reinterpret_cast<SANE_Word*>(val) = s->custom_gamma;
5688 break;
5689
5690 /* string options: */
5691 case OPT_MODE:
5692 std::strcpy(reinterpret_cast<char*>(val), s->mode.c_str());
5693 break;
5694 case OPT_COLOR_FILTER:
5695 std::strcpy(reinterpret_cast<char*>(val), s->color_filter.c_str());
5696 break;
5697 case OPT_CALIBRATION_FILE:
5698 std::strcpy(reinterpret_cast<char*>(val), s->calibration_file.c_str());
5699 break;
5700 case OPT_SOURCE:
5701 std::strcpy(reinterpret_cast<char*>(val), scan_method_to_option_string(s->scan_method));
5702 break;
5703
5704 /* word array options */
5705 case OPT_GAMMA_VECTOR:
5706 if (!sensor)
5707 throw SaneException("Unsupported scanner mode selected");
5708
5709 table = reinterpret_cast<SANE_Word*>(val);
5710 if (s->color_filter == "Red") {
5711 gamma_table = get_gamma_table(dev, *sensor, GENESYS_RED);
5712 } else if (s->color_filter == "Blue") {
5713 gamma_table = get_gamma_table(dev, *sensor, GENESYS_BLUE);
5714 } else {
5715 gamma_table = get_gamma_table(dev, *sensor, GENESYS_GREEN);
5716 }
5717 option_size = s->opt[option].size / sizeof (SANE_Word);
5718 if (gamma_table.size() != option_size) {
5719 throw std::runtime_error("The size of the gamma tables does not match");
5720 }
5721 for (i = 0; i < option_size; i++) {
5722 table[i] = gamma_table[i];
5723 }
5724 break;
5725 case OPT_GAMMA_VECTOR_R:
5726 if (!sensor)
5727 throw SaneException("Unsupported scanner mode selected");
5728
5729 table = reinterpret_cast<SANE_Word*>(val);
5730 gamma_table = get_gamma_table(dev, *sensor, GENESYS_RED);
5731 option_size = s->opt[option].size / sizeof (SANE_Word);
5732 if (gamma_table.size() != option_size) {
5733 throw std::runtime_error("The size of the gamma tables does not match");
5734 }
5735 for (i = 0; i < option_size; i++) {
5736 table[i] = gamma_table[i];
5737 }
5738 break;
5739 case OPT_GAMMA_VECTOR_G:
5740 if (!sensor)
5741 throw SaneException("Unsupported scanner mode selected");
5742
5743 table = reinterpret_cast<SANE_Word*>(val);
5744 gamma_table = get_gamma_table(dev, *sensor, GENESYS_GREEN);
5745 option_size = s->opt[option].size / sizeof (SANE_Word);
5746 if (gamma_table.size() != option_size) {
5747 throw std::runtime_error("The size of the gamma tables does not match");
5748 }
5749 for (i = 0; i < option_size; i++) {
5750 table[i] = gamma_table[i];
5751 }
5752 break;
5753 case OPT_GAMMA_VECTOR_B:
5754 if (!sensor)
5755 throw SaneException("Unsupported scanner mode selected");
5756
5757 table = reinterpret_cast<SANE_Word*>(val);
5758 gamma_table = get_gamma_table(dev, *sensor, GENESYS_BLUE);
5759 option_size = s->opt[option].size / sizeof (SANE_Word);
5760 if (gamma_table.size() != option_size) {
5761 throw std::runtime_error("The size of the gamma tables does not match");
5762 }
5763 for (i = 0; i < option_size; i++) {
5764 table[i] = gamma_table[i];
5765 }
5766 break;
5767 /* sensors */
5768 case OPT_SCAN_SW:
5769 case OPT_FILE_SW:
5770 case OPT_EMAIL_SW:
5771 case OPT_COPY_SW:
5772 case OPT_PAGE_LOADED_SW:
5773 case OPT_OCR_SW:
5774 case OPT_POWER_SW:
5775 case OPT_EXTRA_SW:
5776 case OPT_TRANSP_SW:
5777 case OPT_PDF1_SW:
5778 case OPT_PDF2_SW:
5779 case OPT_PDF3_SW:
5780 case OPT_PDF4_SW:
5781 s->dev->cmd_set->update_hardware_sensors(s);
5782 *reinterpret_cast<SANE_Bool*>(val) = s->buttons[genesys_option_to_button(option)].read();
5783 break;
5784
5785 case OPT_NEED_CALIBRATION_SW: {
5786 if (!sensor) {
5787 throw SaneException("Unsupported scanner mode selected");
5788 }
5789
5790 // scanner needs calibration for current mode unless a matching calibration cache is
5791 // found
5792
5793 bool result = true;
5794
5795 auto session = dev->cmd_set->calculate_scan_session(dev, *sensor, dev->settings);
5796
5797 for (auto& cache : dev->calibration_cache) {
5798 if (sanei_genesys_is_compatible_calibration(dev, session, &cache, false)) {
5799 *reinterpret_cast<SANE_Bool*>(val) = SANE_FALSE;
5800 }
5801 }
5802 *reinterpret_cast<SANE_Bool*>(val) = result;
5803 break;
5804 }
5805 default:
5806 DBG(DBG_warn, "%s: can't get unknown option %d\n", __func__, option);
5807 }
5808 print_option(dbg, *s, option, val);
5809 }
5810
5811 /** @brief set calibration file value
5812 * Set calibration file value. Load new cache values from file if it exists,
5813 * else creates the file*/
set_calibration_value(Genesys_Scanner * s,const char * val)5814 static void set_calibration_value(Genesys_Scanner* s, const char* val)
5815 {
5816 DBG_HELPER(dbg);
5817 auto dev = s->dev;
5818
5819 std::string new_calib_path = val;
5820 Genesys_Device::Calibration new_calibration;
5821
5822 bool is_calib_success = false;
5823 catch_all_exceptions(__func__, [&]()
5824 {
5825 is_calib_success = sanei_genesys_read_calibration(new_calibration, new_calib_path);
5826 });
5827
5828 if (!is_calib_success) {
5829 return;
5830 }
5831
5832 dev->calibration_cache = std::move(new_calibration);
5833 dev->calib_file = new_calib_path;
5834 s->calibration_file = new_calib_path;
5835 DBG(DBG_info, "%s: Calibration filename set to '%s':\n", __func__, new_calib_path.c_str());
5836 }
5837
5838 /* sets an option , called by sane_control_option */
set_option_value(Genesys_Scanner * s,int option,void * val,SANE_Int * myinfo)5839 static void set_option_value(Genesys_Scanner* s, int option, void *val, SANE_Int* myinfo)
5840 {
5841 DBG_HELPER_ARGS(dbg, "option: %s (%d)", s->opt[option].name, option);
5842 print_option(dbg, *s, option, val);
5843
5844 auto* dev = s->dev;
5845
5846 SANE_Word *table;
5847 unsigned int i;
5848 unsigned option_size = 0;
5849
5850 switch (option) {
5851 case OPT_TL_X:
5852 s->pos_top_left_x = *reinterpret_cast<SANE_Word*>(val);
5853 calc_parameters(s);
5854 *myinfo |= SANE_INFO_RELOAD_PARAMS;
5855 break;
5856 case OPT_TL_Y:
5857 s->pos_top_left_y = *reinterpret_cast<SANE_Word*>(val);
5858 calc_parameters(s);
5859 *myinfo |= SANE_INFO_RELOAD_PARAMS;
5860 break;
5861 case OPT_BR_X:
5862 s->pos_bottom_right_x = *reinterpret_cast<SANE_Word*>(val);
5863 calc_parameters(s);
5864 *myinfo |= SANE_INFO_RELOAD_PARAMS;
5865 break;
5866 case OPT_BR_Y:
5867 s->pos_bottom_right_y = *reinterpret_cast<SANE_Word*>(val);
5868 calc_parameters(s);
5869 *myinfo |= SANE_INFO_RELOAD_PARAMS;
5870 break;
5871 case OPT_RESOLUTION:
5872 s->resolution = *reinterpret_cast<SANE_Word*>(val);
5873 calc_parameters(s);
5874 *myinfo |= SANE_INFO_RELOAD_PARAMS;
5875 break;
5876 case OPT_LAMP_OFF:
5877 s->lamp_off = *reinterpret_cast<SANE_Word*>(val);
5878 calc_parameters(s);
5879 *myinfo |= SANE_INFO_RELOAD_PARAMS;
5880 break;
5881 case OPT_PREVIEW:
5882 s->preview = *reinterpret_cast<SANE_Word*>(val);
5883 calc_parameters(s);
5884 *myinfo |= SANE_INFO_RELOAD_PARAMS;
5885 break;
5886 case OPT_BRIGHTNESS:
5887 s->brightness = *reinterpret_cast<SANE_Word*>(val);
5888 calc_parameters(s);
5889 *myinfo |= SANE_INFO_RELOAD_PARAMS;
5890 break;
5891 case OPT_CONTRAST:
5892 s->contrast = *reinterpret_cast<SANE_Word*>(val);
5893 calc_parameters(s);
5894 *myinfo |= SANE_INFO_RELOAD_PARAMS;
5895 break;
5896 /* software enhancement functions only apply to 8 or 1 bits data */
5897 case OPT_BIT_DEPTH:
5898 s->bit_depth = *reinterpret_cast<SANE_Word*>(val);
5899 if(s->bit_depth>8)
5900 {
5901 DISABLE(OPT_CONTRAST);
5902 DISABLE(OPT_BRIGHTNESS);
5903 } else {
5904 ENABLE(OPT_CONTRAST);
5905 ENABLE(OPT_BRIGHTNESS);
5906 }
5907 calc_parameters(s);
5908 *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
5909 break;
5910 case OPT_SOURCE: {
5911 auto scan_method = option_string_to_scan_method(reinterpret_cast<const char*>(val));
5912 if (s->scan_method != scan_method) {
5913 s->scan_method = scan_method;
5914
5915 set_xy_range_option_values(*s);
5916 set_resolution_option_values(*s, false);
5917
5918 *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
5919 }
5920 break;
5921 }
5922 case OPT_MODE: {
5923 s->mode = reinterpret_cast<const char*>(val);
5924
5925 if (s->mode == SANE_VALUE_SCAN_MODE_GRAY) {
5926 if (dev->model->asic_type != AsicType::GL646 || !dev->model->is_cis) {
5927 ENABLE(OPT_COLOR_FILTER);
5928 }
5929 create_bpp_list(s, dev->model->bpp_gray_values);
5930 s->bit_depth = dev->model->bpp_gray_values[0];
5931 } else {
5932 DISABLE(OPT_COLOR_FILTER);
5933 create_bpp_list(s, dev->model->bpp_color_values);
5934 s->bit_depth = dev->model->bpp_color_values[0];
5935 }
5936
5937 calc_parameters(s);
5938
5939 /* if custom gamma, toggle gamma table options according to the mode */
5940 if (s->custom_gamma)
5941 {
5942 if (s->mode == SANE_VALUE_SCAN_MODE_COLOR)
5943 {
5944 DISABLE (OPT_GAMMA_VECTOR);
5945 ENABLE (OPT_GAMMA_VECTOR_R);
5946 ENABLE (OPT_GAMMA_VECTOR_G);
5947 ENABLE (OPT_GAMMA_VECTOR_B);
5948 }
5949 else
5950 {
5951 ENABLE (OPT_GAMMA_VECTOR);
5952 DISABLE (OPT_GAMMA_VECTOR_R);
5953 DISABLE (OPT_GAMMA_VECTOR_G);
5954 DISABLE (OPT_GAMMA_VECTOR_B);
5955 }
5956 }
5957
5958 *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
5959 break;
5960 }
5961 case OPT_COLOR_FILTER:
5962 s->color_filter = reinterpret_cast<const char*>(val);
5963 calc_parameters(s);
5964 break;
5965 case OPT_CALIBRATION_FILE: {
5966 if (dev->force_calibration == 0) {
5967 set_calibration_value(s, reinterpret_cast<const char*>(val));
5968 }
5969 break;
5970 }
5971 case OPT_LAMP_OFF_TIME: {
5972 if (*reinterpret_cast<SANE_Word*>(val) != s->lamp_off_time) {
5973 s->lamp_off_time = *reinterpret_cast<SANE_Word*>(val);
5974 dev->cmd_set->set_powersaving(dev, s->lamp_off_time);
5975 }
5976 break;
5977 }
5978 case OPT_EXPIRATION_TIME: {
5979 if (*reinterpret_cast<SANE_Word*>(val) != s->expiration_time) {
5980 s->expiration_time = *reinterpret_cast<SANE_Word*>(val);
5981 }
5982 break;
5983 }
5984 case OPT_CUSTOM_GAMMA: {
5985 *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
5986 s->custom_gamma = *reinterpret_cast<SANE_Bool*>(val);
5987
5988 if (s->custom_gamma) {
5989 if (s->mode == SANE_VALUE_SCAN_MODE_COLOR)
5990 {
5991 DISABLE (OPT_GAMMA_VECTOR);
5992 ENABLE (OPT_GAMMA_VECTOR_R);
5993 ENABLE (OPT_GAMMA_VECTOR_G);
5994 ENABLE (OPT_GAMMA_VECTOR_B);
5995 }
5996 else
5997 {
5998 ENABLE (OPT_GAMMA_VECTOR);
5999 DISABLE (OPT_GAMMA_VECTOR_R);
6000 DISABLE (OPT_GAMMA_VECTOR_G);
6001 DISABLE (OPT_GAMMA_VECTOR_B);
6002 }
6003 }
6004 else
6005 {
6006 DISABLE (OPT_GAMMA_VECTOR);
6007 DISABLE (OPT_GAMMA_VECTOR_R);
6008 DISABLE (OPT_GAMMA_VECTOR_G);
6009 DISABLE (OPT_GAMMA_VECTOR_B);
6010 for (auto& table : dev->gamma_override_tables) {
6011 table.clear();
6012 }
6013 }
6014 break;
6015 }
6016
6017 case OPT_GAMMA_VECTOR: {
6018 table = reinterpret_cast<SANE_Word*>(val);
6019 option_size = s->opt[option].size / sizeof (SANE_Word);
6020
6021 dev->gamma_override_tables[GENESYS_RED].resize(option_size);
6022 dev->gamma_override_tables[GENESYS_GREEN].resize(option_size);
6023 dev->gamma_override_tables[GENESYS_BLUE].resize(option_size);
6024 for (i = 0; i < option_size; i++) {
6025 dev->gamma_override_tables[GENESYS_RED][i] = table[i];
6026 dev->gamma_override_tables[GENESYS_GREEN][i] = table[i];
6027 dev->gamma_override_tables[GENESYS_BLUE][i] = table[i];
6028 }
6029 break;
6030 }
6031 case OPT_GAMMA_VECTOR_R: {
6032 table = reinterpret_cast<SANE_Word*>(val);
6033 option_size = s->opt[option].size / sizeof (SANE_Word);
6034 dev->gamma_override_tables[GENESYS_RED].resize(option_size);
6035 for (i = 0; i < option_size; i++) {
6036 dev->gamma_override_tables[GENESYS_RED][i] = table[i];
6037 }
6038 break;
6039 }
6040 case OPT_GAMMA_VECTOR_G: {
6041 table = reinterpret_cast<SANE_Word*>(val);
6042 option_size = s->opt[option].size / sizeof (SANE_Word);
6043 dev->gamma_override_tables[GENESYS_GREEN].resize(option_size);
6044 for (i = 0; i < option_size; i++) {
6045 dev->gamma_override_tables[GENESYS_GREEN][i] = table[i];
6046 }
6047 break;
6048 }
6049 case OPT_GAMMA_VECTOR_B: {
6050 table = reinterpret_cast<SANE_Word*>(val);
6051 option_size = s->opt[option].size / sizeof (SANE_Word);
6052 dev->gamma_override_tables[GENESYS_BLUE].resize(option_size);
6053 for (i = 0; i < option_size; i++) {
6054 dev->gamma_override_tables[GENESYS_BLUE][i] = table[i];
6055 }
6056 break;
6057 }
6058 case OPT_CALIBRATE: {
6059 auto& sensor = sanei_genesys_find_sensor_for_write(dev, dev->settings.xres,
6060 dev->settings.get_channels(),
6061 dev->settings.scan_method);
6062 catch_all_exceptions(__func__, [&]()
6063 {
6064 dev->cmd_set->save_power(dev, false);
6065 genesys_scanner_calibration(dev, sensor);
6066 });
6067 catch_all_exceptions(__func__, [&]()
6068 {
6069 dev->cmd_set->save_power(dev, true);
6070 });
6071 *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
6072 break;
6073 }
6074 case OPT_CLEAR_CALIBRATION: {
6075 dev->calibration_cache.clear();
6076
6077 // remove file
6078 unlink(dev->calib_file.c_str());
6079 // signals that sensors will have to be read again
6080 *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
6081 break;
6082 }
6083 case OPT_FORCE_CALIBRATION: {
6084 dev->force_calibration = 1;
6085 dev->calibration_cache.clear();
6086 dev->calib_file.clear();
6087
6088 // signals that sensors will have to be read again
6089 *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
6090 break;
6091 }
6092
6093 case OPT_IGNORE_OFFSETS: {
6094 dev->ignore_offsets = true;
6095 break;
6096 }
6097 default: {
6098 DBG(DBG_warn, "%s: can't set unknown option %d\n", __func__, option);
6099 }
6100 }
6101 }
6102
6103
6104 /* sets and gets scanner option values */
sane_control_option_impl(SANE_Handle handle,SANE_Int option,SANE_Action action,void * val,SANE_Int * info)6105 void sane_control_option_impl(SANE_Handle handle, SANE_Int option,
6106 SANE_Action action, void *val, SANE_Int * info)
6107 {
6108 Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
6109 auto action_str = (action == SANE_ACTION_GET_VALUE) ? "get" :
6110 (action == SANE_ACTION_SET_VALUE) ? "set" :
6111 (action == SANE_ACTION_SET_AUTO) ? "set_auto" : "unknown";
6112 DBG_HELPER_ARGS(dbg, "action = %s, option = %s (%d)", action_str,
6113 s->opt[option].name, option);
6114
6115 SANE_Word cap;
6116 SANE_Int myinfo = 0;
6117
6118 if (info) {
6119 *info = 0;
6120 }
6121
6122 if (s->scanning) {
6123 throw SaneException(SANE_STATUS_DEVICE_BUSY,
6124 "don't call this function while scanning (option = %s (%d))",
6125 s->opt[option].name, option);
6126 }
6127 if (option >= NUM_OPTIONS || option < 0) {
6128 throw SaneException("option %d >= NUM_OPTIONS || option < 0", option);
6129 }
6130
6131 cap = s->opt[option].cap;
6132
6133 if (!SANE_OPTION_IS_ACTIVE (cap)) {
6134 throw SaneException("option %d is inactive", option);
6135 }
6136
6137 switch (action) {
6138 case SANE_ACTION_GET_VALUE:
6139 get_option_value(s, option, val);
6140 break;
6141
6142 case SANE_ACTION_SET_VALUE:
6143 if (!SANE_OPTION_IS_SETTABLE (cap)) {
6144 throw SaneException("option %d is not settable", option);
6145 }
6146
6147 TIE(sanei_constrain_value(s->opt + option, val, &myinfo));
6148
6149 set_option_value(s, option, val, &myinfo);
6150 break;
6151
6152 case SANE_ACTION_SET_AUTO:
6153 throw SaneException("SANE_ACTION_SET_AUTO unsupported since no option "
6154 "has SANE_CAP_AUTOMATIC");
6155 default:
6156 throw SaneException("unknown action %d for option %d", action, option);
6157 }
6158
6159 if (info)
6160 *info = myinfo;
6161 }
6162
6163 SANE_GENESYS_API_LINKAGE
sane_control_option(SANE_Handle handle,SANE_Int option,SANE_Action action,void * val,SANE_Int * info)6164 SANE_Status sane_control_option(SANE_Handle handle, SANE_Int option,
6165 SANE_Action action, void *val, SANE_Int * info)
6166 {
6167 return wrap_exceptions_to_status_code(__func__, [=]()
6168 {
6169 sane_control_option_impl(handle, option, action, val, info);
6170 });
6171 }
6172
sane_get_parameters_impl(SANE_Handle handle,SANE_Parameters * params)6173 void sane_get_parameters_impl(SANE_Handle handle, SANE_Parameters* params)
6174 {
6175 DBG_HELPER(dbg);
6176 Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
6177 auto* dev = s->dev;
6178
6179 /* don't recompute parameters once data reading is active, ie during scan */
6180 if (!dev->read_active) {
6181 calc_parameters(s);
6182 }
6183 if (params) {
6184 *params = s->params;
6185
6186 /* in the case of a sheetfed scanner, when full height is specified
6187 * we override the computed line number with -1 to signal that we
6188 * don't know the real document height.
6189 * We don't do that doing buffering image for digital processing
6190 */
6191 if (dev->model->is_sheetfed &&
6192 s->pos_bottom_right_y == s->opt[OPT_BR_Y].constraint.range->max)
6193 {
6194 params->lines = -1;
6195 }
6196 }
6197 debug_dump(DBG_proc, *params);
6198 }
6199
6200 SANE_GENESYS_API_LINKAGE
sane_get_parameters(SANE_Handle handle,SANE_Parameters * params)6201 SANE_Status sane_get_parameters(SANE_Handle handle, SANE_Parameters* params)
6202 {
6203 return wrap_exceptions_to_status_code(__func__, [=]()
6204 {
6205 sane_get_parameters_impl(handle, params);
6206 });
6207 }
6208
sane_start_impl(SANE_Handle handle)6209 void sane_start_impl(SANE_Handle handle)
6210 {
6211 DBG_HELPER(dbg);
6212 Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
6213 auto* dev = s->dev;
6214
6215 if (s->pos_top_left_x >= s->pos_bottom_right_x) {
6216 throw SaneException("top left x >= bottom right x");
6217 }
6218 if (s->pos_top_left_y >= s->pos_bottom_right_y) {
6219 throw SaneException("top left y >= bottom right y");
6220 }
6221
6222 // fetch stored calibration
6223 if (dev->force_calibration == 0) {
6224 auto path = calibration_filename(dev);
6225 s->calibration_file = path;
6226 dev->calib_file = path;
6227 DBG(DBG_info, "%s: Calibration filename set to:\n", __func__);
6228 DBG(DBG_info, "%s: >%s<\n", __func__, dev->calib_file.c_str());
6229
6230 catch_all_exceptions(__func__, [&]()
6231 {
6232 sanei_genesys_read_calibration(dev->calibration_cache, dev->calib_file);
6233 });
6234 }
6235
6236 // First make sure we have a current parameter set. Some of the
6237 // parameters will be overwritten below, but that's OK.
6238
6239 calc_parameters(s);
6240 genesys_start_scan(dev, s->lamp_off);
6241
6242 s->scanning = true;
6243 }
6244
6245 SANE_GENESYS_API_LINKAGE
sane_start(SANE_Handle handle)6246 SANE_Status sane_start(SANE_Handle handle)
6247 {
6248 return wrap_exceptions_to_status_code(__func__, [=]()
6249 {
6250 sane_start_impl(handle);
6251 });
6252 }
6253
6254 // returns SANE_STATUS_GOOD if there are more data, SANE_STATUS_EOF otherwise
sane_read_impl(SANE_Handle handle,SANE_Byte * buf,SANE_Int max_len,SANE_Int * len)6255 SANE_Status sane_read_impl(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int* len)
6256 {
6257 DBG_HELPER(dbg);
6258 Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
6259 size_t local_len;
6260
6261 if (!s) {
6262 throw SaneException("handle is nullptr");
6263 }
6264
6265 auto* dev = s->dev;
6266 if (!dev) {
6267 throw SaneException("dev is nullptr");
6268 }
6269
6270 if (!buf) {
6271 throw SaneException("buf is nullptr");
6272 }
6273
6274 if (!len) {
6275 throw SaneException("len is nullptr");
6276 }
6277
6278 *len = 0;
6279
6280 if (!s->scanning) {
6281 throw SaneException(SANE_STATUS_CANCELLED,
6282 "scan was cancelled, is over or has not been initiated yet");
6283 }
6284
6285 DBG(DBG_proc, "%s: start, %d maximum bytes required\n", __func__, max_len);
6286 DBG(DBG_io2, "%s: bytes_to_read=%zu, total_bytes_read=%zu\n", __func__,
6287 dev->total_bytes_to_read, dev->total_bytes_read);
6288
6289 if(dev->total_bytes_read>=dev->total_bytes_to_read)
6290 {
6291 DBG(DBG_proc, "%s: nothing more to scan: EOF\n", __func__);
6292
6293 /* issue park command immediately in case scanner can handle it
6294 * so we save time */
6295 if (!dev->model->is_sheetfed && !has_flag(dev->model->flags, ModelFlag::MUST_WAIT) &&
6296 !dev->parking)
6297 {
6298 dev->cmd_set->move_back_home(dev, false);
6299 dev->parking = true;
6300 }
6301 return SANE_STATUS_EOF;
6302 }
6303
6304 local_len = max_len;
6305
6306 genesys_read_ordered_data(dev, buf, &local_len);
6307
6308 *len = local_len;
6309 if (local_len > static_cast<std::size_t>(max_len)) {
6310 dbg.log(DBG_error, "error: returning incorrect length");
6311 }
6312 DBG(DBG_proc, "%s: %d bytes returned\n", __func__, *len);
6313 return SANE_STATUS_GOOD;
6314 }
6315
6316 SANE_GENESYS_API_LINKAGE
sane_read(SANE_Handle handle,SANE_Byte * buf,SANE_Int max_len,SANE_Int * len)6317 SANE_Status sane_read(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int* len)
6318 {
6319 return wrap_exceptions_to_status_code_return(__func__, [=]()
6320 {
6321 return sane_read_impl(handle, buf, max_len, len);
6322 });
6323 }
6324
sane_cancel_impl(SANE_Handle handle)6325 void sane_cancel_impl(SANE_Handle handle)
6326 {
6327 DBG_HELPER(dbg);
6328 Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
6329 auto* dev = s->dev;
6330
6331 s->scanning = false;
6332 dev->read_active = false;
6333
6334 // no need to end scan if we are parking the head
6335 if (!dev->parking) {
6336 dev->cmd_set->end_scan(dev, &dev->reg, true);
6337 }
6338
6339 // park head if flatbed scanner
6340 if (!dev->model->is_sheetfed) {
6341 if (!dev->parking) {
6342 dev->cmd_set->move_back_home(dev, has_flag(dev->model->flags, ModelFlag::MUST_WAIT));
6343 dev->parking = !has_flag(dev->model->flags, ModelFlag::MUST_WAIT);
6344 }
6345 } else {
6346 // in case of sheetfed scanners, we have to eject the document if still present
6347 dev->cmd_set->eject_document(dev);
6348 }
6349
6350 // enable power saving mode unless we are parking ....
6351 if (!dev->parking) {
6352 dev->cmd_set->save_power(dev, true);
6353 }
6354 }
6355
6356 SANE_GENESYS_API_LINKAGE
sane_cancel(SANE_Handle handle)6357 void sane_cancel(SANE_Handle handle)
6358 {
6359 catch_all_exceptions(__func__, [=]() { sane_cancel_impl(handle); });
6360 }
6361
sane_set_io_mode_impl(SANE_Handle handle,SANE_Bool non_blocking)6362 void sane_set_io_mode_impl(SANE_Handle handle, SANE_Bool non_blocking)
6363 {
6364 DBG_HELPER_ARGS(dbg, "handle = %p, non_blocking = %s", handle,
6365 non_blocking == SANE_TRUE ? "true" : "false");
6366 Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
6367
6368 if (!s->scanning) {
6369 throw SaneException("not scanning");
6370 }
6371 if (non_blocking) {
6372 throw SaneException(SANE_STATUS_UNSUPPORTED);
6373 }
6374 }
6375
6376 SANE_GENESYS_API_LINKAGE
sane_set_io_mode(SANE_Handle handle,SANE_Bool non_blocking)6377 SANE_Status sane_set_io_mode(SANE_Handle handle, SANE_Bool non_blocking)
6378 {
6379 return wrap_exceptions_to_status_code(__func__, [=]()
6380 {
6381 sane_set_io_mode_impl(handle, non_blocking);
6382 });
6383 }
6384
sane_get_select_fd_impl(SANE_Handle handle,SANE_Int * fd)6385 void sane_get_select_fd_impl(SANE_Handle handle, SANE_Int* fd)
6386 {
6387 DBG_HELPER_ARGS(dbg, "handle = %p, fd = %p", handle, reinterpret_cast<void*>(fd));
6388 Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
6389
6390 if (!s->scanning) {
6391 throw SaneException("not scanning");
6392 }
6393 throw SaneException(SANE_STATUS_UNSUPPORTED);
6394 }
6395
6396 SANE_GENESYS_API_LINKAGE
sane_get_select_fd(SANE_Handle handle,SANE_Int * fd)6397 SANE_Status sane_get_select_fd(SANE_Handle handle, SANE_Int* fd)
6398 {
6399 return wrap_exceptions_to_status_code(__func__, [=]()
6400 {
6401 sane_get_select_fd_impl(handle, fd);
6402 });
6403 }
6404
genesys_option_to_button(int option)6405 GenesysButtonName genesys_option_to_button(int option)
6406 {
6407 switch (option) {
6408 case OPT_SCAN_SW: return BUTTON_SCAN_SW;
6409 case OPT_FILE_SW: return BUTTON_FILE_SW;
6410 case OPT_EMAIL_SW: return BUTTON_EMAIL_SW;
6411 case OPT_COPY_SW: return BUTTON_COPY_SW;
6412 case OPT_PAGE_LOADED_SW: return BUTTON_PAGE_LOADED_SW;
6413 case OPT_OCR_SW: return BUTTON_OCR_SW;
6414 case OPT_POWER_SW: return BUTTON_POWER_SW;
6415 case OPT_EXTRA_SW: return BUTTON_EXTRA_SW;
6416 case OPT_TRANSP_SW: return BUTTON_TRANSP_SW;
6417 case OPT_PDF1_SW: return BUTTON_PDF1_SW;
6418 case OPT_PDF2_SW: return BUTTON_PDF2_SW;
6419 case OPT_PDF3_SW: return BUTTON_PDF3_SW;
6420 case OPT_PDF4_SW: return BUTTON_PDF4_SW;
6421 default: throw std::runtime_error("Unknown option to convert to button index");
6422 }
6423 }
6424
6425 } // namespace genesys
6426