TinyGPU
Loading...
Searching...
No Matches
FrameBuffer.h
Go to the documentation of this file.
1
2#pragma once
3#include <algorithm>
4#include <cmath>
5#include <memory>
6
7#include "Surface.h"
8#include "TinyGPU/Vector.h"
10#include "TinyGPULogger.h"
11
12namespace tinygpu {
13
14/**
15 * @brief Framebuffer with sprite placement and background restoration support.
16 *
17 * The class extends TinyGPU with sprite bookkeeping so sprites can be added,
18 * moved, scaled, and removed while preserving the pixels behind them.
19 */
20template <typename RGB_T = RGB565, typename SurfaceT = Surface<RGB_T>>
21class FrameBuffer : public ISurface<RGB_T> {
22 public:
23 /**
24 * Tracks a sprite instance together with its saved background pixels.
25 *
26 * Each SpriteInfo stores the sprite position, transparent color, current
27 * sprite image, and a snapshot of the framebuffer region covered by it.
28 */
29 struct SpriteInfo {
30 size_t x = 0;
31 size_t y = 0;
34 RGB_T invisibleColor = RGB_T(0);
35 const ISurface<RGB_T>* sprite = nullptr;
38 IFont<RGB_T>& fontRef;
39
40 SpriteInfo(size_t startX, size_t startY,
41 const ISurface<RGB_T>& sourceSprite, RGB_T transparentColor,
42 IFont<RGB_T>& font)
43 : x(startX),
44 y(startY),
45 invisibleColor(transparentColor),
46 sprite(&sourceSprite),
47 originalPixels(sourceSprite.width(), sourceSprite.height(), font),
48 fontRef(font) {}
49
50 /// Returns the sprite image currently used for drawing.
51 const ISurface<RGB_T>& currentSprite() const {
52 return transformedSprite
53 ? static_cast<const ISurface<RGB_T>&>(*transformedSprite)
54 : *sprite;
55 }
56
57 /// Saves the background pixels currently covered by the sprite.
58 void saveOriginalPixels(ISurface<RGB_T>& framebuffer) {
59 originalPixels.resize(currentSprite().width(), currentSprite().height());
60 framebuffer.copySprite(x, y, originalPixels);
61 }
62
63 /// Set the maximum buffer size for transformedSprite and allocate buffer.
64 void setMaxSize(size_t maxX, size_t maxY) {
65 maxWidth = maxX;
66 maxHeight = maxY;
67 if (!transformedSprite) {
68 transformedSprite =
69 std::make_unique<SurfaceT>(maxWidth, maxHeight, fontRef);
70 transformedSprite->begin();
71 }
72 }
73
74 /// Replaces the current transformed sprite image, using only the allocated
75 /// buffer.
76 void setTransformedSprite(SurfaceT&& newSprite) {
77 if (!transformedSprite) {
78 // If max size not set, use current size as max
79 maxWidth = newSprite.width();
80 maxHeight = newSprite.height();
81 transformedSprite =
82 std::make_unique<SurfaceT>(maxWidth, maxHeight, newSprite.font());
83 transformedSprite->begin();
84 }
85 // Only copy the region that fits
86 size_t copyWidth = std::min(maxWidth, newSprite.width());
87 size_t copyHeight = std::min(maxHeight, newSprite.height());
88 for (size_t y = 0; y < copyHeight; ++y) {
89 for (size_t x = 0; x < copyWidth; ++x) {
90 transformedSprite->setPixel(x, y, newSprite.getPixel(x, y));
91 }
92 }
93 // Optionally clear the rest if newSprite is smaller than max
94 for (size_t y = copyHeight; y < maxHeight; ++y) {
95 for (size_t x = 0; x < maxWidth; ++x) {
96 transformedSprite->setPixel(x, y, RGB_T(0));
97 }
98 }
99 for (size_t y = 0; y < copyHeight; ++y) {
100 for (size_t x = copyWidth; x < maxWidth; ++x) {
101 transformedSprite->setPixel(x, y, RGB_T(0));
102 }
103 }
104 }
105 };
106
107 /// Creates an empty framebuffer.
108 FrameBuffer() = default;
109
110 /// Creates a framebuffer with the specified size and font.
111 FrameBuffer(size_t width, size_t height, IFont<RGB_T>& font)
112 : surface_(width, height, font) {}
113
114 bool begin() override { return surface_.begin(); }
115 void end() override { surface_.end(); }
116
117 /// Adds a sprite to the framebuffer and draws it at the given position.
118 SpriteInfo& addSprite(size_t x, size_t y, const ISurface<RGB_T>& sprite,
119 RGB_T invisibleColor = RGB_T(0)) {
120 TinyGPULogger.log(TinyGPULoggerClass::INFO, "Adding sprite at (%zu, %zu)",
121 x, y);
122 auto info = std::make_unique<SpriteInfo>(x, y, sprite, invisibleColor,
123 activeFont());
124
125 info->saveOriginalPixels(surface_);
126 surface_.drawSprite(x, y, info->currentSprite(), invisibleColor);
127
128 sprites_.push_back(std::move(info));
129 return *sprites_.back();
130 }
131
132 /// Adds a sprite with a preallocated max buffer size for transformations.
133 SpriteInfo& addSprite(size_t x, size_t y, size_t maxX, size_t maxY,
134 const ISurface<RGB_T>& sprite,
135 RGB_T invisibleColor = RGB_T(0)) {
136 TinyGPULogger.log(TinyGPULoggerClass::INFO,
137 "Adding sprite at (%zu, %zu) with max size (%zu, %zu)", x,
138 y, maxX, maxY);
139 auto info = std::make_unique<SpriteInfo>(x, y, sprite, invisibleColor,
140 activeFont());
141 info->setMaxSize(maxX, maxY);
142 info->saveOriginalPixels(surface_);
143 surface_.drawSprite(x, y, info->currentSprite(), invisibleColor);
144 sprites_.push_back(std::move(info));
145 return *sprites_.back();
146 }
147
148 /// Removes a sprite and restores the pixels behind it.
149 void removeSprite(SpriteInfo& spriteInfo) {
150 TinyGPULogger.log(TinyGPULoggerClass::INFO, "Removing sprite at (%zu, %zu)",
151 spriteInfo.x, spriteInfo.y);
152 restoreOriginalPixels(spriteInfo);
153 auto it = findSprite(spriteInfo);
154 if (it != sprites_.end()) {
155 sprites_.erase(it);
156 }
157 }
158
159 /// Moves a sprite to a new position and redraws it.
160 void moveSprite(SpriteInfo& spriteInfo, size_t newX, size_t newY) {
161 TinyGPULogger.log(TinyGPULoggerClass::INFO,
162 "Moving sprite from (%zu, %zu) to (%zu, %zu)",
163 spriteInfo.x, spriteInfo.y, newX, newY);
164 if (spriteInfo.x == newX && spriteInfo.y == newY) {
165 return;
166 }
167
168 const Rect oldBounds{spriteInfo.x, spriteInfo.y,
169 spriteInfo.currentSprite().width(),
170 spriteInfo.currentSprite().height()};
171 const Rect newBounds{newX, newY, spriteInfo.currentSprite().width(),
172 spriteInfo.currentSprite().height()};
173 const Rect overlap = intersect(oldBounds, newBounds);
174
175 restoreExposedPixels(spriteInfo, oldBounds, overlap);
176 Surface<RGB_T> movedBackground =
177 captureUpdatedBackground(spriteInfo, oldBounds, newBounds, overlap);
178
179 spriteInfo.x = newX;
180 spriteInfo.y = newY;
181 spriteInfo.originalPixels = std::move(movedBackground);
182 surface_.drawSprite(spriteInfo.x, spriteInfo.y, spriteInfo.currentSprite(),
183 spriteInfo.invisibleColor);
184 }
185
186 /// Scales a sprite image and redraws it at its current position.
187 void scaleSprite(SpriteInfo& spriteInfo, float scale) {
189 "Scaling sprite at (%zu, %zu) by %.2f", spriteInfo.x,
190 spriteInfo.y, scale);
192 spriteInfo,
193 scaleSpriteImage(spriteInfo.currentSprite(), scale, activeFont()));
194 }
195
196 /// Rotates a sprite image and redraws it at its current position.
197 void rotateSprite(SpriteInfo& spriteInfo, float angleDegrees) {
199 "Rotating sprite at (%zu, %zu) by %.2f degrees",
200 spriteInfo.x, spriteInfo.y, angleDegrees);
202 spriteInfo, rotateSpriteImage(spriteInfo.currentSprite(), angleDegrees,
203 spriteInfo.invisibleColor, activeFont()));
204 }
205
206 /// ISurface<RGB_T> interface delegation
207 void setPixel(size_t x, size_t y, RGB_T color) override {
208 surface_.setPixel(x, y, color);
209 }
210 /// Returns the pixel at the given position.
211 RGB_T getPixel(size_t x, size_t y) const override {
212 return surface_.getPixel(x, y);
213 }
214 /// Resizes the framebuffer surface.
215 bool resize(size_t w, size_t h) override { return surface_.resize(w, h); }
216 /// Returns the framebuffer width in pixels.
217 size_t width() const override { return surface_.width(); }
218 /// Returns the framebuffer height in pixels.
219 size_t height() const override { return surface_.height(); }
220 /// Sets the font for text rendering.
221 void setFont(IFont<RGB_T>& font) { surface_.setFont(font); }
222 /// Returns the currently set font for text rendering.
223 IFont<RGB_T>& font() { return surface_.font(); }
224 /// Clears the framebuffer with a single color.
225 void clear(RGB_T color = RGB_T()) { surface_.clear(color); }
226 /// Scrolls the framebuffer content by the specified offsets.
227 void scroll(int dx, int dy) override { surface_.scroll(dx, dy); }
228 /// Draws a line between two points.
229 void drawLine(size_t x0, size_t y0, size_t x1, size_t y1, RGB_T color) {
230 surface_.drawLine(x0, y0, x1, y1, color);
231 }
232 /// Draws a rectangle outline.
233 void drawRect(size_t x, size_t y, size_t w, size_t h, RGB_T color) {
234 surface_.drawRect(x, y, w, h, color);
235 }
236 /// Fills a rectangle.
237 void fillRect(size_t x, size_t y, size_t w, size_t h, RGB_T color) {
238 surface_.fillRect(x, y, w, h, color);
239 }
240 /// Draws a circle outline.
241 void drawCircle(size_t x, size_t y, size_t r, RGB_T color) {
242 surface_.drawCircle(x, y, r, color);
243 }
244 /// Fills a circle.
245 void fillCircle(size_t x, size_t y, size_t r, RGB_T color) {
246 surface_.fillCircle(x, y, r, color);
247 }
248 /// Draws a sprite.
249 void drawSprite(size_t x, size_t y, const ISurface<RGB_T>& sprite,
250 RGB_T invisibleColor = RGB_T()) {
251 surface_.drawSprite(x, y, sprite, invisibleColor);
252 }
253 /// Clears a sprite.
254 void clearSprite(size_t x, size_t y, ISurface<RGB_T>& sprite,
255 RGB_T clearColor = RGB_T()) {
256 surface_.clearSprite(x, y, sprite, clearColor);
257 }
258 /// Copies a sprite.
259 void copySprite(size_t x, size_t y, const ISurface<RGB_T>& sprite) {
260 surface_.copySprite(x, y, sprite);
261 }
262 /// Draws UTF-8 text.
263 void drawText(int16_t x, int16_t y, const char* text, RGB_T foreground,
264 RGB_T background = RGB_T(), bool opaque = false,
265 uint8_t scale = 1, uint8_t spacing = 1,
266 uint8_t lineSpacing = 1) {
267 surface_.drawText(x, y, text, foreground, background, opaque, scale,
268 spacing, lineSpacing);
269 }
270 /// Returns the line printer for text rendering.
271 LinePrinter<RGB_T>& linePrinter() { return surface_.linePrinter(); }
272 /// Checks if the given coordinates are within the surface bounds.
273 bool isInBounds(size_t x, size_t y) const {
274 return surface_.isInBounds(x, y);
275 }
276 /// Sets a pixel with clipping.
277 void setPixelClipped(size_t x, size_t y, RGB_T color) {
278 surface_.setPixelClipped(x, y, color);
279 }
280 /// Draws a horizontal line with clipping.
281 void drawHorizontalLineClipped(int x0, int x1, int y, RGB_T color) {
282 surface_.drawHorizontalLineClipped(x0, x1, y, color);
283 }
284
285 /// Returns the raw pixel buffer as bytes.
286 const uint8_t* data() const override { return surface_.data(); }
287 /// Returns the size of the buffer in bytes.
288 size_t size() const override { return surface_.size(); }
289
290 /// Sets the framebuffer pixel data directly, replacing the current content.
291 bool setData(uint8_t *data, size_t dataSize) {
292 memset(const_cast<uint8_t*>(surface_.data()), 0, surface_.size());
293 if (dataSize > surface_.size()) {
294 return false;
295 }
296 std::memcpy(const_cast<uint8_t*>(surface_.data()), data, dataSize);
297 }
298
299 protected:
300 // Underlying surface for all drawing/storage
301 SurfaceT surface_;
302
303 struct Rect {
308 };
309
310 /// Returns the currently active font used by the framebuffer.
311 IFont<RGB_T>& activeFont() { return surface_.font(); }
313
314 /// Returns the intersection rectangle of two rectangles.
315 static Rect intersect(const Rect& first, const Rect& second) {
316 const size_t overlapX = std::max(first.x, second.x);
317 const size_t overlapY = std::max(first.y, second.y);
318 const size_t firstRight = first.x + first.width;
319 const size_t firstBottom = first.y + first.height;
320 const size_t secondRight = second.x + second.width;
321 const size_t secondBottom = second.y + second.height;
322 const size_t overlapRight = std::min(firstRight, secondRight);
323 const size_t overlapBottom = std::min(firstBottom, secondBottom);
324
325 if (overlapX >= overlapRight || overlapY >= overlapBottom) {
326 return {};
327 }
328
329 return {overlapX, overlapY, overlapRight - overlapX,
330 overlapBottom - overlapY};
331 }
332
333 /// Returns true if the rectangle is empty (zero width or height).
334 static bool isEmpty(const Rect& rect) {
335 return rect.width == 0 || rect.height == 0;
336 }
337
338 /// Applies a transformed sprite image to the framebuffer and updates
339 /// bookkeeping.
341 Surface<RGB_T>&& transformedSprite) {
342 const Rect oldBounds{spriteInfo.x, spriteInfo.y,
343 spriteInfo.currentSprite().width(),
344 spriteInfo.currentSprite().height()};
345 const size_t anchoredX = centeredCoordinate(spriteInfo.x, oldBounds.width,
346 transformedSprite.width());
347 const size_t anchoredY = centeredCoordinate(spriteInfo.y, oldBounds.height,
348 transformedSprite.height());
349 const Rect newBounds{anchoredX, anchoredY, transformedSprite.width(),
350 transformedSprite.height()};
351 const Rect overlap = intersect(oldBounds, newBounds);
352
353 restoreExposedPixels(spriteInfo, oldBounds, overlap);
354 Surface<RGB_T> updatedBackground =
355 captureUpdatedBackground(spriteInfo, oldBounds, newBounds, overlap);
356
357 spriteInfo.x = anchoredX;
358 spriteInfo.y = anchoredY;
359 spriteInfo.setTransformedSprite(std::move(transformedSprite));
360 spriteInfo.originalPixels = std::move(updatedBackground);
361 this->drawSprite(spriteInfo.x, spriteInfo.y, spriteInfo.currentSprite(),
362 spriteInfo.invisibleColor);
363 }
364
365 /// Calculates the coordinate to center a new size over an old position/size.
367 size_t newSize) {
368 const float centeredPosition =
369 static_cast<float>(oldPosition) +
370 (static_cast<float>(oldSize) - static_cast<float>(newSize)) / 2.0;
371 return centeredPosition <= 0.0
372 ? 0
373 : static_cast<size_t>(std::lround(centeredPosition));
374 }
375
376 /// Restores only the pixels exposed by moving a sprite, using the overlap
377 /// region.
378 void restoreExposedPixels(const SpriteInfo& spriteInfo, const Rect& oldBounds,
379 const Rect& overlap) {
380 if (isEmpty(overlap)) {
381 restoreOriginalPixels(spriteInfo);
382 return;
383 }
384
385 drawSpriteRegion(spriteInfo.originalPixels, 0, 0, oldBounds.x, oldBounds.y,
386 oldBounds.width, overlap.y - oldBounds.y);
387
388 const size_t overlapBottom = overlap.y + overlap.height;
389 drawSpriteRegion(spriteInfo.originalPixels, 0, overlapBottom - oldBounds.y,
390 oldBounds.x, overlapBottom, oldBounds.width,
391 (oldBounds.y + oldBounds.height) - overlapBottom);
392
393 drawSpriteRegion(spriteInfo.originalPixels, 0, overlap.y - oldBounds.y,
394 oldBounds.x, overlap.y, overlap.x - oldBounds.x,
395 overlap.height);
396
397 const size_t overlapRight = overlap.x + overlap.width;
398 drawSpriteRegion(spriteInfo.originalPixels, overlapRight - oldBounds.x,
399 overlap.y - oldBounds.y, overlapRight, overlap.y,
400 (oldBounds.x + oldBounds.width) - overlapRight,
401 overlap.height);
402 }
403
404 /// Captures the updated background pixels after a sprite move/transform.
406 const Rect& oldBounds,
407 const Rect& newBounds,
408 const Rect& overlap) {
409 Surface<RGB_T> updatedBackground(newBounds.width, newBounds.height,
411 updatedBackground.begin();
412
413 if (isEmpty(overlap)) {
414 captureFramebufferRegion(updatedBackground, 0, 0, newBounds.x,
415 newBounds.y, newBounds.width, newBounds.height);
416 return updatedBackground;
417 }
418
419 copySpriteRegion(spriteInfo.originalPixels, overlap.x - oldBounds.x,
420 overlap.y - oldBounds.y, updatedBackground,
421 overlap.x - newBounds.x, overlap.y - newBounds.y,
422 overlap.width, overlap.height);
423
424 captureFramebufferRegion(updatedBackground, 0, 0, newBounds.x, newBounds.y,
425 newBounds.width, overlap.y - newBounds.y);
426
427 const size_t overlapBottom = overlap.y + overlap.height;
428 captureFramebufferRegion(updatedBackground, 0, overlapBottom - newBounds.y,
429 newBounds.x, overlapBottom, newBounds.width,
430 (newBounds.y + newBounds.height) - overlapBottom);
431
432 captureFramebufferRegion(updatedBackground, 0, overlap.y - newBounds.y,
433 newBounds.x, overlap.y, overlap.x - newBounds.x,
434 overlap.height);
435
436 const size_t overlapRight = overlap.x + overlap.width;
437 captureFramebufferRegion(updatedBackground, overlapRight - newBounds.x,
438 overlap.y - newBounds.y, overlapRight, overlap.y,
439 (newBounds.x + newBounds.width) - overlapRight,
440 overlap.height);
441
442 return updatedBackground;
443 }
444
445 /// Draws a rectangular region from a source surface onto the framebuffer.
446 void drawSpriteRegion(const ISurface<RGB_T>& source, size_t sourceX,
447 size_t sourceY, size_t destX, size_t destY,
448 size_t width, size_t height) {
449 for (size_t currentY = 0; currentY < height; ++currentY) {
450 for (size_t currentX = 0; currentX < width; ++currentX) {
451 const size_t framebufferX = destX + currentX;
452 const size_t framebufferY = destY + currentY;
453 if (framebufferX < surface_.width() &&
454 framebufferY < surface_.height()) {
455 surface_.setPixel(
456 framebufferX, framebufferY,
457 source.getPixel(sourceX + currentX, sourceY + currentY));
458 }
459 }
460 }
461 }
462
463 /// Copies a rectangular region from a source surface to a destination
464 /// surface.
465 static void copySpriteRegion(const ISurface<RGB_T>& source, size_t sourceX,
466 size_t sourceY, Surface<RGB_T>& destination,
467 size_t destX, size_t destY, size_t width,
468 size_t height) {
469 for (size_t currentY = 0; currentY < height; ++currentY) {
470 for (size_t currentX = 0; currentX < width; ++currentX) {
471 destination.setPixel(
472 destX + currentX, destY + currentY,
473 source.getPixel(sourceX + currentX, sourceY + currentY));
474 }
475 }
476 }
477
478 /// Captures a rectangular region from the framebuffer into a destination
479 /// surface.
480 void captureFramebufferRegion(Surface<RGB_T>& destination, size_t destX,
481 size_t destY, size_t sourceX, size_t sourceY,
482 size_t width, size_t height) {
483 for (size_t currentY = 0; currentY < height; ++currentY) {
484 for (size_t currentX = 0; currentX < width; ++currentX) {
485 const size_t framebufferX = sourceX + currentX;
486 const size_t framebufferY = sourceY + currentY;
487 const RGB_T color =
488 framebufferX < surface_.width() && framebufferY < surface_.height()
489 ? surface_.getPixel(framebufferX, framebufferY)
490 : RGB_T(0);
491 destination.setPixel(destX + currentX, destY + currentY, color);
492 }
493 }
494 }
495
496 /// Returns a scaled copy of a sprite image.
497 static Surface<RGB_T> scaleSpriteImage(const ISurface<RGB_T>& source,
498 float scale, IFont<RGB_T>& font) {
499 size_t scaledWidth = static_cast<size_t>(source.width() * scale);
500 size_t scaledHeight = static_cast<size_t>(source.height() * scale);
501
502 if (scaledWidth == 0) {
503 scaledWidth = 1;
504 }
505 if (scaledHeight == 0) {
506 scaledHeight = 1;
507 }
508
509 Surface<RGB_T> scaledSprite(scaledWidth, scaledHeight, font);
510 scaledSprite.begin();
511 for (size_t currentY = 0; currentY < scaledHeight; ++currentY) {
512 const size_t sourceY =
513 static_cast<size_t>(static_cast<float>(currentY) / scale);
514 const size_t clampedY =
515 sourceY < source.height() ? sourceY : source.height() - 1;
516 for (size_t currentX = 0; currentX < scaledWidth; ++currentX) {
517 const size_t sourceX =
518 static_cast<size_t>(static_cast<float>(currentX) / scale);
519 const size_t clampedX =
520 sourceX < source.width() ? sourceX : source.width() - 1;
521 scaledSprite.setPixel(currentX, currentY,
522 source.getPixel(clampedX, clampedY));
523 }
524 }
525
526 return scaledSprite;
527 }
528
529 /// Returns a rotated copy of a sprite image, filling empty space with
530 /// fillColor.
531 static Surface<RGB_T> rotateSpriteImage(const ISurface<RGB_T>& source,
532 float angleDegrees, RGB_T fillColor,
533 IFont<RGB_T>& font) {
534 const float radians =
535 static_cast<float>(angleDegrees) * 3.14159265358979323846 / 180.0;
536 const float cosine = std::cos(radians);
537 const float sine = std::sin(radians);
538 const float sourceWidth = static_cast<float>(source.width());
539 const float sourceHeight = static_cast<float>(source.height());
540 size_t rotatedWidth = static_cast<size_t>(std::ceil(
541 std::fabs(sourceWidth * cosine) + std::fabs(sourceHeight * sine)));
542 size_t rotatedHeight = static_cast<size_t>(std::ceil(
543 std::fabs(sourceWidth * sine) + std::fabs(sourceHeight * cosine)));
544
545 if (rotatedWidth == 0) {
546 rotatedWidth = 1;
547 }
548 if (rotatedHeight == 0) {
549 rotatedHeight = 1;
550 }
551
552 Surface<RGB_T> rotatedSprite(rotatedWidth, rotatedHeight, font);
553 rotatedSprite.begin();
554 rotatedSprite.clear(fillColor);
555
556 const float sourceCenterX = (sourceWidth - 1.0) / 2.0;
557 const float sourceCenterY = (sourceHeight - 1.0) / 2.0;
558 const float rotatedCenterX = (static_cast<float>(rotatedWidth) - 1.0) / 2.0;
559 const float rotatedCenterY =
560 (static_cast<float>(rotatedHeight) - 1.0) / 2.0;
561
562 for (size_t currentY = 0; currentY < rotatedHeight; ++currentY) {
563 for (size_t currentX = 0; currentX < rotatedWidth; ++currentX) {
564 const float targetX = static_cast<float>(currentX) - rotatedCenterX;
565 const float targetY = static_cast<float>(currentY) - rotatedCenterY;
566 const float sourceX =
567 (targetX * cosine) + (targetY * sine) + sourceCenterX;
568 const float sourceY =
569 (-targetX * sine) + (targetY * cosine) + sourceCenterY;
570 const long nearestX = std::lround(sourceX);
571 const long nearestY = std::lround(sourceY);
572
573 if (nearestX >= 0 && nearestY >= 0 &&
574 static_cast<size_t>(nearestX) < source.width() &&
575 static_cast<size_t>(nearestY) < source.height()) {
576 rotatedSprite.setPixel(
577 currentX, currentY,
578 source.getPixel(static_cast<size_t>(nearestX),
579 static_cast<size_t>(nearestY)));
580 }
581 }
582 }
583
584 return rotatedSprite;
585 }
586
587 /// Restores the original background pixels behind a sprite.
588 void restoreOriginalPixels(const SpriteInfo& spriteInfo) {
589 surface_.drawSprite(spriteInfo.x, spriteInfo.y, spriteInfo.originalPixels);
590 }
591
592 /// Finds the iterator to a sprite in the internal sprite list.
593 auto findSprite(SpriteInfo& spriteInfo) {
594 return std::find_if(
595 sprites_.begin(), sprites_.end(),
596 [&spriteInfo](const std::unique_ptr<SpriteInfo>& entry) {
597 return entry.get() == &spriteInfo;
598 });
599 }
600};
601
602} // namespace tinygpu
Framebuffer with sprite placement and background restoration support.
Definition: FrameBuffer.h:21
static Surface< RGB_T > scaleSpriteImage(const ISurface< RGB_T > &source, float scale, IFont< RGB_T > &font)
Returns a scaled copy of a sprite image.
Definition: FrameBuffer.h:497
static bool isEmpty(const Rect &rect)
Returns true if the rectangle is empty (zero width or height).
Definition: FrameBuffer.h:334
IFont< RGB_T > & font()
Returns the currently set font for text rendering.
Definition: FrameBuffer.h:223
Surface< RGB_T > captureUpdatedBackground(const SpriteInfo &spriteInfo, const Rect &oldBounds, const Rect &newBounds, const Rect &overlap)
Captures the updated background pixels after a sprite move/transform.
Definition: FrameBuffer.h:405
void captureFramebufferRegion(Surface< RGB_T > &destination, size_t destX, size_t destY, size_t sourceX, size_t sourceY, size_t width, size_t height)
Definition: FrameBuffer.h:480
SurfaceT surface_
Definition: FrameBuffer.h:301
void fillRect(size_t x, size_t y, size_t w, size_t h, RGB_T color)
Fills a rectangle.
Definition: FrameBuffer.h:237
void scaleSprite(SpriteInfo &spriteInfo, float scale)
Scales a sprite image and redraws it at its current position.
Definition: FrameBuffer.h:187
void drawText(int16_t x, int16_t y, const char *text, RGB_T foreground, RGB_T background=RGB_T(), bool opaque=false, uint8_t scale=1, uint8_t spacing=1, uint8_t lineSpacing=1)
Draws UTF-8 text.
Definition: FrameBuffer.h:263
void drawLine(size_t x0, size_t y0, size_t x1, size_t y1, RGB_T color)
Draws a line between two points.
Definition: FrameBuffer.h:229
void drawSpriteRegion(const ISurface< RGB_T > &source, size_t sourceX, size_t sourceY, size_t destX, size_t destY, size_t width, size_t height)
Draws a rectangular region from a source surface onto the framebuffer.
Definition: FrameBuffer.h:446
void moveSprite(SpriteInfo &spriteInfo, size_t newX, size_t newY)
Moves a sprite to a new position and redraws it.
Definition: FrameBuffer.h:160
LinePrinter< RGB_T > & linePrinter()
Returns the line printer for text rendering.
Definition: FrameBuffer.h:271
Vector< std::unique_ptr< SpriteInfo > > sprites_
Definition: FrameBuffer.h:312
void copySprite(size_t x, size_t y, const ISurface< RGB_T > &sprite)
Copies a sprite.
Definition: FrameBuffer.h:259
void setFont(IFont< RGB_T > &font)
Sets the font for text rendering.
Definition: FrameBuffer.h:221
static size_t centeredCoordinate(size_t oldPosition, size_t oldSize, size_t newSize)
Calculates the coordinate to center a new size over an old position/size.
Definition: FrameBuffer.h:366
void end() override
Closes the framebuffer and releases resources.
Definition: FrameBuffer.h:115
static Rect intersect(const Rect &first, const Rect &second)
Returns the intersection rectangle of two rectangles.
Definition: FrameBuffer.h:315
void clear(RGB_T color=RGB_T())
Clears the framebuffer with a single color.
Definition: FrameBuffer.h:225
size_t height() const override
Returns the framebuffer height in pixels.
Definition: FrameBuffer.h:219
IFont< RGB_T > & activeFont()
Returns the currently active font used by the framebuffer.
Definition: FrameBuffer.h:311
bool setData(uint8_t *data, size_t dataSize)
Sets the framebuffer pixel data directly, replacing the current content.
Definition: FrameBuffer.h:291
void applyTransformedSprite(SpriteInfo &spriteInfo, Surface< RGB_T > &&transformedSprite)
Definition: FrameBuffer.h:340
void fillCircle(size_t x, size_t y, size_t r, RGB_T color)
Fills a circle.
Definition: FrameBuffer.h:245
bool resize(size_t w, size_t h) override
Resizes the framebuffer surface.
Definition: FrameBuffer.h:215
auto findSprite(SpriteInfo &spriteInfo)
Finds the iterator to a sprite in the internal sprite list.
Definition: FrameBuffer.h:593
const uint8_t * data() const override
Returns the raw pixel buffer as bytes.
Definition: FrameBuffer.h:286
SpriteInfo & addSprite(size_t x, size_t y, const ISurface< RGB_T > &sprite, RGB_T invisibleColor=RGB_T(0))
Adds a sprite to the framebuffer and draws it at the given position.
Definition: FrameBuffer.h:118
void clearSprite(size_t x, size_t y, ISurface< RGB_T > &sprite, RGB_T clearColor=RGB_T())
Clears a sprite.
Definition: FrameBuffer.h:254
FrameBuffer()=default
Creates an empty framebuffer.
FrameBuffer(size_t width, size_t height, IFont< RGB_T > &font)
Creates a framebuffer with the specified size and font.
Definition: FrameBuffer.h:111
void drawCircle(size_t x, size_t y, size_t r, RGB_T color)
Draws a circle outline.
Definition: FrameBuffer.h:241
void drawSprite(size_t x, size_t y, const ISurface< RGB_T > &sprite, RGB_T invisibleColor=RGB_T())
Draws a sprite.
Definition: FrameBuffer.h:249
size_t width() const override
Returns the framebuffer width in pixels.
Definition: FrameBuffer.h:217
void scroll(int dx, int dy) override
Scrolls the framebuffer content by the specified offsets.
Definition: FrameBuffer.h:227
SpriteInfo & addSprite(size_t x, size_t y, size_t maxX, size_t maxY, const ISurface< RGB_T > &sprite, RGB_T invisibleColor=RGB_T(0))
Adds a sprite with a preallocated max buffer size for transformations.
Definition: FrameBuffer.h:133
bool begin() override
Initializes the framebuffer surface.
Definition: FrameBuffer.h:114
void setPixelClipped(size_t x, size_t y, RGB_T color)
Sets a pixel with clipping.
Definition: FrameBuffer.h:277
void rotateSprite(SpriteInfo &spriteInfo, float angleDegrees)
Rotates a sprite image and redraws it at its current position.
Definition: FrameBuffer.h:197
void setPixel(size_t x, size_t y, RGB_T color) override
ISurface<RGB_T> interface delegation.
Definition: FrameBuffer.h:207
size_t size() const override
Returns the size of the buffer in bytes.
Definition: FrameBuffer.h:288
void restoreExposedPixels(const SpriteInfo &spriteInfo, const Rect &oldBounds, const Rect &overlap)
Definition: FrameBuffer.h:378
void drawRect(size_t x, size_t y, size_t w, size_t h, RGB_T color)
Draws a rectangle outline.
Definition: FrameBuffer.h:233
void removeSprite(SpriteInfo &spriteInfo)
Removes a sprite and restores the pixels behind it.
Definition: FrameBuffer.h:149
void restoreOriginalPixels(const SpriteInfo &spriteInfo)
Restores the original background pixels behind a sprite.
Definition: FrameBuffer.h:588
RGB_T getPixel(size_t x, size_t y) const override
Returns the pixel at the given position.
Definition: FrameBuffer.h:211
static void copySpriteRegion(const ISurface< RGB_T > &source, size_t sourceX, size_t sourceY, Surface< RGB_T > &destination, size_t destX, size_t destY, size_t width, size_t height)
Definition: FrameBuffer.h:465
void drawHorizontalLineClipped(int x0, int x1, int y, RGB_T color)
Draws a horizontal line with clipping.
Definition: FrameBuffer.h:281
bool isInBounds(size_t x, size_t y) const
Checks if the given coordinates are within the surface bounds.
Definition: FrameBuffer.h:273
static Surface< RGB_T > rotateSpriteImage(const ISurface< RGB_T > &source, float angleDegrees, RGB_T fillColor, IFont< RGB_T > &font)
Definition: FrameBuffer.h:531
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
RGB color stored in 16-bit RGB565 format.
Definition: RGB565.h:13
In-memory bitmap surface with basic 2D drawing and text rendering.
Definition: Surface.h:39
Simple header-only logger for TinyGPU with log levels and vararg support.
Definition: TinyGPULogger.h:24
@ INFO
General information.
Definition: TinyGPULogger.h:32
void log(Level level, const char *fmt,...)
Log a message with printf-style formatting.
Definition: TinyGPULogger.h:56
Definition: AVIWriter.h:9
Definition: FrameBuffer.h:303
size_t height
Definition: FrameBuffer.h:307
size_t width
Definition: FrameBuffer.h:306
size_t y
Definition: FrameBuffer.h:305
size_t x
Definition: FrameBuffer.h:304
Definition: FrameBuffer.h:29
size_t y
Definition: FrameBuffer.h:31
size_t maxWidth
Definition: FrameBuffer.h:32
std::unique_ptr< SurfaceT > transformedSprite
Definition: FrameBuffer.h:36
void setMaxSize(size_t maxX, size_t maxY)
Set the maximum buffer size for transformedSprite and allocate buffer.
Definition: FrameBuffer.h:64
void saveOriginalPixels(ISurface< RGB_T > &framebuffer)
Saves the background pixels currently covered by the sprite.
Definition: FrameBuffer.h:58
size_t x
Definition: FrameBuffer.h:30
IFont< RGB_T > & fontRef
Definition: FrameBuffer.h:38
SpriteInfo(size_t startX, size_t startY, const ISurface< RGB_T > &sourceSprite, RGB_T transparentColor, IFont< RGB_T > &font)
Definition: FrameBuffer.h:40
void setTransformedSprite(SurfaceT &&newSprite)
Definition: FrameBuffer.h:76
const ISurface< RGB_T > & currentSprite() const
Returns the sprite image currently used for drawing.
Definition: FrameBuffer.h:51
SurfaceT originalPixels
Definition: FrameBuffer.h:37
RGB_T invisibleColor
Definition: FrameBuffer.h:34
const ISurface< RGB_T > * sprite
Definition: FrameBuffer.h:35
size_t maxHeight
Definition: FrameBuffer.h:33