TinyGPU
Loading...
Searching...
No Matches
SpriteDisplay.h
Go to the documentation of this file.
1#pragma once
2#include <algorithm>
3#include <memory>
4
5#include "TinyGPU/Color/RGB565.h"
6#include "TinyGPU/Surface/SpriteInfo.h"
7#include "TinyGPU/Surface/Surface.h"
8#include "TinyGPU/ThreeD/Vector.h"
10#include "TinyGPU/Util/TinyGPULogger.h"
11#include "TinyGPU/Input/TouchDriver.h"
12#include "TinyGPU/Boards/LCDBoards.h"
13
14namespace tinygpu {
15
16/***
17 * @brief Sprite Display without FrameBuffer. Usually we use a Framebuffer to
18 * compose and display sprites, but in some cases we want to display a sprite
19 * directly on the screen without using a framebuffer to minimize the required
20 * memory.
21 * The Sprites are also supporting touch events and can be moved, scaled and
22 * rotated.
23 * @tparam RGB_T The pixel format, default is RGB565.
24 */
25
26template <typename RGB_T = RGB565>
28 public:
29 SpriteDisplay(size_t width, size_t height, DisplayDriver<RGB_T>& driver,
30 RGB_T backgroundColor = RGB_T(0))
31 : width_(width),
33 driver_(driver),
34 backgroundColor_(backgroundColor) {}
35
36 /// Constructs from an LCDBoard (see LCDBoards.h): takes its display
37 /// driver and dimensions, and auto-attaches its touch controller if one
38 /// is present. Only valid for RGB_T = RGB565, since every LCDBoard's
39 /// display() is RGB565.
40 ///
41 /// begin() on the resulting SpriteDisplay calls LCDBoard::begin()
42 /// (backlight + SPI/QSPI bus + display controller + touch controller)
43 /// instead of just the display driver's begin() - do not also call
44 /// board.begin() yourself, or the display controller's init sequence
45 /// (and, on boards that need it, the display-inversion fix) runs twice.
46 explicit SpriteDisplay(LCDBoard& board, RGB_T backgroundColor = RGB_T(0))
47 : driver_(board.display()),
49 p_board_(&board),
52 backgroundColor_(backgroundColor) {}
53
54 /// Adds a sprite to the framebuffer and draws it at the given position.
55 SpriteInfo<RGB_T, Surface<RGB_T>>& addSprite(size_t x, size_t y,
56 const ISurface<RGB_T>& sprite) {
57 TinyGPULogger.log(TinyGPULoggerClass::INFO, "Adding sprite at (%zu, %zu)",
58 x, y);
59 auto info = std::make_unique<SpriteInfo<RGB_T, Surface<RGB_T>>>(
60 x, y, sprite, backgroundColor_, defaultFont_);
61
62 displaySprite(x, y, info->currentSprite());
63
64 sprites_.push_back(std::move(info));
65 return *sprites_.back();
66 }
67
68 /// Starts the processing. If constructed from an LCDBoard, this brings
69 /// up the whole board (LCDBoard::begin()); otherwise just the display
70 /// driver (driver_.begin()).
71 bool begin() {
72 bool ok = p_board_ != nullptr ? p_board_->begin() : driver_.begin();
73 clear();
74 return ok;
75 }
76
77 /// Ends the processing
78 void end() {
79 driver_.end();
80 clear();
81 }
82
83 /// Adds a sprite with a preallocated max buffer size for transformations.
84 SpriteInfo<RGB_T, Surface<RGB_T>>& addSprite(size_t x, size_t y, size_t maxX,
85 size_t maxY,
86 RGB_T backgroundColor) {
87 auto sprite = std::make_unique<Sprite<RGB_T>>(maxX, maxY, defaultFont_);
88 sprite->begin();
89 sprite->clear(backgroundColor);
90
91 // Pass color during addSprite or rely on constructor initialization
92 SpriteInfo<RGB_T, Surface<RGB_T>>& result = addSprite(x, y, *sprite);
93 result.isSurfaceAutoDelete = true;
94 // Ownership of the Sprite now belongs to the SpriteInfo (deleted via
95 // isSurfaceAutoDelete), so release it here to avoid a double free.
96 sprite.release();
97 return result;
98 }
99
100 /// Adds a sprite with a preallocated max buffer size for transformations.
101 SpriteInfo<RGB_T, Surface<RGB_T>>& addSprite(size_t x, size_t y, size_t maxX,
102 size_t maxY,
103 const ISurface<RGB_T>& sprite) {
104 TinyGPULogger.log(TinyGPULoggerClass::INFO,
105 "Adding sprite at (%zu, %zu) with max size (%zu, %zu)", x,
106 y, maxX, maxY);
107 auto info =
108 std::make_unique<SpriteInfoWithoutBackground<RGB_T, Surface<RGB_T>>>(
109 x, y, sprite, backgroundColor_, defaultFont_);
110 info->setMaxSize(maxX, maxY);
111 displaySprite(x, y, info->currentSprite());
112 sprites_.push_back(std::move(info));
113 return *sprites_.back();
114 }
115
116 /// Removes a sprite and restores the pixels behind it.
117 void removeSprite(SpriteInfo<RGB_T, Surface<RGB_T>>& sprite) {
118 TinyGPULogger.log(TinyGPULoggerClass::INFO, "Removing sprite at (%zu, %zu)",
119 sprite.x, sprite.y);
120
121 auto it = tinygpu::findSprite(sprites_, sprite);
122 if (it != sprites_.end()) {
123 clearSprite(sprite.x, sprite.y, sprite.currentSprite());
124 sprites_.erase(it);
125 }
126 }
127
128 /// Returns a reference to the sprite at the given position, if any.
129 SpriteInfo<RGB_T, Surface<RGB_T>>* getSprite(size_t x, size_t y) {
130 for (const auto& sprite : sprites_) {
131 if (x >= sprite->x && x < sprite->x + sprite->currentSprite().width() &&
132 y >= sprite->y && y < sprite->y + sprite->currentSprite().height()) {
133 return sprite.get();
134 }
135 }
136 return nullptr;
137 }
138
139 /// Returns the number of sprites currently managed by the framebuffer.
140 size_t getSpriteCount() const { return sprites_.size(); }
141
142 /// Returns a reference to the sprite at the given index, if valid.
143 SpriteInfo<RGB_T, Surface<RGB_T>>* getSprite(int idx) {
144 if (idx < 0 || idx >= static_cast<int>(sprites_.size())) {
145 return nullptr;
146 }
147 return sprites_[idx].get();
148 }
149
150 /// Returns a reference to the SpriteInfo for the indicated surface, if it
151 /// exists.
152 SpriteInfo<RGB_T, Surface<RGB_T>>* getSprite(const ISurface<RGB_T>& sprite) {
153 for (auto& spriteInfo : sprites_) {
154 if (spriteInfo->currentSprite().data() == sprite.data()) {
155 return spriteInfo.get();
156 }
157 }
158 return nullptr;
159 }
160
161 /// Moves a sprite to a new position and redraws it.
162 void moveSprite(SpriteInfo<RGB_T, Surface<RGB_T>>& sprite, size_t newX,
163 size_t newY) {
164 TinyGPULogger.log(TinyGPULoggerClass::INFO,
165 "Moving sprite from (%zu, %zu) to (%zu, %zu)", sprite.x,
166 sprite.y, newX, newY);
167 if (sprite.x == newX && sprite.y == newY) {
168 return;
169 }
170
172 clearSprite(sprite.x, sprite.y, sprite.currentSprite());
173 }
174
175 sprite.x = newX;
176 sprite.y = newY;
177 displaySprite(sprite.x, sprite.y, sprite.currentSprite());
178 }
179
180 /// Scales a sprite image and redraws it at its current position. Always
181 /// re-renders from the pristine original at (scale, currentAngleDegrees)
182 /// - see renderTransformedSprite() in SpriteInfo.h for why.
183 void scaleSprite(SpriteInfo<RGB_T, Surface<RGB_T>>& sprite, float scale) {
185 "Scaling sprite at (%zu, %zu) by %.2f", sprite.x,
186 sprite.y, scale);
187 sprite.currentScale = scale;
189 renderTransformedSprite(sprite, defaultFont_));
190 }
191
192 /// Rotates a sprite image and redraws it at its current position. Always
193 /// re-renders from the pristine original at (currentScale, angleDegrees)
194 /// - see renderTransformedSprite() in SpriteInfo.h for why.
195 void rotateSprite(SpriteInfo<RGB_T, Surface<RGB_T>>& sprite,
196 float angleDegrees) {
198 "Rotating sprite at (%zu, %zu) by %.2f degrees", sprite.x,
199 sprite.y, angleDegrees);
200 sprite.currentAngleDegrees = angleDegrees;
202 renderTransformedSprite(sprite, defaultFont_));
203 }
204
205 /// Clears the display and resets the background color.
206 void clear() {
207 TinyGPULogger.log(TinyGPULoggerClass::INFO, "Clearing display");
209 sprites_.clear();
210 }
211
212 /// Refreshes the display and redraws all sprites in their current positions
213 /// and states.
214 void refresh() {
216 for (const auto& sprite : sprites_) {
217 displaySprite(sprite->x, sprite->y, sprite->currentSprite());
218 }
219 }
220
221 /// Defines the touch driver
222 void setTouchDriver(TouchDriver& touch) { p_touchDriver = &touch; }
223
224 /// Processes touch input and dispatches events to sprites if touched.
227 Point point;
228 if (p_touchDriver->getPoint(point)) {
230 "Touch detected at (%d, %d) with pressure %d",
231 point.x, point.y, point.pressure);
232 SpriteInfo<RGB_T, Surface<RGB_T>>* sprite = getSprite(point.x, point.y);
233 if (sprite) {
235 "Touch is within sprite at (%zu, %zu)", sprite->x,
236 sprite->y);
237 sprite->onTouch(point);
238 }
239 }
240 }
241 }
242
243 /// Fills/clears the entire display with the specified color.
244 void fillScreen(RGB_T color) { driver_.writeColor(width_, height_, color); }
245
246 /// Activates/deactivates the automatic clearing when we move a sprite
247 void setClearOnSpriteMove(bool clear) { is_clear_on_sprite_move = clear; }
248
249 /// Returns whether the automatic clearing is active when we move a sprite
251
252 protected:
255 LCDBoard* p_board_ = nullptr;
263
264 /// Helper to calculate centered coordinate when scaling/rotating
266 if (newDim >= oldDim) {
267 size_t diff = (newDim - oldDim) / 2;
268 return (pos > diff) ? (pos - diff) : 0;
269 } else {
270 return pos + (oldDim - newDim) / 2;
271 }
272 }
273
274 /// Applies a transformed sprite image to the display and updates bookkeeping.
275 void applyTransformedSprite(SpriteInfo<RGB_T, Surface<RGB_T>>& spriteInfo,
276 Surface<RGB_T>&& transformedSprite) {
277 const Rect oldBounds{spriteInfo.x, spriteInfo.y,
278 spriteInfo.currentSprite().width(),
279 spriteInfo.currentSprite().height()};
280 // See FrameBuffer::applyTransformedSprite() for why this must be the
281 // clamped (post-setTransformedSprite) size, not transformedSprite's
282 // own raw size.
283 const Rect clamped = spriteInfo.clampedSize(transformedSprite.width(),
284 transformedSprite.height());
285 const size_t anchoredX =
286 centeredCoordinate(spriteInfo.x, oldBounds.width, clamped.width);
287 const size_t anchoredY =
288 centeredCoordinate(spriteInfo.y, oldBounds.height, clamped.height);
289
290 clearSprite(spriteInfo.x, spriteInfo.y, oldBounds.width, oldBounds.height);
291
292 spriteInfo.x = anchoredX;
293 spriteInfo.y = anchoredY;
294 spriteInfo.setTransformedSprite(std::move(transformedSprite));
295 displaySprite(spriteInfo.x, spriteInfo.y, spriteInfo.currentSprite());
296 }
297
298 /// Clears the background of the indicated sprite at (x, y) with the
299 /// background color.
300 void clearSprite(size_t x, size_t y, const ISurface<RGB_T>& sprite) {
301 clearSprite(x, y, sprite.width(), sprite.height());
302 }
303
304 void clearSprite(Rect rect) {
305 clearSprite(rect.x, rect.y, rect.width, rect.height);
306 }
307
308 void clearSprite(size_t x, size_t y, size_t width, size_t height) {
309 actual_sprite_background_.resize(width, height);
310 actual_sprite_background_.clear(backgroundColor_);
311
312 // Route via writeData instead of drawSurface
313 driver_.writeData(actual_sprite_background_, x, y);
314 }
315 /// Draws a sprite at (x, y)
316 void displaySprite(size_t x, size_t y, const ISurface<RGB_T>& sprite) {
317 // Route via writeData instead of drawSurface
318 driver_.writeData(const_cast<ISurface<RGB_T>&>(sprite), x, y);
319 }
320};
321
322} // namespace tinygpu
Definition: DisplayDriver.h:49
Fixed-size 5x7 bitmap font with ASCII and extended character support.
Definition: Font5x7.h:23
Definition: LCDBoardsCommon.h:62
virtual TouchDriver * touch()=0
The board's touch controller, or nullptr if none is wired up.
virtual DisplayDriver< RGB565 > & display()=0
The board's display driver.
virtual bool begin()=0
RGB color stored in 16-bit RGB565 format, byte-swapped from the conventional bit layout.
Definition: RGB565.h:24
Definition: SpriteDisplay.h:27
void rotateSprite(SpriteInfo< RGB_T, Surface< RGB_T > > &sprite, float angleDegrees)
Definition: SpriteDisplay.h:195
Font5x7< RGB_T > defaultFont_
Definition: SpriteDisplay.h:261
void scaleSprite(SpriteInfo< RGB_T, Surface< RGB_T > > &sprite, float scale)
Definition: SpriteDisplay.h:183
void clearSprite(Rect rect)
Definition: SpriteDisplay.h:304
SpriteInfo< RGB_T, Surface< RGB_T > > & addSprite(size_t x, size_t y, size_t maxX, size_t maxY, const ISurface< RGB_T > &sprite)
Adds a sprite with a preallocated max buffer size for transformations.
Definition: SpriteDisplay.h:101
SpriteDisplay(size_t width, size_t height, DisplayDriver< RGB_T > &driver, RGB_T backgroundColor=RGB_T(0))
Definition: SpriteDisplay.h:29
Sprite< RGB_T > actual_sprite_background_
Definition: SpriteDisplay.h:256
size_t getSpriteCount() const
Returns the number of sprites currently managed by the framebuffer.
Definition: SpriteDisplay.h:140
RGB_T backgroundColor_
Definition: SpriteDisplay.h:259
SpriteInfo< RGB_T, Surface< RGB_T > > * getSprite(size_t x, size_t y)
Returns a reference to the sprite at the given position, if any.
Definition: SpriteDisplay.h:129
size_t width_
Definition: SpriteDisplay.h:257
void refresh()
Definition: SpriteDisplay.h:214
bool begin()
Definition: SpriteDisplay.h:71
void setTouchDriver(TouchDriver &touch)
Defines the touch driver.
Definition: SpriteDisplay.h:222
SpriteInfo< RGB_T, Surface< RGB_T > > * getSprite(const ISurface< RGB_T > &sprite)
Definition: SpriteDisplay.h:152
bool isClearOnSpriteMove() const
Returns whether the automatic clearing is active when we move a sprite.
Definition: SpriteDisplay.h:250
void removeSprite(SpriteInfo< RGB_T, Surface< RGB_T > > &sprite)
Removes a sprite and restores the pixels behind it.
Definition: SpriteDisplay.h:117
size_t height_
Definition: SpriteDisplay.h:258
DisplayDriver< RGB_T > & driver_
Definition: SpriteDisplay.h:253
SpriteDisplay(LCDBoard &board, RGB_T backgroundColor=RGB_T(0))
Definition: SpriteDisplay.h:46
bool is_clear_on_sprite_move
Definition: SpriteDisplay.h:262
SpriteInfo< RGB_T, Surface< RGB_T > > & addSprite(size_t x, size_t y, const ISurface< RGB_T > &sprite)
Adds a sprite to the framebuffer and draws it at the given position.
Definition: SpriteDisplay.h:55
void fillScreen(RGB_T color)
Fills/clears the entire display with the specified color.
Definition: SpriteDisplay.h:244
Vector< std::unique_ptr< SpriteInfo< RGB_T, Surface< RGB_T > > > > sprites_
Definition: SpriteDisplay.h:260
TouchDriver * p_touchDriver
Definition: SpriteDisplay.h:254
void applyTransformedSprite(SpriteInfo< RGB_T, Surface< RGB_T > > &spriteInfo, Surface< RGB_T > &&transformedSprite)
Applies a transformed sprite image to the display and updates bookkeeping.
Definition: SpriteDisplay.h:275
void end()
Ends the processing.
Definition: SpriteDisplay.h:78
void processTouch()
Processes touch input and dispatches events to sprites if touched.
Definition: SpriteDisplay.h:225
void clearSprite(size_t x, size_t y, size_t width, size_t height)
Definition: SpriteDisplay.h:308
SpriteInfo< RGB_T, Surface< RGB_T > > & addSprite(size_t x, size_t y, size_t maxX, size_t maxY, RGB_T backgroundColor)
Adds a sprite with a preallocated max buffer size for transformations.
Definition: SpriteDisplay.h:84
void moveSprite(SpriteInfo< RGB_T, Surface< RGB_T > > &sprite, size_t newX, size_t newY)
Moves a sprite to a new position and redraws it.
Definition: SpriteDisplay.h:162
void setClearOnSpriteMove(bool clear)
Activates/deactivates the automatic clearing when we move a sprite.
Definition: SpriteDisplay.h:247
void clear()
Clears the display and resets the background color.
Definition: SpriteDisplay.h:206
static size_t centeredCoordinate(size_t pos, size_t oldDim, size_t newDim)
Helper to calculate centered coordinate when scaling/rotating.
Definition: SpriteDisplay.h:265
void displaySprite(size_t x, size_t y, const ISurface< RGB_T > &sprite)
Draws a sprite at (x, y)
Definition: SpriteDisplay.h:316
LCDBoard * p_board_
Definition: SpriteDisplay.h:255
void clearSprite(size_t x, size_t y, const ISurface< RGB_T > &sprite)
Definition: SpriteDisplay.h:300
SpriteInfo< RGB_T, Surface< RGB_T > > * getSprite(int idx)
Returns a reference to the sprite at the given index, if valid.
Definition: SpriteDisplay.h:143
In-memory bitmap surface with basic 2D drawing and text rendering.
Definition: Surface.h:38
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
Base class for touch controllers.
Definition: TouchDriverCommon.h:72
virtual bool isTouched()=0
Return whether the panel currently appears touched.
virtual bool getPoint(Point &outPoint)=0
Read the current touch point.
Definition: DSIBusESP32.h:19
Touch point.
Definition: TouchDriverCommon.h:32
uint16_t pressure
Definition: TouchDriverCommon.h:35
int16_t x
Definition: TouchDriverCommon.h:33
int16_t y
Definition: TouchDriverCommon.h:34
Represents a rectangular area on the screen or surface.
Definition: SpriteInfo.h:19
Definition: SpriteInfo.h:52