Graphics drawing primitives.
Graphics drawing primitives.Stateless drawing functions that operate on any FramebufferLike type using C++20/23 concepts for compile-time polymorphism. Implements standard computer graphics algorithms optimized for integer-only e-paper displays.
MonoFramebuffer fb{176, 264, DisplayMode::BlackWhite};
Graphics::draw_line(fb, {0, 0}, {100, 100},
LineStyle::Solid, Color::Black,
Orientation::Portrait0);
Graphics::draw_circle(fb, {88, 132}, 50,
LineStyle::Solid, Color::Black,
DrawFill::Full, Orientation::Portrait0);
Graphics::draw_rectangle(fb, {10, 10}, {60, 60},
LineStyle::Solid, Color::Black,
DrawFill::Empty, Orientation::Portrait0);
Font font = Font::load("font.bdf");
Graphics::draw_text(fb, {10, 70}, "Hello!", font,
Color::Black, Color::White,
Orientation::Portrait0);
#pragma once
#include <cmath>
#include <cstddef>
#include <string_view>
class Graphics {
public:
template <FramebufferLike FB>
template <FramebufferLike FB>
template <FramebufferLike FB>
template <FramebufferLike FB>
static auto draw_text(FB &fb, Point pos, std::string_view text,
const Font &font,
Color foreground,
Color background,
template <FramebufferLike FB>
static auto draw_bitmap(FB &fb, Point pos, std::span<const std::uint8_t> data, std::size_t w, std::size_t h,
std::size_t target_w, std::size_t target_h,
Orientation orientation) -> void;
};
template <FramebufferLike FB>
-> void {
int x0 = static_cast<int>(start.x);
int y0 = static_cast<int>(start.y);
int x1 = static_cast<int>(end.x);
int y1 = static_cast<int>(end.y);
int dx = std::abs(x1 - x0);
int dy = std::abs(y1 - y0);
int sx = (x0 < x1) ? 1 : -1;
int sy = (y0 < y1) ? 1 : -1;
int err = dx - dy;
int step_count = 0;
while (true) {
fb.set_pixel(static_cast<std::size_t>(x0), static_cast<std::size_t>(y0), color, orientation);
}
if (x0 == x1 && y0 == y1) {
break;
}
int e2 = 2 * err;
if (e2 > -dy) {
err -= dy;
x0 += sx;
}
if (e2 < dx) {
err += dx;
y0 += sy;
}
++step_count;
}
}
template <FramebufferLike FB>
draw_line(fb, top_left, {bottom_right.x, top_left.y}, style, color, orientation);
draw_line(fb, {bottom_right.x, top_left.y}, bottom_right, style, color, orientation);
draw_line(fb, bottom_right, {top_left.x, bottom_right.y}, style, color, orientation);
draw_line(fb, {top_left.x, bottom_right.y}, top_left, style, color, orientation);
for (std::size_t y = top_left.y + 1; y < bottom_right.y; ++y) {
draw_line(fb, {top_left.x + 1, y}, {bottom_right.x - 1, y}, style, color, orientation);
}
}
}
template <FramebufferLike FB>
int x = static_cast<int>(radius);
int y = 0;
int err = 0;
auto plot_points = [&](int cx, int cy, int px, int py) {
fb.set_pixel(static_cast<std::size_t>(cx + px), static_cast<std::size_t>(cy + py), color, orientation);
fb.set_pixel(static_cast<std::size_t>(cx - px), static_cast<std::size_t>(cy + py), color, orientation);
fb.set_pixel(static_cast<std::size_t>(cx + px), static_cast<std::size_t>(cy - py), color, orientation);
fb.set_pixel(static_cast<std::size_t>(cx - px), static_cast<std::size_t>(cy - py), color, orientation);
fb.set_pixel(static_cast<std::size_t>(cx + py), static_cast<std::size_t>(cy + px), color, orientation);
fb.set_pixel(static_cast<std::size_t>(cx - py), static_cast<std::size_t>(cy + px), color, orientation);
fb.set_pixel(static_cast<std::size_t>(cx + py), static_cast<std::size_t>(cy - px), color, orientation);
fb.set_pixel(static_cast<std::size_t>(cx - py), static_cast<std::size_t>(cy - px), color, orientation);
};
auto fill_horizontal_line = [&](int cx, int cy, int px, int py) {
for (int fx = -px; fx <= px; ++fx) {
fb.set_pixel(static_cast<std::size_t>(cx + fx), static_cast<std::size_t>(cy + py), color, orientation);
fb.set_pixel(static_cast<std::size_t>(cx + fx), static_cast<std::size_t>(cy - py), color, orientation);
}
for (int fx = -py; fx <= py; ++fx) {
fb.set_pixel(static_cast<std::size_t>(cx + fx), static_cast<std::size_t>(cy + px), color, orientation);
fb.set_pixel(static_cast<std::size_t>(cx + fx), static_cast<std::size_t>(cy - px), color, orientation);
}
}
};
while (x >= y) {
plot_points(static_cast<int>(center.x), static_cast<int>(center.y), x, y);
fill_horizontal_line(static_cast<int>(center.x), static_cast<int>(center.y), x, y);
if (err <= 0) {
y += 1;
err += 2 * y + 1;
}
if (err > 0) {
x -= 1;
err -= 2 * x + 1;
}
}
}
template <FramebufferLike FB>
std::size_t cursor_x = pos.x;
std::size_t cursor_y = pos.y;
const auto &metrics = font.metrics();
const auto font_width = metrics.width;
const auto font_height = metrics.height;
const auto width_bytes = (font_width % 8 == 0) ? (font_width / 8) : ((font_width / 8) + 1);
for (char c : text) {
auto bitmap = font.char_data(c);
if (bitmap.empty()) {
continue;
}
for (std::size_t j = 0; j < font_height; ++j) {
for (std::size_t i = 0; i < font_width; ++i) {
std::size_t byte_idx = (j * width_bytes) + (i / 8);
if (byte_idx >= bitmap.size()) {
break;
}
std::uint8_t byte = bitmap[byte_idx];
bool is_set = (byte & (0x80 >> (i % 8))) != 0;
Color pixel_color = is_set ? foreground : background;
fb.set_pixel(cursor_x + i, cursor_y + j, pixel_color, orientation);
}
}
cursor_x += font_width;
}
}
template <FramebufferLike FB>
auto Graphics::draw_bitmap(FB &fb, Point pos, std::span<const std::uint8_t> data, std::size_t w, std::size_t h,
std::size_t target_w, std::size_t target_h,
Orientation orientation) ->
void {
std::size_t tw = (target_w > 0) ? target_w : w;
std::size_t th = (target_h > 0) ? target_h : h;
float scale_x = static_cast<float>(w) / static_cast<float>(tw);
float scale_y = static_cast<float>(h) / static_cast<float>(th);
for (std::size_t y = 0; y < th; ++y) {
for (std::size_t x = 0; x < tw; ++x) {
auto src_x = static_cast<std::size_t>(static_cast<float>(x) * scale_x);
auto src_y = static_cast<std::size_t>(static_cast<float>(y) * scale_y);
std::size_t idx = (src_y * w) + src_x;
if (idx < data.size()) {
fb.set_pixel(pos.x + x, pos.y + y, color, orientation);
}
}
}
}
}
static auto draw_line(FB &fb, Point start, Point end, LineStyle style, Color color, Orientation orientation) -> void
Draw a line on the framebuffer.
Definition graphics.hpp:171
static auto draw_circle(FB &fb, Point center, std::size_t radius, LineStyle style, Color color, DrawFill fill, Orientation orientation) -> void
Draw a circle on the framebuffer.
Definition graphics.hpp:265
static auto draw_rectangle(FB &fb, Point top_left, Point bottom_right, LineStyle style, Color color, DrawFill fill, Orientation orientation) -> void
Draw a rectangle on the framebuffer.
Definition graphics.hpp:226
static auto draw_bitmap(FB &fb, Point pos, std::span< const std::uint8_t > data, std::size_t w, std::size_t h, std::size_t target_w, std::size_t target_h, Orientation orientation) -> void
Draw a bitmap on the framebuffer.
Definition graphics.hpp:376
static auto draw_text(FB &fb, Point pos, std::string_view text, const Font &font, Color foreground, Color background, Orientation orientation) -> void
Draw text string on the framebuffer.
Definition graphics.hpp:328
Bitmap font rendering for e-paper displays.
DrawFill
Definition types.hpp:151
@ Full
Draw filled shape (solid interior)
Orientation
Definition types.hpp:66
Color
Definition types.hpp:32
@ White
White (or lightest gray in grayscale modes)
@ Black
Black (or darkest gray in grayscale modes)
LineStyle
Definition types.hpp:120
@ Solid
Continuous solid line.