roo_display
API Documentation for roo_display
Loading...
Searching...
No Matches
clip_mask.h
Go to the documentation of this file.
1#pragma once
2
3#include <type_traits>
4
5#include "roo_backport.h"
6#include "roo_backport/byte.h"
9
10namespace roo_display {
11
12/// Binary clip mask using a packed bit buffer.
13///
14/// Each bit represents a pixel within `bounds`. Rows are byte-aligned. For
15/// best performance, align the mask to 8-pixel boundaries in device
16/// coordinates.
17class ClipMask {
18 public:
19 /// Construct a clip mask.
20 ClipMask(const roo::byte* data, Box bounds, bool inverted = false)
21 : data_(data),
22 bounds_(std::move(bounds)),
23 inverted_(inverted),
24 line_width_bytes_((bounds.width() + 7) / 8) {}
25
26 /// Deprecated: use the `roo::byte*` constructor instead.
27 template <typename U = uint8_t,
28 typename std::enable_if<!std::is_same<U, roo::byte>::value,
29 int>::type = 0>
32
33 /// Return raw data buffer.
34 const roo::byte* data() const { return data_; }
35 /// Return mask bounds.
36 const Box& bounds() const { return bounds_; }
37 /// Return inversion flag.
38 bool inverted() const { return inverted_; }
39
40 /// Return whether a point is masked out.
41 inline bool isMasked(int16_t x, int16_t y) const {
42 if (!bounds_.contains(x, y)) return inverted_;
43 x -= bounds_.xMin();
44 y -= bounds_.yMin();
45 uint32_t byte_offset = x / 8 + y * line_width_bytes_;
46 uint32_t pixel_index = x % 8;
47 roo::byte b = data_[byte_offset];
48 return (bool)(b & (roo::byte{0x80} >> pixel_index)) ^ inverted_;
49 }
50
51 /// Return whether all bits in a block are masked.
52 inline bool isAllMasked(int16_t x, int16_t y, roo::byte mask,
53 uint8_t lines) const {
54 return inverted_ ? isAllUnset(x, y, mask, ~lines)
55 : isAllSet(x, y, mask, lines);
56 }
57
58 /// Return whether all bits in a block are unmasked.
59 inline bool isAllUnmasked(int16_t x, int16_t y, roo::byte mask,
60 uint8_t lines) const {
61 return inverted_ ? isAllSet(x, y, mask, ~lines)
62 : isAllUnset(x, y, mask, lines);
63 }
64
65 /// Set inversion behavior.
66 void setInverted(bool inverted) { inverted_ = inverted; }
67
68 private:
69 inline bool isAllSet(int16_t x, int16_t y, roo::byte mask,
70 uint8_t lines) const {
71 const roo::byte* ptr = data_ + (x - bounds_.xMin()) / 8 +
72 (y - bounds_.yMin()) * line_width_bytes_;
73 while (lines-- > 0) {
74 if ((*ptr & mask) != mask) return false;
75 ptr += line_width_bytes_;
76 }
77 return true;
78 }
79
80 inline bool isAllUnset(int16_t x, int16_t y, roo::byte mask,
81 uint8_t lines) const {
82 const roo::byte* ptr = data_ + (x - bounds_.xMin()) / 8 +
83 (y - bounds_.yMin()) * line_width_bytes_;
84 while (lines-- > 0) {
85 if ((*ptr & mask) != roo::byte{0}) return false;
86 ptr += line_width_bytes_;
87 }
88 return true;
89 }
90
91 const roo::byte* data_;
92
93 Box bounds_;
94
95 // If true, the meaning of 'set' and 'unset' is inverted.
96 // Normally, 'set' = 'masked out'. If inverted, 'set' = 'unmasked'.
97 bool inverted_;
98
99 uint32_t line_width_bytes_;
100};
101
102/// Filtering device that applies a clip mask.
104 public:
106 int16_t dx = 0, int16_t dy = 0)
107 : output_(output),
108 clip_mask_(clip_mask->data(), clip_mask->bounds().translate(dx, dy),
109 clip_mask->inverted()),
110 address_window_(0, 0, 0, 0),
111 cursor_x_(0),
112 cursor_y_(0) {}
113
114 virtual ~ClipMaskFilter() {}
115
117 BlendingMode mode) override {
118 address_window_ = Box(x0, y0, x1, y1);
119 blending_mode_ = mode;
120 cursor_x_ = x0;
121 cursor_y_ = y0;
122 }
123
124 void write(Color* color, uint32_t pixel_count) override {
125 // Naive implementation, for now.
126 uint32_t i = 0;
127 BufferedPixelWriter writer(output_, blending_mode_);
128 while (i < pixel_count) {
129 if (!clip_mask_.isMasked(cursor_x_, cursor_y_)) {
130 writer.writePixel(cursor_x_, cursor_y_, color[i]);
131 }
132 if (++cursor_x_ > address_window_.xMax()) {
133 ++cursor_y_;
134 cursor_x_ = address_window_.xMin();
135 }
136 ++i;
137 }
138 }
139
140 void fill(Color color, uint32_t pixel_count) override {
141 uint32_t i = 0;
142 BufferedPixelFiller filler(output_, color, blending_mode_);
143 while (i < pixel_count) {
144 if (!clip_mask_.isMasked(cursor_x_, cursor_y_)) {
145 filler.fillPixel(cursor_x_, cursor_y_);
146 }
147 if (++cursor_x_ > address_window_.xMax()) {
148 ++cursor_y_;
149 cursor_x_ = address_window_.xMin();
150 }
151 ++i;
152 }
153 }
154
155 void writeRects(BlendingMode mode, Color* color, int16_t* x0, int16_t* y0,
156 int16_t* x1, int16_t* y1, uint16_t count) override {
157 while (count-- > 0) {
158 fillSingleRect(mode, *color++, *x0++, *y0++, *x1++, *y1++);
159 }
160 }
161
162 void fillRects(BlendingMode mode, Color color, int16_t* x0, int16_t* y0,
163 int16_t* x1, int16_t* y1, uint16_t count) override {
164 while (count-- > 0) {
165 fillSingleRect(mode, color, *x0++, *y0++, *x1++, *y1++);
166 }
167 }
168
169 void writePixels(BlendingMode mode, Color* color, int16_t* x, int16_t* y,
170 uint16_t pixel_count) override {
171 int16_t* x_out = x;
172 int16_t* y_out = y;
175 for (uint16_t i = 0; i < pixel_count; ++i) {
176 if (!clip_mask_.isMasked(x[i], y[i])) {
177 *x_out++ = x[i];
178 *y_out++ = y[i];
179 *color_out++ = color[i];
181 }
182 }
183 if (new_pixel_count > 0) {
184 output_.writePixels(mode, color, x, y, new_pixel_count);
185 }
186 }
187
188 void fillPixels(BlendingMode mode, Color color, int16_t* x, int16_t* y,
189 uint16_t pixel_count) override {
190 int16_t* x_out = x;
191 int16_t* y_out = y;
193 for (uint16_t i = 0; i < pixel_count; ++i) {
194 if (!clip_mask_.isMasked(x[i], y[i])) {
195 *x_out++ = x[i];
196 *y_out++ = y[i];
198 }
199 }
200 if (new_pixel_count > 0) {
201 output_.fillPixels(mode, color, x, y, new_pixel_count);
202 }
203 }
204
205 const ColorFormat& getColorFormat() const override {
206 return output_.getColorFormat();
207 }
208
215
216 private:
217 void fillSingleRect(BlendingMode mode, Color color, int16_t x0, int16_t y0,
218 int16_t x1, int16_t y1) {
219 // Note: we need to flush these every rect, because the rectangles may
220 // be overlapping. (A possible alternative would be to only use
221 // rectfiller).
222 BufferedPixelFiller pfiller(output_, color, mode);
223 BufferedRectFiller rfiller(output_, color, mode);
224 const Box& bounds = clip_mask_.bounds();
225 if (y0 < bounds.yMin()) {
226 if (y1 < bounds.yMin()) {
227 if (!clip_mask_.inverted()) {
228 rfiller.fillRect(x0, y0, x1, y1);
229 }
230 return;
231 }
232 if (!clip_mask_.inverted()) {
233 rfiller.fillRect(x0, y0, x1, bounds.yMin() - 1);
234 }
235 y0 = bounds.yMin();
236 }
237 if (x0 < bounds.xMin()) {
238 if (x1 < bounds.xMin()) {
239 if (!clip_mask_.inverted()) {
240 rfiller.fillRect(x0, y0, x1, y1);
241 }
242 return;
243 }
244 if (!clip_mask_.inverted()) {
245 rfiller.fillRect(x0, y0, bounds.xMin() - 1, y1);
246 }
247 x0 = bounds.xMin();
248 }
249 if (x1 > bounds.xMax()) {
250 if (x0 > bounds.xMax()) {
251 if (!clip_mask_.inverted()) {
252 rfiller.fillRect(x0, y0, x1, y1);
253 }
254 return;
255 }
256 if (!clip_mask_.inverted()) {
257 rfiller.fillRect(bounds.xMax() + 1, y0, x1, y1);
258 }
259 x1 = bounds.xMax();
260 }
261 if (y1 > bounds.yMax()) {
262 if (y0 > bounds.yMax()) {
263 if (!clip_mask_.inverted()) {
264 rfiller.fillRect(x0, y0, x1, y1);
265 }
266 return;
267 }
268 if (!clip_mask_.inverted()) {
269 rfiller.fillRect(x0, bounds.yMax() + 1, x1, y1);
270 }
271 y1 = bounds.yMax();
272 }
273 // Now, [x0, y0, x1, y1] is entirely within bounds.
274 // Iterate in 8x8 blocks, filling (or skipping) the entire block when at all
275 // possible.
276 uint8_t yshift = (y0 - bounds.yMin()) % 8;
277 for (int16_t yc0 = y0; yc0 <= y1;) {
278 int16_t yc1 = yc0 - yshift + 7;
279 if (yc1 > y1) yc1 = y1;
280 uint8_t lines = yc1 - yc0 + 1;
281 uint8_t xshift = (x0 - bounds.xMin()) % 8;
282 for (int16_t xc0 = x0; xc0 <= x1;) {
283 int16_t xc1 = xc0 - xshift + 7;
284 roo::byte mask = roo::byte{0xFF} >> xshift;
285 if (xc1 > x1) {
286 mask &= (roo::byte{0xFF} << (xc1 - x1));
287 xc1 = x1;
288 }
289 if (!clip_mask_.isAllMasked(xc0, yc0, mask, lines)) {
290 if (clip_mask_.isAllUnmasked(xc0, yc0, mask, lines)) {
291 rfiller.fillRect(xc0, yc0, xc1, yc1);
292 } else {
293 // Degenerate to the slow version.
294 for (int16_t y = yc0; y <= yc1; ++y) {
295 for (int16_t x = xc0; x <= xc1; ++x) {
296 if (!clip_mask_.isMasked(x, y)) {
297 pfiller.fillPixel(x, y);
298 }
299 }
300 }
301 }
302 }
303
304 xc0 = xc0 - xshift + 8;
305 xshift = 0;
306 }
307 yc0 = yc0 - yshift + 8;
308 yshift = 0;
309 }
310 }
311
312 const Box& bounds() const { return clip_mask_.bounds(); }
313
314 DisplayOutput& output_;
315 ClipMask clip_mask_;
316 Box address_window_;
317 BlendingMode blending_mode_;
318 int16_t cursor_x_;
319 int16_t cursor_y_;
320};
321
322} // namespace roo_display
BufferedRectWriter & writer
Axis-aligned integer rectangle.
Definition box.h:12
int16_t xMin() const
Minimum x (inclusive).
Definition box.h:65
int16_t xMax() const
Maximum x (inclusive).
Definition box.h:71
bool contains(int16_t x, int16_t y) const
Return whether the point (x, y) lies within the box.
Definition box.h:86
int16_t yMax() const
Maximum y (inclusive).
Definition box.h:74
int16_t yMin() const
Minimum y (inclusive).
Definition box.h:68
Buffered filler for arbitrary pixels using a single color.
Buffered writer for arbitrary pixels with per-pixel colors.
Filtering device that applies a clip mask.
Definition clip_mask.h:103
void setAddress(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, BlendingMode mode) override
Set a rectangular window filled by subsequent calls to write().
Definition clip_mask.h:116
void write(Color *color, uint32_t pixel_count) override
Write pixels into the current address window.
Definition clip_mask.h:124
void writeRects(BlendingMode mode, Color *color, int16_t *x0, int16_t *y0, int16_t *x1, int16_t *y1, uint16_t count) override
Draw the specified rectangles (per-rectangle colors). Invalidates the address window.
Definition clip_mask.h:155
ClipMaskFilter(DisplayOutput &output, const ClipMask *clip_mask, int16_t dx=0, int16_t dy=0)
Definition clip_mask.h:105
void drawDirectRect(const roo::byte *data, size_t row_width_bytes, int16_t src_x0, int16_t src_y0, int16_t src_x1, int16_t src_y1, int16_t dst_x0, int16_t dst_y0) override
Draw a rectangle represented in the device's native color format.
Definition clip_mask.h:209
const ColorFormat & getColorFormat() const override
Return the native color format used by this device for direct drawing.
Definition clip_mask.h:205
void fill(Color color, uint32_t pixel_count) override
Write pixel_count copies of the same color into the current address window.
Definition clip_mask.h:140
void writePixels(BlendingMode mode, Color *color, int16_t *x, int16_t *y, uint16_t pixel_count) override
Draw the specified pixels (per-pixel colors). Invalidates the address window.
Definition clip_mask.h:169
void fillPixels(BlendingMode mode, Color color, int16_t *x, int16_t *y, uint16_t pixel_count) override
Draw the specified pixels using the same color. Invalidates the address window.
Definition clip_mask.h:188
void fillRects(BlendingMode mode, Color color, int16_t *x0, int16_t *y0, int16_t *x1, int16_t *y1, uint16_t count) override
Draw the specified rectangles using the same color. Invalidates the address window.
Definition clip_mask.h:162
Binary clip mask using a packed bit buffer.
Definition clip_mask.h:17
bool isAllMasked(int16_t x, int16_t y, roo::byte mask, uint8_t lines) const
Return whether all bits in a block are masked.
Definition clip_mask.h:52
bool isMasked(int16_t x, int16_t y) const
Return whether a point is masked out.
Definition clip_mask.h:41
bool inverted() const
Return inversion flag.
Definition clip_mask.h:38
const roo::byte * data() const
Return raw data buffer.
Definition clip_mask.h:34
ClipMask(const U *data, Box bounds)
Deprecated: use the roo::byte* constructor instead.
Definition clip_mask.h:30
void setInverted(bool inverted)
Set inversion behavior.
Definition clip_mask.h:66
bool isAllUnmasked(int16_t x, int16_t y, roo::byte mask, uint8_t lines) const
Return whether all bits in a block are unmasked.
Definition clip_mask.h:59
const Box & bounds() const
Return mask bounds.
Definition clip_mask.h:36
ClipMask(const roo::byte *data, Box bounds, bool inverted=false)
Construct a clip mask.
Definition clip_mask.h:20
ARGB8888 color stored as a 32-bit unsigned integer.
Definition color.h:16
The abstraction for drawing to a display.
Definition device.h:15
virtual const ColorFormat & getColorFormat() const =0
Return the native color format used by this device for direct drawing.
virtual void drawDirectRect(const roo::byte *data, size_t row_width_bytes, int16_t src_x0, int16_t src_y0, int16_t src_x1, int16_t src_y1, int16_t dst_x0, int16_t dst_y0)
Draw a rectangle represented in the device's native color format.
Definition device.cpp:33
virtual void fillPixels(BlendingMode blending_mode, Color color, int16_t *x, int16_t *y, uint16_t pixel_count)=0
Draw the specified pixels using the same color. Invalidates the address window.
virtual void writePixels(BlendingMode blending_mode, Color *color, int16_t *x, int16_t *y, uint16_t pixel_count)=0
Draw the specified pixels (per-pixel colors). Invalidates the address window.
Defines 140 opaque HTML named colors.
BlendingMode
Porter-Duff style blending modes.
Definition blending.h:17
#define const
Definition zconf.h:230