/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_LINE_BUFFER_H #define BACKEND_GENESYS_LINE_BUFFER_H #include "error.h" #include #include #include #include namespace genesys { class RowBuffer { public: RowBuffer(std::size_t line_bytes) : row_bytes_{line_bytes} {} RowBuffer(const RowBuffer&) = default; RowBuffer& operator=(const RowBuffer&) = default; ~RowBuffer() = default; const std::uint8_t* get_row_ptr(std::size_t y) const { if (y >= height()) { throw SaneException("y %zu is out of range", y); } return data_.data() + row_bytes_ * get_row_index(y); } std::uint8_t* get_row_ptr(std::size_t y) { if (y >= height()) { throw SaneException("y %zu is out of range", y); } return data_.data() + row_bytes_ * get_row_index(y); } const std::uint8_t* get_front_row_ptr() const { return get_row_ptr(0); } std::uint8_t* get_front_row_ptr() { return get_row_ptr(0); } const std::uint8_t* get_back_row_ptr() const { return get_row_ptr(height() - 1); } std::uint8_t* get_back_row_ptr() { return get_row_ptr(height() - 1); } bool empty() const { return is_linear_ && first_ == last_; } bool full() { if (is_linear_) { return last_ == buffer_end_; } return first_ == last_; } bool is_linear() const { return is_linear_; } void linearize() { if (!is_linear_) { std::rotate(data_.begin(), data_.begin() + row_bytes_ * first_, data_.end()); last_ = height(); first_ = 0; is_linear_ = true; } } void pop_front() { if (empty()) { throw SaneException("Trying to pop out of empty() line buffer"); } first_++; if (first_ == last_) { first_ = 0; last_ = 0; is_linear_ = true; } else if (first_ == buffer_end_) { first_ = 0; is_linear_ = true; } } void push_front() { if (height() + 1 >= height_capacity()) { ensure_capacity(std::max(1, height() * 2)); } if (first_ == 0) { is_linear_ = false; first_ = buffer_end_; } first_--; } void pop_back() { if (empty()) { throw SaneException("Trying to pop out of empty() line buffer"); } if (last_ == 0) { last_ = buffer_end_; is_linear_ = true; } last_--; if (first_ == last_) { first_ = 0; last_ = 0; is_linear_ = true; } } void push_back() { if (height() + 1 >= height_capacity()) { ensure_capacity(std::max(1, height() * 2)); } if (last_ == buffer_end_) { is_linear_ = false; last_ = 0; } last_++; } std::size_t row_bytes() const { return row_bytes_; } std::size_t height() const { if (!is_linear_) { return last_ + buffer_end_ - first_; } return last_ - first_; } std::size_t height_capacity() const { return buffer_end_; } void clear() { first_ = 0; last_ = 0; } private: std::size_t get_row_index(std::size_t index) const { if (index >= buffer_end_ - first_) { return index - (buffer_end_ - first_); } return index + first_; } void ensure_capacity(std::size_t capacity) { if (capacity < height_capacity()) return; linearize(); data_.resize(capacity * row_bytes_); buffer_end_ = capacity; } private: std::size_t row_bytes_ = 0; std::size_t first_ = 0; std::size_t last_ = 0; std::size_t buffer_end_ = 0; bool is_linear_ = true; std::vector data_; }; } // namespace genesys #endif // BACKEND_GENESYS_LINE_BUFFER_H