roo_display
API Documentation for roo_display
Loading...
Searching...
No Matches
blender.h
Go to the documentation of this file.
1#pragma once
2
6
7namespace roo_display {
8
9/// Filtering device that blends with a rasterizable image.
10///
11/// The Blender template matches `BlendOp`:
12/// ```
13/// struct T {
14/// Color operator()(Color dst, Color src) const;
15/// };
16/// ```
17/// where `dst` is the raster color and `src` is the drawable color.
18template <typename Blender>
20 public:
21 /// Create a blending filter with default blender and no offset.
23 Color bgcolor = color::Transparent)
24 : BlendingFilter(output, Blender(), raster, bgcolor) {}
25
26 /// Create a blending filter with an offset.
28 int16_t dy, Color bgcolor = color::Transparent)
29 : BlendingFilter(output, Blender(), raster, dx, dy, bgcolor) {}
30
31 /// Create a blending filter with a custom blender.
33 const Rasterizable* raster, Color bgcolor = color::Transparent)
34 : BlendingFilter(output, std::move(blender), raster, 0, 0, bgcolor) {}
35
36 /// Create a blending filter with a custom blender and offset.
38 const Rasterizable* raster, int16_t dx, int16_t dy,
39 Color bgcolor = color::Transparent)
40 : output_(&output),
41 blender_(std::move(blender)),
42 raster_(raster),
43 address_window_(0, 0, 0, 0),
44 cursor_x_(0),
45 cursor_y_(0),
46 dx_(dx),
47 dy_(dy),
48 bgcolor_(bgcolor) {}
49
50 virtual ~BlendingFilter() {}
51
52 /// Replace the underlying output.
53 void setOutput(DisplayOutput& output) { output_ = &output; }
54
56 BlendingMode mode) override {
57 address_window_ = Box(x0 - dx_, y0 - dy_, x1 - dx_, y1 - dy_);
58 cursor_x_ = x0 - dx_;
59 cursor_y_ = y0 - dy_;
60 output_->setAddress(x0, y0, x1, y1, mode);
61 }
62
63 void write(Color* color, uint32_t pixel_count) override {
64 if (pixel_count == 0) return;
68
69 for (uint32_t i = 0; i < pixel_count; ++i) {
70 x[i] = cursor_x_++;
71 y[i] = cursor_y_;
72 if (cursor_x_ > address_window_.xMax()) {
73 cursor_y_++;
74 cursor_x_ = address_window_.xMin();
75 }
76 }
77 // TODO: detect (common) cases when all pixels are out of bounds.
79 for (uint32_t i = 0; i < pixel_count; ++i) {
80 newcolor[i] = blender_(newcolor[i], color[i]);
81 }
82 if (bgcolor_ != color::Transparent) {
83 for (uint32_t i = 0; i < pixel_count; ++i) {
84 newcolor[i] = AlphaBlend(bgcolor_, newcolor[i]);
85 }
86 }
87 output_->write(newcolor, pixel_count);
88 }
89
90 void fill(Color color, uint32_t pixel_count) override {
91 if (pixel_count == 0) return;
95
96 for (uint32_t i = 0; i < pixel_count; ++i) {
97 x[i] = cursor_x_++;
98 y[i] = cursor_y_;
99 if (cursor_x_ > address_window_.xMax()) {
100 cursor_y_++;
101 cursor_x_ = address_window_.xMin();
102 }
103 }
105 for (uint32_t i = 0; i < pixel_count; ++i) {
106 newcolor[i] = blender_(newcolor[i], color);
107 }
108 if (bgcolor_ != color::Transparent) {
109 for (uint32_t i = 0; i < pixel_count; ++i) {
110 newcolor[i] = AlphaBlend(bgcolor_, newcolor[i]);
111 }
112 }
113 output_->write(newcolor, pixel_count);
114 }
115
116 // void fill(BlendingMode mode, Color color, uint32_t pixel_count) override {
117 // // Naive implementation, for now.
118 // uint32_t i = 0;
119 // BufferedPixelFiller filler(output_, color, mode);
120 // while (i < pixel_count) {
121 // if (clip_mask_->isSet(cursor_x_, cursor_y_)) {
122 // filler.fillPixel(cursor_x_, cursor_y_);
123 // }
124 // if (++cursor_x_ > address_window_.xMax()) {
125 // ++cursor_y_;
126 // cursor_x_ = address_window_.xMin();
127 // }
128 // ++i;
129 // }
130 // }
131
132 void writeRects(BlendingMode mode, Color* color, int16_t* x0, int16_t* y0,
133 int16_t* x1, int16_t* y1, uint16_t count) override {
134 while (count-- > 0) {
135 fillRect(mode, *x0++, *y0++, *x1++, *y1++, *color++);
136 }
137 }
138
139 void fillRects(BlendingMode mode, Color color, int16_t* x0, int16_t* y0,
140 int16_t* x1, int16_t* y1, uint16_t count) override {
141 while (count-- > 0) {
142 fillRect(mode, *x0++, *y0++, *x1++, *y1++, color);
143 }
144 }
145
146 void writePixels(BlendingMode mode, Color* color, int16_t* x, int16_t* y,
147 uint16_t pixel_count) override {
148 if (pixel_count == 0) return;
150 read(x, y, pixel_count, newcolor);
151 for (uint32_t i = 0; i < pixel_count; ++i) {
152 newcolor[i] = blender_(newcolor[i], color[i]);
153 }
154 if (bgcolor_ != color::Transparent) {
155 for (uint32_t i = 0; i < pixel_count; ++i) {
156 newcolor[i] = AlphaBlend(bgcolor_, newcolor[i]);
157 }
158 }
159 output_->writePixels(mode, newcolor, x, y, pixel_count);
160 }
161
162 void fillPixels(BlendingMode mode, Color color, int16_t* x, int16_t* y,
163 uint16_t pixel_count) override {
164 if (pixel_count == 0) return;
166 read(x, y, pixel_count, newcolor);
167 for (uint32_t i = 0; i < pixel_count; ++i) {
168 newcolor[i] = blender_(newcolor[i], color);
169 }
170 if (bgcolor_ != color::Transparent) {
171 for (uint32_t i = 0; i < pixel_count; ++i) {
172 newcolor[i] = AlphaBlend(bgcolor_, newcolor[i]);
173 }
174 }
175 output_->writePixels(mode, newcolor, x, y, pixel_count);
176 }
177
178 const ColorFormat& getColorFormat() const override {
179 return output_->getColorFormat();
180 }
181
189
190 private:
191 void read(int16_t* x, int16_t* y, uint16_t pixel_count, Color* result) {
192 if (dx_ == 0 && dy_ == 0) {
194 } else {
195 // NOTE(dawidk): to conserve stack, this could be done in-place and then
196 // undone, although it would take 2x longer.
197 int16_t xp[pixel_count];
198 int16_t yp[pixel_count];
199 for (uint32_t i = 0; i < pixel_count; ++i) {
200 xp[i] = x[i] - dx_;
201 yp[i] = y[i] - dy_;
202 }
203 raster_->readColorsMaybeOutOfBounds(xp, yp, pixel_count, result);
204 }
205 }
206
207 void fillRect(BlendingMode mode, int16_t xMin, int16_t yMin, int16_t xMax,
208 int16_t yMax, Color color) {
209 Color out_of_bounds_color = AlphaBlend(bgcolor_, color);
210 Box trimmed = Box::Intersect(Box(xMin, yMin, xMax, yMax),
211 raster_->extents().translate(dx_, dy_));
212 if (trimmed.empty()) {
213 output_->fillRect(mode, xMin, yMin, xMax, yMax, out_of_bounds_color);
214 return;
215 }
216 if (yMin < trimmed.yMin()) {
217 // Draw top bar of the border.
218 output_->fillRect(mode, xMin, yMin, xMax, trimmed.yMin() - 1,
219 out_of_bounds_color);
220 }
221 if (xMin < trimmed.xMin()) {
222 // Draw left bar of the border.
223 output_->fillRect(mode, xMin, trimmed.yMin(), trimmed.xMin() - 1,
224 trimmed.yMax(), out_of_bounds_color);
225 }
226 fillRectIntersectingRaster(mode, trimmed.xMin(), trimmed.yMin(),
227 trimmed.xMax(), trimmed.yMax(), color);
228 if (xMax > trimmed.xMax()) {
229 // Draw right bar of the border.
230 output_->fillRect(mode, trimmed.xMax() + 1, trimmed.yMin(), xMax,
231 trimmed.yMax(), out_of_bounds_color);
232 }
233 if (yMax > trimmed.yMax()) {
234 // Draw bottom bar of the border.
235 output_->fillRect(mode, xMin, trimmed.yMax() + 1, xMax, yMax,
236 out_of_bounds_color);
237 }
238 }
239
240 void fillRectIntersectingRaster(BlendingMode mode, int16_t xMin, int16_t yMin,
241 int16_t xMax, int16_t yMax, Color color) {
242 {
243 uint32_t pixel_count = (xMax - xMin + 1) * (yMax - yMin + 1);
244 if (pixel_count <= 64) {
245 fillRectInternal(mode, xMin, yMin, xMax, yMax, color);
246 return;
247 }
248 }
249 const int16_t xMinOuter = (xMin / 8) * 8;
250 const int16_t yMinOuter = (yMin / 8) * 8;
251 const int16_t xMaxOuter = (xMax / 8) * 8 + 7;
252 const int16_t yMaxOuter = (yMax / 8) * 8 + 7;
253 for (int16_t y = yMinOuter; y < yMaxOuter; y += 8) {
254 for (int16_t x = xMinOuter; x < xMaxOuter; x += 8) {
255 fillRectInternal(mode, std::max(x, xMin), std::max(y, yMin),
256 std::min((int16_t)(x + 7), xMax),
257 std::min((int16_t)(y + 7), yMax), color);
258 }
259 }
260 }
261
262 // Called for rectangles with area <= 64 pixels.
263 void fillRectInternal(BlendingMode mode, int16_t xMin, int16_t yMin,
264 int16_t xMax, int16_t yMax, Color color) {
265 uint16_t pixel_count = (xMax - xMin + 1) * (yMax - yMin + 1);
266 Color newcolor[pixel_count];
267 bool same = raster_->readColorRect(xMin - dx_, yMin - dy_, xMax - dx_,
268 yMax - dy_, newcolor);
269 if (same) {
270 output_->fillRect(mode, Box(xMin, yMin, xMax, yMax),
271 AlphaBlend(bgcolor_, blender_(newcolor[0], color)));
272 } else {
273 for (uint16_t i = 0; i < pixel_count; ++i) {
274 newcolor[i] = blender_(newcolor[i], color);
275 }
276 if (bgcolor_ != color::Transparent) {
277 for (uint16_t i = 0; i < pixel_count; ++i) {
278 newcolor[i] = AlphaBlend(bgcolor_, newcolor[i]);
279 }
280 }
281 output_->setAddress(Box(xMin, yMin, xMax, yMax), mode);
282 output_->write(newcolor, pixel_count);
283 }
284 }
285
286 DisplayOutput* output_;
287 Blender blender_;
288 const Rasterizable* raster_;
289 Box address_window_;
290 int16_t cursor_x_;
291 int16_t cursor_y_;
292 int16_t dx_;
293 int16_t dy_;
294 Color bgcolor_;
295};
296
297} // namespace roo_display
Filtering device that blends with a rasterizable image.
Definition blender.h:19
BlendingFilter(DisplayOutput &output, const Rasterizable *raster, Color bgcolor=color::Transparent)
Create a blending filter with default blender and no offset.
Definition blender.h:22
void setOutput(DisplayOutput &output)
Replace the underlying output.
Definition blender.h:53
BlendingFilter(DisplayOutput &output, const Rasterizable *raster, int16_t dx, int16_t dy, Color bgcolor=color::Transparent)
Create a blending filter with an offset.
Definition blender.h:27
void write(Color *color, uint32_t pixel_count) override
Write pixels into the current address window.
Definition blender.h:63
BlendingFilter(DisplayOutput &output, Blender blender, const Rasterizable *raster, int16_t dx, int16_t dy, Color bgcolor=color::Transparent)
Create a blending filter with a custom blender and offset.
Definition blender.h:37
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 blender.h:132
void fill(Color color, uint32_t pixel_count) override
Write pixel_count copies of the same color into the current address window.
Definition blender.h:90
BlendingFilter(DisplayOutput &output, Blender blender, const Rasterizable *raster, Color bgcolor=color::Transparent)
Create a blending filter with a custom blender.
Definition blender.h:32
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 blender.h:146
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 blender.h:182
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 blender.h:55
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 blender.h:162
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 blender.h:139
const ColorFormat & getColorFormat() const override
Return the native color format used by this device for direct drawing.
Definition blender.h:178
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
static Box Intersect(const Box &a, const Box &b)
Return the intersection of two boxes (may be empty).
Definition box.h:25
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 void write(Color *color, uint32_t pixel_count)=0
Write pixels into the current address window.
virtual const ColorFormat & getColorFormat() const =0
Return the native color format used by this device for direct drawing.
void fillRect(BlendingMode blending_mode, const Box &rect, Color color)
Fill a single rectangle. Invalidates the address window.
Definition device.h:135
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 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.
void setAddress(const Box &bounds, BlendingMode blending_mode)
Convenience overload for setAddress() using a Box.
Definition device.h:45
virtual Box extents() const =0
Return the bounding box encompassing all pixels that need to be drawn.
Drawable that can provide a color for any point within its extents.
void readColorsMaybeOutOfBounds(const int16_t *x, const int16_t *y, uint32_t count, Color *result, Color out_of_bounds_color=color::Transparent) const
Read colors for points that may be out of bounds.
virtual bool readColorRect(int16_t xMin, int16_t yMin, int16_t xMax, int16_t yMax, Color *result) const
Read colors for a rectangle.
Defines 140 opaque HTML named colors.
BlendingMode
Porter-Duff style blending modes.
Definition blending.h:17
Color AlphaBlend(Color bgc, Color fgc)
Definition blending.h:598
Color bgcolor
Definition smooth.cpp:889