kopia lustrzana https://gitlab.com/sane-project/backends
genesys: Use new image pipeline for handling segmented sensors
rodzic
7f852e0417
commit
af17797d3c
|
@ -3400,119 +3400,12 @@ static void genesys_start_scan(Genesys_Device* dev, SANE_Bool lamp_off)
|
|||
|
||||
#include "genesys_conv.cc"
|
||||
|
||||
static void accurate_line_read(Genesys_Device* dev, Genesys_Buffer& buffer)
|
||||
{
|
||||
DBG_HELPER(dbg);
|
||||
buffer.reset();
|
||||
|
||||
dev->cmd_set->bulk_read_data(dev, 0x45, buffer.get_write_pos(buffer.size()),
|
||||
buffer.size());
|
||||
|
||||
buffer.produce(buffer.size());
|
||||
}
|
||||
|
||||
/** @brief fill buffer for segmented sensors
|
||||
* This function fills a read buffer with scanned data from a sensor segmented
|
||||
* in several parts (multi-lines sensors). Data of the same valid area is read
|
||||
* back to back and must be interleaved to get usable by the other stages
|
||||
* of the backend
|
||||
*/
|
||||
void genesys_fill_segmented_buffer(Genesys_Device* dev, uint8_t* work_buffer_dst, size_t size)
|
||||
{
|
||||
DBG_HELPER(dbg);
|
||||
|
||||
unsigned depth = dev->session.params.depth;
|
||||
|
||||
// fill buffer if needed
|
||||
if (dev->oe_buffer.avail() == 0)
|
||||
{
|
||||
accurate_line_read(dev, dev->oe_buffer);
|
||||
}
|
||||
|
||||
auto read_desegmented = [&](size_t curr_byte, size_t curr_segment) -> uint8_t
|
||||
{
|
||||
curr_byte += dev->session.output_segment_start_offset;
|
||||
curr_byte += dev->session.conseq_pixel_dist_bytes * dev->segment_order[curr_segment];
|
||||
return dev->oe_buffer.get_read_pos()[curr_byte];
|
||||
};
|
||||
|
||||
// copy size bytes of data, copying from a subwindow of each line
|
||||
// when last line of buffer is exhausted, read another one
|
||||
size_t count = 0;
|
||||
while (count < size) {
|
||||
if (depth == 1) {
|
||||
while (dev->deseg_curr_byte < dev->session.output_segment_pixel_group_count &&
|
||||
count < size)
|
||||
{
|
||||
for (unsigned n = 0; n < dev->session.segment_count; n++) {
|
||||
work_buffer_dst[count + n] = 0;
|
||||
}
|
||||
/* interleaving is at bit level */
|
||||
for (unsigned i = 0; i < 8; i++) {
|
||||
unsigned k = count + (i * dev->session.segment_count) / 8;
|
||||
for (unsigned n = 0; n < dev->session.segment_count; n++) {
|
||||
work_buffer_dst[k] = work_buffer_dst[k] << 1;
|
||||
if (read_desegmented(dev->deseg_curr_byte, n) & (0x80 >> i)) {
|
||||
work_buffer_dst[k] |= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* update counter and pointer */
|
||||
count += dev->session.segment_count;
|
||||
dev->deseg_curr_byte++;
|
||||
}
|
||||
}
|
||||
|
||||
if (depth == 8) {
|
||||
while (dev->deseg_curr_byte < dev->session.output_segment_pixel_group_count &&
|
||||
count < size)
|
||||
{
|
||||
for (unsigned n = 0; n < dev->session.segment_count; n++) {
|
||||
work_buffer_dst[count + n] = read_desegmented(dev->deseg_curr_byte, n);
|
||||
}
|
||||
/* update counter and pointer */
|
||||
count += dev->session.segment_count;
|
||||
dev->deseg_curr_byte++;
|
||||
}
|
||||
}
|
||||
|
||||
if (depth == 16) {
|
||||
while (dev->deseg_curr_byte < dev->session.output_segment_pixel_group_count &&
|
||||
count < size) {
|
||||
for (unsigned n = 0; n < dev->session.segment_count; n++) {
|
||||
work_buffer_dst[count + n * 2] = read_desegmented(dev->deseg_curr_byte, n);
|
||||
work_buffer_dst[count + n * 2 + 1] = read_desegmented(dev->deseg_curr_byte + 1, n);
|
||||
}
|
||||
/* update counter and pointer */
|
||||
count += dev->session.segment_count * 2;
|
||||
dev->deseg_curr_byte += 2;
|
||||
}
|
||||
}
|
||||
|
||||
// go to next line if needed
|
||||
if (dev->deseg_curr_byte == dev->session.output_segment_pixel_group_count) {
|
||||
dev->oe_buffer.set_pos(dev->oe_buffer.pos() + dev->session.output_line_bytes_raw);
|
||||
dev->deseg_curr_byte = 0;
|
||||
}
|
||||
|
||||
// read a new buffer if needed
|
||||
if (dev->oe_buffer.pos() >= dev->oe_buffer.avail())
|
||||
{
|
||||
accurate_line_read(dev, dev->oe_buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void genesys_fill_read_buffer(Genesys_Device* dev)
|
||||
{
|
||||
DBG_HELPER(dbg);
|
||||
size_t size;
|
||||
size_t space;
|
||||
uint8_t *work_buffer_dst;
|
||||
|
||||
/* for sheetfed scanner, we must check is document is shorter than
|
||||
* the requested scan */
|
||||
|
@ -3521,33 +3414,7 @@ static void genesys_fill_read_buffer(Genesys_Device* dev)
|
|||
dev->cmd_set->detect_document_end(dev);
|
||||
}
|
||||
|
||||
space = dev->read_buffer.size() - dev->read_buffer.avail();
|
||||
|
||||
work_buffer_dst = dev->read_buffer.get_write_pos(space);
|
||||
|
||||
size = space;
|
||||
|
||||
/* never read an odd number. exception: last read
|
||||
the chip internal counter does not count half words. */
|
||||
size &= ~1;
|
||||
/* Some setups need the reads to be multiples of 256 bytes */
|
||||
size &= ~0xff;
|
||||
|
||||
if (dev->read_bytes_left_after_deseg < size) {
|
||||
size = dev->read_bytes_left_after_deseg;
|
||||
/*round up to a multiple of 256 bytes */
|
||||
size += (size & 0xff) ? 0x100 : 0x00;
|
||||
size &= ~0xff;
|
||||
}
|
||||
|
||||
/* early out if our remaining buffer capacity is too low */
|
||||
if (size == 0)
|
||||
return;
|
||||
|
||||
DBG(DBG_io, "%s: reading %lu bytes\n", __func__, (u_long) size);
|
||||
|
||||
/* size is already maxed to our needs. for most models bulk_read_data
|
||||
will read as much data as requested. */
|
||||
std::size_t size = dev->read_buffer.size() - dev->read_buffer.avail();
|
||||
|
||||
/* due to sensors and motors, not all data can be directly used. It
|
||||
* may have to be read from another intermediate buffer and then processed.
|
||||
|
@ -3559,22 +3426,9 @@ static void genesys_fill_read_buffer(Genesys_Device* dev)
|
|||
*
|
||||
* This is also the place where full duplex data will be handled.
|
||||
*/
|
||||
if (dev->session.segment_count > 1) {
|
||||
// multi-segment sensors processing
|
||||
genesys_fill_segmented_buffer(dev, work_buffer_dst, size);
|
||||
}
|
||||
else /* regular case with no extra copy */
|
||||
{
|
||||
dev->cmd_set->bulk_read_data(dev, 0x45, work_buffer_dst, size);
|
||||
}
|
||||
dev->pipeline_buffer.get_data(size, dev->read_buffer.get_write_pos(size));
|
||||
|
||||
if (size > dev->read_bytes_left_after_deseg) {
|
||||
size = dev->read_bytes_left_after_deseg;
|
||||
}
|
||||
|
||||
dev->read_bytes_left_after_deseg -= size;
|
||||
|
||||
dev->read_buffer.produce(size);
|
||||
dev->read_buffer.produce(size);
|
||||
}
|
||||
|
||||
/* this function does the effective data read in a manner that suits
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "genesys_calibration.h"
|
||||
#include "genesys_buffer.h"
|
||||
#include "genesys_enums.h"
|
||||
#include "genesys_image_pipeline.h"
|
||||
#include "genesys_motor.h"
|
||||
#include "genesys_settings.h"
|
||||
#include "genesys_sensor.h"
|
||||
|
@ -300,9 +301,6 @@ struct Genesys_Device
|
|||
// total bytes read to be sent to frontend
|
||||
size_t total_bytes_to_read = 0;
|
||||
|
||||
// The current byte in line during desegmentation process
|
||||
size_t deseg_curr_byte = 0;
|
||||
|
||||
// contains the real used values
|
||||
Genesys_Current_Setup current_setup;
|
||||
// contains computed data for the current setup
|
||||
|
@ -329,6 +327,12 @@ struct Genesys_Device
|
|||
// buffer to handle even/odd data
|
||||
Genesys_Buffer oe_buffer = {};
|
||||
|
||||
// stores information about how the input image should be processed
|
||||
ImagePipelineStack pipeline;
|
||||
|
||||
// an buffer that allows reading from `pipeline` in chunks of any size
|
||||
ImageBuffer pipeline_buffer;
|
||||
|
||||
// when true the scanned picture is first buffered to allow software image enhancements
|
||||
SANE_Bool buffer_image = 0;
|
||||
|
||||
|
|
|
@ -945,7 +945,6 @@ static void gl124_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens
|
|||
reg->set24(REG_ENDPIXEL, endx / session.segment_count);
|
||||
DBG(DBG_io2, "%s: endpixel used=%d\n", __func__, endx / session.segment_count);
|
||||
|
||||
dev->deseg_curr_byte = 0;
|
||||
dev->line_count = 0;
|
||||
|
||||
DBG (DBG_io2, "%s: pixels =%d\n", __func__, session.optical_pixels);
|
||||
|
|
|
@ -727,6 +727,8 @@ static void gl646_setup_registers(Genesys_Device* dev,
|
|||
dev->out_buffer.clear();
|
||||
dev->out_buffer.alloc(session.buffer_size_out);
|
||||
|
||||
build_image_pipeline(dev, session);
|
||||
|
||||
/* scan bytes to read */
|
||||
unsigned cis_channel_multiplier = dev->model->is_cis ? session.params.channels : 1;
|
||||
|
||||
|
|
|
@ -1884,6 +1884,8 @@ dummy \ scanned lines
|
|||
dev->out_buffer.clear();
|
||||
dev->out_buffer.alloc(session.buffer_size_out);
|
||||
|
||||
build_image_pipeline(dev, session);
|
||||
|
||||
dev->read_bytes_left_after_deseg = session.output_line_bytes * session.output_line_count;
|
||||
|
||||
DBG(DBG_info, "%s: desegmented bytes to read = %lu\n", __func__,
|
||||
|
|
|
@ -807,8 +807,6 @@ static void gl846_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens
|
|||
}*/
|
||||
}
|
||||
|
||||
dev->deseg_curr_byte = 0;
|
||||
|
||||
unsigned dpiset = session.params.xres * ccd_pixels_per_system_pixel;
|
||||
reg->set16(REG_DPISET, dpiset);
|
||||
DBG(DBG_io2, "%s: dpiset used=%d\n", __func__, dpiset);
|
||||
|
|
|
@ -824,8 +824,6 @@ static void gl847_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens
|
|||
}*/
|
||||
}
|
||||
|
||||
dev->deseg_curr_byte = 0;
|
||||
|
||||
unsigned dpiset = session.params.xres * ccd_pixels_per_system_pixel;
|
||||
reg->set16(REG_DPISET, dpiset);
|
||||
DBG (DBG_io2, "%s: dpiset used=%d\n", __func__, dpiset);
|
||||
|
|
|
@ -1438,10 +1438,72 @@ static std::size_t get_usb_buffer_read_size(AsicType asic, const ScanSession& se
|
|||
}
|
||||
}
|
||||
|
||||
static FakeBufferModel get_fake_usb_buffer_model(const ScanSession& session)
|
||||
{
|
||||
FakeBufferModel model;
|
||||
model.push_step(session.buffer_size_read, 1);
|
||||
|
||||
if (session.pipeline_needs_reorder) {
|
||||
model.push_step(session.buffer_size_lines, session.output_line_bytes);
|
||||
}
|
||||
if (session.pipeline_needs_ccd) {
|
||||
model.push_step(session.buffer_size_shrink, session.output_line_bytes);
|
||||
}
|
||||
if (session.pipeline_needs_shrink) {
|
||||
model.push_step(session.buffer_size_out, session.output_line_bytes);
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
void build_image_pipeline(Genesys_Device* dev, const ScanSession& session)
|
||||
{
|
||||
dev->oe_buffer.clear();
|
||||
dev->oe_buffer.alloc(get_usb_buffer_read_size(dev->model->asic_type, session));
|
||||
auto format = create_pixel_format(session.params.depth,
|
||||
dev->model->is_cis ? 1 : session.params.channels,
|
||||
dev->model->line_mode_color_order);
|
||||
auto width = get_pixels_from_row_bytes(format, session.output_line_bytes_raw);
|
||||
|
||||
auto read_data_from_usb = [dev](std::size_t size, std::uint8_t* data)
|
||||
{
|
||||
dev->cmd_set->bulk_read_data(dev, 0x45, data, size);
|
||||
};
|
||||
|
||||
auto lines = session.output_line_count * (dev->model->is_cis ? session.params.channels : 1);
|
||||
|
||||
dev->pipeline.clear();
|
||||
|
||||
// FIXME: here we are complicating things for the time being to preserve the existing behaviour
|
||||
// This allows to be sure that the changes to the image pipeline have not introduced
|
||||
// regressions.
|
||||
|
||||
if (session.segment_count > 1) {
|
||||
// BUG: we're reading one line too much
|
||||
dev->pipeline.push_first_node<ImagePipelineNodeBufferedCallableSource>(
|
||||
width, lines + 1, format,
|
||||
get_usb_buffer_read_size(dev->model->asic_type, session), read_data_from_usb);
|
||||
|
||||
auto output_width = session.output_segment_pixel_group_count * session.segment_count;
|
||||
dev->pipeline.push_node<ImagePipelineNodeDesegment>(output_width, dev->segment_order,
|
||||
session.conseq_pixel_dist_bytes,
|
||||
1, 1);
|
||||
} else {
|
||||
auto read_bytes_left_after_deseg = session.output_line_bytes * session.output_line_count;
|
||||
if (dev->model->asic_type == AsicType::GL646) {
|
||||
read_bytes_left_after_deseg *= dev->model->is_cis ? session.params.channels : 1;
|
||||
}
|
||||
|
||||
dev->pipeline.push_first_node<ImagePipelineNodeBufferedGenesysUsb>(
|
||||
width, lines, format, read_bytes_left_after_deseg,
|
||||
get_fake_usb_buffer_model(session), read_data_from_usb);
|
||||
}
|
||||
|
||||
auto read_from_pipeline = [dev](std::size_t size, std::uint8_t* out_data)
|
||||
{
|
||||
(void) size; // will be always equal to dev->pipeline.get_output_row_bytes()
|
||||
dev->pipeline.get_next_row_data(out_data);
|
||||
};
|
||||
dev->pipeline_buffer = ImageBuffer{dev->pipeline.get_output_row_bytes(),
|
||||
read_from_pipeline};
|
||||
}
|
||||
|
||||
std::uint8_t compute_frontend_gain_wolfson(float value, float target_value)
|
||||
|
|
|
@ -640,8 +640,6 @@ void compute_session(Genesys_Device* dev, ScanSession& s, const Genesys_Sensor&
|
|||
|
||||
void build_image_pipeline(Genesys_Device* dev, const ScanSession& session);
|
||||
|
||||
void genesys_fill_segmented_buffer(Genesys_Device* dev, uint8_t* work_buffer_dst, size_t size);
|
||||
|
||||
std::uint8_t compute_frontend_gain(float value, float target_value,
|
||||
FrontendType frontend_type);
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@ genesys_tests_SOURCES = tests.cc tests.h minigtest.cc minigtest.h tests_printers
|
|||
tests_calibration.cc \
|
||||
tests_image.cc \
|
||||
tests_image_pipeline.cc \
|
||||
tests_row_buffer.cc \
|
||||
tests_sensor.cc
|
||||
tests_row_buffer.cc
|
||||
|
||||
genesys_tests_LDADD = $(TEST_LDADD)
|
||||
|
|
|
@ -31,6 +31,5 @@ int main()
|
|||
test_image();
|
||||
test_image_pipeline();
|
||||
test_row_buffer();
|
||||
test_sensor();
|
||||
return finish_tests();
|
||||
}
|
||||
|
|
|
@ -27,6 +27,5 @@ void test_calibration_parsing();
|
|||
void test_image();
|
||||
void test_image_pipeline();
|
||||
void test_row_buffer();
|
||||
void test_sensor();
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
/* sane - Scanner Access Now Easy.
|
||||
|
||||
Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
|
||||
|
||||
This file is part of the SANE package.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#define DEBUG_DECLARE_ONLY
|
||||
|
||||
#include "tests.h"
|
||||
#include "minigtest.h"
|
||||
#include "tests_printers.h"
|
||||
|
||||
#include "../../../backend/genesys_low.h"
|
||||
|
||||
#include <numeric>
|
||||
|
||||
void test_fill_segmented_buffer_depth8()
|
||||
{
|
||||
Genesys_Device dev;
|
||||
dev.settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
|
||||
dev.settings.dynamic_lineart = false;
|
||||
dev.settings.depth = 8;
|
||||
|
||||
// 2 lines, 4 segments, 5 bytes each
|
||||
dev.deseg_curr_byte = 0;
|
||||
dev.session.output_segment_start_offset = 0;
|
||||
dev.session.output_segment_pixel_group_count = 5;
|
||||
dev.session.segment_count = 4;
|
||||
dev.session.conseq_pixel_dist_bytes = 5;
|
||||
dev.session.output_line_bytes_raw = 20;
|
||||
dev.segment_order = { 0, 2, 1, 3 };
|
||||
|
||||
dev.oe_buffer.alloc(1024);
|
||||
uint8_t* data = dev.oe_buffer.get_write_pos(40);
|
||||
std::array<uint8_t, 41> input_data = { {
|
||||
1, 5, 9, 13, 17,
|
||||
3, 7, 11, 15, 19,
|
||||
2, 6, 10, 14, 18,
|
||||
4, 8, 12, 16, 20,
|
||||
21, 25, 29, 33, 37,
|
||||
23, 27, 31, 35, 39,
|
||||
22, 26, 30, 34, 38,
|
||||
24, 28, 32, 36, 40,
|
||||
0 // one extra byte so that we don't attempt to refill the buffer
|
||||
} };
|
||||
std::copy(input_data.begin(), input_data.end(), data);
|
||||
dev.oe_buffer.produce(41);
|
||||
|
||||
std::vector<uint8_t> out_data;
|
||||
out_data.resize(40, 0);
|
||||
|
||||
genesys_fill_segmented_buffer(&dev, out_data.data(), 16);
|
||||
ASSERT_EQ(dev.deseg_curr_byte, 4u);
|
||||
genesys_fill_segmented_buffer(&dev, out_data.data() + 16, 4);
|
||||
ASSERT_EQ(dev.deseg_curr_byte, 0u);
|
||||
genesys_fill_segmented_buffer(&dev, out_data.data() + 20, 20);
|
||||
ASSERT_EQ(dev.deseg_curr_byte, 0u);
|
||||
|
||||
std::vector<uint8_t> expected;
|
||||
expected.resize(40, 0);
|
||||
std::iota(expected.begin(), expected.end(), 1); // will fill with 1, 2, 3, ..., 40
|
||||
|
||||
ASSERT_EQ(out_data, expected);
|
||||
}
|
||||
|
||||
void test_sensor()
|
||||
{
|
||||
test_fill_segmented_buffer_depth8();
|
||||
}
|
Ładowanie…
Reference in New Issue