TinyGPU
Loading...
Searching...
No Matches
SurfaceBase.h
Go to the documentation of this file.
1#pragma once
2#include <stddef.h>
3#include <stdint.h>
4
5#include <algorithm>
6#include <vector>
7
8#include "IFont.h"
9#include "ISurface.h"
10#include "LinePrinter.h"
11
12namespace tinygpu {
13
14/**
15 * @brief Base class for 2D surfaces with drawing and text rendering support.
16 *
17 * SurfaceBase provides common logic for managing surface dimensions, font
18 * handling, and basic geometry operations. It is intended to be subclassed by
19 * concrete surface implementations that provide pixel storage and access (e.g.,
20 * Surface, SurfaceMonochrome).
21 *
22 * @tparam PixelT The pixel color type (e.g., RGB565, bool, etc.)
23 */
24template <typename PixelT>
25class SurfaceBase : public ISurface<PixelT> {
26 public:
27 /// Default constructor.
28 SurfaceBase() = default;
29
30 /// Construct with width, height, and font.
31 SurfaceBase(size_t width, size_t height, IFont<PixelT>& font) : font_(&font) {
32 setFont(font);
33 width_ = width;
34 height_ = height;
35 }
36
37 /// Initializes the surface by resizing it to the current dimensions.
38 bool begin() {
39 return resize(width_, height_);
40 }
41
42 /// Clears the surface and releases any allocated resources.
43 void end() {
44 resizeBuffer(0, 0);
45 }
46
47 /// Virtual destructor.
48 ~SurfaceBase() override = default;
49
50 /// Abstract method: set a pixel at (x, y) to the specified color.
51 void setPixel(size_t x, size_t y, PixelT color) override = 0;
52
53 /// Abstract method: get the pixel color at (x, y).
54 PixelT getPixel(size_t x, size_t y) const override = 0;
55
56 /// Set the font for text rendering.
57 void setFont(IFont<PixelT>& font) {
58 linePrinter_.setFont(font);
59 font_ = &font;
60 }
61
62 /// Get the current font (mutable).
63 IFont<PixelT>& font() override { return *font_; }
64
65 /// Get the current font (const).
66 const IFont<PixelT>& font() const { return *font_; }
67
68 /// Resize the surface to new dimensions.
69 bool resize(size_t newWidth, size_t newHeight) override {
70 width_ = newWidth;
71 height_ = newHeight;
72 return resizeBuffer(newWidth, newHeight);
73 }
74
75 /// Get the width of the surface.
76 size_t width() const override { return width_; }
77
78 /// Get the height of the surface.
79 size_t height() const override { return height_; }
80
81 // Pixel access (to be implemented by derived classes)
82 virtual bool resizeBuffer(size_t, size_t) = 0;
83
84 // Drawing/geometry logic (now generic, uses setPixel/getPixel)
85 void clear(PixelT color = PixelT()) override {
86 for (size_t y = 0; y < height_; ++y) {
87 for (size_t x = 0; x < width_; ++x) {
88 setPixel(x, y, color);
89 }
90 }
91 }
92
93 /// Scroll the surface content by (dx, dy) pixels.
94 void scroll(int dx, int dy) {
95 if (dx == 0 && dy == 0) return;
96 Vector<PixelT> newBuffer(width_ * height_);
97 for (size_t y = 0; y < height_; ++y) {
98 for (size_t x = 0; x < width_; ++x) {
99 int srcX = (int)x + dx;
100 int srcY = (int)y + dy;
101 PixelT color = (srcX >= 0 && srcX < (int)width_ && srcY >= 0 &&
102 srcY < (int)height_)
103 ? getPixel(srcX, srcY)
104 : PixelT();
105 newBuffer[y * width_ + x] = color;
106 }
107 }
108 // Copy new buffer back to the surface
109 for (size_t i = 0; i < newBuffer.size(); ++i) {
110 setPixel(i % width_, i / width_, newBuffer[i]);
111 }
112 }
113
114 /// Draw a line from (x0, y0) to (x1, y1) with the given color.
115 void drawLine(size_t x0, size_t y0, size_t x1, size_t y1,
116 PixelT color) override {
117 int dx = std::abs((int)x1 - (int)x0), sx = x0 < x1 ? 1 : -1;
118 int dy = -std::abs((int)y1 - (int)y0), sy = y0 < y1 ? 1 : -1;
119 int err = dx + dy, e2;
120 while (true) {
121 setPixelClipped(x0, y0, color);
122 if (x0 == x1 && y0 == y1) break;
123 e2 = 2 * err;
124 if (e2 >= dy) {
125 err += dy;
126 x0 += sx;
127 }
128 if (e2 <= dx) {
129 err += dx;
130 y0 += sy;
131 }
132 }
133 }
134
135 /// Draw a rectangle outline at (x, y) with width w, height h, and color.
136 void drawRect(size_t x, size_t y, size_t w, size_t h, PixelT color) override {
137 if (w == 0 || h == 0) return;
138 size_t x1 = x + w - 1, y1 = y + h - 1;
139 drawLine(x, y, x1, y, color);
140 drawLine(x, y1, x1, y1, color);
141 drawLine(x, y, x, y1, color);
142 drawLine(x1, y, x1, y1, color);
143 }
144
145 /// Fill a rectangle at (x, y) with width w, height h, and color.
146 void fillRect(size_t x, size_t y, size_t w, size_t h, PixelT color) override {
147 if (w == 0 || h == 0) return;
148 size_t endX = std::min(x + w, width_);
149 size_t endY = std::min(y + h, height_);
150 for (size_t yy = y; yy < endY; ++yy) {
151 for (size_t xx = x; xx < endX; ++xx) {
152 setPixel(xx, yy, color);
153 }
154 }
155 }
156
157 /// Draw a circle outline centered at (x, y) with radius r and color.
158 void drawCircle(size_t x, size_t y, size_t r, PixelT color) override {
159 int offsetX = r, offsetY = 0, decision = 1 - (int)offsetX;
160 while (offsetX >= offsetY) {
161 setPixelClipped(x + offsetX, y + offsetY, color);
162 setPixelClipped(x + offsetY, y + offsetX, color);
163 setPixelClipped(x - offsetY, y + offsetX, color);
164 setPixelClipped(x - offsetX, y + offsetY, color);
165 setPixelClipped(x - offsetX, y - offsetY, color);
166 setPixelClipped(x - offsetY, y - offsetX, color);
167 setPixelClipped(x + offsetY, y - offsetX, color);
168 setPixelClipped(x + offsetX, y - offsetY, color);
169 ++offsetY;
170 if (decision <= 0)
171 decision += 2 * offsetY + 1;
172 else {
173 --offsetX;
174 decision += 2 * (offsetY - offsetX) + 1;
175 }
176 }
177 }
178
179 /// Fill a circle centered at (x, y) with radius r and color.
180 void fillCircle(size_t x, size_t y, size_t r, PixelT color) override {
181 int offsetX = r, offsetY = 0, decision = 1 - (int)offsetX;
182 while (offsetX >= offsetY) {
183 drawHorizontalLineClipped(x - offsetX, x + offsetX, y + offsetY, color);
184 drawHorizontalLineClipped(x - offsetX, x + offsetX, y - offsetY, color);
185 drawHorizontalLineClipped(x - offsetY, x + offsetY, y + offsetX, color);
186 drawHorizontalLineClipped(x - offsetY, x + offsetY, y - offsetX, color);
187 ++offsetY;
188 if (decision <= 0)
189 decision += 2 * offsetY + 1;
190 else {
191 --offsetX;
192 decision += 2 * (offsetY - offsetX) + 1;
193 }
194 }
195 }
196
197 /// Draw a sprite at (x, y), skipping pixels matching invisibleColor.
198 void drawSprite(size_t x, size_t y, const ISurface<PixelT>& sprite,
199 PixelT invisibleColor = PixelT()) override {
200 for (size_t spriteY = 0; spriteY < sprite.height(); ++spriteY) {
201 for (size_t spriteX = 0; spriteX < sprite.width(); ++spriteX) {
202 PixelT color = sprite.getPixel(spriteX, spriteY);
203 if (color != invisibleColor) {
204 setPixelClipped(x + spriteX, y + spriteY, color);
205 }
206 }
207 }
208 }
209
210 /// Clear the region covered by a sprite at (x, y) to clearColor.
211 void clearSprite(size_t x, size_t y, ISurface<PixelT>& sprite,
212 PixelT clearColor = PixelT()) override {
213 for (size_t spriteY = 0; spriteY < sprite.height(); ++spriteY) {
214 for (size_t spriteX = 0; spriteX < sprite.width(); ++spriteX) {
215 setPixelClipped(x + spriteX, y + spriteY, clearColor);
216 }
217 }
218 }
219
220 /// Copy the framebuffer region at (x, y) into the sprite.
221 void copySprite(size_t x, size_t y, const ISurface<PixelT>& sprite) override {
222 for (size_t spriteY = 0; spriteY < sprite.height(); ++spriteY) {
223 for (size_t spriteX = 0; spriteX < sprite.width(); ++spriteX) {
224 size_t framebufferX = x + spriteX;
225 size_t framebufferY = y + spriteY;
226 PixelT color = (framebufferX < width_ && framebufferY < height_)
227 ? getPixel(framebufferX, framebufferY)
228 : PixelT();
229 const_cast<ISurface<PixelT>&>(sprite).setPixel(spriteX, spriteY, color);
230 }
231 }
232 }
233
234 /// Draw text at (x, y) with foreground/background color and formatting
235 /// options.
236 void drawText(int16_t x, int16_t y, const char* text, PixelT foreground,
237 PixelT background = PixelT(), bool opaque = false,
238 uint8_t scale = 1, uint8_t spacing = 1,
239 uint8_t lineSpacing = 1) override {
240 font_->drawText(*this, x, y, text, foreground, background, opaque, scale,
241 spacing, lineSpacing);
242 }
243
244 /// Get the line printer for this surface.
246 linePrinter_.setFont(*font_);
247 linePrinter_.setTarget(*this);
248 return linePrinter_;
249 }
250
251 /// Returns true if (x, y) is within the surface bounds.
252 bool isInBounds(size_t x, size_t y) const {
253 return x < width_ && y < height_;
254 }
255 /// Set a pixel only if (x, y) is in bounds.
256 void setPixelClipped(size_t x, size_t y, PixelT color) {
257 if (isInBounds(x, y)) setPixel(x, y, color);
258 }
259 /// Draw a horizontal line from x0 to x1 at y, clipped to bounds.
260 void drawHorizontalLineClipped(int x0, int x1, int y, PixelT color) {
261 if (y < 0 || (size_t)y >= height_) return;
262 int startX = std::max(0, std::min(x0, x1));
263 int endX = std::min((int)width_ - 1, std::max(x0, x1));
264 if (startX > endX) return;
265 for (int x = startX; x <= endX; ++x) setPixel(x, y, color);
266 }
267
268 protected:
271 IFont<PixelT>* font_ = nullptr;
273};
274
275} // namespace tinygpu
Font rendering interface for TinyGPU-compatible framebuffers.
Definition: IFont.h:19
Helper for printing wrapped lines of text onto a TinyGPU target.
Definition: LinePrinter.h:17
Base class for 2D surfaces with drawing and text rendering support.
Definition: SurfaceBase.h:25
void drawLine(size_t x0, size_t y0, size_t x1, size_t y1, PixelT color) override
Draw a line from (x0, y0) to (x1, y1) with the given color.
Definition: SurfaceBase.h:115
void fillRect(size_t x, size_t y, size_t w, size_t h, PixelT color) override
Fill a rectangle at (x, y) with width w, height h, and color.
Definition: SurfaceBase.h:146
void setPixelClipped(size_t x, size_t y, PixelT color)
Set a pixel only if (x, y) is in bounds.
Definition: SurfaceBase.h:256
void clearSprite(size_t x, size_t y, ISurface< PixelT > &sprite, PixelT clearColor=PixelT()) override
Clear the region covered by a sprite at (x, y) to clearColor.
Definition: SurfaceBase.h:211
PixelT getPixel(size_t x, size_t y) const override=0
Abstract method: get the pixel color at (x, y).
IFont< PixelT > * font_
Definition: SurfaceBase.h:271
void drawSprite(size_t x, size_t y, const ISurface< PixelT > &sprite, PixelT invisibleColor=PixelT()) override
Draw a sprite at (x, y), skipping pixels matching invisibleColor.
Definition: SurfaceBase.h:198
void drawCircle(size_t x, size_t y, size_t r, PixelT color) override
Draw a circle outline centered at (x, y) with radius r and color.
Definition: SurfaceBase.h:158
void setFont(IFont< PixelT > &font)
Set the font for text rendering.
Definition: SurfaceBase.h:57
size_t width_
Definition: SurfaceBase.h:269
IFont< PixelT > & font() override
Get the current font (mutable).
Definition: SurfaceBase.h:63
bool begin()
Initializes the surface by resizing it to the current dimensions.
Definition: SurfaceBase.h:38
size_t height_
Definition: SurfaceBase.h:270
const IFont< PixelT > & font() const
Get the current font (const).
Definition: SurfaceBase.h:66
void drawRect(size_t x, size_t y, size_t w, size_t h, PixelT color) override
Draw a rectangle outline at (x, y) with width w, height h, and color.
Definition: SurfaceBase.h:136
size_t height() const override
Get the height of the surface.
Definition: SurfaceBase.h:79
SurfaceBase(size_t width, size_t height, IFont< PixelT > &font)
Construct with width, height, and font.
Definition: SurfaceBase.h:31
LinePrinter< PixelT > & linePrinter()
Get the line printer for this surface.
Definition: SurfaceBase.h:245
void clear(PixelT color=PixelT()) override
Clears the framebuffer with a single color.
Definition: SurfaceBase.h:85
void drawText(int16_t x, int16_t y, const char *text, PixelT foreground, PixelT background=PixelT(), bool opaque=false, uint8_t scale=1, uint8_t spacing=1, uint8_t lineSpacing=1) override
Definition: SurfaceBase.h:236
void setPixel(size_t x, size_t y, PixelT color) override=0
Abstract method: set a pixel at (x, y) to the specified color.
void fillCircle(size_t x, size_t y, size_t r, PixelT color) override
Fill a circle centered at (x, y) with radius r and color.
Definition: SurfaceBase.h:180
void scroll(int dx, int dy)
Scroll the surface content by (dx, dy) pixels.
Definition: SurfaceBase.h:94
void end()
Clears the surface and releases any allocated resources.
Definition: SurfaceBase.h:43
virtual bool resizeBuffer(size_t, size_t)=0
SurfaceBase()=default
Default constructor.
size_t width() const override
Get the width of the surface.
Definition: SurfaceBase.h:76
LinePrinter< PixelT > linePrinter_
Definition: SurfaceBase.h:272
~SurfaceBase() override=default
Virtual destructor.
bool resize(size_t newWidth, size_t newHeight) override
Resize the surface to new dimensions.
Definition: SurfaceBase.h:69
void copySprite(size_t x, size_t y, const ISurface< PixelT > &sprite) override
Copy the framebuffer region at (x, y) into the sprite.
Definition: SurfaceBase.h:221
void drawHorizontalLineClipped(int x0, int x1, int y, PixelT color)
Draw a horizontal line from x0 to x1 at y, clipped to bounds.
Definition: SurfaceBase.h:260
bool isInBounds(size_t x, size_t y) const
Returns true if (x, y) is within the surface bounds.
Definition: SurfaceBase.h:252
Definition: AVIWriter.h:9