kopia lustrzana https://gitlab.com/sane-project/backends
genesys: Use new image pipeline for row scaling
rodzic
bf7e890fa4
commit
cd712f9f99
|
@ -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
|
||||
|
|
|
@ -3442,10 +3442,7 @@ static void genesys_read_ordered_data(Genesys_Device* dev, SANE_Byte* destinatio
|
|||
size_t bytes;
|
||||
unsigned int channels, depth, src_pixels;
|
||||
uint8_t *work_buffer_src;
|
||||
uint8_t *work_buffer_dst;
|
||||
unsigned int dst_lines;
|
||||
Genesys_Buffer *src_buffer;
|
||||
Genesys_Buffer *dst_buffer;
|
||||
|
||||
if (dev->read_active != SANE_TRUE)
|
||||
{
|
||||
|
@ -3520,54 +3517,6 @@ Problems with the first approach:
|
|||
|
||||
src_buffer = &(dev->read_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,105 +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_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,7 +55,6 @@ Genesys_Device::~Genesys_Device()
|
|||
void Genesys_Device::clear()
|
||||
{
|
||||
read_buffer.clear();
|
||||
out_buffer.clear();
|
||||
binarize_buffer.clear();
|
||||
local_buffer.clear();
|
||||
|
||||
|
|
|
@ -282,7 +282,6 @@ struct Genesys_Device
|
|||
SANE_Bool needs_home_ta = 0;
|
||||
|
||||
Genesys_Buffer read_buffer;
|
||||
Genesys_Buffer out_buffer;
|
||||
|
||||
// buffer for digital lineart from gray data
|
||||
Genesys_Buffer binarize_buffer;
|
||||
|
|
|
@ -1053,9 +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->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,9 +718,6 @@ static void gl646_setup_registers(Genesys_Device* dev,
|
|||
dev->read_buffer.clear();
|
||||
dev->read_buffer.alloc(session.buffer_size_read);
|
||||
|
||||
dev->out_buffer.clear();
|
||||
dev->out_buffer.alloc(session.buffer_size_out);
|
||||
|
||||
build_image_pipeline(dev, session);
|
||||
|
||||
/* scan bytes to read */
|
||||
|
|
|
@ -1875,9 +1875,6 @@ dummy \ scanned lines
|
|||
dev->read_buffer.clear();
|
||||
dev->read_buffer.alloc(session.buffer_size_read);
|
||||
|
||||
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,9 +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->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,9 +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->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,9 +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->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__,
|
||||
|
|
|
@ -451,6 +451,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_) {
|
||||
|
|
|
@ -383,6 +383,25 @@ 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 ImagePipelineStack
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -1531,6 +1531,10 @@ void build_image_pipeline(Genesys_Device* dev, const ScanSession& session)
|
|||
dev->pipeline.push_node<ImagePipelineNodePixelShiftLines>(shifts);
|
||||
}
|
||||
|
||||
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()
|
||||
|
|
Ładowanie…
Reference in New Issue