genesys: Implement image data source that mimics current reading from USB

merge-requests/174/head
Povilas Kanapickas 2019-09-13 10:38:05 +03:00
rodzic 7d7a395277
commit 1c7ef4b55e
5 zmienionych plików z 239 dodań i 0 usunięć

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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