kopia lustrzana https://gitlab.com/sane-project/backends
Merge branch 'genesys-use-image-pipeline' into 'master'
genesys: Use new image operations pipeline for image data reading and conversion See merge request sane-project/backends!183merge-requests/183/merge
commit
bf3e170dba
|
@ -520,7 +520,7 @@ libsane_genesys_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
|
|||
libsane_genesys_la_LIBADD = $(COMMON_LIBS) libgenesys.la ../sanei/sanei_magic.lo ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
|
||||
EXTRA_DIST += genesys.conf.in
|
||||
# TODO: Why are this distributed but not compiled?
|
||||
EXTRA_DIST += genesys_conv.cc genesys_conv_hlp.cc
|
||||
EXTRA_DIST += genesys_conv.cc
|
||||
|
||||
libgphoto2_i_la_SOURCES = gphoto2.c gphoto2.h
|
||||
libgphoto2_i_la_CPPFLAGS = $(AM_CPPFLAGS) $(GPHOTO2_CPPFLAGS) -DBACKEND_NAME=gphoto2
|
||||
|
|
|
@ -3439,15 +3439,10 @@ static void genesys_fill_read_buffer(Genesys_Device* dev)
|
|||
static void genesys_read_ordered_data(Genesys_Device* dev, SANE_Byte* destination, size_t* len)
|
||||
{
|
||||
DBG_HELPER(dbg);
|
||||
size_t bytes, extra;
|
||||
size_t bytes;
|
||||
unsigned int channels, depth, src_pixels;
|
||||
unsigned int ccd_shift[12], shift_count;
|
||||
uint8_t *work_buffer_src;
|
||||
uint8_t *work_buffer_dst;
|
||||
unsigned int dst_lines;
|
||||
unsigned int step_1_mode;
|
||||
Genesys_Buffer *src_buffer;
|
||||
Genesys_Buffer *dst_buffer;
|
||||
|
||||
if (dev->read_active != SANE_TRUE)
|
||||
{
|
||||
|
@ -3490,31 +3485,6 @@ static void genesys_read_ordered_data(Genesys_Device* dev, SANE_Byte* destinatio
|
|||
((dev->read_bytes_left_after_deseg + dev->read_buffer.avail()) * 8UL) /
|
||||
(src_pixels * channels * depth));
|
||||
|
||||
if (channels == 1)
|
||||
{
|
||||
ccd_shift[0] = 0;
|
||||
ccd_shift[1] = dev->current_setup.stagger;
|
||||
shift_count = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
ccd_shift[0] =
|
||||
((dev->ld_shift_r * dev->settings.yres) /
|
||||
dev->motor.base_ydpi);
|
||||
ccd_shift[1] =
|
||||
((dev->ld_shift_g * dev->settings.yres) /
|
||||
dev->motor.base_ydpi);
|
||||
ccd_shift[2] =
|
||||
((dev->ld_shift_b * dev->settings.yres) /
|
||||
dev->motor.base_ydpi);
|
||||
|
||||
ccd_shift[3] = ccd_shift[0] + dev->current_setup.stagger;
|
||||
ccd_shift[4] = ccd_shift[1] + dev->current_setup.stagger;
|
||||
ccd_shift[5] = ccd_shift[2] + dev->current_setup.stagger;
|
||||
|
||||
shift_count = 6;
|
||||
}
|
||||
|
||||
|
||||
/* convert data */
|
||||
/*
|
||||
|
@ -3547,199 +3517,6 @@ Problems with the first approach:
|
|||
|
||||
src_buffer = &(dev->read_buffer);
|
||||
|
||||
/* maybe reorder components/bytes */
|
||||
if (dev->session.pipeline_needs_reorder) {
|
||||
if (depth == 1) {
|
||||
throw SaneException("Can't reorder single bit data\n");
|
||||
}
|
||||
|
||||
dst_buffer = &(dev->lines_buffer);
|
||||
|
||||
work_buffer_src = src_buffer->get_read_pos();
|
||||
bytes = src_buffer->avail();
|
||||
|
||||
/*how many bytes can be processed here?*/
|
||||
/*we are greedy. we work as much as possible*/
|
||||
if (bytes > dst_buffer->size() - dst_buffer->avail())
|
||||
bytes = dst_buffer->size() - dst_buffer->avail();
|
||||
|
||||
dst_lines = (bytes * 8) / (src_pixels * channels * depth);
|
||||
bytes = (dst_lines * src_pixels * channels * depth) / 8;
|
||||
|
||||
work_buffer_dst = dst_buffer->get_write_pos(bytes);
|
||||
|
||||
DBG(DBG_info, "%s: reordering %d lines\n", __func__, dst_lines);
|
||||
|
||||
if (dst_lines != 0)
|
||||
{
|
||||
|
||||
if (channels == 3)
|
||||
{
|
||||
step_1_mode = 0;
|
||||
|
||||
if (depth == 16) {
|
||||
step_1_mode |= 1;
|
||||
}
|
||||
|
||||
if (dev->model->is_cis) {
|
||||
step_1_mode |= 2;
|
||||
}
|
||||
|
||||
if (dev->model->line_mode_color_order == ColorOrder::BGR) {
|
||||
step_1_mode |= 4;
|
||||
}
|
||||
|
||||
switch (step_1_mode)
|
||||
{
|
||||
case 1: /* RGB, chunky, 16 bit */
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
genesys_reorder_components_endian_16(work_buffer_src, work_buffer_dst, dst_lines,
|
||||
src_pixels, 3);
|
||||
break;
|
||||
#endif /*WORDS_BIGENDIAN */
|
||||
case 0: /* RGB, chunky, 8 bit */
|
||||
break;
|
||||
case 2: /* RGB, cis, 8 bit */
|
||||
genesys_reorder_components_cis_8(work_buffer_src, work_buffer_dst, dst_lines,
|
||||
src_pixels);
|
||||
break;
|
||||
case 3: /* RGB, cis, 16 bit */
|
||||
genesys_reorder_components_cis_16(work_buffer_src, work_buffer_dst, dst_lines,
|
||||
src_pixels);
|
||||
break;
|
||||
case 4: /* BGR, chunky, 8 bit */
|
||||
genesys_reorder_components_bgr_8(work_buffer_src, work_buffer_dst, dst_lines,
|
||||
src_pixels);
|
||||
break;
|
||||
case 5: /* BGR, chunky, 16 bit */
|
||||
genesys_reorder_components_bgr_16(work_buffer_src, work_buffer_dst, dst_lines,
|
||||
src_pixels);
|
||||
break;
|
||||
case 6: /* BGR, cis, 8 bit */
|
||||
genesys_reorder_components_cis_bgr_8(work_buffer_src, work_buffer_dst, dst_lines,
|
||||
src_pixels);
|
||||
break;
|
||||
case 7: /* BGR, cis, 16 bit */
|
||||
genesys_reorder_components_cis_bgr_16(work_buffer_src, work_buffer_dst, dst_lines,
|
||||
src_pixels);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
if (depth == 16)
|
||||
{
|
||||
genesys_reorder_components_endian_16(work_buffer_src, work_buffer_dst, dst_lines,
|
||||
src_pixels, 1);
|
||||
}
|
||||
#endif /*WORDS_BIGENDIAN */
|
||||
}
|
||||
|
||||
dst_buffer->produce(bytes);
|
||||
src_buffer->consume(bytes);
|
||||
}
|
||||
src_buffer = dst_buffer;
|
||||
}
|
||||
|
||||
// maybe reverse effects of ccd layout
|
||||
if (dev->session.pipeline_needs_ccd)
|
||||
{
|
||||
// should not happen with depth == 1.
|
||||
if (depth == 1) {
|
||||
throw SaneException("Can't reverse ccd single bit data\n");
|
||||
}
|
||||
|
||||
dst_buffer = &(dev->shrink_buffer);
|
||||
|
||||
work_buffer_src = src_buffer->get_read_pos();
|
||||
bytes = src_buffer->avail();
|
||||
|
||||
extra =
|
||||
(dev->current_setup.max_shift * src_pixels * channels * depth) / 8;
|
||||
|
||||
/*extra bytes are reserved, and should not be consumed*/
|
||||
if (bytes < extra)
|
||||
bytes = 0;
|
||||
else
|
||||
bytes -= extra;
|
||||
|
||||
/*how many bytes can be processed here?*/
|
||||
/*we are greedy. we work as much as possible*/
|
||||
if (bytes > dst_buffer->size() - dst_buffer->avail())
|
||||
bytes = dst_buffer->size() - dst_buffer->avail();
|
||||
|
||||
dst_lines = (bytes * 8) / (src_pixels * channels * depth);
|
||||
bytes = (dst_lines * src_pixels * channels * depth) / 8;
|
||||
|
||||
work_buffer_dst = dst_buffer->get_write_pos(bytes);
|
||||
|
||||
DBG(DBG_info, "%s: un-ccd-ing %d lines\n", __func__, dst_lines);
|
||||
|
||||
if (dst_lines != 0)
|
||||
{
|
||||
|
||||
if (depth == 8) {
|
||||
genesys_reverse_ccd_8(work_buffer_src, work_buffer_dst, dst_lines,
|
||||
src_pixels * channels, ccd_shift, shift_count);
|
||||
} else {
|
||||
genesys_reverse_ccd_16(work_buffer_src, work_buffer_dst, dst_lines,
|
||||
src_pixels * channels, ccd_shift, shift_count);
|
||||
}
|
||||
dst_buffer->produce(bytes);
|
||||
src_buffer->consume(bytes);
|
||||
}
|
||||
src_buffer = dst_buffer;
|
||||
}
|
||||
|
||||
// maybe shrink(or enlarge) lines
|
||||
if (dev->session.pipeline_needs_shrink) {
|
||||
|
||||
dst_buffer = &(dev->out_buffer);
|
||||
|
||||
work_buffer_src = src_buffer->get_read_pos();
|
||||
bytes = src_buffer->avail();
|
||||
|
||||
/*lines in input*/
|
||||
dst_lines = (bytes * 8) / (src_pixels * channels * depth);
|
||||
|
||||
/* how many lines can be processed here? */
|
||||
/* we are greedy. we work as much as possible */
|
||||
bytes = dst_buffer->size() - dst_buffer->avail();
|
||||
|
||||
if (dst_lines > (bytes * 8) / (dev->settings.requested_pixels * channels * depth)) {
|
||||
dst_lines = (bytes * 8) / (dev->settings.requested_pixels * channels * depth);
|
||||
}
|
||||
|
||||
bytes = (dst_lines * dev->settings.requested_pixels * channels * depth) / 8;
|
||||
|
||||
work_buffer_dst = dst_buffer->get_write_pos(bytes);
|
||||
|
||||
DBG(DBG_info, "%s: shrinking %d lines\n", __func__, dst_lines);
|
||||
|
||||
if (dst_lines != 0)
|
||||
{
|
||||
if (depth == 1)
|
||||
genesys_shrink_lines_1(work_buffer_src, work_buffer_dst, dst_lines, src_pixels,
|
||||
dev->settings.requested_pixels, channels);
|
||||
else if (depth == 8)
|
||||
genesys_shrink_lines_8(work_buffer_src, work_buffer_dst, dst_lines, src_pixels,
|
||||
dev->settings.requested_pixels, channels);
|
||||
else
|
||||
genesys_shrink_lines_16(work_buffer_src, work_buffer_dst, dst_lines, src_pixels,
|
||||
dev->settings.requested_pixels, channels);
|
||||
|
||||
/* we just consumed this many bytes*/
|
||||
bytes = (dst_lines * src_pixels * channels * depth) / 8;
|
||||
src_buffer->consume(bytes);
|
||||
|
||||
/* we just created this many bytes*/
|
||||
bytes = (dst_lines * dev->settings.requested_pixels * channels * depth) / 8;
|
||||
dst_buffer->produce(bytes);
|
||||
}
|
||||
src_buffer = dst_buffer;
|
||||
}
|
||||
|
||||
/* move data to destination */
|
||||
bytes = src_buffer->avail();
|
||||
if (bytes > *len)
|
||||
|
|
|
@ -46,37 +46,6 @@
|
|||
* Conversion filters for genesys backend
|
||||
*/
|
||||
|
||||
|
||||
/*8 bit*/
|
||||
#define SINGLE_BYTE
|
||||
#define BYTES_PER_COMPONENT 1
|
||||
#define COMPONENT_TYPE uint8_t
|
||||
|
||||
#define FUNC_NAME(f) f ## _8
|
||||
|
||||
#include "genesys_conv_hlp.cc"
|
||||
|
||||
#undef FUNC_NAME
|
||||
|
||||
#undef COMPONENT_TYPE
|
||||
#undef BYTES_PER_COMPONENT
|
||||
#undef SINGLE_BYTE
|
||||
|
||||
/*16 bit*/
|
||||
#define DOUBLE_BYTE
|
||||
#define BYTES_PER_COMPONENT 2
|
||||
#define COMPONENT_TYPE uint16_t
|
||||
|
||||
#define FUNC_NAME(f) f ## _16
|
||||
|
||||
#include "genesys_conv_hlp.cc"
|
||||
|
||||
#undef FUNC_NAME
|
||||
|
||||
#undef COMPONENT_TYPE
|
||||
#undef BYTES_PER_COMPONENT
|
||||
#undef DOUBLE_BYTE
|
||||
|
||||
static void genesys_reverse_bits(uint8_t* src_data, uint8_t* dst_data, size_t bytes)
|
||||
{
|
||||
DBG_HELPER(dbg);
|
||||
|
@ -195,114 +164,6 @@ static void genesys_gray_lineart(Genesys_Device* dev,
|
|||
}
|
||||
}
|
||||
|
||||
/** @brief shrink or grow scanned data to fit the final scan size
|
||||
* This function shrinks the scanned data it the required resolution is lower than the hardware one,
|
||||
* or grows it in case it is the opposite like when motor resolution is higher than
|
||||
* sensor's one.
|
||||
*/
|
||||
static void genesys_shrink_lines_1(uint8_t* src_data, uint8_t* dst_data,
|
||||
unsigned int lines,
|
||||
unsigned int src_pixels, unsigned int dst_pixels,
|
||||
unsigned int channels)
|
||||
{
|
||||
DBG_HELPER(dbg);
|
||||
unsigned int dst_x, src_x, y, c, cnt;
|
||||
unsigned int avg[3], val;
|
||||
uint8_t *src = (uint8_t *) src_data;
|
||||
uint8_t *dst = (uint8_t *) dst_data;
|
||||
|
||||
/* choose between case where me must reduce or grow the scanned data */
|
||||
if (src_pixels > dst_pixels)
|
||||
{
|
||||
/* shrink data */
|
||||
/* TODO action must be taken at bit level, no bytes */
|
||||
src_pixels /= 8;
|
||||
dst_pixels /= 8;
|
||||
/*take first _byte_ */
|
||||
for (y = 0; y < lines; y++)
|
||||
{
|
||||
cnt = src_pixels / 2;
|
||||
src_x = 0;
|
||||
for (dst_x = 0; dst_x < dst_pixels; dst_x++)
|
||||
{
|
||||
while (cnt < src_pixels && src_x < src_pixels)
|
||||
{
|
||||
cnt += dst_pixels;
|
||||
|
||||
for (c = 0; c < channels; c++)
|
||||
avg[c] = *src++;
|
||||
src_x++;
|
||||
}
|
||||
cnt -= src_pixels;
|
||||
|
||||
for (c = 0; c < channels; c++)
|
||||
*dst++ = avg[c];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* common case where y res is double x res */
|
||||
for (y = 0; y < lines; y++)
|
||||
{
|
||||
if (2 * src_pixels == dst_pixels)
|
||||
{
|
||||
/* double and interleave on line */
|
||||
for (c = 0; c < src_pixels/8; c++)
|
||||
{
|
||||
/* first 4 bits */
|
||||
val = 0;
|
||||
val |= (*src & 0x80) >> 0; /* X___ ____ --> X___ ____ */
|
||||
val |= (*src & 0x80) >> 1; /* X___ ____ --> _X__ ____ */
|
||||
val |= (*src & 0x40) >> 1; /* _X__ ____ --> __X_ ____ */
|
||||
val |= (*src & 0x40) >> 2; /* _X__ ____ --> ___X ____ */
|
||||
val |= (*src & 0x20) >> 2; /* __X_ ____ --> ____ X___ */
|
||||
val |= (*src & 0x20) >> 3; /* __X_ ____ --> ____ _X__ */
|
||||
val |= (*src & 0x10) >> 3; /* ___X ____ --> ____ __X_ */
|
||||
val |= (*src & 0x10) >> 4; /* ___X ____ --> ____ ___X */
|
||||
*dst = val;
|
||||
dst++;
|
||||
|
||||
/* last for bits */
|
||||
val = 0;
|
||||
val |= (*src & 0x08) << 4; /* ____ X___ --> X___ ____ */
|
||||
val |= (*src & 0x08) << 3; /* ____ X___ --> _X__ ____ */
|
||||
val |= (*src & 0x04) << 3; /* ____ _X__ --> __X_ ____ */
|
||||
val |= (*src & 0x04) << 2; /* ____ _X__ --> ___X ____ */
|
||||
val |= (*src & 0x02) << 2; /* ____ __X_ --> ____ X___ */
|
||||
val |= (*src & 0x02) << 1; /* ____ __X_ --> ____ _X__ */
|
||||
val |= (*src & 0x01) << 1; /* ____ ___X --> ____ __X_ */
|
||||
val |= (*src & 0x01) << 0; /* ____ ___X --> ____ ___X */
|
||||
*dst = val;
|
||||
dst++;
|
||||
src++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* TODO: since depth is 1, we must interpolate bit within bytes */
|
||||
DBG (DBG_warn, "%s: inaccurate bit expansion!\n", __func__);
|
||||
cnt = dst_pixels / 2;
|
||||
dst_x = 0;
|
||||
for (src_x = 0; src_x < src_pixels; src_x++)
|
||||
{
|
||||
for (c = 0; c < channels; c++)
|
||||
avg[c] = *src++;
|
||||
while (cnt < dst_pixels && dst_x < dst_pixels)
|
||||
{
|
||||
cnt += src_pixels;
|
||||
for (c = 0; c < channels; c++)
|
||||
*dst++ = avg[c];
|
||||
dst_x++;
|
||||
}
|
||||
cnt -= dst_pixels;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Look in image for likely left/right/bottom paper edges, then crop image.
|
||||
*/
|
||||
static void genesys_crop(Genesys_Scanner* s)
|
||||
|
|
|
@ -1,320 +0,0 @@
|
|||
/* sane - Scanner Access Now Easy.
|
||||
|
||||
Copyright (C) 2005 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
|
||||
|
||||
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.
|
||||
|
||||
As a special exception, the authors of SANE give permission for
|
||||
additional uses of the libraries contained in this release of SANE.
|
||||
|
||||
The exception is that, if you link a SANE library with other files
|
||||
to produce an executable, this does not by itself cause the
|
||||
resulting executable to be covered by the GNU General Public
|
||||
License. Your use of that executable is in no way restricted on
|
||||
account of linking the SANE library code into it.
|
||||
|
||||
This exception does not, however, invalidate any other reasons why
|
||||
the executable file might be covered by the GNU General Public
|
||||
License.
|
||||
|
||||
If you submit changes to SANE to the maintainers to be included in
|
||||
a subsequent release, you agree by submitting the changes that
|
||||
those changes may be distributed with this exception intact.
|
||||
|
||||
If you write modifications of your own for SANE, it is your choice
|
||||
whether to permit this exception to apply to your modifications.
|
||||
If you do not wish that, delete this exception notice.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Conversion filters for genesys backend
|
||||
*/
|
||||
|
||||
static void FUNC_NAME(genesys_reorder_components_cis) (uint8_t* src_data, uint8_t* dst_data,
|
||||
unsigned int lines, unsigned int pixels)
|
||||
{
|
||||
DBG_HELPER(dbg);
|
||||
unsigned int x, y;
|
||||
uint8_t *src[3];
|
||||
uint8_t *dst = dst_data;
|
||||
unsigned int rest = pixels * 2 * BYTES_PER_COMPONENT;
|
||||
|
||||
src[0] = src_data + pixels * BYTES_PER_COMPONENT * 0;
|
||||
src[1] = src_data + pixels * BYTES_PER_COMPONENT * 1;
|
||||
src[2] = src_data + pixels * BYTES_PER_COMPONENT * 2;
|
||||
|
||||
for(y = 0; y < lines; y++) {
|
||||
for(x = 0; x < pixels; x++) {
|
||||
|
||||
#ifndef DOUBLE_BYTE
|
||||
*dst++ = *src[0]++;
|
||||
*dst++ = *src[1]++;
|
||||
*dst++ = *src[2]++;
|
||||
#else
|
||||
# ifndef WORDS_BIGENDIAN
|
||||
*dst++ = *src[0]++;
|
||||
*dst++ = *src[0]++;
|
||||
*dst++ = *src[1]++;
|
||||
*dst++ = *src[1]++;
|
||||
*dst++ = *src[2]++;
|
||||
*dst++ = *src[2]++;
|
||||
# else
|
||||
*dst++ = src[0][1];
|
||||
*dst++ = src[0][0];
|
||||
*dst++ = src[1][1];
|
||||
*dst++ = src[1][0];
|
||||
*dst++ = src[2][1];
|
||||
*dst++ = src[2][0];
|
||||
src[0] += 2;
|
||||
src[1] += 2;
|
||||
src[2] += 2;
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
src[0] += rest;
|
||||
src[1] += rest;
|
||||
src[2] += rest;
|
||||
}
|
||||
}
|
||||
|
||||
static void FUNC_NAME(genesys_reorder_components_cis_bgr) (uint8_t* src_data, uint8_t* dst_data,
|
||||
unsigned int lines, unsigned int pixels)
|
||||
{
|
||||
DBG_HELPER(dbg);
|
||||
unsigned int x, y;
|
||||
uint8_t *src[3];
|
||||
uint8_t *dst = dst_data;
|
||||
unsigned int rest = pixels * 2 * BYTES_PER_COMPONENT;
|
||||
|
||||
src[0] = src_data + pixels * BYTES_PER_COMPONENT * 0;
|
||||
src[1] = src_data + pixels * BYTES_PER_COMPONENT * 1;
|
||||
src[2] = src_data + pixels * BYTES_PER_COMPONENT * 2;
|
||||
|
||||
for(y = 0; y < lines; y++) {
|
||||
for(x = 0; x < pixels; x++) {
|
||||
#ifndef DOUBLE_BYTE
|
||||
*dst++ = *src[2]++;
|
||||
*dst++ = *src[1]++;
|
||||
*dst++ = *src[0]++;
|
||||
#else
|
||||
# ifndef WORDS_BIGENDIAN
|
||||
*dst++ = *src[2]++;
|
||||
*dst++ = *src[2]++;
|
||||
*dst++ = *src[1]++;
|
||||
*dst++ = *src[1]++;
|
||||
*dst++ = *src[0]++;
|
||||
*dst++ = *src[0]++;
|
||||
# else
|
||||
*dst++ = src[2][1];
|
||||
*dst++ = src[2][0];
|
||||
*dst++ = src[1][1];
|
||||
*dst++ = src[1][0];
|
||||
*dst++ = src[0][1];
|
||||
*dst++ = src[0][0];
|
||||
src[0] += 2;
|
||||
src[1] += 2;
|
||||
src[2] += 2;
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
src[0] += rest;
|
||||
src[1] += rest;
|
||||
src[2] += rest;
|
||||
}
|
||||
}
|
||||
|
||||
static void FUNC_NAME(genesys_reorder_components_bgr) (uint8_t* src_data, uint8_t* dst_data,
|
||||
unsigned int lines, unsigned int pixels)
|
||||
{
|
||||
DBG_HELPER(dbg);
|
||||
unsigned int c;
|
||||
uint8_t *src = src_data;
|
||||
uint8_t *dst = dst_data;
|
||||
|
||||
for(c = 0; c < lines * pixels; c++) {
|
||||
|
||||
#ifndef DOUBLE_BYTE
|
||||
*dst++ = src[2];
|
||||
*dst++ = src[1];
|
||||
*dst++ = src[0];
|
||||
src += 3;
|
||||
#else
|
||||
# ifndef WORDS_BIGENDIAN
|
||||
*dst++ = src[2 * 2 + 0];
|
||||
*dst++ = src[2 * 2 + 1];
|
||||
*dst++ = src[1 * 2 + 0];
|
||||
*dst++ = src[1 * 2 + 1];
|
||||
*dst++ = src[0 * 2 + 0];
|
||||
*dst++ = src[0 * 2 + 1];
|
||||
# else
|
||||
*dst++ = src[2 * 2 + 1];
|
||||
*dst++ = src[2 * 2 + 0];
|
||||
*dst++ = src[1 * 2 + 1];
|
||||
*dst++ = src[1 * 2 + 0];
|
||||
*dst++ = src[0 * 2 + 1];
|
||||
*dst++ = src[0 * 2 + 0];
|
||||
# endif
|
||||
src += 3 * 2;
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(DOUBLE_BYTE) && defined(WORDS_BIGENDIAN)
|
||||
static void FUNC_NAME(genesys_reorder_components_endian) (uint8_t* src_data, uint8_t* dst_data,
|
||||
unsigned int lines, unsigned int pixels,
|
||||
unsigned int channels)
|
||||
{
|
||||
DBG_HELPER(dbg);
|
||||
unsigned int c;
|
||||
uint8_t *src = src_data;
|
||||
uint8_t *dst = dst_data;
|
||||
|
||||
for(c = 0; c < lines * pixels * channels; c++) {
|
||||
*dst++ = src[1];
|
||||
*dst++ = src[0];
|
||||
src += 2;
|
||||
}
|
||||
}
|
||||
#endif /*defined(DOUBLE_BYTE) && defined(WORDS_BIGENDIAN)*/
|
||||
|
||||
|
||||
static void FUNC_NAME(genesys_reverse_ccd) (uint8_t* src_data, uint8_t* dst_data,
|
||||
unsigned int lines, unsigned int components_per_line,
|
||||
unsigned int *ccd_shift, unsigned int component_count)
|
||||
{
|
||||
DBG_HELPER(dbg);
|
||||
unsigned int x, y, c;
|
||||
COMPONENT_TYPE *src = (COMPONENT_TYPE *)src_data;
|
||||
COMPONENT_TYPE *dst = (COMPONENT_TYPE *)dst_data;
|
||||
COMPONENT_TYPE *srcp;
|
||||
COMPONENT_TYPE *dstp;
|
||||
unsigned int pitch = components_per_line;
|
||||
unsigned int ccd_shift_pitch[12];
|
||||
unsigned int *csp;
|
||||
|
||||
for (c = 0; c < component_count; c++)
|
||||
ccd_shift_pitch[c] = ccd_shift[c] * pitch;
|
||||
|
||||
/*
|
||||
* cache efficiency:
|
||||
we are processing a single line component_count times, so it should fit
|
||||
into the cpu cache for maximum efficiency. our lines take
|
||||
maximum 252kb(3 channels, 16bit, 2400dpi, full gl841 shading range)
|
||||
* instruction efficiency:
|
||||
the innermost loop runs long and consists of 3 adds, one compare,
|
||||
2 derefences.
|
||||
*/
|
||||
/*
|
||||
for (y = 0; y < lines; y++) {
|
||||
csp = ccd_shift_pitch;
|
||||
for (c = 0; c < component_count; c++) {
|
||||
srcp = src + c + *csp++;
|
||||
dstp = dst + c;
|
||||
for (x = 0; x < pitch; x += component_count) {
|
||||
*dstp = *srcp;
|
||||
srcp += component_count;
|
||||
dstp += component_count;
|
||||
}
|
||||
}
|
||||
dst += pitch;
|
||||
src += pitch;
|
||||
}
|
||||
*/
|
||||
/*
|
||||
* cache efficency:
|
||||
here only line_dist_pitch needs to stay in cache. 12*4 = 48 bytes
|
||||
* instruction efficiency:
|
||||
we have a short running inner loop, consisting of 4 incs, 2 compare, 1 add,
|
||||
2 dereference and 1 indexed dereference.
|
||||
the enclosing loop is long running, consisting of 1 add, 1 compare.
|
||||
*/
|
||||
srcp = src;
|
||||
dstp = dst;
|
||||
for (y = 0; y < lines; y++) {
|
||||
for (x = 0; x < pitch; x += component_count) {
|
||||
csp = ccd_shift_pitch;
|
||||
for (c = 0; c < component_count && c + x < pitch; c++) {
|
||||
*dstp = srcp[*csp++];
|
||||
dstp++;
|
||||
srcp++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void FUNC_NAME(genesys_shrink_lines) (uint8_t* src_data, uint8_t* dst_data,
|
||||
unsigned int lines,
|
||||
unsigned int src_pixels, unsigned int dst_pixels,
|
||||
unsigned int channels)
|
||||
{
|
||||
DBG_HELPER(dbg);
|
||||
unsigned int dst_x, src_x, y, c, cnt;
|
||||
unsigned int avg[3];
|
||||
unsigned int count;
|
||||
COMPONENT_TYPE *src = (COMPONENT_TYPE *)src_data;
|
||||
COMPONENT_TYPE *dst = (COMPONENT_TYPE *)dst_data;
|
||||
|
||||
if (src_pixels > dst_pixels) {
|
||||
/*average*/
|
||||
for (c = 0; c < channels; c++)
|
||||
avg[c] = 0;
|
||||
for(y = 0; y < lines; y++) {
|
||||
cnt = src_pixels / 2;
|
||||
src_x = 0;
|
||||
for (dst_x = 0; dst_x < dst_pixels; dst_x++) {
|
||||
count = 0;
|
||||
while (cnt < src_pixels && src_x < src_pixels) {
|
||||
cnt += dst_pixels;
|
||||
|
||||
for (c = 0; c < channels; c++)
|
||||
avg[c] += *src++;
|
||||
src_x++;
|
||||
count++;
|
||||
}
|
||||
cnt -= src_pixels;
|
||||
|
||||
for (c = 0; c < channels; c++) {
|
||||
*dst++ = avg[c] / count;
|
||||
avg[c] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*interpolate. copy pixels*/
|
||||
for(y = 0; y < lines; y++) {
|
||||
cnt = dst_pixels / 2;
|
||||
dst_x = 0;
|
||||
for (src_x = 0; src_x < src_pixels; src_x++) {
|
||||
for (c = 0; c < channels; c++)
|
||||
avg[c] = *src++;
|
||||
while ((cnt < dst_pixels || src_x + 1 == src_pixels) &&
|
||||
dst_x < dst_pixels) {
|
||||
cnt += src_pixels;
|
||||
|
||||
for (c = 0; c < channels; c++)
|
||||
*dst++ = avg[c];
|
||||
dst_x++;
|
||||
}
|
||||
cnt -= dst_pixels;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -55,9 +55,6 @@ Genesys_Device::~Genesys_Device()
|
|||
void Genesys_Device::clear()
|
||||
{
|
||||
read_buffer.clear();
|
||||
lines_buffer.clear();
|
||||
shrink_buffer.clear();
|
||||
out_buffer.clear();
|
||||
binarize_buffer.clear();
|
||||
local_buffer.clear();
|
||||
|
||||
|
|
|
@ -282,9 +282,6 @@ struct Genesys_Device
|
|||
SANE_Bool needs_home_ta = 0;
|
||||
|
||||
Genesys_Buffer read_buffer;
|
||||
Genesys_Buffer lines_buffer;
|
||||
Genesys_Buffer shrink_buffer;
|
||||
Genesys_Buffer out_buffer;
|
||||
|
||||
// buffer for digital lineart from gray data
|
||||
Genesys_Buffer binarize_buffer;
|
||||
|
|
|
@ -1053,15 +1053,6 @@ static void gl124_init_scan_regs(Genesys_Device* dev, const Genesys_Sensor& sens
|
|||
dev->read_buffer.clear();
|
||||
dev->read_buffer.alloc(session.buffer_size_read);
|
||||
|
||||
dev->lines_buffer.clear();
|
||||
dev->lines_buffer.alloc(session.buffer_size_lines);
|
||||
|
||||
dev->shrink_buffer.clear();
|
||||
dev->shrink_buffer.alloc(session.buffer_size_shrink);
|
||||
|
||||
dev->out_buffer.clear();
|
||||
dev->out_buffer.alloc(session.buffer_size_out);
|
||||
|
||||
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__,
|
||||
|
|
|
@ -718,15 +718,6 @@ static void gl646_setup_registers(Genesys_Device* dev,
|
|||
dev->read_buffer.clear();
|
||||
dev->read_buffer.alloc(session.buffer_size_read);
|
||||
|
||||
dev->lines_buffer.clear();
|
||||
dev->lines_buffer.alloc(session.buffer_size_lines);
|
||||
|
||||
dev->shrink_buffer.clear();
|
||||
dev->shrink_buffer.alloc(session.buffer_size_shrink);
|
||||
|
||||
dev->out_buffer.clear();
|
||||
dev->out_buffer.alloc(session.buffer_size_out);
|
||||
|
||||
build_image_pipeline(dev, session);
|
||||
|
||||
/* scan bytes to read */
|
||||
|
|
|
@ -1875,15 +1875,6 @@ dummy \ scanned lines
|
|||
dev->read_buffer.clear();
|
||||
dev->read_buffer.alloc(session.buffer_size_read);
|
||||
|
||||
dev->lines_buffer.clear();
|
||||
dev->lines_buffer.alloc(session.buffer_size_lines);
|
||||
|
||||
dev->shrink_buffer.clear();
|
||||
dev->shrink_buffer.alloc(session.buffer_size_shrink);
|
||||
|
||||
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;
|
||||
|
|
|
@ -1292,15 +1292,6 @@ static void gl843_init_scan_regs(Genesys_Device* dev, const Genesys_Sensor& sens
|
|||
dev->read_buffer.clear();
|
||||
dev->read_buffer.alloc(session.buffer_size_read);
|
||||
|
||||
dev->lines_buffer.clear();
|
||||
dev->lines_buffer.alloc(session.buffer_size_lines);
|
||||
|
||||
dev->shrink_buffer.clear();
|
||||
dev->shrink_buffer.alloc(session.buffer_size_shrink);
|
||||
|
||||
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;
|
||||
|
|
|
@ -918,15 +918,6 @@ static void gl846_init_scan_regs(Genesys_Device* dev, const Genesys_Sensor& sens
|
|||
dev->read_buffer.clear();
|
||||
dev->read_buffer.alloc(session.buffer_size_read);
|
||||
|
||||
dev->lines_buffer.clear();
|
||||
dev->lines_buffer.alloc(session.buffer_size_lines);
|
||||
|
||||
dev->shrink_buffer.clear();
|
||||
dev->shrink_buffer.alloc(session.buffer_size_shrink);
|
||||
|
||||
dev->out_buffer.clear();
|
||||
dev->out_buffer.alloc(session.buffer_size_out);
|
||||
|
||||
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__,
|
||||
|
|
|
@ -928,15 +928,6 @@ static void gl847_init_scan_regs(Genesys_Device* dev, const Genesys_Sensor& sens
|
|||
dev->read_buffer.clear();
|
||||
dev->read_buffer.alloc(session.buffer_size_read);
|
||||
|
||||
dev->lines_buffer.clear();
|
||||
dev->lines_buffer.alloc(session.buffer_size_lines);
|
||||
|
||||
dev->shrink_buffer.clear();
|
||||
dev->shrink_buffer.alloc(session.buffer_size_shrink);
|
||||
|
||||
dev->out_buffer.clear();
|
||||
dev->out_buffer.alloc(session.buffer_size_out);
|
||||
|
||||
dev->read_bytes_left_after_deseg = session.output_line_bytes * session.output_line_count;
|
||||
|
||||
DBG(DBG_info, "%s: desegment bytes to read = %lu\n", __func__,
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#define DEBUG_DECLARE_ONLY
|
||||
|
||||
#include "genesys_image_pipeline.h"
|
||||
#include "genesys_low.h"
|
||||
#include <numeric>
|
||||
|
||||
ImagePipelineNode::~ImagePipelineNode() {}
|
||||
|
@ -210,6 +211,29 @@ ImagePipelineNodeDeinterleaveLines::ImagePipelineNodeDeinterleaveLines(
|
|||
interleaved_lines, pixels_per_chunk)
|
||||
{}
|
||||
|
||||
ImagePipelineNodeSwap16BitEndian::ImagePipelineNodeSwap16BitEndian(ImagePipelineNode& source) :
|
||||
source_(source),
|
||||
needs_swapping_{false}
|
||||
{
|
||||
if (get_pixel_format_depth(source_.get_format()) == 16) {
|
||||
needs_swapping_ = true;
|
||||
} else {
|
||||
DBG(DBG_info, "%s: this pipeline node does nothing for non 16-bit formats", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
void ImagePipelineNodeSwap16BitEndian::get_next_row_data(std::uint8_t* out_data)
|
||||
{
|
||||
source_.get_next_row_data(out_data);
|
||||
if (needs_swapping_) {
|
||||
std::size_t pixels = get_row_bytes() / 2;
|
||||
for (std::size_t i = 0; i < pixels; ++i) {
|
||||
std::swap(*out_data, *(out_data + 1));
|
||||
out_data += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImagePipelineNodeMergeMonoLines::ImagePipelineNodeMergeMonoLines(ImagePipelineNode& source,
|
||||
ColorOrder color_order) :
|
||||
source_(source),
|
||||
|
@ -428,6 +452,71 @@ ImagePipelineNodeExtract::ImagePipelineNodeExtract(ImagePipelineNode& source,
|
|||
|
||||
ImagePipelineNodeExtract::~ImagePipelineNodeExtract() {}
|
||||
|
||||
ImagePipelineNodeScaleRows::ImagePipelineNodeScaleRows(ImagePipelineNode& source,
|
||||
std::size_t width) :
|
||||
source_(source),
|
||||
width_{width}
|
||||
{
|
||||
cached_line_.resize(source_.get_row_bytes());
|
||||
}
|
||||
|
||||
void ImagePipelineNodeScaleRows::get_next_row_data(std::uint8_t* out_data)
|
||||
{
|
||||
auto src_width = source_.get_width();
|
||||
auto dst_width = width_;
|
||||
|
||||
source_.get_next_row_data(cached_line_.data());
|
||||
|
||||
const auto* src_data = cached_line_.data();
|
||||
auto format = get_format();
|
||||
auto channels = get_pixel_channels(format);
|
||||
|
||||
if (src_width > dst_width) {
|
||||
// average
|
||||
std::uint32_t counter = src_width / 2;
|
||||
unsigned src_x = 0;
|
||||
for (unsigned dst_x = 0; dst_x < dst_width; dst_x++) {
|
||||
unsigned avg[3] = {0, 0, 0};
|
||||
unsigned count = 0;
|
||||
while (counter < src_width && src_x < src_width) {
|
||||
counter += dst_width;
|
||||
|
||||
for (unsigned c = 0; c < channels; c++) {
|
||||
avg[c] += get_raw_channel_from_row(src_data, src_x, c, format);
|
||||
}
|
||||
|
||||
src_x++;
|
||||
count++;
|
||||
}
|
||||
counter -= src_width;
|
||||
|
||||
for (unsigned c = 0; c < channels; c++) {
|
||||
set_raw_channel_to_row(out_data, dst_x, c, avg[c] / count, format);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// interpolate and copy pixels
|
||||
std::uint32_t counter = dst_width / 2;
|
||||
unsigned dst_x = 0;
|
||||
|
||||
for (unsigned src_x = 0; src_x < src_width; src_x++) {
|
||||
unsigned avg[3] = {0, 0, 0};
|
||||
for (unsigned c = 0; c < channels; c++) {
|
||||
avg[c] += get_raw_channel_from_row(src_data, src_x, c, format);
|
||||
}
|
||||
while ((counter < dst_width || src_x + 1 == src_width) && dst_x < dst_width) {
|
||||
counter += src_width;
|
||||
|
||||
for (unsigned c = 0; c < channels; c++) {
|
||||
set_raw_channel_to_row(out_data, dst_x, c, avg[c], format);
|
||||
}
|
||||
dst_x++;
|
||||
}
|
||||
counter -= dst_width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ImagePipelineNodeExtract::get_next_row_data(std::uint8_t* out_data)
|
||||
{
|
||||
while (current_line_ < offset_y_) {
|
||||
|
@ -472,6 +561,36 @@ void ImagePipelineNodeExtract::get_next_row_data(std::uint8_t* out_data)
|
|||
current_line_++;
|
||||
}
|
||||
|
||||
ImagePipelineNodeDebug::ImagePipelineNodeDebug(ImagePipelineNode& source,
|
||||
const std::string& path) :
|
||||
source_(source),
|
||||
path_{path},
|
||||
buffer_{source_.get_row_bytes()}
|
||||
{}
|
||||
|
||||
ImagePipelineNodeDebug::~ImagePipelineNodeDebug()
|
||||
{
|
||||
catch_all_exceptions(__func__, [&]()
|
||||
{
|
||||
if (buffer_.empty())
|
||||
return;
|
||||
|
||||
auto format = get_format();
|
||||
buffer_.linearize();
|
||||
sanei_genesys_write_pnm_file(path_.c_str(), buffer_.get_front_row_ptr(),
|
||||
get_pixel_format_depth(format),
|
||||
get_pixel_channels(format),
|
||||
get_width(), buffer_.height());
|
||||
});
|
||||
}
|
||||
|
||||
void ImagePipelineNodeDebug::get_next_row_data(std::uint8_t* out_data)
|
||||
{
|
||||
buffer_.push_back();
|
||||
source_.get_next_row_data(out_data);
|
||||
std::memcpy(buffer_.get_back_row_ptr(), out_data, get_row_bytes());
|
||||
}
|
||||
|
||||
std::size_t ImagePipelineStack::get_input_width() const
|
||||
{
|
||||
ensure_node_exists();
|
||||
|
@ -527,6 +646,16 @@ void ImagePipelineStack::ensure_node_exists() const
|
|||
}
|
||||
}
|
||||
|
||||
void ImagePipelineStack::clear()
|
||||
{
|
||||
// we need to destroy the nodes back to front, so that the destructors still have valid
|
||||
// references to sources
|
||||
for (auto it = nodes_.rbegin(); it != nodes_.rend(); ++it) {
|
||||
it->reset();
|
||||
}
|
||||
nodes_.clear();
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> ImagePipelineStack::get_all_data()
|
||||
{
|
||||
auto row_bytes = get_output_row_bytes();
|
||||
|
|
|
@ -248,6 +248,23 @@ public:
|
|||
std::size_t pixels_per_chunk);
|
||||
};
|
||||
|
||||
// A pipeline that swaps bytes in 16-bit components on big-endian systems
|
||||
class ImagePipelineNodeSwap16BitEndian : public ImagePipelineNode
|
||||
{
|
||||
public:
|
||||
ImagePipelineNodeSwap16BitEndian(ImagePipelineNode& source);
|
||||
|
||||
std::size_t get_width() const override { return source_.get_width(); }
|
||||
std::size_t get_height() const override { return source_.get_height(); }
|
||||
PixelFormat get_format() const override { return source_.get_format(); }
|
||||
|
||||
void get_next_row_data(std::uint8_t* out_data) override;
|
||||
|
||||
private:
|
||||
ImagePipelineNode& source_;
|
||||
bool needs_swapping_ = false;
|
||||
};
|
||||
|
||||
// A pipeline node that merges 3 mono lines into a color channel
|
||||
class ImagePipelineNodeMergeMonoLines : public ImagePipelineNode
|
||||
{
|
||||
|
@ -366,6 +383,43 @@ private:
|
|||
std::vector<uint8_t> cached_line_;
|
||||
};
|
||||
|
||||
// A pipeline node that scales rows to the specified width by using a point filter
|
||||
class ImagePipelineNodeScaleRows : public ImagePipelineNode
|
||||
{
|
||||
public:
|
||||
ImagePipelineNodeScaleRows(ImagePipelineNode& source, std::size_t width);
|
||||
|
||||
std::size_t get_width() const override { return width_; }
|
||||
std::size_t get_height() const override { return source_.get_height(); }
|
||||
PixelFormat get_format() const override { return source_.get_format(); }
|
||||
|
||||
void get_next_row_data(std::uint8_t* out_data) override;
|
||||
|
||||
private:
|
||||
ImagePipelineNode& source_;
|
||||
std::size_t width_ = 0;
|
||||
|
||||
std::vector<uint8_t> cached_line_;
|
||||
};
|
||||
|
||||
class ImagePipelineNodeDebug : public ImagePipelineNode
|
||||
{
|
||||
public:
|
||||
ImagePipelineNodeDebug(ImagePipelineNode& source, const std::string& path);
|
||||
~ImagePipelineNodeDebug() override;
|
||||
|
||||
std::size_t get_width() const override { return source_.get_width(); }
|
||||
std::size_t get_height() const override { return source_.get_height(); }
|
||||
PixelFormat get_format() const override { return source_.get_format(); }
|
||||
|
||||
void get_next_row_data(std::uint8_t* out_data) override;
|
||||
|
||||
private:
|
||||
ImagePipelineNode& source_;
|
||||
std::string path_;
|
||||
RowBuffer buffer_;
|
||||
};
|
||||
|
||||
class ImagePipelineStack
|
||||
{
|
||||
public:
|
||||
|
@ -381,10 +435,7 @@ public:
|
|||
PixelFormat get_output_format() const;
|
||||
std::size_t get_output_row_bytes() const;
|
||||
|
||||
void clear()
|
||||
{
|
||||
nodes_.clear();
|
||||
}
|
||||
void clear();
|
||||
|
||||
template<class Node, class... Args>
|
||||
void push_first_node(Args&&... args)
|
||||
|
|
|
@ -1463,6 +1463,10 @@ static FakeBufferModel get_fake_usb_buffer_model(const ScanSession& session)
|
|||
|
||||
void build_image_pipeline(Genesys_Device* dev, const ScanSession& session)
|
||||
{
|
||||
static unsigned s_pipeline_index = 0;
|
||||
|
||||
s_pipeline_index++;
|
||||
|
||||
auto format = create_pixel_format(session.params.depth,
|
||||
dev->model->is_cis ? 1 : session.params.channels,
|
||||
dev->model->line_mode_color_order);
|
||||
|
@ -1502,6 +1506,58 @@ void build_image_pipeline(Genesys_Device* dev, const ScanSession& session)
|
|||
get_fake_usb_buffer_model(session), read_data_from_usb);
|
||||
}
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
if (get_pixel_format_depth(format) == 16) {
|
||||
dev->pipeline.push_node<ImagePipelineNodeSwap16BitEndian>();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (DBG_LEVEL >= DBG_io2) {
|
||||
dev->pipeline.push_node<ImagePipelineNodeDebug>("gl_pipeline_" +
|
||||
std::to_string(s_pipeline_index) +
|
||||
"_0_after_swap.pnm");
|
||||
}
|
||||
|
||||
if (dev->model->is_cis && session.params.channels == 3) {
|
||||
dev->pipeline.push_node<ImagePipelineNodeMergeMonoLines>(dev->model->line_mode_color_order);
|
||||
}
|
||||
|
||||
if (dev->pipeline.get_output_format() == PixelFormat::BGR888) {
|
||||
dev->pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB888);
|
||||
}
|
||||
|
||||
if (dev->pipeline.get_output_format() == PixelFormat::BGR161616) {
|
||||
dev->pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB161616);
|
||||
}
|
||||
|
||||
if (session.max_color_shift_lines > 0 && session.params.channels == 3) {
|
||||
std::size_t shift_r = (dev->ld_shift_r * session.params.yres) / dev->motor.base_ydpi;
|
||||
std::size_t shift_g = (dev->ld_shift_g * session.params.yres) / dev->motor.base_ydpi;
|
||||
std::size_t shift_b = (dev->ld_shift_b * session.params.yres) / dev->motor.base_ydpi;
|
||||
dev->pipeline.push_node<ImagePipelineNodeComponentShiftLines>(shift_r, shift_g, shift_b);
|
||||
}
|
||||
|
||||
if (DBG_LEVEL >= DBG_io2) {
|
||||
dev->pipeline.push_node<ImagePipelineNodeDebug>("gl_pipeline_" +
|
||||
std::to_string(s_pipeline_index) +
|
||||
"_1_after_shift.pnm");
|
||||
}
|
||||
|
||||
if (session.num_staggered_lines > 0) {
|
||||
std::vector<std::size_t> shifts{0, session.num_staggered_lines};
|
||||
dev->pipeline.push_node<ImagePipelineNodePixelShiftLines>(shifts);
|
||||
}
|
||||
|
||||
if (DBG_LEVEL >= DBG_io2) {
|
||||
dev->pipeline.push_node<ImagePipelineNodeDebug>("gl_pipeline_" +
|
||||
std::to_string(s_pipeline_index) +
|
||||
"_2_after_stagger.pnm");
|
||||
}
|
||||
|
||||
if (session.output_pixels != session.params.get_requested_pixels()) {
|
||||
dev->pipeline.push_node<ImagePipelineNodeScaleRows>(session.params.get_requested_pixels());
|
||||
}
|
||||
|
||||
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()
|
||||
|
@ -2199,7 +2255,7 @@ void run_functions_at_backend_exit()
|
|||
|
||||
void debug_dump(unsigned level, const Genesys_Settings& settings)
|
||||
{
|
||||
DBG(level, "settings:\n"
|
||||
DBG(level, "Genesys_Settings:\n"
|
||||
"Resolution X/Y : %u / %u dpi\n"
|
||||
"Lines : %u\n"
|
||||
"Pixels per line : %u\n"
|
||||
|
@ -2215,7 +2271,7 @@ void debug_dump(unsigned level, const Genesys_Settings& settings)
|
|||
|
||||
void debug_dump(unsigned level, const SetupParams& params)
|
||||
{
|
||||
DBG(level, "settings:\n"
|
||||
DBG(level, "SetupParams:\n"
|
||||
"Resolution X/Y : %u / %u dpi\n"
|
||||
"Lines : %u\n"
|
||||
"Pixels per line : %u\n"
|
||||
|
|
|
@ -175,7 +175,7 @@ void test_node_desegment_1_line()
|
|||
ASSERT_EQ(out_data, expected_data);
|
||||
}
|
||||
|
||||
void test_node_deinterleave_lines()
|
||||
void test_node_deinterleave_lines_i8()
|
||||
{
|
||||
using Data = std::vector<std::uint8_t>;
|
||||
|
||||
|
@ -203,6 +203,67 @@ void test_node_deinterleave_lines()
|
|||
ASSERT_EQ(out_data, expected_data);
|
||||
}
|
||||
|
||||
void test_node_deinterleave_lines_rgb888()
|
||||
{
|
||||
using Data = std::vector<std::uint8_t>;
|
||||
|
||||
Data in_data = {
|
||||
1, 2, 3, 7, 8, 9, 13, 14, 15, 19, 20, 21,
|
||||
4, 5, 6, 10, 11, 12, 16, 17, 18, 22, 23, 24,
|
||||
};
|
||||
|
||||
ImagePipelineStack stack;
|
||||
stack.push_first_node<ImagePipelineNodeArraySource>(4, 2, PixelFormat::RGB888,
|
||||
std::move(in_data));
|
||||
stack.push_node<ImagePipelineNodeDeinterleaveLines>(2, 1);
|
||||
|
||||
ASSERT_EQ(stack.get_output_width(), 8u);
|
||||
ASSERT_EQ(stack.get_output_height(), 1u);
|
||||
ASSERT_EQ(stack.get_output_row_bytes(), 24u);
|
||||
ASSERT_EQ(stack.get_output_format(), PixelFormat::RGB888);
|
||||
|
||||
auto out_data = stack.get_all_data();
|
||||
|
||||
Data expected_data;
|
||||
expected_data.resize(24, 0);
|
||||
std::iota(expected_data.begin(), expected_data.end(), 1); // will fill with 1, 2, 3, ..., 20
|
||||
|
||||
ASSERT_EQ(out_data, expected_data);
|
||||
}
|
||||
|
||||
void test_node_swap_16bit_endian()
|
||||
{
|
||||
using Data = std::vector<std::uint8_t>;
|
||||
|
||||
Data in_data = {
|
||||
0x10, 0x20, 0x30, 0x11, 0x21, 0x31,
|
||||
0x12, 0x22, 0x32, 0x13, 0x23, 0x33,
|
||||
0x14, 0x24, 0x34, 0x15, 0x25, 0x35,
|
||||
0x16, 0x26, 0x36, 0x17, 0x27, 0x37,
|
||||
};
|
||||
|
||||
ImagePipelineStack stack;
|
||||
stack.push_first_node<ImagePipelineNodeArraySource>(4, 1, PixelFormat::RGB161616,
|
||||
std::move(in_data));
|
||||
stack.push_node<ImagePipelineNodeSwap16BitEndian>();
|
||||
|
||||
ASSERT_EQ(stack.get_output_width(), 4u);
|
||||
ASSERT_EQ(stack.get_output_height(), 1u);
|
||||
ASSERT_EQ(stack.get_output_row_bytes(), 24u);
|
||||
ASSERT_EQ(stack.get_output_format(), PixelFormat::RGB161616);
|
||||
|
||||
auto out_data = stack.get_all_data();
|
||||
|
||||
Data expected_data = {
|
||||
0x20, 0x10, 0x11, 0x30, 0x31, 0x21,
|
||||
0x22, 0x12, 0x13, 0x32, 0x33, 0x23,
|
||||
0x24, 0x14, 0x15, 0x34, 0x35, 0x25,
|
||||
0x26, 0x16, 0x17, 0x36, 0x37, 0x27,
|
||||
};
|
||||
|
||||
ASSERT_EQ(out_data, expected_data);
|
||||
}
|
||||
|
||||
void test_node_merge_mono_lines()
|
||||
{
|
||||
using Data = std::vector<std::uint8_t>;
|
||||
|
@ -335,7 +396,9 @@ void test_image_pipeline()
|
|||
test_node_buffered_callable_source();
|
||||
test_node_format_convert();
|
||||
test_node_desegment_1_line();
|
||||
test_node_deinterleave_lines();
|
||||
test_node_deinterleave_lines_i8();
|
||||
test_node_deinterleave_lines_rgb888();
|
||||
test_node_swap_16bit_endian();
|
||||
test_node_merge_mono_lines();
|
||||
test_node_split_mono_lines();
|
||||
test_node_component_shift_lines();
|
||||
|
|
Ładowanie…
Reference in New Issue