genesys: Use new image pipeline for handling segmented sensors

merge-requests/178/head
Povilas Kanapickas 2019-09-13 15:37:32 +03:00
rodzic 7f852e0417
commit af17797d3c
13 zmienionych plików z 79 dodań i 250 usunięć

Wyświetl plik

@ -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

Wyświetl plik

@ -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;

Wyświetl plik

@ -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);

Wyświetl plik

@ -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;

Wyświetl plik

@ -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__,

Wyświetl plik

@ -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);

Wyświetl plik

@ -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);

Wyświetl plik

@ -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)

Wyświetl plik

@ -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);

Wyświetl plik

@ -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)

Wyświetl plik

@ -31,6 +31,5 @@ int main()
test_image();
test_image_pipeline();
test_row_buffer();
test_sensor();
return finish_tests();
}

Wyświetl plik

@ -27,6 +27,5 @@ void test_calibration_parsing();
void test_image();
void test_image_pipeline();
void test_row_buffer();
void test_sensor();
#endif

Wyświetl plik

@ -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();
}