// // Created by erki on 13.05.21. // #ifndef SKULLC_UTILITY_PIXELBUFFER_HPP #define SKULLC_UTILITY_PIXELBUFFER_HPP #include #include #include #include namespace Utility { template class PixelBuffer { public: static constexpr std::size_t width = W; static constexpr std::size_t height = H; using value = std::uint8_t; using container = std::array; using iterator = typename container::iterator; using const_iterator = typename container::const_iterator; using coordinates = std::pair; PixelBuffer() : data_({0}) {} explicit PixelBuffer(const value& fill_value) : data_() { fill(fill_value); } constexpr value& at(const std::size_t& x, const std::size_t& y) { return data_[x + ((y) *width)]; } constexpr const value& at(const std::size_t& x, const std::size_t& y) const { return data_[x + ((y) *width)]; } constexpr value& at(const coordinates& c) { return at(x_(c), y_(c)); } constexpr const value& at(const coordinates& c) const { return at(x_(c), y_(c)); } const container& data() const { return data_; } const value* ptr() const { return data_.data(); } iterator begin() { return data_.begin(); } iterator end() { return data_.end(); } const_iterator cbegin() const { return data_.cbegin(); } const_iterator cend() const { return data_.cend(); } void fill(const value& color) { data_.fill(color); } /** * Draws the text in the font provided onto the pixel buffer. * * Currently only supports fonts which are 1 byte high at most. * * @todo: make the function work with multi-byte fonts. * * @tparam Font The font object to be used. Must have the following members: * * height, indicating how many pixels tall the font is. * * width, indicating how many pixels wide the font is. * * getCharacterBytes(char) that returns start and stop iterators for a given character. */ template void text(const std::string_view& text, const coordinates& start, const value& color, const value& background = 0x00) { std::uint32_t x = x_(start); const std::uint32_t y = y_(start); for (const char& c : text) { const auto [c_start, c_stop] = Font::getCharacterBytes(c); for (auto it = c_start; it != c_stop; it++, x++) { const std::uint8_t& column_byte = *it; if (x >= width) return; for (std::uint32_t i = 0; i < Font::height && i + y < height; i++) { at(x, y + i) = column_byte & (1 << i) ? color : background; } } x++; } } void rectangle(const coordinates& top_left, const coordinates& size, const value& color) { for (auto i = x_(top_left); i < (x_(top_left) + x_(size)); i++) { at(i, y_(top_left)) = color; at(i, y_(top_left) + y_(size) - 1) = color; } for (auto i = y_(top_left); i < (y_(top_left) + y_(size)); i++) { at(x_(top_left), i) = color; at(x_(top_left) + x_(size) - 1, i) = color; } } void line(const coordinates& p1, const coordinates& p2, const value& color) { const coordinates start = {std::min(x_(p1), x_(p2)), std::min(y_(p1), y_(p2))}; const coordinates stop = {std::max(x_(p1), x_(p2)), std::max(y_(p1), y_(p2))}; const auto dx = std::int32_t(x_(stop)) - std::int32_t(x_(start)); const auto dy = std::int32_t(y_(stop)) - std::int32_t(y_(start)); auto x = std::int32_t(x_(start)); auto y = std::int32_t(y_(start)); // We shortcut the direct lines, as Bresenham's algorithm cannot handle them. if (dx == 0 && dy == 0) { at(start) = color; } else if (dx == 0) { for (; y < y_(stop); y++) at({x, y}) = color; } else if (dy == 0) { for (; x < x_(stop); x++) at({x, y}) = color; } else { // Bresenham's algorithm. std::int32_t p = 2 * dy - dx; while (x < x_(stop)) { at({x, y}) = color; if (p >= 0) { y++; p = p + 2 * dy - 2 * dx; } else { p = p + 2 * dy; } x++; } } } void circle(const coordinates& center, const std::size_t radius, const value& color) { auto draw_pixels = [this, color, center](const std::size_t& x, const std::size_t& y) { at(x_(center) + x, y_(center) + y) = color; at(x_(center) - x, y_(center) + y) = color; at(x_(center) + x, y_(center) - y) = color; at(x_(center) - x, y_(center) - y) = color; at(x_(center) + y, y_(center) + x) = color; at(x_(center) - y, y_(center) + x) = color; at(x_(center) + y, y_(center) - x) = color; at(x_(center) - y, y_(center) - x) = color; }; auto x = std::int32_t(0); auto y = std::int32_t(radius); auto d = 3 - 2 * std::int32_t(radius); draw_pixels(x, y); while (y >= x) { x++; if (d > 0) { y--; d = d + 4 * (x - y) + 10; } else { d = d + 4 * x + 6; } draw_pixels(x, y); } } template struct View { constexpr static std::size_t width = VW; constexpr static std::size_t height = VH; using parent_type = std::decay_t; using value = typename parent_type::value; using coordinates = typename parent_type::coordinates; static_assert(width <= parent_type::width, "Parent type width is smaller than view's width."); static_assert(height <= parent_type::height, "Parent type height is smaller than view's height."); coordinates offset; parent_type& parent; View() = delete; explicit View(const coordinates& offset, parent_type& buffer) : offset(offset), parent(buffer) {} constexpr value& at(const std::size_t& x, const std::size_t& y) { return parent.at(parent.x_(offset) + x, parent.y_(offset) + y); } constexpr const value& at(const std::size_t& x, const std::size_t& y) const { return parent.at(parent.x_(offset) + x, parent.y_(offset) + y); } constexpr value& at(const coordinates& c) { return parent.at(parent.x_(offset) + parent.x_(c), parent.y_(offset) + parent.y_(c)); } constexpr const value& at(const coordinates& c) const { return parent.at(parent.x_(offset) + parent.x_(c), parent.y_(offset) + parent.y_(c)); } template auto view(const coordinates& offset) { return View(offset, parent); } }; template friend struct View; template auto view(const coordinates& offset) -> View { using rtype = View; return rtype(offset, *this); } private: container data_; constexpr const std::size_t& x_(const coordinates& c) const { return c.first; } constexpr const std::size_t& y_(const coordinates& c) const { return c.second; } }; }// namespace Utility #endif//SKULLC_UTILITY_PIXELBUFFER_HPP