TinyGPU
Loading...
Searching...
No Matches
WindowedSurface.h
Go to the documentation of this file.
1#pragma once
2#include "TinyGPU/Surface/ISurface.h"
3#include "TinyGPU/Surface/SurfaceBase.h"
4
5namespace tinygpu {
6
7/**
8 * @brief An ISurface that *reports* a full `width x height` extent but
9 * physically redirects every pixel write into a much smaller `backing`
10 * surface, offset by `(offsetX, offsetY)`, dropping anything that then
11 * falls outside `backing`'s own bounds.
12 *
13 * Exists so a widget (or a handful of them) can be rendered into one
14 * small reused buffer instead of a full-screen framebuffer - see
15 * tinymd::Screen::drawDirect(), which uses this two ways:
16 *
17 * - Per-widget: `backing` resized to exactly one widget's own bounds,
18 * `offsetX`/`offsetY` at that widget's screen position, `fullWidth`/
19 * `fullHeight` equal to the widget's own w/h - so the widget draws
20 * into what looks to it like a normal, appropriately-sized surface.
21 * - Banded (for a presented modal, whose scrim needs the *full*
22 * viewport's width/height to size correctly - see Dialog.h/Drawer.h -
23 * and which can span most of the screen): `backing` is one short,
24 * fixed-width strip; `offsetY` steps through the viewport a strip at
25 * a time while `fullWidth`/`fullHeight` stay pinned to the *whole*
26 * viewport, so the scrim still fills correctly even though `backing`
27 * only ever holds one strip's worth of pixels.
28 *
29 * Either way, every higher-level drawing call a widget's draw() makes
30 * (fillRect, drawLine, drawCircle, drawText, drawSprite, fillRoundRect,
31 * a modal's full-width/height scrim fill, ...) is inherited unchanged
32 * from SurfaceBase, which implements all of them purely in terms of
33 * setPixel()/getPixel() - the only two primitives overridden here - so
34 * nothing being drawn needs to know the difference from a real surface.
35 *
36 * A widget always draws using *absolute* screen coordinates (its own
37 * `this->bounds.x/y`, not 0), so - critically - the `width_`/`height_`
38 * SurfaceBase itself uses internally to clip fillRect()/drawSprite()/etc.
39 * (inherited, not reimplemented here) must be set to where `backing`'s
40 * far edge actually falls *in that same absolute space* -
41 * `offsetX + backing.width()`, not just `backing.width()` - or every one
42 * of those clips silently rejects nearly everything for any widget not
43 * sitting at (0, 0). What width()/height() *report* to callers (a
44 * modal's scrim math, chiefly) is a separate, independently overridden
45 * pair of accessors below, exactly because the two needs (correct
46 * internal clipping vs. "how big does the caller think the canvas is")
47 * are different numbers whenever offsetX/offsetY aren't both 0.
48 *
49 * One instance is (cheaply) constructed per widget/strip; it owns no
50 * pixel storage of its own.
51 */
52template <typename RGB_T = RGB565>
53class WindowedSurface : public SurfaceBase<RGB_T> {
54 public:
55 /// `backing` must outlive this object. `fullWidth`/`fullHeight` are
56 /// what width()/height() report (see the class comment for the two
57 /// ways callers pick these); `offsetX`/`offsetY` is where `backing`'s
58 /// own (0, 0) sits within that full logical space.
59 WindowedSurface(ISurface<RGB_T>& backing, int32_t offsetX, int32_t offsetY, size_t fullWidth,
60 size_t fullHeight, IFont<RGB_T>& font)
61 : SurfaceBase<RGB_T>(static_cast<size_t>(offsetX) + backing.width(),
62 static_cast<size_t>(offsetY) + backing.height(), font),
63 backing_(backing),
64 offsetX_(offsetX),
65 offsetY_(offsetY),
68
69 /// Reports the *logical* size (see class comment) - not the same as
70 /// the internal clip bound SurfaceBase's inherited fillRect() etc. use,
71 /// when offsetX/offsetY aren't both 0.
72 size_t width() const override { return fullWidth_; }
73 size_t height() const override { return fullHeight_; }
74
75 void setPixel(size_t x, size_t y, RGB_T color) override {
76 const long bx = static_cast<long>(x) - offsetX_;
77 const long by = static_cast<long>(y) - offsetY_;
78 if (bx < 0 || by < 0) return;
79 if (static_cast<size_t>(bx) >= backing_.width() || static_cast<size_t>(by) >= backing_.height()) {
80 return;
81 }
82 backing_.setPixel(static_cast<size_t>(bx), static_cast<size_t>(by), color);
83 }
84
85 RGB_T getPixel(size_t x, size_t y) const override {
86 const long bx = static_cast<long>(x) - offsetX_;
87 const long by = static_cast<long>(y) - offsetY_;
88 if (bx < 0 || by < 0) return RGB_T();
89 if (static_cast<size_t>(bx) >= backing_.width() || static_cast<size_t>(by) >= backing_.height()) {
90 return RGB_T();
91 }
92 return backing_.getPixel(static_cast<size_t>(bx), static_cast<size_t>(by));
93 }
94
95 /// No storage of its own to (re)allocate - the internal clip bound is
96 /// fixed at construction (see above). Only exists to satisfy
97 /// SurfaceBase's pure-virtual resizeBuffer(); never meaningfully called.
98 bool resizeBuffer(size_t, size_t) override { return true; }
99
100 /// Not backed by one contiguous buffer, so there's no single pointer/
101 /// size to hand back - nothing in the normal widget draw() path calls
102 /// these (pixels go through setPixel()/getPixel() instead), so these
103 /// are stubs rather than real implementations.
104 const uint8_t* data() const override { return nullptr; }
105 size_t size() const override { return 0; }
106
107 /// Absolute-coordinate bounds check, consistent with setPixel()/
108 /// getPixel() above (and so with SurfaceBase's inherited fillRect()
109 /// etc., which clip against the same underlying width_/height_) -
110 /// deliberately *not* fullWidth_/fullHeight_, which is what a caller
111 /// (Dialog's/Drawer's own drawArc()-free code never calls contains()
112 /// directly, but this keeps the two consistent regardless).
113 bool contains(size_t x, size_t y) override {
114 return x < SurfaceBase<RGB_T>::width_ && y < SurfaceBase<RGB_T>::height_ &&
115 this->isClipVisible(x, y);
116 }
117
118 private:
119 ISurface<RGB_T>& backing_;
120 int32_t offsetX_;
121 int32_t offsetY_;
122 size_t fullWidth_;
123 size_t fullHeight_;
124};
125
126} // namespace tinygpu
Font rendering interface for TinyGPU-compatible framebuffers.
Definition: IFont.h:19
RGB color stored in 16-bit RGB565 format, byte-swapped from the conventional bit layout.
Definition: RGB565.h:24
Base class for 2D surfaces with drawing and text rendering support.
Definition: SurfaceBase.h:25
An ISurface that reports a full width x height extent but physically redirects every pixel write into...
Definition: WindowedSurface.h:53
bool contains(size_t x, size_t y) override
Definition: WindowedSurface.h:113
WindowedSurface(ISurface< RGB_T > &backing, int32_t offsetX, int32_t offsetY, size_t fullWidth, size_t fullHeight, IFont< RGB_T > &font)
Definition: WindowedSurface.h:59
size_t height() const override
Get the height of the surface.
Definition: WindowedSurface.h:73
const uint8_t * data() const override
Definition: WindowedSurface.h:104
bool resizeBuffer(size_t, size_t) override
Definition: WindowedSurface.h:98
size_t width() const override
Definition: WindowedSurface.h:72
void setPixel(size_t x, size_t y, RGB_T color) override
Definition: WindowedSurface.h:75
size_t size() const override
Provides the total data size in bytes.
Definition: WindowedSurface.h:105
RGB_T getPixel(size_t x, size_t y) const override
Abstract method: get the pixel color at (x, y).
Definition: WindowedSurface.h:85
Definition: DSIBusESP32.h:19