roo_display
API Documentation for roo_display
Loading...
Searching...
No Matches
rasterizable.cpp
Go to the documentation of this file.
1#include "rasterizable.h"
2
3namespace roo_display {
4
5namespace {
6
7class Stream : public PixelStream {
8 public:
9 Stream(const Rasterizable *data, Box bounds)
10 : data_(data),
11 bounds_(std::move(bounds)),
12 x_(bounds_.xMin()),
13 y_(bounds_.yMin()) {}
14
15 void Read(Color *buf, uint16_t size) override {
16 int16_t x[size];
17 int16_t y[size];
18 for (int i = 0; i < size; ++i) {
19 x[i] = x_;
20 y[i] = y_;
21 if (x_ < bounds_.xMax()) {
22 ++x_;
23 } else {
24 x_ = bounds_.xMin();
25 ++y_;
26 }
27 }
28 if (y[0] == y[size - 1]) {
29 if (data_->readColorRect(x[0], y[0], x[size - 1], y[0], buf)) {
30 for (int i = 1; i < size; ++i) {
31 buf[i] = buf[0];
32 }
33 }
34 } else {
35 data_->readColors(x, y, size, buf);
36 }
37 }
38
39 void Skip(uint32_t count) override {
40 auto w = bounds_.width();
41 y_ += count / w;
42 x_ += count % w;
43 if (x_ > bounds_.xMax()) {
44 x_ -= w;
45 ++y_;
46 }
47 }
48
49 private:
50 const Rasterizable *data_;
51 Box bounds_;
52 int16_t x_, y_;
53};
54
55static const int kMaxBufSize = 32;
56
57} // namespace
58
60 const int16_t *y, uint32_t count,
63 Box bounds = extents();
64 // First process as much as we can without copying and extra memory.
65 uint32_t offset = 0;
66 bool fastpath = true;
67 while (true) {
68 uint32_t buf_size = 64;
69 if (buf_size > count) buf_size = count;
70 for (uint32_t i = 0; i < buf_size && fastpath; ++i) {
71 fastpath &= bounds.contains(x[i], y[i]);
72 }
73 if (!fastpath) break;
75 count -= buf_size;
76 if (count == 0) return;
77 x += buf_size;
78 y += buf_size;
80 }
81
86 offset = 0;
87 while (offset < count) {
88 int buf_size = 0;
89 uint32_t start_offset = offset;
90 do {
91 if (bounds.contains(x[offset], y[offset])) {
92 newx[buf_size] = x[offset];
93 newy[buf_size] = y[offset];
94 offsets[buf_size] = offset;
95 ++buf_size;
96 }
97 offset++;
98 } while (offset < count && buf_size < kMaxBufSize);
100 int buf_idx = 0;
101 for (uint32_t i = start_offset; i < offset; ++i) {
103 if (buf_idx < buf_size && offsets[buf_idx] == i) {
104 // Found point in the bounds for which we have the color.
106 ++buf_idx;
107 }
108 result[i] = c;
109 }
110 }
111}
112
114 int16_t yMax, Color *result) const {
115 uint32_t pixel_count = (xMax - xMin + 1) * (yMax - yMin + 1);
118 int16_t *cx = x;
119 int16_t *cy = y;
120 for (int16_t y_cursor = yMin; y_cursor <= yMax; ++y_cursor) {
121 for (int16_t x_cursor = xMin; x_cursor <= xMax; ++x_cursor) {
122 *cx++ = x_cursor;
123 *cy++ = y_cursor;
124 }
125 }
127 Color c = result[0];
128 for (uint32_t i = 1; i < pixel_count; i++) {
129 if (result[i] != c) return false;
130 }
131 return true;
132}
133
134std::unique_ptr<PixelStream> Rasterizable::createStream() const {
135 return std::unique_ptr<PixelStream>(new Stream(this, this->extents()));
136}
137
138std::unique_ptr<PixelStream> Rasterizable::createStream(
139 const Box &bounds) const {
140 return std::unique_ptr<PixelStream>(new Stream(this, bounds));
141}
142
143// void Rasterizable::drawTo(const Surface& s) const {
144// Box ext = extents();
145// Box bounds = Box::Intersect(s.clip_box(), ext.translate(s.dx(), s.dy()));
146// if (bounds.empty()) return;
147// Stream stream(this, bounds.translate(-s.dx(), -s.dy()));
148// internal::FillRectFromStream(s.out(), bounds, &stream, s.bgcolor(),
149// s.fill_mode(), s.blending_mode(),
150// getTransparencyMode());
151// }
152
153namespace {
154
155inline void FillReplaceRect(DisplayOutput &output, const Box &extents,
156 int16_t dx, int16_t dy, const Rasterizable &object,
157 BlendingMode mode) {
158 int32_t count = extents.area();
159 Color buf[count];
160 bool same =
161 object.readColorRect(extents.xMin() - dx, extents.yMin() - dy,
162 extents.xMax() - dx, extents.yMax() - dy, buf);
163 if (same) {
164 output.fillRect(mode, extents, buf[0]);
165 } else {
166 output.setAddress(extents, mode);
167 output.write(buf, count);
168 }
169}
170
171inline void FillPaintRectOverOpaqueBg(DisplayOutput &output, const Box &extents,
172 int16_t dx, int16_t dy, Color bgcolor,
173 const Rasterizable &object,
174 BlendingMode mode) {
175 int32_t count = extents.area();
176 Color buf[count];
177 bool same =
178 object.readColorRect(extents.xMin() - dx, extents.yMin() - dy,
179 extents.xMax() - dx, extents.yMax() - dy, buf);
180 if (same) {
181 output.fillRect(mode, extents, AlphaBlendOverOpaque(bgcolor, buf[0]));
182 } else {
183 for (int i = 0; i < count; i++) {
184 buf[i] = AlphaBlendOverOpaque(bgcolor, buf[i]);
185 }
186 output.setAddress(extents, mode);
187 output.write(buf, count);
188 }
189}
190
191inline void FillPaintRectOverBg(DisplayOutput &output, const Box &extents,
192 int16_t dx, int16_t dy, Color bgcolor,
193 const Rasterizable &object, BlendingMode mode) {
194 int32_t count = extents.area();
195 Color buf[count];
196 bool same =
197 object.readColorRect(extents.xMin() - dx, extents.yMin() - dy,
198 extents.xMax() - dx, extents.yMax() - dy, buf);
199 if (same) {
200 output.fillRect(mode, extents, AlphaBlend(bgcolor, buf[0]));
201 } else {
202 for (int i = 0; i < count; i++) {
203 buf[i] = AlphaBlend(bgcolor, buf[i]);
204 }
205 output.setAddress(extents, mode);
206 output.write(buf, count);
207 }
208}
209
210// Assumes no bgcolor.
211inline void WriteRectVisible(DisplayOutput &output, const Box &extents,
212 int16_t dx, int16_t dy, const Rasterizable &object,
213 BlendingMode mode) {
214 int32_t count = extents.area();
215 Color buf[count];
216 bool same =
217 object.readColorRect(extents.xMin() - dx, extents.yMin() - dy,
218 extents.xMax() - dx, extents.yMax() - dy, buf);
219 if (same) {
220 if (buf[0] != color::Transparent) {
221 output.fillRect(mode, extents, buf[0]);
222 }
223 } else {
224 BufferedPixelWriter writer(output, mode);
225 Color *ptr = buf;
226 for (int16_t j = extents.yMin(); j <= extents.yMax(); ++j) {
227 for (int16_t i = extents.xMin(); i <= extents.xMax(); ++i) {
228 if (*ptr != color::Transparent) writer.writePixel(i, j, *ptr);
229 ++ptr;
230 }
231 }
232 }
233}
234
235inline void WriteRectVisibleOverOpaqueBg(DisplayOutput &output,
236 const Box &extents, int16_t dx,
237 int16_t dy, Color bgcolor,
238 const Rasterizable &object,
239 BlendingMode mode) {
240 int32_t count = extents.area();
241 Color buf[count];
242 bool same =
243 object.readColorRect(extents.xMin() - dx, extents.yMin() - dy,
244 extents.xMax() - dx, extents.yMax() - dy, buf);
245 if (same) {
246 if (buf[0] != color::Transparent) {
247 output.fillRect(mode, extents, AlphaBlendOverOpaque(bgcolor, buf[0]));
248 }
249 } else {
250 BufferedPixelWriter writer(output, mode);
251 Color *ptr = buf;
252 for (int16_t j = extents.yMin(); j <= extents.yMax(); ++j) {
253 for (int16_t i = extents.xMin(); i <= extents.xMax(); ++i) {
254 if (*ptr != color::Transparent)
256 ++ptr;
257 }
258 }
259 }
260}
261
262inline void WriteRectVisibleOverBg(DisplayOutput &output, const Box &extents,
263 int16_t dx, int16_t dy, Color bgcolor,
264 const Rasterizable &object,
265 BlendingMode mode) {
266 int32_t count = extents.area();
267 Color buf[count];
268 bool same =
269 object.readColorRect(extents.xMin() - dx, extents.yMin() - dy,
270 extents.xMax() - dx, extents.yMax() - dy, buf);
271 if (same) {
272 if (buf[0] != color::Transparent) {
273 output.fillRect(mode, extents, AlphaBlend(bgcolor, buf[0]));
274 }
275 } else {
276 BufferedPixelWriter writer(output, mode);
277 Color *ptr = buf;
278 for (int16_t j = extents.yMin(); j <= extents.yMax(); ++j) {
279 for (int16_t i = extents.xMin(); i <= extents.xMax(); ++i) {
280 if (*ptr != color::Transparent)
281 writer.writePixel(i, j, AlphaBlend(bgcolor, *ptr));
282 ++ptr;
283 }
284 }
285 }
286}
287
288} // namespace
289
290void Rasterizable::drawTo(const Surface &s) const {
291 Box box = s.clip_box();
292 int16_t xMin = box.xMin();
293 int16_t xMax = box.xMax();
294 int16_t yMin = box.yMin();
295 int16_t yMax = box.yMax();
296 uint32_t pixel_count = box.area();
297 const int16_t xMinOuter = (xMin / 8) * 8;
298 const int16_t yMinOuter = (yMin / 8) * 8;
299 const int16_t xMaxOuter = (xMax / 8) * 8 + 7;
300 const int16_t yMaxOuter = (yMax / 8) * 8 + 7;
301 FillMode fill_mode = s.fill_mode();
302 TransparencyMode transparency = getTransparencyMode();
303 Color bgcolor = s.bgcolor();
304 DisplayOutput &output = s.out();
305 BlendingMode mode = s.blending_mode();
307 transparency == TransparencyMode::kNone) {
308 if (pixel_count <= 64) {
309 FillReplaceRect(output, box, s.dx(), s.dy(), *this, mode);
310 return;
311 }
312 if (bgcolor.a() == 0 || transparency == TransparencyMode::kNone) {
313 for (int16_t y = yMinOuter; y < yMaxOuter; y += 8) {
314 for (int16_t x = xMinOuter; x < xMaxOuter; x += 8) {
315 FillReplaceRect(output,
316 Box(std::max(x, box.xMin()), std::max(y, box.yMin()),
317 std::min((int16_t)(x + 7), box.xMax()),
318 std::min((int16_t)(y + 7), box.yMax())),
319 s.dx(), s.dy(), *this, mode);
320 }
321 }
322 } else if (bgcolor.a() == 0xFF) {
323 if (pixel_count <= 64) {
324 FillPaintRectOverOpaqueBg(output, box, s.dx(), s.dy(), bgcolor, *this,
325 mode);
326 return;
327 }
328 for (int16_t y = yMinOuter; y < yMaxOuter; y += 8) {
329 for (int16_t x = xMinOuter; x < xMaxOuter; x += 8) {
331 output,
332 Box(std::max(x, box.xMin()), std::max(y, box.yMin()),
333 std::min((int16_t)(x + 7), box.xMax()),
334 std::min((int16_t)(y + 7), box.yMax())),
335 s.dx(), s.dy(), bgcolor, *this, mode);
336 }
337 }
338 } else {
339 if (pixel_count <= 64) {
340 FillPaintRectOverBg(output, box, s.dx(), s.dy(), bgcolor, *this, mode);
341 return;
342 }
343 for (int16_t y = yMinOuter; y < yMaxOuter; y += 8) {
344 for (int16_t x = xMinOuter; x < xMaxOuter; x += 8) {
346 output,
347 Box(std::max(x, box.xMin()), std::max(y, box.yMin()),
348 std::min((int16_t)(x + 7), box.xMax()),
349 std::min((int16_t)(y + 7), box.yMax())),
350 s.dx(), s.dy(), bgcolor, *this, mode);
351 }
352 }
353 }
354 } else {
355 if (bgcolor.a() == 0) {
356 if (pixel_count <= 64) {
357 WriteRectVisible(output, box, s.dx(), s.dy(), *this, mode);
358 return;
359 }
360 for (int16_t y = yMinOuter; y < yMaxOuter; y += 8) {
361 for (int16_t x = xMinOuter; x < xMaxOuter; x += 8) {
362 WriteRectVisible(output,
363 Box(std::max(x, box.xMin()), std::max(y, box.yMin()),
364 std::min((int16_t)(x + 7), box.xMax()),
365 std::min((int16_t)(y + 7), box.yMax())),
366 s.dx(), s.dy(), *this, mode);
367 }
368 }
369 } else if (bgcolor.a() == 0xFF) {
370 if (pixel_count <= 64) {
371 WriteRectVisibleOverOpaqueBg(output, box, s.dx(), s.dy(), bgcolor,
372 *this, mode);
373 return;
374 }
375 for (int16_t y = yMinOuter; y < yMaxOuter; y += 8) {
376 for (int16_t x = xMinOuter; x < xMaxOuter; x += 8) {
378 output,
379 Box(std::max(x, box.xMin()), std::max(y, box.yMin()),
380 std::min((int16_t)(x + 7), box.xMax()),
381 std::min((int16_t)(y + 7), box.yMax())),
382 s.dx(), s.dy(), bgcolor, *this, mode);
383 }
384 }
385 } else {
386 if (pixel_count <= 64) {
387 WriteRectVisibleOverBg(output, box, s.dx(), s.dy(), bgcolor, *this,
388 mode);
389 return;
390 }
391 for (int16_t y = yMinOuter; y < yMaxOuter; y += 8) {
392 for (int16_t x = xMinOuter; x < xMaxOuter; x += 8) {
394 output,
395 Box(std::max(x, box.xMin()), std::max(y, box.yMin()),
396 std::min((int16_t)(x + 7), box.xMax()),
397 std::min((int16_t)(y + 7), box.yMax())),
398 s.dx(), s.dy(), bgcolor, *this, mode);
399 }
400 }
401 }
402 }
403}
404
405} // 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
int32_t area() const
Area in pixels.
Definition box.h:83
int16_t yMin() const
Minimum y (inclusive).
Definition box.h:68
void writePixel(int16_t x, int16_t y, Color color)
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.
void fillRect(BlendingMode blending_mode, const Box &rect, Color color)
Fill a single rectangle. Invalidates the address window.
Definition device.h:135
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.
void drawTo(const Surface &s) const override
Default drawTo() using readColors().
std::unique_ptr< PixelStream > createStream() const override
Default createStream() using readColors().
virtual void readColors(const int16_t *x, const int16_t *y, uint32_t count, Color *result) const =0
Read colors for the given points.
virtual bool readColorRect(int16_t xMin, int16_t yMin, int16_t xMax, int16_t yMax, Color *result) const
Read colors for a rectangle.
virtual TransparencyMode getTransparencyMode() const
Return the transparency mode for pixels in this stream.
Definition streamable.h:441
Low-level handle used to draw to an underlying device.
Definition drawable.h:60
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
TransparencyMode
Transparency information for a stream or color mode.
Definition blending.h:103
@ kNone
All colors are fully opaque.
Color AlphaBlendOverOpaque(Color bgc, Color fgc)
Definition blending.h:465
FillMode
Specifies whether a Drawable should fill its entire extents box, including fully transparent pixels.
Definition drawable.h:15
@ kExtents
Fill the entire extents box (possibly with fully transparent pixels).
Color bgcolor
Definition smooth.cpp:889
FillMode fill_mode
Definition smooth.cpp:887