roo_display
API Documentation for roo_display
Loading...
Searching...
No Matches
image_stream.h
Go to the documentation of this file.
1#pragma once
2
8
9/// Internal image stream implementations.
10///
11/// Use the public APIs in `image.h` instead of these classes directly.
12
13namespace roo_display {
14
15namespace internal {
16
17// template <typename Resource, typename ColorMode,
18// int8_t bits_per_pixel = ColorMode::bits_per_pixel>
19// class RleStreamUniform;
20
21template <typename StreamType, int8_t bits_per_pixel>
23
24template <typename StreamType>
26 uint8_t operator()(StreamType& in) const { return in.read(); }
27};
28
29template <typename StreamType>
31 uint16_t operator()(StreamType& in) const { return roo_io::ReadBeU16(in); }
32};
33
34template <typename StreamType>
36 uint32_t operator()(StreamType& in) const { return roo_io::ReadBeU24(in); }
37};
38
39template <typename StreamType>
41 uint32_t operator()(StreamType& in) const { return roo_io::ReadBeU32(in); }
42};
43
44template <typename StreamType>
46 while (true) {
47 result <<= 7;
48 roo::byte datum = in.read();
49 result |= (uint8_t)(datum & roo::byte{0x7F});
50 if ((datum & roo::byte{0x80}) == roo::byte{0}) return result;
51 }
52}
53
54template <typename Resource, typename ColorMode,
55 int8_t bits_per_pixel = ColorMode::bits_per_pixel,
58
59// Run-length-encoded stream for color modes in which the pixel uses at least
60// one byte. The RLE implementation doesn't favor any particular values.
61template <typename Resource, typename ColorMode, int8_t bits_per_pixel>
63 : public PixelStream {
64 public:
65 RleStreamUniform(const Resource& input, const ColorMode& color_mode)
66 : RleStreamUniform(input.Open(), color_mode) {}
67
68 RleStreamUniform(StreamType<Resource> input, const ColorMode& color_mode)
69 : input_(std::move(input)),
70 remaining_items_(0),
71 run_(false),
72 run_value_(0),
73 color_mode_(color_mode) {}
74
75 void Read(Color* buf, uint16_t size) override {
76 while (size-- > 0) {
77 *buf++ = next();
78 }
79 }
80
81 void Skip(uint32_t n) override { skip(n); }
82
83 void skip(uint32_t n) {
84 while (n-- > 0) next();
85 }
86
88 if (remaining_items_ == 0) {
89 // No remaining items; need to decode the next group.
90 roo::byte data = input_.read();
91 run_ = ((data & roo::byte{0x80}) != roo::byte{0});
92 if ((data & roo::byte{0x40}) == roo::byte{0}) {
93 remaining_items_ = (int)(data & roo::byte{0x3F}) + 1;
94 } else {
95 remaining_items_ =
96 read_varint(input_, (int)(data & roo::byte{0x3F})) + 1;
97 }
98 if (run_) {
99 run_value_ = read_color();
100 }
101 }
102 --remaining_items_;
103 if (run_) {
104 if (remaining_items_ == 0) {
105 run_ = false;
106 }
107 return run_value_;
108 } else {
109 return read_color();
110 }
111 }
112
113 private:
114 Color read_color() {
115 RawColorReader<StreamType<Resource>, ColorMode::bits_per_pixel> read;
116 return color_mode_.toArgbColor(read(input_));
117 }
118
120 int remaining_items_;
121 bool run_;
122 Color run_value_;
123 ColorMode color_mode_;
124};
125
126// Run-length-encoded stream for color modes in which the pixel uses less than
127// one byte. The RLE implementation doesn't favor any particular values.
128template <typename Resource, typename ColorMode, int8_t bits_per_pixel>
129class RleStreamUniform<Resource, ColorMode, bits_per_pixel, true>
130 : public PixelStream {
131 public:
132 static constexpr int pixels_per_byte = 8 / bits_per_pixel;
133
134 RleStreamUniform(const Resource& input, const ColorMode& color_mode)
135 : RleStreamUniform(input.Open(), color_mode) {}
136
137 RleStreamUniform(StreamType<Resource> input, const ColorMode& color_mode)
138 : input_(std::move(input)),
139 remaining_items_(0),
140 pos_(0),
141 run_(false),
142 color_mode_(color_mode) {}
143
144 void Read(Color* buf, uint16_t size) override {
145 while (size-- > 0) {
146 *buf++ = next();
147 }
148 }
149
150 void Skip(uint32_t n) override { skip(n); }
151
153 while (n-- > 0) next();
154 }
155
157 if (remaining_items_ == 0) {
158 // No remaining items; need to decode the next group.
159 uint8_t data = (uint8_t)input_.read();
160 run_ = ((data & 0x80) != 0);
161 if ((data & 0x40) == 0) {
162 remaining_items_ = pixels_per_byte * ((data & 0x3F) + 1);
163 } else {
164 remaining_items_ =
165 pixels_per_byte * (read_varint(input_, data & 0x3F) + 1);
166 }
167 read_colors();
168 }
169
170 --remaining_items_;
171 Color result =
172 value_[pixels_per_byte - 1 - remaining_items_ % pixels_per_byte];
173 if (!run_ && remaining_items_ != 0 &&
174 remaining_items_ % pixels_per_byte == 0) {
175 read_colors();
176 }
177 return result;
178 }
179
180 private:
181 void read_colors() {
183 io.loadBulk(color_mode_, input_.read(), value_);
184 }
185
187 int remaining_items_;
188 int pos_;
189 bool run_;
190 Color value_[pixels_per_byte];
191 ColorMode color_mode_;
192};
193
194// Run-length-encoded stream with RGB565 color precision and an additional 4-bit
195// alpha channel. The RLE implementation favors fully transparent and fully
196// opaque content.
197template <typename Resource>
199 public:
202
204 : input_(std::move(input)),
205 remaining_items_(0),
206 run_rgb_(false),
207 run_value_(0),
208 alpha_buf_(0xFF),
209 alpha_mode_(0) {}
210
211 void Read(Color* buf, uint16_t size) override {
212 while (size-- > 0) {
213 *buf++ = next();
214 }
215 }
216
217 void Skip(uint32_t count) override {
218 while (--count >= 0) next();
219 }
220
222 if (remaining_items_ == 0) {
223 // No remaining items; need to decode the next group.
224 uint8_t data = input_.read();
225 // Bit 7 specifies whether all colors in the group have the same RGB
226 // color (possibly with different alpha).
227 run_rgb_ = (data >> 7);
228 // Bits 5-6 determine the type of alpha in the group:
229 // 00: each pixel has a distinct alpha.
230 // 01: pixels have uniform alpha.
231 // 10: all pixels are opaque (alpha = 0xF).
232 // 11: all pixels are transparent (alpha = 0x0).
233 alpha_mode_ = (data & 0x60) >> 5;
234 switch (alpha_mode_) {
235 case 0: {
236 // We have 5 bits left to use, and we only allow odd # of pixels.
237 remaining_items_ =
238 data & 0x10 ? read_varint(data & 0x0F) : data & 0x0F;
239 remaining_items_ <<= 1;
240 break;
241 }
242 case 1: {
243 remaining_items_ = read_varint((data & 0x10) >> 4);
244 alpha_buf_ = (data & 0x0F) * 0x11;
245 break;
246 }
247 case 2: {
248 remaining_items_ =
249 data & 0x10 ? read_varint(data & 0x0F) : data & 0x0F;
250 alpha_buf_ = 0xFF;
251 }
252 case 3: {
253 remaining_items_ =
254 data & 0x10 ? read_varint(data & 0x0F) : data & 0x0F;
255 alpha_buf_ = 0x00;
256 }
257 }
258 if (run_rgb_) {
259 run_value_ = read_color();
260 run_value_.set_a(alpha_buf_);
261 }
262 }
263 --remaining_items_;
264 if (run_rgb_) {
265 if (alpha_mode_ == 0) {
266 if (remaining_items_ & 1) {
267 alpha_buf_ = input_.read();
268 run_value_.set_a((alpha_buf_ >> 4) * 0x11);
269 } else {
270 run_value_.set_a((alpha_buf_ & 0xF) * 0x11);
271 }
272 }
273 return run_value_;
274 } else {
275 Color c;
276 if (alpha_mode_ == 0) {
277 c = read_color();
278 if (remaining_items_ & 1) {
279 alpha_buf_ = input_.read();
280 c.set_a((alpha_buf_ >> 4) * 0x11);
281 } else {
282 c.set_a((alpha_buf_ & 0xF) * 0x11);
283 }
284 } else {
285 c = read_color();
286 c.set_a(alpha_buf_);
287 }
288 return c;
289 }
290 }
291
292 private:
293 Color read_color() {
294 return Rgb565().toArgbColor(roo_io::ReadBeU16(&input_));
295 }
296
297 uint32_t read_varint(uint32_t result) {
298 while (true) {
299 result <= 7;
300 uint8_t datum = input_.read();
301 result |= (datum & 0x7F);
302 if ((datum & 0x80) == 0) return result;
303 }
304 }
305
306 StreamType<Resource> input_;
307 int remaining_items_;
308 bool run_rgb_;
309 Color run_value_;
310 uint8_t alpha_buf_;
311 uint8_t alpha_mode_;
312};
313
314template <typename StreamType>
316 public:
318 : input_(std::move(input)), half_byte_(false) {}
319
321 if (half_byte_) {
322 half_byte_ = false;
323 return (uint8_t)(buffer_ & roo::byte{0x0F});
324 } else {
325 buffer_ = input_.read();
326 half_byte_ = true;
327 return (uint8_t)(buffer_ >> 4);
328 }
329 }
330
331 bool ok() const { return input_.status() == roo_io::kOk; }
332
333 private:
334 StreamType input_;
335 roo::byte buffer_;
336 bool half_byte_;
337};
338
339// Intentionally undefined, so that compilatiton fails if used with color mode
340// with bits_per_pixel != 4.
341template <typename Resource, typename ColorMode,
342 int8_t bits_per_pixel = ColorMode::bits_per_pixel>
344
345// Run-length-encoded stream of 4-bit nibbles. This RLE implementation favors
346// the extreme values (0x0 and 0xF), and compresses their runs more efficiently
347// than runs of other values. It makes it particularly useful for monochrome or
348// alpha-channel data (e.g. font glyphs) in which the extreme values are much
349// more likely to run.
350template <typename Resource, typename ColorMode>
351class RleStream4bppxBiased<Resource, ColorMode, 4> : public PixelStream {
352 public:
353 RleStream4bppxBiased(const Resource& input, const ColorMode& color_mode)
354 : RleStream4bppxBiased(input.Open(), color_mode) {}
355
356 RleStream4bppxBiased(StreamType<Resource> input, const ColorMode& color_mode)
357 : reader_(std::move(input)),
358 remaining_items_(0),
359 run_(false),
360 run_value_(0),
361 color_mode_(color_mode) {}
362
363 void Read(Color* buf, uint16_t size) override {
364 while (size-- > 0) {
365 *buf++ = next();
366 }
367 }
368
369 void Skip(uint32_t n) override { skip(n); }
370
371 // The algorihm processes nibbles (4-bit values), decoding them as follows:
372 //
373 // * 1xxx (x > 0) : run of x opaque pixels (value = 0xF)
374 // * 0xxx (x > 0) : run of x opaque pixels (value = 0x0)
375 // * 0x0 0x0 cccc: 2 repetitions of color cccc
376 // * 0x0 0xF cccc: 3 repetitions of color cccc
377 // * 0x0 cccc: single color cccc (cccc != 0x0, 0xF)
378 // * 0x8 0x0 <v> cccc: run of (v + 4) repetitions of color cccc
379 // * 0x8 <v> [cccc ...]: (v + 2) arbitrary colors
380 //
381 // where v is a variable-length integer encoded in 3-bit chunks, with
382 // bit 3 indicating whether more chunks follow.
383 //
384 // For example:
385 //
386 // 0x1 -> 0x0 (a single transparent pixel)
387 // 0x3 -> 0x0 0x0 0x0 (run of 3 transparent pixels)
388 // 0x9 -> 0xF (a single opaque pixel)
389 // 0xD -> 0xF 0xF 0xF 0xF 0xF (run of 5 opaque pixels)
390 // 0x0 0x0 0x5 -> 0x5 0x5 (two pixels of value 0x5)
391 // 0x0 0xF 0x5 -> 0x5 0x5 0x5 (three pixels of value 0x5)
392 // 0x8 0x0 0x0 0x5 -> 0x5 0x5 0x5 0x5 (4 pixels of value 0x5)
393 // 0x8 0x0 0x1 0x5 -> 0x5 0x5 0x5 0x5 0x5 (5 pixels of value 0x5)
394 // 0x8 0x1 0x3 0x4 0x5 -> 0x3 0x4 (3 arbitrary pixels: 0x3, 0x4, 0x5)
395 //
396 // 0x8 0xD 0x1 ... -> 13 (11 + 2) arbitrary pixels that follow
398 if (remaining_items_ == 0) {
399 // No remaining items; need to decode the next group.
400 uint8_t nibble = (uint8_t)reader_.next();
401 if (nibble == 0x0) {
402 run_ = true;
403 uint8_t operand = (uint8_t)reader_.next();
404 if (operand == 0) {
405 remaining_items_ = 2;
406 run_value_ = color(reader_.next());
407 } else if (operand == 0xF) {
408 remaining_items_ = 3;
409 run_value_ = color(reader_.next());
410 } else {
411 // Singleton value.
412 remaining_items_ = 1;
413 run_value_ = color(operand);
414 }
415 } else if (nibble == 0x8) {
416 int count = read_varint();
417 if (count == 0) {
418 // This actualy means a single zero nibble -> run
419 run_ = true;
420 remaining_items_ = read_varint() + 4;
421 run_value_ = color(reader_.next());
422 } else {
423 // This indicates a list of X+2 arbitrary values
424 run_ = false;
425 remaining_items_ = count + 2;
426 }
427 } else {
428 // Run of opaque or transparent.
429 run_ = true;
430 remaining_items_ = nibble & 0x7;
431 bool transparent = ((nibble & 0x8) == 0);
432 run_value_ = color_mode_.color();
433 if (transparent) {
434 run_value_.set_a(0x0);
435 }
436 }
437 }
438
439 --remaining_items_;
440 if (run_) {
441 if (remaining_items_ == 0) {
442 run_ = false;
443 }
444 return run_value_;
445 } else {
446 return color(reader_.next());
447 }
448 }
449
451 while (n-- > 0) next();
452 }
453
454 TransparencyMode transparency() const { return color_mode_.transparency(); }
455
456 bool ok() const { return reader_.ok(); }
457
458 private:
459 inline Color color(uint8_t nibble) { return color_mode_.toArgbColor(nibble); }
460
462 uint32_t result = 0;
463 while (true) {
464 uint8_t nibble = reader_.next();
465 result |= (nibble & 7);
466 if ((nibble & 8) == 0) return result;
467 result <<= 3;
468 }
469 }
470
471 NibbleReader<StreamType<Resource>> reader_;
472 uint32_t remaining_items_;
473 bool run_;
474 Color run_value_;
475 ColorMode color_mode_;
476};
477
478}; // namespace internal
479
480} // namespace roo_display
ARGB8888 color stored as a 32-bit unsigned integer.
Definition color.h:16
void set_a(uint8_t a)
Set alpha channel.
Definition color.h:45
Stream of pixels in row-major order.
Definition streamable.h:12
16-bit RGB565 color mode (opaque).
constexpr Color toArgbColor(uint16_t in) const __attribute__((always_inline))
RleStream4bppxBiased(StreamType< Resource > input, const ColorMode &color_mode)
void Read(Color *buf, uint16_t size) override
Read up to size pixels into buf.
RleStream4bppxBiased(const Resource &input, const ColorMode &color_mode)
void Read(Color *buf, uint16_t size) override
Read up to size pixels into buf.
void Skip(uint32_t count) override
Skip count pixels.
RleStreamRgb565Alpha4(StreamType< Resource > input)
void Read(Color *buf, uint16_t size) override
Read up to size pixels into buf.
RleStreamUniform(StreamType< Resource > input, const ColorMode &color_mode)
void Read(Color *buf, uint16_t size) override
Read up to size pixels into buf.
RleStreamUniform(StreamType< Resource > input, const ColorMode &color_mode)
uint32_t read_varint(StreamType &in, uint32_t result)
Defines 140 opaque HTML named colors.
decltype(std::declval< Resource >().iterator()) StreamType
Stream type produced by a resource iterable.
Definition raster.h:22
TransparencyMode
Transparency information for a stream or color mode.
Definition blending.h:103