308 lines
7.2 KiB
C++
308 lines
7.2 KiB
C++
//
|
|
// Created by erki on 13.05.21.
|
|
//
|
|
|
|
#ifndef SKULLC_UTILITY_PIXELBUFFER_HPP
|
|
#define SKULLC_UTILITY_PIXELBUFFER_HPP
|
|
|
|
#include <array>
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
#include <string_view>
|
|
|
|
namespace Utility
|
|
{
|
|
|
|
template<std::size_t W, std::size_t H>
|
|
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<value, width * height>;
|
|
using iterator = typename container::iterator;
|
|
using const_iterator = typename container::const_iterator;
|
|
using coordinates = std::pair<std::size_t, std::size_t>;
|
|
|
|
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<typename Font>
|
|
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<std::size_t VW, std::size_t VH, typename TP>
|
|
struct View
|
|
{
|
|
constexpr static std::size_t width = VW;
|
|
constexpr static std::size_t height = VH;
|
|
using parent_type = std::decay_t<TP>;
|
|
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<std::size_t VW_, std::size_t VH_>
|
|
auto view(const coordinates& offset)
|
|
{
|
|
return View<VW_, VH_, parent_type>(offset, parent);
|
|
}
|
|
};
|
|
|
|
template<std::size_t VW, std::size_t VH, typename TP>
|
|
friend struct View;
|
|
|
|
template<std::size_t VW, std::size_t VH>
|
|
auto view(const coordinates& offset) -> View<VW, VH, decltype(*this)>
|
|
{
|
|
using rtype = View<VW, VH, decltype(*this)>;
|
|
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
|