kopia lustrzana https://gitlab.com/sane-project/backends
genesys: Implement image data source that mimics current reading from USB
rodzic
7d7a395277
commit
1c7ef4b55e
|
@ -83,3 +83,113 @@ void ImageBuffer::get_data(std::size_t size, std::uint8_t* out_data)
|
|||
copy_buffer();
|
||||
} while(out_data < out_data_end);
|
||||
}
|
||||
|
||||
void FakeBufferModel::push_step(std::size_t buffer_size, std::size_t row_bytes)
|
||||
{
|
||||
sizes_.push_back(buffer_size);
|
||||
available_sizes_.push_back(0);
|
||||
row_bytes_.push_back(row_bytes);
|
||||
}
|
||||
|
||||
std::size_t FakeBufferModel::available_space() const
|
||||
{
|
||||
if (sizes_.empty())
|
||||
throw SaneException("Model has not been setup");
|
||||
return sizes_.front() - available_sizes_.front();
|
||||
}
|
||||
|
||||
void FakeBufferModel::simulate_read(std::size_t size)
|
||||
{
|
||||
if (sizes_.empty()) {
|
||||
throw SaneException("Model has not been setup");
|
||||
}
|
||||
if (available_space() < size) {
|
||||
throw SaneException("Attempted to simulate read of too much memory");
|
||||
}
|
||||
|
||||
available_sizes_.front() += size;
|
||||
|
||||
for (unsigned i = 1; i < sizes_.size(); ++i) {
|
||||
auto avail_src = available_sizes_[i - 1];
|
||||
auto avail_dst = sizes_[i] - available_sizes_[i];
|
||||
|
||||
auto avail = (std::min(avail_src, avail_dst) / row_bytes_[i]) * row_bytes_[i];
|
||||
available_sizes_[i - 1] -= avail;
|
||||
available_sizes_[i] += avail;
|
||||
}
|
||||
available_sizes_.back() = 0;
|
||||
}
|
||||
|
||||
ImageBufferGenesysUsb::ImageBufferGenesysUsb(std::size_t total_size,
|
||||
const FakeBufferModel& buffer_model,
|
||||
ProducerCallback producer) :
|
||||
remaining_size_{total_size},
|
||||
buffer_model_{buffer_model},
|
||||
producer_{producer}
|
||||
{}
|
||||
|
||||
void ImageBufferGenesysUsb::get_data(std::size_t size, std::uint8_t* out_data)
|
||||
{
|
||||
const std::uint8_t* out_data_end = out_data + size;
|
||||
|
||||
auto copy_buffer = [&]()
|
||||
{
|
||||
std::size_t bytes_copy = std::min<std::size_t>(out_data_end - out_data, available());
|
||||
std::memcpy(out_data, buffer_.data() + buffer_offset_, bytes_copy);
|
||||
out_data += bytes_copy;
|
||||
buffer_offset_ += bytes_copy;
|
||||
};
|
||||
|
||||
// first, read remaining data from buffer
|
||||
if (available() > 0) {
|
||||
copy_buffer();
|
||||
}
|
||||
|
||||
if (out_data == out_data_end) {
|
||||
return;
|
||||
}
|
||||
|
||||
// now the buffer is empty and there's more data to be read
|
||||
do {
|
||||
if (remaining_size_ == 0)
|
||||
return; // BUG: the caller should correctly handle the read amount
|
||||
|
||||
auto bytes_to_read = get_read_size();
|
||||
buffer_offset_ = 0;
|
||||
buffer_end_ = bytes_to_read;
|
||||
buffer_.resize(bytes_to_read);
|
||||
|
||||
producer_(bytes_to_read, buffer_.data());
|
||||
|
||||
if (remaining_size_ < bytes_to_read) {
|
||||
remaining_size_ = 0;
|
||||
} else {
|
||||
remaining_size_ -= bytes_to_read;
|
||||
}
|
||||
|
||||
copy_buffer();
|
||||
} while(out_data < out_data_end);
|
||||
}
|
||||
|
||||
std::size_t ImageBufferGenesysUsb::get_read_size()
|
||||
{
|
||||
std::size_t size = buffer_model_.available_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 (remaining_size_ < size) {
|
||||
size = remaining_size_;
|
||||
/*round up to a multiple of 256 bytes */
|
||||
size += (size & 0xff) ? 0x100 : 0x00;
|
||||
size &= ~0xff;
|
||||
}
|
||||
|
||||
buffer_model_.simulate_read(size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
|
|
@ -104,4 +104,53 @@ private:
|
|||
std::vector<std::uint8_t> buffer_;
|
||||
};
|
||||
|
||||
class FakeBufferModel
|
||||
{
|
||||
public:
|
||||
FakeBufferModel() {}
|
||||
|
||||
void push_step(std::size_t buffer_size, std::size_t row_bytes);
|
||||
|
||||
std::size_t available_space() const;
|
||||
|
||||
void simulate_read(std::size_t size);
|
||||
|
||||
private:
|
||||
std::vector<std::size_t> sizes_;
|
||||
std::vector<std::size_t> available_sizes_;
|
||||
std::vector<std::size_t> row_bytes_;
|
||||
};
|
||||
|
||||
// This class is similar to ImageBuffer, but preserves historical peculiarities of buffer handling
|
||||
// in the backend to preserve exact behavior
|
||||
class ImageBufferGenesysUsb
|
||||
{
|
||||
public:
|
||||
using ProducerCallback = std::function<void(std::size_t size, std::uint8_t* out_data)>;
|
||||
|
||||
ImageBufferGenesysUsb() {}
|
||||
ImageBufferGenesysUsb(std::size_t total_size, const FakeBufferModel& buffer_model,
|
||||
ProducerCallback producer);
|
||||
|
||||
std::size_t remaining_size() const { return remaining_size_; }
|
||||
|
||||
std::size_t available() const { return buffer_end_ - buffer_offset_; }
|
||||
|
||||
void get_data(std::size_t size, std::uint8_t* out_data);
|
||||
|
||||
private:
|
||||
|
||||
std::size_t get_read_size();
|
||||
|
||||
std::size_t remaining_size_ = 0;
|
||||
|
||||
std::size_t buffer_offset_ = 0;
|
||||
std::size_t buffer_end_ = 0;
|
||||
std::vector<std::uint8_t> buffer_;
|
||||
|
||||
FakeBufferModel buffer_model_;
|
||||
|
||||
ProducerCallback producer_;
|
||||
};
|
||||
|
||||
#endif // BACKEND_GENESYS_IMAGE_BUFFER_H
|
||||
|
|
|
@ -69,6 +69,21 @@ void ImagePipelineNodeBufferedCallableSource::get_next_row_data(std::uint8_t* ou
|
|||
curr_row_++;
|
||||
}
|
||||
|
||||
|
||||
ImagePipelineNodeBufferedGenesysUsb::ImagePipelineNodeBufferedGenesysUsb(
|
||||
std::size_t width, std::size_t height, PixelFormat format, std::size_t total_size,
|
||||
const FakeBufferModel& buffer_model, ProducerCallback producer) :
|
||||
width_{width},
|
||||
height_{height},
|
||||
format_{format},
|
||||
buffer_{total_size, buffer_model, producer}
|
||||
{}
|
||||
|
||||
void ImagePipelineNodeBufferedGenesysUsb::get_next_row_data(std::uint8_t* out_data)
|
||||
{
|
||||
buffer_.get_data(get_row_bytes(), out_data);
|
||||
}
|
||||
|
||||
ImagePipelineNodeArraySource::ImagePipelineNodeArraySource(std::size_t width, std::size_t height,
|
||||
PixelFormat format,
|
||||
std::vector<std::uint8_t> data) :
|
||||
|
|
|
@ -127,6 +127,33 @@ private:
|
|||
ImageBuffer buffer_;
|
||||
};
|
||||
|
||||
class ImagePipelineNodeBufferedGenesysUsb : public ImagePipelineNode
|
||||
{
|
||||
public:
|
||||
using ProducerCallback = std::function<void(std::size_t size, std::uint8_t* out_data)>;
|
||||
|
||||
ImagePipelineNodeBufferedGenesysUsb(std::size_t width, std::size_t height,
|
||||
PixelFormat format, std::size_t total_size,
|
||||
const FakeBufferModel& buffer_model,
|
||||
ProducerCallback producer);
|
||||
|
||||
std::size_t get_width() const override { return width_; }
|
||||
std::size_t get_height() const override { return height_; }
|
||||
PixelFormat get_format() const override { return format_; }
|
||||
|
||||
void get_next_row_data(std::uint8_t* out_data) override;
|
||||
|
||||
std::size_t buffer_available() const { return buffer_.available(); }
|
||||
|
||||
private:
|
||||
ProducerCallback producer_;
|
||||
std::size_t width_ = 0;
|
||||
std::size_t height_ = 0;
|
||||
PixelFormat format_ = PixelFormat::UNKNOWN;
|
||||
|
||||
ImageBufferGenesysUsb buffer_;
|
||||
};
|
||||
|
||||
// A pipeline node that produces data from the given array.
|
||||
class ImagePipelineNodeArraySource : public ImagePipelineNode
|
||||
{
|
||||
|
|
|
@ -30,6 +30,43 @@
|
|||
|
||||
#include <numeric>
|
||||
|
||||
void test_image_buffer_genesys_usb()
|
||||
{
|
||||
std::vector<std::size_t> requests;
|
||||
|
||||
auto on_read_usb = [&](std::size_t x, std::uint8_t* data)
|
||||
{
|
||||
(void) data;
|
||||
requests.push_back(x);
|
||||
};
|
||||
|
||||
FakeBufferModel model;
|
||||
model.push_step(453120, 1);
|
||||
model.push_step(56640, 3540);
|
||||
ImageBufferGenesysUsb buffer{1086780, model, on_read_usb};
|
||||
|
||||
std::vector<std::uint8_t> dummy;
|
||||
dummy.resize(1086780);
|
||||
|
||||
buffer.get_data(453120, dummy.data());
|
||||
buffer.get_data(56640, dummy.data());
|
||||
buffer.get_data(56640, dummy.data());
|
||||
buffer.get_data(56640, dummy.data());
|
||||
buffer.get_data(56640, dummy.data());
|
||||
buffer.get_data(56640, dummy.data());
|
||||
buffer.get_data(56640, dummy.data());
|
||||
buffer.get_data(56640, dummy.data());
|
||||
buffer.get_data(56640, dummy.data());
|
||||
buffer.get_data(56640, dummy.data());
|
||||
buffer.get_data(56640, dummy.data());
|
||||
buffer.get_data(56640, dummy.data());
|
||||
|
||||
std::vector<std::size_t> expected = {
|
||||
453120, 56576, 56576, 56576, 56832, 56576, 56576, 56576, 56832, 56576, 56576, 56576, 11008
|
||||
};
|
||||
ASSERT_EQ(requests, expected);
|
||||
}
|
||||
|
||||
void test_node_buffered_callable_source()
|
||||
{
|
||||
using Data = std::vector<std::uint8_t>;
|
||||
|
@ -294,6 +331,7 @@ void test_node_pixel_shift_lines()
|
|||
|
||||
void test_image_pipeline()
|
||||
{
|
||||
test_image_buffer_genesys_usb();
|
||||
test_node_buffered_callable_source();
|
||||
test_node_format_convert();
|
||||
test_node_desegment_1_line();
|
||||
|
|
Ładowanie…
Reference in New Issue