roo_display
API Documentation for roo_display
Loading...
Searching...
No Matches
drawable.h
Go to the documentation of this file.
1#pragma once
2
6#include "roo_logging/stream.h"
7
8namespace roo_display {
9
10class DisplayOutput;
11class Drawable;
12
13/// Specifies whether a `Drawable` should fill its entire extents box,
14/// including fully transparent pixels.
15enum class FillMode {
16 /// Fill the entire extents box (possibly with fully transparent pixels).
17 ///
18 /// Useful when drawing over a synthetic background and you want previous
19 /// content replaced with that background.
20 kExtents = 0,
21
22 /// Fully transparent pixels do not need to be filled.
23 kVisible = 1,
24};
25
26[[deprecated("Use `FillMode::kExtents` instead.")]]
28[[deprecated("Use `FillMode::kVisible` instead.")]]
30
31roo_logging::Stream &operator<<(roo_logging::Stream &os, FillMode mode);
32
33class Rasterizable;
34
35/// Low-level handle used to draw to an underlying device.
36///
37/// This is passed by the library to `Drawable::drawTo()`; do not create it
38/// directly.
39///
40/// The device uses absolute coordinates starting at (0, 0). To translate an
41/// object's extents into device coordinates, apply the surface offsets:
42///
43/// extents.translate(s.dx(), s.dy())
44///
45/// Constrain to `clip_box()` when drawing:
46///
47/// Box::Intersect(s.clip_box(), extents.translate(s.dx(), s.dy()))
48///
49/// To quickly reject objects outside the clip box:
50///
51/// Box bounds =
52/// Box::Intersect(s.clip_box(), extents.translate(s.dx(), s.dy()));
53/// if (bounds.empty()) return;
54///
55/// To compute clipped bounds in object coordinates:
56///
57/// Box clipped =
58/// Box::Intersect(s.clip_box().translate(-s.dx(), -s.dy()), extents);
59///
60class Surface {
61 public:
62 /// Create a surface targeting a device output.
63 ///
64 /// @param out Output device.
65 /// @param dx X offset to apply to drawn objects.
66 /// @param dy Y offset to apply to drawn objects.
67 /// @param clip Clip box in device coordinates.
68 /// @param is_write_once Whether this surface is write-once.
69 /// @param bg Background color used for blending.
70 /// @param fill_mode Fill behavior for transparent pixels.
71 /// @param blending_mode DefaFillMode::kVisiblee.
73 bool is_write_once, Color bg = color::Transparent,
76 : out_(&out),
77 dx_(dx),
78 dy_(dy),
79 clip_box_(std::move(clip)),
80 is_write_once_(is_write_once),
81 bgcolor_(bg),
82 fill_mode_(fill_mode),
83 blending_mode_(blending_mode) {
84 if (bg.a() == 0xFF) {
86 }
87 }
88
89 /// Create a surface targeting a device output with no offset.
90 ///
91 /// @param out Output device.
92 /// @param clip Clip box in device coordinates.
93 /// @param is_write_once Whether this surface is write-once.
94 /// @param bg Background color used for blending.
95 /// @param fill_mode Fill behavior for transparent pixels.FillMode::kVisible
96 /// @param blending_mode Default blending mode.
98 Color bg = color::Transparent,
101 : out_(out),
102 dx_(0),
103 dy_(0),
104 clip_box_(std::move(clip)),
105 is_write_once_(is_write_once),
106 bgcolor_(bg),
107 fill_mode_(fill_mode),
108 blending_mode_(blending_mode) {
109 if (bg.a() == 0xFF && (blending_mode == BlendingMode::kSourceOver ||
112 }
113 }
114
115 Surface(Surface &&other) = default;
116 Surface(const Surface &other) = default;
117
118 /// Return the device output.
119 DisplayOutput &out() const { return *out_; }
120
121 /// Replace the output device pointer.
122 void set_out(DisplayOutput *out) { out_ = out; }
123
124 /// Return the x offset to apply to drawn objects.
125 int16_t dx() const { return dx_; }
126
127 /// Return the y offset to apply to drawn objects.
128 int16_t dy() const { return dy_; }
129
130 /// Return whether this surface is write-once.
131 bool is_write_once() const { return is_write_once_; }
132
133 /// Return the clip box in device coordinates (independent of offsets).
134 const Box &clip_box() const { return clip_box_; }
135
136 /// Set the clip box in device coordinates.
137 void set_clip_box(const Box &clip_box) { clip_box_ = clip_box; }
138
139 /// Return the background color used for blending.
140 Color bgcolor() const { return bgcolor_; }
141
142 /// Set the background color used for blending.
143 void set_bgcolor(Color bgcolor) { bgcolor_ = bgcolor; }
144
145 /// Return the fill mode the drawable should observe.
146 /// FillMode::kVisible
147 /// If `FillMode::kExtents`, the drawable must fill its entire (clipped)
148 /// extents even if some pixels are completely transparent. If
149 /// `FillMode::kVisible`, the drawable may omit fully transparent pixels.
150 /// This assumes the appropriate background has been pre-applied.
151 FillMode fill_mode() const { return fill_mode_; }
152
153 /// Set the fill mode for drawables.
155
156 /// Return the default blending mode for drawing.
157 ///
158 /// If the mode is `BlendingMode::kSourceOver`, a drawable may replace it
159 /// with `BlendingMode::kSource` when all pixels it writes are fully opaque.
160 /// If an opaque background is specified, `BlendingMode::kSourceOver` is
161 /// automatically replaced with `BlendingMode::kSource`.
162 BlendingMode blending_mode() const { return blending_mode_; }
163
164 /// Set the default blending mode.
166 blending_mode_ = blending_mode;
167 }
168
169 /// Set the x offset applied to drawables.
170 void set_dx(int16_t dx) { dx_ = dx; }
171 /// Set the y offset applied to drawables.
172 void set_dy(int16_t dy) { dy_ = dy; }
173
174 /// Draw a drawable object to this surface.
175 ///
176 /// Intended for custom `Drawable` implementations to draw child objects.
177 inline void drawObject(const Drawable &object) const;
178
179 /// Draw a drawable object with an additional offset.
180 ///
181 /// Convenience wrapper around `drawObject()`.
182 inline void drawObject(const Drawable &object, int16_t dx, int16_t dy) const {
183 if (dx == 0 && dy == 0) {
184 drawObject(object);
185 return;
186 }
187 Surface s = *this;
188 s.set_dx(s.dx() + dx);
189 s.set_dy(s.dy() + dy);
190 s.drawObject(object);
191 }
192
193 /// Clip the given extents to the surface clip box.
195 return clip_box_.clip(extents.translate(dx_, dy_));
196 }
197
198 private:
199 friend class DrawingContext;
200
201 DisplayOutput &output() const { return *out_; }
202 Box extents() const { return clip_box_.translate(-dx_, -dy_); }
203 void nest() const {}
204 void unnest() const {}
205 const Rasterizable *getRasterizableBackground() const { return nullptr; }
206 Color getBackgroundColor() const { return bgcolor_; }
207
208 DisplayOutput *out_;
209 int16_t dx_;
210 int16_t dy_;
211 Box clip_box_;
212 bool is_write_once_;
213 Color bgcolor_;
214 FillMode fill_mode_;
215 BlendingMode blending_mode_;
216};
217
218/// Interface for objects that can be drawn to an output device.
219///
220/// Examples include images, shapes, and UI widgets. Some drawables also
221/// implement Streamable (including Rasterizable types). Streamable and
222/// Rasterizable objects can be used in specialized contexts that generic
223/// drawables cannot, such as backgrounds, overlays, and streamable or
224/// rasterizable stacks.
225///
226/// Implementations must:
227/// - Override `extents()` to return the bounding box at (0, 0).
228/// - Override either `drawTo()` or `drawInteriorTo()` to implement drawing.
229class Drawable {
230 public:
231 virtual ~Drawable() {}
232
233 /// Return the bounding box encompassing all pixels that need to be drawn.
234 ///
235 /// This method is called during a transaction and must not block or perform
236 /// I/O.
237 virtual Box extents() const = 0;
238
239 /// Return the bounds used for alignment.
240 ///
241 /// Defaults to `extents()`. Some drawables (notably text labels) may want
242 /// different alignment bounds.
243 ///
244 /// This method is called during a transaction and must not block or perform
245 /// I/O.
246 virtual Box anchorExtents() const { return extents(); }
247
248 /// A singleton representing a no-op drawable with no bounding box.
249 ///
250 /// @return Pointer to a shared empty drawable instance.
251 static const Drawable *Empty();
252
253 private:
254 friend void Surface::drawObject(const Drawable &object) const;
255
256 /// Draw this object's content, respecting the fill mode.
257 ///
258 /// If `s.fill_mode() == FillMode::kExtents`, the method must fill the entire
259 /// (clipped) `extents()` rectangle (using `s.bgcolor()` for transparent
260 /// parts).
261 ///
262 /// The default implementation fills the clipped `extents()` rectangle with
263 /// `bgcolor` and then calls `drawInteriorTo()`. That can cause flicker, so
264 /// override this method if a better implementation is possible.
265 ///
266 /// The implementation must respect surface parameters, particularly
267 /// `clip_box()`, and must not draw outside it. The surface clip box is
268 /// pre-clipped to fit within this drawable's `extents()`.
269 virtual void drawTo(const Surface &s) const;
270
271 /// Draw this object's content, ignoring `s.fill_mode()`. See `drawTo()`.
272 ///
273 /// The implementation must respect surface parameters, particularly
274 /// `clip_box()`, and must not draw outside it.
275 virtual void drawInteriorTo(const Surface &s) const {}
276};
277
278inline void Surface::drawObject(const Drawable &object) const {
279 Surface s = *this;
280 if (s.clipToExtents(object.extents()) == Box::ClipResult::kEmpty) return;
281 object.drawTo(s);
282}
283
284} // namespace roo_display
Axis-aligned integer rectangle.
Definition box.h:12
ClipResult
Result of clipping a box to a clip region.
Definition box.h:15
Box translate(int16_t x_offset, int16_t y_offset) const
Return a translated copy of this box.
Definition box.h:127
ClipResult clip(const Box &clip_box)
Clip this box to the given clip box.
Definition box.h:105
ARGB8888 color stored as a 32-bit unsigned integer.
Definition color.h:16
The abstraction for drawing to a display.
Definition device.h:15
Interface for objects that can be drawn to an output device.
Definition drawable.h:229
virtual Box anchorExtents() const
Return the bounds used for alignment.
Definition drawable.h:246
static const Drawable * Empty()
A singleton representing a no-op drawable with no bounding box.
Definition drawable.cpp:40
virtual Box extents() const =0
Return the bounding box encompassing all pixels that need to be drawn.
Primary top-level interface for drawing to screens, off-screen buffers, or other devices.
Drawable that can provide a color for any point within its extents.
Low-level handle used to draw to an underlying device.
Definition drawable.h:60
void set_bgcolor(Color bgcolor)
Set the background color used for blending.
Definition drawable.h:143
void set_out(DisplayOutput *out)
Replace the output device pointer.
Definition drawable.h:122
int16_t dy() const
Return the y offset to apply to drawn objects.
Definition drawable.h:128
Surface(Surface &&other)=default
void drawObject(const Drawable &object, int16_t dx, int16_t dy) const
Draw a drawable object with an additional offset.
Definition drawable.h:182
Color bgcolor() const
Return the background color used for blending.
Definition drawable.h:140
Box::ClipResult clipToExtents(Box extents)
Clip the given extents to the surface clip box.
Definition drawable.h:194
void set_dy(int16_t dy)
Set the y offset applied to drawables.
Definition drawable.h:172
void set_fill_mode(FillMode fill_mode)
Set the fill mode for drawables.
Definition drawable.h:154
Surface(const Surface &other)=default
const Box & clip_box() const
Return the clip box in device coordinates (independent of offsets).
Definition drawable.h:134
void set_blending_mode(BlendingMode blending_mode)
Set the default blending mode.
Definition drawable.h:165
int16_t dx() const
Return the x offset to apply to drawn objects.
Definition drawable.h:125
void set_clip_box(const Box &clip_box)
Set the clip box in device coordinates.
Definition drawable.h:137
bool is_write_once() const
Return whether this surface is write-once.
Definition drawable.h:131
Surface(DisplayOutput *out, Box clip, bool is_write_once, Color bg=color::Transparent, FillMode fill_mode=FillMode::kVisible, BlendingMode blending_mode=BlendingMode::kSourceOver)
Create a surface targeting a device output with no offset.
Definition drawable.h:97
BlendingMode blending_mode() const
Return the default blending mode for drawing.
Definition drawable.h:162
Surface(DisplayOutput &out, int16_t dx, int16_t dy, Box clip, bool is_write_once, Color bg=color::Transparent, FillMode fill_mode=FillMode::kVisible, BlendingMode blending_mode=BlendingMode::kSourceOver)
Create a surface targeting a device output.
Definition drawable.h:72
void drawObject(const Drawable &object) const
Draw a drawable object to this surface.
Definition drawable.h:278
void set_dx(int16_t dx)
Set the x offset applied to drawables.
Definition drawable.h:170
FillMode fill_mode() const
Return the fill mode the drawable should observe. FillMode::kVisible If FillMode::kExtents,...
Definition drawable.h:151
DisplayOutput & out() const
Return the device output.
Definition drawable.h:119
Defines 140 opaque HTML named colors.
BlendingMode
Porter-Duff style blending modes.
Definition blending.h:17
@ kSourceOverOpaque
Similar to kSourceOver, but assumes that the destination is opaque.
@ kSource
The new ARGB8888 value completely replaces the old one.
@ kSourceOver
Source is placed (alpha-blended) over the destination. This is the default blending mode.
roo_logging::Stream & operator<<(roo_logging::Stream &os, BlendingMode mode)
Definition blending.cpp:54
constexpr FillMode FILL_MODE_VISIBLE
Definition drawable.h:29
FillMode
Specifies whether a Drawable should fill its entire extents box, including fully transparent pixels.
Definition drawable.h:15
@ kVisible
Fully transparent pixels do not need to be filled.
@ kExtents
Fill the entire extents box (possibly with fully transparent pixels).
constexpr FillMode FILL_MODE_RECTANGLE
Definition drawable.h:27