roo_display
API Documentation for roo_display
Loading...
Searching...
No Matches
raw_streamable.h
Go to the documentation of this file.
1#pragma once
2
7
8namespace roo_display {
9
10// RawPixelStream template contract:
11// class RawPixelStream {
12// typedef ptr_type; // usually const uint8_t *PROGMEM
13// typedef ColorMode;
14// TransparencyMode transparency() const;
15//
16// RawPixelStream(const ptr_type data, ColorMode mode);
17// Color next();
18// };
19
20// RawPixelStreamable template contract:
21// class RawPixelStreamable {
22// const Box& extents() const;
23// std::unique_ptr<any> createRawStream() const;
24// }
25
26template <typename RawStreamable>
27using RawStreamTypeOf = typename decltype(std::declval<const RawStreamable>()
28 .createRawStream())::element_type;
29
30template <typename RawStreamable>
32 typename decltype(std::declval<const RawStreamable>()
33 .CreateClippedRawStream(
34 std::declval<Box>()))::element_type;
35
36template <typename RawStreamable>
37using ColorModeOf = decltype(std::declval<const RawStreamable>().color_mode());
38
39namespace internal {
40
41template <typename RawPixelStream>
43 void operator()(DisplayOutput &output, const Box &extents, Color bgcolor,
45 TransparencyMode transparency_mode) const {
46 BufferedPixelWriter writer(output, mode);
47 if (bgcolor.a() == 0) {
48 for (int16_t j = extents.yMin(); j <= extents.yMax(); ++j) {
49 for (int16_t i = extents.xMin(); i <= extents.xMax(); ++i) {
50 Color color = stream->next();
51 if (color.a() != 0) {
52 writer.writePixel(i, j, color);
53 }
54 }
55 }
56 } else if (transparency_mode == TransparencyMode::kFull) {
57 for (int16_t j = extents.yMin(); j <= extents.yMax(); ++j) {
58 for (int16_t i = extents.xMin(); i <= extents.xMax(); ++i) {
59 Color color = stream->next();
60 if (color.a() != 0) {
61 writer.writePixel(i, j, AlphaBlend(bgcolor, color));
62 }
63 }
64 }
65 } else {
66 for (int16_t j = extents.yMin(); j <= extents.yMax(); ++j) {
67 for (int16_t i = extents.xMin(); i <= extents.xMax(); ++i) {
68 Color color = stream->next();
69 if (color.a() != 0) {
70 writer.writePixel(i, j, color);
71 }
72 }
73 }
74 }
75 }
76};
77
78template <typename RawPixelStream>
80 void operator()(DisplayOutput &output, const Box &extents, Color bgcolor,
82 TransparencyMode transparency_mode) const {
83 output.setAddress(extents, blending_mode);
84 int count = extents.area();
86 if (bgcolor.a() == 0) {
87 while (count-- > 0) {
88 writer.writeColor(stream->next());
89 }
90 } else if (transparency_mode == TransparencyMode::kFull) {
91 if (bgcolor.a() == 0xFF) {
92 while (count-- > 0) {
93 writer.writeColor(AlphaBlendOverOpaque(bgcolor, stream->next()));
94 }
95 } else {
96 while (count-- > 0) {
97 writer.writeColor(AlphaBlend(bgcolor, stream->next()));
98 }
99 }
100 } else {
101 while (count-- > 0) {
102 Color color = stream->next();
103 writer.writeColor(color.a() == 0 ? bgcolor : color);
104 }
105 }
106 }
107};
108
109// This function will fill in the specified rectangle using the most appropriate
110// method given the stream's transparency mode.
111template <typename RawPixelStream>
112void FillRectFromRawStream(DisplayOutput &output, const Box &extents,
115 if (stream->transparency() == TransparencyMode::kNone &&
118 // The stream will overwrite the entire rectangle anyway.
120 bgcolor = color::Transparent;
121 }
124 fill(output, extents, bgcolor, stream, blending_mode,
125 stream->transparency());
126 } else {
128 fill(output, extents, bgcolor, stream, blending_mode,
129 stream->transparency());
130 }
131};
132
133// Restricts the stream to the specified sub-rectangle. More specifically,
134// since streams don't know their rectangle dimension, it:
135// * assumes that the delegate stream has been already skipped to the right
136// starting place, so that the first next() call would return the pixel
137// corresponding to the top-left corner of the sub-rectanble
138// * then, returns the specified number of colored pixels, and then skips
139// by specified amount (to get to the next 'line') in the delegate stream.
140// The caller should guarantee that the sum of these two quantities
141// (width and width_skip) is equal to the pixel width of the image represented
142// by the underlying stream.
143template <typename RawPixelStream>
145 public:
146 SubRectangleRawStream(std::unique_ptr<RawPixelStream> delegate, int16_t width,
148 : delegate_(std::move(delegate)),
149 x_(0),
150 width_(width),
151 width_skip_(width_skip) {}
152
154
156 if (x_ >= width_) {
157 delegate_->skip(width_skip_);
158 x_ = 0;
159 }
160 Color result = delegate_->next();
161 ++x_;
162 return result;
163 }
164
165 void skip(uint32_t count) {
166 // TODO: optimize
167 for (int i = 0; i < count; i++) next();
168 }
169
170 TransparencyMode transparency() const { return delegate_->transparency(); }
171
172 private:
174
175 std::unique_ptr<RawPixelStream> delegate_;
176 int16_t x_;
177 const int16_t width_;
178 const int16_t width_skip_;
179};
180
181template <typename RawPixelStream>
182std::unique_ptr<SubRectangleRawStream<RawPixelStream>> SubRectangle(
183 std::unique_ptr<RawPixelStream> delegate, int16_t width,
185 return std::unique_ptr<SubRectangleRawStream<RawPixelStream>>(
187 width_skip));
188}
189
190template <typename RawStreamable,
191 typename RawStream = RawStreamTypeOf<RawStreamable>, typename = void>
192struct Clipper {
193 std::unique_ptr<SubRectangleRawStream<RawStream>> operator()(
194 const RawStreamable &streamable, const Box &clip_box) const {
195 if (clip_box.contains(streamable.extents())) {
196 // Optimized case: nothing has been clipped; rendering full stream.
197 return internal::SubRectangle(streamable.createRawStream(),
198 streamable.extents().area(), 0);
199 } else {
200 // Non-optimized case: rendering sub-rectangle. Need to go line-by-line.
201 Box bounds = Box::Intersect(streamable.extents(), clip_box);
202 int line_offset = streamable.extents().width() - bounds.width();
203 int xoffset = bounds.xMin() - streamable.extents().xMin();
204 int yoffset = bounds.yMin() - streamable.extents().yMin();
205 std::unique_ptr<RawStream> stream = streamable.createRawStream();
206 stream->skip(yoffset * streamable.extents().width() + xoffset);
207 return internal::SubRectangle(std::move(stream), bounds.width(),
209 }
210 }
211};
212
213template <typename RawStreamable>
215 decltype(std::declval<RawStreamable>().CreateClippedRawStream(
216 std::declval<Box>()),
217 void())> {
218 std::unique_ptr<NativelyClippedRawStreamTypeOf<RawStreamable>> operator()(
219 const RawStreamable &streamable, const Box &clip_box) const {
220 return streamable.CreateClippedRawStream(clip_box);
221 }
222};
223
224} // namespace internal
225
226template <typename RawStreamable>
228 typename decltype(internal::Clipper<RawStreamable>().operator()(
229 std::declval<RawStreamable>(), std::declval<Box>()))::element_type;
230
231template <typename RawStreamable>
232std::unique_ptr<ClipperedRawStreamTypeOf<RawStreamable>>
234 const Box &clip_box) {
236 return clip(streamable, clip_box);
237}
238
239// Color-filled rectangle, useful as a background for super-imposed images.
241 public:
242 class RawStream {
243 public:
244 RawStream(Color color) : color_(color) {}
245 Color next() { return color_; }
246 void skip(uint32_t count) {}
247
249 return color_.isOpaque() ? TransparencyMode::kNone
250 : color_.a() == 0 ? TransparencyMode::kCrude
252 }
253
254 private:
255 Color color_;
256 };
257
259 : extents_(std::move(extents)), color_(color) {}
260
262 : extents_(0, 0, width - 1, height - 1), color_(color) {}
263
264 const Box &extents() const { return extents_; }
265 const Box &anchorExtents() const { return extents_; }
266 std::unique_ptr<RawStream> createRawStream() const {
267 return std::unique_ptr<RawStream>(new RawStream(color_));
268 }
269 std::unique_ptr<RawStream> CreateClippedRawStream(const Box &clip_box) const {
270 // No need to clip anything, since the stream is a uniform color.
271 return std::unique_ptr<RawStream>(new RawStream(color_));
272 }
273
274 private:
275 Box extents_;
276 Color color_;
277};
278
279template <typename RawStreamable>
281 public:
284
286 : streamable_(std::move(other.streamable_)) {}
287
288 Box extents() const override { return streamable_.extents(); }
289
290 Box anchorExtents() const override { return streamable_.anchorExtents(); }
291
292 const RawStreamable &contents() const { return streamable_; }
293
294 std::unique_ptr<PixelStream> createStream() const override {
295 return std::unique_ptr<PixelStream>(
297 streamable_.createRawStream()));
298 }
299
300 std::unique_ptr<PixelStream> createStream(const Box &bounds) const override {
301 return std::unique_ptr<PixelStream>(
303 CreateClippedRawStreamFor(streamable_, bounds)));
304 }
305
306 decltype(std::declval<RawStreamable>().createRawStream()) createRawStream()
307 const {
308 return streamable_.createRawStream();
309 }
310
311 private:
312 template <typename RawStream>
313 class Stream : public PixelStream {
314 public:
315 Stream(std::unique_ptr<RawStream> raw) : raw_(std::move(raw)) {}
316
317 void Read(Color *buf, uint16_t count) override {
318 while (count-- > 0) *buf++ = raw_->next();
319 }
320
321 private:
322 std::unique_ptr<RawStream> raw_;
323 };
324
325 void drawTo(const Surface &s) const override {
326 Box bounds = Box::Intersect(
327 s.clip_box(), streamable_.extents().translate(s.dx(), s.dy()));
328 if (bounds.empty()) return;
329 if (streamable_.extents().width() == bounds.width() &&
330 streamable_.extents().height() == bounds.height()) {
331 // Optimized case: rendering full stream.
332 auto stream = streamable_.createRawStream();
333 internal::FillRectFromRawStream(s.out(), bounds, stream.get(),
334 s.bgcolor(), s.fill_mode(),
335 s.blending_mode());
336 } else {
337 auto stream = CreateClippedRawStreamFor(
338 streamable_, bounds.translate(-s.dx(), -s.dy()));
339 internal::FillRectFromRawStream(s.out(), bounds, stream.get(),
340 s.bgcolor(), s.fill_mode(),
341 s.blending_mode());
342 }
343 }
344
345 RawStreamable streamable_;
346};
347
348template <typename RawStreamable>
353
354template <typename RawStreamable>
358
359template <typename RawStreamable,
360 typename RawStream = RawStreamTypeOf<RawStreamable>>
362 s.drawObject(MakeDrawableRawStreamable(std::move(streamable)));
363}
364
365// Convenience wrapper for image classes that are read from a byte stream
366// (as opposed to e.g. generated on the fly).
367template <typename Resource, typename ColorMode, typename RawStreamType>
369 public:
371 const ColorMode &color_mode = ColorMode())
372 : SimpleRawStreamable(Box(0, 0, width - 1, height - 1),
373 Box(0, 0, width - 1, height - 1),
374 std::move(resource), std::move(color_mode)) {}
375
377 const ColorMode &color_mode = ColorMode())
378 : extents_(std::move(extents)),
379 anchor_extents_(anchor_extents),
380 resource_(std::move(resource)),
381 color_mode_(color_mode) {}
382
383 void setColorMode(const ColorMode &color_mode) { color_mode_ = color_mode; }
384
385 const Box &extents() const { return extents_; }
386 const Box &anchorExtents() const { return anchor_extents_; }
387 const Resource &resource() const { return resource_; }
388 const ColorMode &color_mode() const { return color_mode_; }
389
390 std::unique_ptr<RawStreamType> createRawStream() const {
391 return std::unique_ptr<RawStreamType>(
392 new RawStreamType(resource_, color_mode_));
393 }
394
395 private:
396 Box extents_;
397 Box anchor_extents_;
398 Resource resource_;
399 ColorMode color_mode_;
400};
401
402// Similar to SimpleRawStreamable, but additionally allows to clip the stream
403// to a specified viewport. Useful for image classes like XBitmap, with
404// alignment to full byte or word boundaries.
405template <typename RawStreamable,
406 typename RawStream =
407 decltype(std::declval<RawStreamable>().createRawStream())>
408class Clipping {
409 public:
410 template <typename... Args>
411 Clipping(const Box &clip_box, Args &&...args)
412 : streamable_(std::forward<Args>(args)...),
413 extents_(Box::Intersect(streamable_.extents(), clip_box)) {}
414
416 : streamable_(std::move(streamable)),
417 extents_(Box::Intersect(streamable_.extents(), clip_box)) {}
418
419 const Box &extents() const { return extents_; }
420 Box anchorExtents() const { return streamable_.anchorExtents(); }
421
423 return streamable_.color_mode();
424 }
425
426 std::unique_ptr<ClipperedRawStreamTypeOf<RawStreamable>> createRawStream()
427 const {
428 return CreateClippedRawStreamFor(streamable_, extents_);
429 }
430
431 std::unique_ptr<ClipperedRawStreamTypeOf<RawStreamable>>
432 CreateClippedRawStream(const Box &clip_box) const {
433 return CreateClippedRawStreamFor(streamable_,
434 Box::Intersect(clip_box, extents_));
435 }
436
437 private:
438 const RawStreamable streamable_;
439 Box extents_;
440};
441
442template <typename RawStreamable>
444 return Clipping<RawStreamable>(clip_box, std::move(streamable));
445}
446
447} // namespace roo_display
BufferedRectWriter & writer
Axis-aligned integer rectangle.
Definition box.h:12
int16_t width() const
Width in pixels (inclusive coordinates).
Definition box.h:77
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
static Box Intersect(const Box &a, const Box &b)
Return the intersection of two boxes (may be empty).
Definition box.h:25
int16_t yMin() const
Minimum y (inclusive).
Definition box.h:68
Buffered writer for sequential color values (used with setAddress()).
Buffered writer for arbitrary pixels with per-pixel colors.
std::unique_ptr< ClipperedRawStreamTypeOf< RawStreamable > > CreateClippedRawStream(const Box &clip_box) const
Clipping(const Box &clip_box, Args &&...args)
std::unique_ptr< ClipperedRawStreamTypeOf< RawStreamable > > createRawStream() const
const Box & extents() const
Clipping(const Box &clip_box, RawStreamable streamable)
const ColorModeOf< RawStreamable > & color_mode() const
ARGB8888 color stored as a 32-bit unsigned integer.
Definition color.h:16
constexpr bool isOpaque() const
Return true if the color is fully opaque (alpha = 255).
Definition color.h:89
constexpr uint8_t a() const
Alpha channel.
Definition color.h:36
The abstraction for drawing to a display.
Definition device.h:15
void setAddress(const Box &bounds, BlendingMode blending_mode)
Convenience overload for setAddress() using a Box.
Definition device.h:45
const RawStreamable & contents() const
DrawableRawStreamable(DrawableRawStreamable &&other)
Box extents() const override
Return the bounding box encompassing all pixels that need to be drawn.
std::unique_ptr< PixelStream > createStream(const Box &bounds) const override
Create a stream for the given clipped bounds.
decltype(std::declval< RawStreamable >().createRawStream()) createRawStream() const
Box anchorExtents() const override
Return the bounds used for alignment.
DrawableRawStreamable(RawStreamable streamable)
std::unique_ptr< PixelStream > createStream() const override
Create a stream covering the full extents().
Interface for objects that can be drawn to an output device.
Definition drawable.h:229
Stream of pixels in row-major order.
Definition streamable.h:12
RawStreamableFilledRect(Box extents, Color color)
RawStreamableFilledRect(uint16_t width, uint16_t height, Color color)
std::unique_ptr< RawStream > CreateClippedRawStream(const Box &clip_box) const
std::unique_ptr< RawStream > createRawStream() const
void setColorMode(const ColorMode &color_mode)
SimpleRawStreamable(int16_t width, int16_t height, Resource resource, const ColorMode &color_mode=ColorMode())
SimpleRawStreamable(Box extents, Box anchor_extents, Resource resource, const ColorMode &color_mode=ColorMode())
const ColorMode & color_mode() const
const Resource & resource() const
std::unique_ptr< RawStreamType > createRawStream() const
Drawable that can provide a sequential pixel stream.
Definition streamable.h:426
Low-level handle used to draw to an underlying device.
Definition drawable.h:60
SubRectangleRawStream(std::unique_ptr< RawPixelStream > delegate, int16_t width, int16_t width_skip)
SubRectangleRawStream(SubRectangleRawStream &&)=default
void FillRectFromRawStream(DisplayOutput &output, const Box &extents, RawPixelStream *stream, Color bgcolor, FillMode fill_mode, BlendingMode blending_mode)
std::unique_ptr< SubRectangleRawStream< RawPixelStream > > SubRectangle(std::unique_ptr< RawPixelStream > delegate, int16_t width, int16_t width_skip)
Defines 140 opaque HTML named colors.
typename decltype(internal::Clipper< RawStreamable >().operator()(std::declval< RawStreamable >(), std::declval< Box >()))::element_type ClipperedRawStreamTypeOf
std::unique_ptr< ClipperedRawStreamTypeOf< RawStreamable > > CreateClippedRawStreamFor(const RawStreamable &streamable, const Box &clip_box)
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.
decltype(std::declval< const RawStreamable >().color_mode()) ColorModeOf
DrawableRawStreamable< RawStreamable > MakeDrawableRawStreamable(RawStreamable streamable)
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.
@ kCrude
Colors are either fully opaque or fully transparent.
@ kFull
Colors may include partial transparency (alpha channel).
typename decltype(std::declval< const RawStreamable >() .createRawStream())::element_type RawStreamTypeOf
Color AlphaBlendOverOpaque(Color bgc, Color fgc)
Definition blending.h:465
typename decltype(std::declval< const RawStreamable >() .CreateClippedRawStream(std::declval< Box >()))::element_type NativelyClippedRawStreamTypeOf
Drawable * MakeNewDrawableRawStreamable(RawStreamable streamable)
Clipping< RawStreamable > Clipped(const Box &clip_box, RawStreamable streamable)
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).
void streamToSurface(const Surface &s, RawStreamable streamable)
Color bgcolor
Definition smooth.cpp:889
FillMode fill_mode
Definition smooth.cpp:887
BlendingMode blending_mode
Definition smooth.cpp:888
std::unique_ptr< NativelyClippedRawStreamTypeOf< RawStreamable > > operator()(const RawStreamable &streamable, const Box &clip_box) const
std::unique_ptr< SubRectangleRawStream< RawStream > > operator()(const RawStreamable &streamable, const Box &clip_box) const
void operator()(DisplayOutput &output, const Box &extents, Color bgcolor, RawPixelStream *stream, BlendingMode blending_mode, TransparencyMode transparency_mode) const
void operator()(DisplayOutput &output, const Box &extents, Color bgcolor, RawPixelStream *stream, BlendingMode mode, TransparencyMode transparency_mode) const