TinyGPU
Loading...
Searching...
No Matches
ISurface.h
Go to the documentation of this file.
1#pragma once
2
3#include <math.h>
4#include <stddef.h>
5#include <stdint.h>
6
7
8#include "TinyGPU/Color/RGB565.h"
9
10namespace tinygpu {
11
12
13// Forward declaration of IFont to avoid circular dependency.
14template <typename RGB_T> class IFont;
15
16/**
17 * @brief Abstract framebuffer and drawing interface for TinyGPU surfaces.
18 *
19 * Implementations expose pixel access, primitive drawing, sprite operations,
20 * and text rendering through a common API.
21 *
22 * @note The coordinate x=0 and y=0 corresponds to the top-left corner of the
23 * surface. The x coordinate increases to the right and the y coordinate
24 * increases downwards.
25 */
26template <typename RGB_T = RGB565>
27class ISurface {
28 public:
29 /// Destroys the framebuffer interface.
30 virtual ~ISurface() = default;
31
32 /// Initializes the framebuffer surface.
33 virtual bool begin() = 0;
34 /// Closes the framebuffer and releases resources.
35 virtual void end() = 0;
36
37 /// Resizes the framebuffer surface.
38 virtual bool resize(size_t newWidth, size_t newHeight) = 0;
39 /// Sets a pixel at the given position.
40 virtual void setPixel(size_t x, size_t y, RGB_T color) = 0;
41 /// Returns the pixel at the given position.
42 virtual RGB_T getPixel(size_t x, size_t y) const = 0;
43 /// Scrolls the framebuffer content by the specified offsets.
44 virtual void scroll(int dx, int dy) = 0;
45 /// Clears the framebuffer with a single color.
46 virtual void clear(RGB_T color = RGB_T(0)) = 0;
47 /// Draws a line between two points.
48 virtual void drawLine(size_t x0, size_t y0, size_t x1, size_t y1,
49 RGB_T color) = 0;
50 /// Draws a rectangle outline.
51 virtual void drawRect(size_t x, size_t y, size_t w, size_t h,
52 RGB_T color) = 0;
53 /// Fills a rectangle.
54 virtual void fillRect(size_t x, size_t y, size_t w, size_t h,
55 RGB_T color) = 0;
56 /// Draws a circle outline.
57 virtual void drawCircle(size_t x, size_t y, size_t r, RGB_T color) = 0;
58 /// Fills a circle.
59 virtual void fillCircle(size_t x, size_t y, size_t r, RGB_T color) = 0;
60
61 /// Draws a rectangle outline with rounded corners.
62 ///
63 /// @param radius Corner radius in pixels; clamped to min(w,h)/2.
64 ///
65 /// Implemented here (not left pure virtual) purely in terms of drawLine(),
66 /// setPixel() and contains(), so every existing ISurface implementation
67 /// gets a correct default for free. Override only to provide a faster,
68 /// hardware-specific version.
69 virtual void drawRoundRect(size_t x, size_t y, size_t w, size_t h,
70 size_t radius, RGB_T color) {
71 if (w == 0 || h == 0) return;
72 const size_t maxRadius = (w < h ? w : h) / 2;
73 if (radius > maxRadius) radius = maxRadius;
74 if (radius == 0) {
75 drawRect(x, y, w, h, color);
76 return;
77 }
78
79 drawLine(x + radius, y, x + w - 1 - radius, y, color);
80 drawLine(x + radius, y + h - 1, x + w - 1 - radius, y + h - 1, color);
81 drawLine(x, y + radius, x, y + h - 1 - radius, color);
82 drawLine(x + w - 1, y + radius, x + w - 1, y + h - 1 - radius, color);
83
84 plotRoundRectCorner(x + radius, y + radius, radius,
85 RoundRectCorner::kTopLeft, color);
86 plotRoundRectCorner(x + w - 1 - radius, y + radius, radius,
87 RoundRectCorner::kTopRight, color);
88 plotRoundRectCorner(x + w - 1 - radius, y + h - 1 - radius, radius,
89 RoundRectCorner::kBottomRight, color);
90 plotRoundRectCorner(x + radius, y + h - 1 - radius, radius,
91 RoundRectCorner::kBottomLeft, color);
92 }
93
94 /// Fills a rectangle with rounded corners.
95 ///
96 /// @param radius Corner radius in pixels; clamped to min(w,h)/2.
97 ///
98 /// Row-scan fill: each row's horizontal inset comes from the corner
99 /// circle's equation (0 for the straight middle rows), then the existing
100 /// fillRect() draws that single row. Produces the same shape as the
101 /// classic "middle band + two rounded end caps" decomposition, just
102 /// expressed as one loop. Implemented here (not pure virtual) so every
103 /// existing ISurface implementation gets it for free.
104 virtual void fillRoundRect(size_t x, size_t y, size_t w, size_t h,
105 size_t radius, RGB_T color) {
106 if (w == 0 || h == 0) return;
107 const size_t maxRadius = (w < h ? w : h) / 2;
108 if (radius > maxRadius) radius = maxRadius;
109 if (radius == 0) {
110 fillRect(x, y, w, h, color);
111 return;
112 }
113
114 for (size_t row = 0; row < h; ++row) {
115 size_t dy = 0;
116 if (row < radius) {
117 dy = radius - row;
118 } else if (row >= h - radius) {
119 dy = radius - (h - 1 - row);
120 }
121
122 size_t inset = 0;
123 if (dy > 0) {
124 const double dx = sqrt(static_cast<double>(radius) * radius -
125 static_cast<double>(dy) * dy);
126 inset = radius - static_cast<size_t>(dx);
127 }
128
129 const size_t rowWidth = (w > 2 * inset) ? (w - 2 * inset) : 0;
130 if (rowWidth > 0) {
131 fillRect(x + inset, y + row, rowWidth, 1, color);
132 }
133 }
134 }
135
136 /// Draws a stroked arc around center (cx, cy) with radius r, sweeping from
137 /// startDeg to endDeg (degrees, clockwise on screen since y increases
138 /// downward; 0 deg = +X/3 o'clock direction; pass endDeg < startDeg to
139 /// wrap through 0, e.g. 350 -> 370). thickness draws that many concentric
140 /// 1px radii ending at r. Used for circular progress/spinner widgets.
141 virtual void drawArc(size_t cx, size_t cy, size_t r, float startDeg,
142 float endDeg, RGB_T color, uint8_t thickness = 1) {
143 if (r == 0) return;
144 if (thickness == 0) thickness = 1;
145 const size_t minRadius =
146 (static_cast<size_t>(thickness) - 1 >= r)
147 ? 1
148 : (r - (static_cast<size_t>(thickness) - 1));
149
150 constexpr float kPi = 3.14159265358979323846f;
151 float startRad = startDeg * kPi / 180.0f;
152 float endRad = endDeg * kPi / 180.0f;
153 if (endRad < startRad) endRad += 2.0f * kPi;
154
155 for (size_t radius = minRadius; radius <= r; ++radius) {
156 const float step = 1.0f / static_cast<float>(radius);
157 for (float angle = startRad; angle <= endRad; angle += step) {
158 const long px =
159 static_cast<long>(cx) +
160 static_cast<long>(lroundf(cosf(angle) * static_cast<float>(radius)));
161 const long py =
162 static_cast<long>(cy) +
163 static_cast<long>(lroundf(sinf(angle) * static_cast<float>(radius)));
164 if (px >= 0 && py >= 0 &&
165 contains(static_cast<size_t>(px), static_cast<size_t>(py))) {
166 setPixel(static_cast<size_t>(px), static_cast<size_t>(py), color);
167 }
168 }
169 }
170 }
171
172 /// Draws a sprite using an optional transparent color.
173 virtual void drawSprite(size_t x, size_t y, const ISurface<RGB_T>& sprite,
174 RGB_T invisibleColor = RGB_T(0)) = 0;
175 /// Clears the area covered by a sprite.
176 virtual void clearSprite(size_t x, size_t y, ISurface<RGB_T>& sprite,
177 RGB_T clearColor = RGB_T(0)) = 0;
178 /// Copies pixels from the framebuffer into a sprite.
179 virtual void copySprite(size_t x, size_t y, const ISurface<RGB_T>& sprite) = 0;
180 /// Returns the framebuffer width in pixels.
181 virtual size_t width() const = 0;
182 /// Returns the framebuffer height in pixels.
183 virtual size_t height() const = 0;
184 /// Draws UTF-8 text using the configured font.
185 virtual void drawText(int16_t x, int16_t y, const char* text,
186 RGB_T foreground, RGB_T background = RGB_T(0),
187 bool opaque = false, uint8_t scale = 1,
188 uint8_t spacing = 1, uint8_t lineSpacing = 1) = 0;
189
190 /// Returns the currently set font for text rendering.
191 virtual IFont<RGB_T>& font() = 0;
192 /// Provides access to the framebuffer as a byte buffer.
193 virtual const uint8_t* data() const = 0;
194 /// Provides the total data size in bytes.
195 virtual size_t size() const = 0;
196 ///Checks if the given coordinates are within the surface bounds.
197 virtual bool contains(size_t x, size_t y) = 0;
198
199 /// Restricts all subsequent drawing to the intersection of
200 /// (x, y, w, h) and whatever clip rect is already active - a nested
201 /// pushClipRect() call narrows further, never widens. Every drawing
202 /// primitive on this surface (fillRect(), drawLine(), drawCircle(),
203 /// drawSprite(), drawText(), ...) respects the currently active clip;
204 /// reads (getPixel(), copySprite()) do not. Pair with a matching
205 /// popClipRect() - typically wrapping one widget/container's own
206 /// children so nothing they draw can spill past that widget's bounds.
207 ///
208 /// Default: a no-op, so an ISurface implementation that doesn't
209 /// override this (anything not deriving from SurfaceBase, which
210 /// implements the actual clip stack) simply never clips - existing
211 /// implementations keep drawing exactly as before.
212 virtual void pushClipRect(size_t x, size_t y, size_t w, size_t h) {
213 (void)x;
214 (void)y;
215 (void)w;
216 (void)h;
217 }
218
219 /// Restores whatever clip rect was active before the matching
220 /// pushClipRect() call. Popping with no active push is a no-op.
221 virtual void popClipRect() {}
222
223 /// True if (x, y) lies within the currently active clip rect - always
224 /// true if pushClipRect() was never called (see the default there).
225 /// Exposed so code doing its own pixel math (drawArc(), the
226 /// plotRoundRectCorner() helper below) can respect the same clip
227 /// primitive drawing calls do.
228 virtual bool isClipVisible(size_t x, size_t y) const {
229 (void)x;
230 (void)y;
231 return true;
232 }
233
234 private:
235 /// Which corner a plotRoundRectCorner() arc belongs to; selects which
236 /// quadrant of the midpoint-circle loop's symmetric points to plot.
237 enum class RoundRectCorner : uint8_t {
238 kTopLeft,
239 kTopRight,
240 kBottomRight,
241 kBottomLeft
242 };
243
244 /// Plots one quarter-circle arc for a drawRoundRect() corner, using the
245 /// same midpoint-circle loop as SurfaceBase::drawCircle, restricted to the
246 /// single quadrant matching `corner` so the 4 arcs tile the full rounded
247 /// rectangle outline with no gaps or overlap.
248 void plotRoundRectCorner(size_t cx, size_t cy, size_t r,
249 RoundRectCorner corner, RGB_T color) {
250 long offsetX = static_cast<long>(r);
251 long offsetY = 0;
252 long decision = 1 - offsetX;
253
254 while (offsetX >= offsetY) {
255 long px1, py1, px2, py2;
256 switch (corner) {
257 case RoundRectCorner::kTopLeft:
258 px1 = static_cast<long>(cx) - offsetX;
259 py1 = static_cast<long>(cy) - offsetY;
260 px2 = static_cast<long>(cx) - offsetY;
261 py2 = static_cast<long>(cy) - offsetX;
262 break;
263 case RoundRectCorner::kTopRight:
264 px1 = static_cast<long>(cx) + offsetY;
265 py1 = static_cast<long>(cy) - offsetX;
266 px2 = static_cast<long>(cx) + offsetX;
267 py2 = static_cast<long>(cy) - offsetY;
268 break;
269 case RoundRectCorner::kBottomRight:
270 px1 = static_cast<long>(cx) + offsetX;
271 py1 = static_cast<long>(cy) + offsetY;
272 px2 = static_cast<long>(cx) + offsetY;
273 py2 = static_cast<long>(cy) + offsetX;
274 break;
275 case RoundRectCorner::kBottomLeft:
276 default:
277 px1 = static_cast<long>(cx) - offsetY;
278 py1 = static_cast<long>(cy) + offsetX;
279 px2 = static_cast<long>(cx) - offsetX;
280 py2 = static_cast<long>(cy) + offsetY;
281 break;
282 }
283
284 if (px1 >= 0 && py1 >= 0 &&
285 contains(static_cast<size_t>(px1), static_cast<size_t>(py1))) {
286 setPixel(static_cast<size_t>(px1), static_cast<size_t>(py1), color);
287 }
288 if (px2 >= 0 && py2 >= 0 &&
289 contains(static_cast<size_t>(px2), static_cast<size_t>(py2))) {
290 setPixel(static_cast<size_t>(px2), static_cast<size_t>(py2), color);
291 }
292
293 ++offsetY;
294 if (decision <= 0) {
295 decision += 2 * offsetY + 1;
296 } else {
297 --offsetX;
298 decision += 2 * (offsetY - offsetX) + 1;
299 }
300 }
301 }
302};
303
304
305} // namespace tinygpu
Font rendering interface for TinyGPU-compatible framebuffers.
Definition: IFont.h:19
virtual ~ISurface()=default
Destroys the framebuffer interface.
virtual void fillRect(size_t x, size_t y, size_t w, size_t h, RGB_T color)=0
Fills a rectangle.
virtual size_t height() const =0
Returns the framebuffer height in pixels.
virtual void fillRoundRect(size_t x, size_t y, size_t w, size_t h, size_t radius, RGB_T color)
Definition: ISurface.h:104
virtual void drawRoundRect(size_t x, size_t y, size_t w, size_t h, size_t radius, RGB_T color)
Definition: ISurface.h:69
virtual void drawArc(size_t cx, size_t cy, size_t r, float startDeg, float endDeg, RGB_T color, uint8_t thickness=1)
Definition: ISurface.h:141
virtual void drawLine(size_t x0, size_t y0, size_t x1, size_t y1, RGB_T color)=0
Draws a line between two points.
virtual void copySprite(size_t x, size_t y, const ISurface< RGB_T > &sprite)=0
Copies pixels from the framebuffer into a sprite.
virtual void drawRect(size_t x, size_t y, size_t w, size_t h, RGB_T color)=0
Draws a rectangle outline.
virtual void fillCircle(size_t x, size_t y, size_t r, RGB_T color)=0
Fills a circle.
virtual bool contains(size_t x, size_t y)=0
Checks if the given coordinates are within the surface bounds.
virtual bool resize(size_t newWidth, size_t newHeight)=0
Resizes the framebuffer surface.
virtual bool isClipVisible(size_t x, size_t y) const
Definition: ISurface.h:228
virtual void clearSprite(size_t x, size_t y, ISurface< RGB_T > &sprite, RGB_T clearColor=RGB_T(0))=0
Clears the area covered by a sprite.
virtual void popClipRect()
Definition: ISurface.h:221
virtual void setPixel(size_t x, size_t y, RGB_T color)=0
Sets a pixel at the given position.
virtual void scroll(int dx, int dy)=0
Scrolls the framebuffer content by the specified offsets.
virtual void end()=0
Closes the framebuffer and releases resources.
virtual size_t size() const =0
Provides the total data size in bytes.
virtual void drawText(int16_t x, int16_t y, const char *text, RGB_T foreground, RGB_T background=RGB_T(0), bool opaque=false, uint8_t scale=1, uint8_t spacing=1, uint8_t lineSpacing=1)=0
Draws UTF-8 text using the configured font.
virtual void pushClipRect(size_t x, size_t y, size_t w, size_t h)
Definition: ISurface.h:212
virtual void clear(RGB_T color=RGB_T(0))=0
Clears the framebuffer with a single color.
virtual size_t width() const =0
Returns the framebuffer width in pixels.
virtual RGB_T getPixel(size_t x, size_t y) const =0
Returns the pixel at the given position.
virtual IFont< RGB_T > & font()=0
Returns the currently set font for text rendering.
virtual bool begin()=0
Initializes the framebuffer surface.
virtual void drawCircle(size_t x, size_t y, size_t r, RGB_T color)=0
Draws a circle outline.
virtual void drawSprite(size_t x, size_t y, const ISurface< RGB_T > &sprite, RGB_T invisibleColor=RGB_T(0))=0
Draws a sprite using an optional transparent color.
virtual const uint8_t * data() const =0
Provides access to the framebuffer as a byte buffer.
RGB color stored in 16-bit RGB565 format, byte-swapped from the conventional bit layout.
Definition: RGB565.h:24
Definition: DSIBusESP32.h:19