roo_display
API Documentation for roo_display
Loading...
Searching...
No Matches
smooth_font.cpp
Go to the documentation of this file.
2
7#include "roo_io/core/input_iterator.h"
8#include "roo_io/text/unicode.h"
9#include "roo_logging.h"
10
11namespace roo_display {
12
14 public:
16 roo_io::UnsafeGenericMemoryIterator<const roo::byte PROGMEM*>& reader,
18 : reader_(reader), font_metric_bytes_(font_metric_bytes) {}
19
20 int read() {
21 return font_metric_bytes_ == 1 ? roo_io::ReadS8(reader_)
22 : roo_io::ReadBeS16(reader_);
23 }
24
25 private:
26 roo_io::UnsafeGenericMemoryIterator<const roo::byte PROGMEM*>& reader_;
27 int font_metric_bytes_;
28};
29
30static int8_t readByte(const roo::byte* PROGMEM ptr) {
31 return pgm_read_byte(ptr);
32}
33
34static int16_t readWord(const roo::byte* PROGMEM ptr) {
35 return (pgm_read_byte(ptr) << 8) | pgm_read_byte(ptr + 1);
36}
37
39 public:
40 GlyphMetadataReader(const SmoothFont& font, const roo::byte* PROGMEM ptr)
41 : font_(font), ptr_(ptr + font.encoding_bytes_) {}
42
44 int16_t glyphXMin, glyphYMin, glyphXMax, glyphYMax, x_advance;
45 if (font_.font_metric_bytes_ == 1) {
46 uint8_t b = readByte(ptr_ + 0);
47 compressed = ((b >> 7) == ((b >> 6) & 1));
48 glyphXMin = (int8_t)(b ^ ((!compressed) << 6));
49 glyphYMin = readByte(ptr_ + 1);
50 glyphXMax = readByte(ptr_ + 2);
51 glyphYMax = readByte(ptr_ + 3);
52 x_advance = readByte(ptr_ + 4);
53 } else {
54 uint16_t b = readWord(ptr_ + 0);
55 compressed = ((b >> 15) == ((b >> 14) & 1));
56 glyphXMin = (int16_t)(b ^ ((!compressed) << 14));
57 glyphYMin = readWord(ptr_ + 2);
58 glyphXMax = readWord(ptr_ + 4);
59 glyphYMax = readWord(ptr_ + 6);
60 x_advance = readWord(ptr_ + 8);
61 }
62 return GlyphMetrics(
63 glyphXMin, glyphYMin, glyphXMax, glyphYMax,
65 ? x_advance
66 : glyphYMax - glyphYMin + 1 + font_.metrics().linegap());
67 }
68
69 long data_offset() const {
70 int offset_bytes = font_.offset_bytes_;
71 const roo::byte* PROGMEM ptr = ptr_ + 5 * font_.font_metric_bytes_;
72 return offset_bytes == 2
73 ? (pgm_read_byte(ptr) << 8) | pgm_read_byte(ptr + 1)
74 : offset_bytes == 3
75 ? (pgm_read_byte(ptr) << 16) | (pgm_read_byte(ptr + 1) << 8) |
78 }
79
80 private:
81 const SmoothFont& font_;
82 const roo::byte* PROGMEM ptr_;
83};
84
86 : glyph_count_(0), default_glyph_(0), default_space_width_(0) {
87 roo_io::UnsafeGenericMemoryIterator<const roo::byte PROGMEM*> reader(
88 font_data);
89 uint16_t version = roo_io::ReadBeU16(reader);
90 CHECK(version == 0x0101 || version == 0x0102)
91 << "Unsupported version number: " << version;
92
93 alpha_bits_ = roo_io::ReadU8(reader);
94 CHECK_EQ(alpha_bits_, 4) << "Unsupported alpha encoding: " << alpha_bits_;
95 encoding_bytes_ = roo_io::ReadU8(reader);
96 CHECK_LE(encoding_bytes_, 2)
97 << "Unsupported character encoding: " << encoding_bytes_;
98 font_metric_bytes_ = roo_io::ReadU8(reader);
99 CHECK_LE(font_metric_bytes_, 2)
100 << "Unsupported count of metric bytes: " << encoding_bytes_;
101
102 offset_bytes_ = roo_io::ReadU8(reader);
103 CHECK_LE(offset_bytes_, 3)
104 << "Unsupported count of offset bytes: " << offset_bytes_;
105
106 compression_method_ = roo_io::ReadU8(reader);
107 CHECK_LE(compression_method_, 1)
108 << "Unsupported compression method: " << compression_method_;
109
110 glyph_count_ = roo_io::ReadBeU16(reader);
111 kerning_pairs_count_ = roo_io::ReadBeU16(reader);
112
113 FontMetricReader fm_reader(reader, font_metric_bytes_);
114 int xMin = fm_reader.read();
115 int yMin = fm_reader.read();
116 int xMax = fm_reader.read();
117 int yMax = fm_reader.read();
118 int ascent = fm_reader.read();
119 int descent = fm_reader.read();
120 int linegap = fm_reader.read();
121 int min_advance = fm_reader.read();
122 int max_advance = fm_reader.read();
123 int max_right_overhang = fm_reader.read();
124 default_space_width_ = fm_reader.read();
125
126 if (encoding_bytes_ == 1) {
127 default_glyph_ = roo_io::ReadU8(reader);
128 } else {
129 default_glyph_ = roo_io::ReadBeU16(reader);
130 }
131
133 glyph_metadata_size_ =
134 (5 * font_metric_bytes_) + offset_bytes_ + encoding_bytes_;
135 glyph_kerning_size_ = 2 * encoding_bytes_ + 1;
137 glyph_metadata_begin_ + glyph_metadata_size_ * glyph_count_;
139 glyph_kerning_begin_ + glyph_kerning_size_ * kerning_pairs_count_;
140
141 Font::init(FontMetrics(ascent, descent, linegap, xMin, yMin, xMax, yMax,
144 encoding_bytes_ > 1 ? FontProperties::Charset::kUnicodeBmp
146 min_advance == max_advance && kerning_pairs_count_ == 0
151 kerning_pairs_count_ > 0 ? FontProperties::Kerning::kPairs
153
154 // Serial.println(String() + "Loaded font with " + glyph_count_ +
155 // " glyphs, size " + (ascent - descent));
156}
157
158inline bool is_space(char32_t code) {
159 // http://en.cppreference.com/w/cpp/string/wide/iswspace; see POSIX
160 return code == 0x0020 || code == 0x00A0 ||
161 (code >= 0x0009 && code <= 0x000D) || code == 0x1680 ||
162 code == 0x180E || (code >= 0x2000 && code <= 0x200B) ||
163 code == 0x2028 || code == 0x2029 || code == 0x205F || code == 0x3000 ||
164 code == 0xFEFF;
165}
166
167void SmoothFont::drawGlyphModeVisible(
168 DisplayOutput& output, int16_t x, int16_t y, const GlyphMetrics& metrics,
169 bool compressed, const roo::byte* PROGMEM data, const Box& clip_box,
170 Color color, Color bgcolor, BlendingMode blending_mode) const {
171 Surface s(output, x + metrics.bearingX(), y - metrics.bearingY(), clip_box,
173 if (rle() && compressed) {
174 RleImage4bppxBiased<Alpha4> glyph(metrics.width(), metrics.height(), data,
175 color);
176 streamToSurface(s, std::move(glyph));
177 } else {
178 // Identical as above, but using Raster<> instead of MonoAlpha4RleImage.
179 ProgMemRaster<Alpha4> glyph(metrics.width(), metrics.height(), data, color);
180 streamToSurface(s, std::move(glyph));
181 }
182}
183
184void SmoothFont::drawBordered(DisplayOutput& output, int16_t x, int16_t y,
185 int16_t bgwidth, const Drawable& glyph,
186 const Box& clip_box, Color bgColor,
188 Box outer(x, y - metrics().glyphYMax(), x + bgwidth - 1,
189 y - metrics().glyphYMin());
190
191 // NOTE: bgColor is part of the source, not destination.
192 if (bgColor.a() == 0xFF &&
195 // All souce pixels will be fully opaque.
197 }
198
199 if (outer.clip(clip_box) == Box::ClipResult::kEmpty) return;
200 Box inner = glyph.extents().translate(x, y);
201 if (inner.clip(clip_box) == Box::ClipResult::kEmpty) {
202 output.fillRect(blending_mode, outer, bgColor);
203 return;
204 }
205 if (outer.yMin() < inner.yMin()) {
206 output.fillRect(
208 Box(outer.xMin(), outer.yMin(), outer.xMax(), inner.yMin() - 1),
209 bgColor);
210 }
211 if (outer.xMin() < inner.xMin()) {
212 output.fillRect(
214 Box(outer.xMin(), inner.yMin(), inner.xMin() - 1, inner.yMax()),
215 bgColor);
216 }
217 Surface s(output, x, y, clip_box, false, bgColor, FillMode::kExtents,
219 s.drawObject(glyph);
220 if (outer.xMax() > inner.xMax()) {
221 output.fillRect(
223 Box(inner.xMax() + 1, inner.yMin(), outer.xMax(), inner.yMax()),
224 bgColor);
225 }
226 if (outer.yMax() > inner.yMax()) {
227 output.fillRect(
229 Box(outer.xMin(), inner.yMax() + 1, outer.xMax(), outer.yMax()),
230 bgColor);
231 }
232}
233
234void SmoothFont::drawGlyphModeFill(
235 DisplayOutput& output, int16_t x, int16_t y, int16_t bgwidth,
236 const GlyphMetrics& glyph_metrics, bool compressed,
237 const roo::byte* PROGMEM data, int16_t offset, const Box& clip_box,
238 Color color, Color bgColor, BlendingMode blending_mode) const {
239 Box box = glyph_metrics.screen_extents().translate(offset, 0);
240 if (rle() && compressed) {
241 auto glyph = MakeDrawableRawStreamable(
242 RleImage4bppxBiased<Alpha4>(box, data, color));
243 drawBordered(output, x, y, bgwidth, glyph, clip_box, bgColor,
245 } else {
246 // Identical as above, but using Raster<>
247 auto glyph =
248 MakeDrawableRawStreamable(ProgMemRaster<Alpha4>(box, data, color));
249 drawBordered(output, x, y, bgwidth, glyph, clip_box, bgColor,
251 }
252}
253
254void SmoothFont::drawKernedGlyphsModeFill(
255 DisplayOutput& output, int16_t x, int16_t y, int16_t bgwidth,
256 const GlyphMetrics& left_metrics, bool left_compressed,
257 const roo::byte* PROGMEM left_data, int16_t left_offset,
258 const GlyphMetrics& right_metrics, bool right_compressed,
259 const roo::byte* PROGMEM right_data, int16_t right_offset,
260 const Box& clip_box, Color color, Color bgColor,
262 Box lb = left_metrics.screen_extents().translate(left_offset, 0);
263 Box rb = right_metrics.screen_extents().translate(right_offset, 0);
264 if (rle() && left_compressed && right_compressed) {
265 auto glyph = MakeDrawableRawStreamable(
266 Overlay(RleImage4bppxBiased<Alpha4>(lb, left_data, color), 0, 0,
267 RleImage4bppxBiased<Alpha4>(rb, right_data, color), 0, 0));
268 drawBordered(output, x, y, bgwidth, glyph, clip_box, bgColor,
270 } else if (rle() && left_compressed) {
271 auto glyph = MakeDrawableRawStreamable(
272 Overlay(RleImage4bppxBiased<Alpha4>(lb, left_data, color), 0, 0,
273 ProgMemRaster<Alpha4>(rb, right_data, color), 0, 0));
274 drawBordered(output, x, y, bgwidth, glyph, clip_box, bgColor,
276 } else if (rle() && right_compressed) {
277 auto glyph = MakeDrawableRawStreamable(
278 Overlay(ProgMemRaster<Alpha4>(lb, left_data, color), 0, 0,
279 RleImage4bppxBiased<Alpha4>(rb, right_data, color), 0, 0));
280 drawBordered(output, x, y, bgwidth, glyph, clip_box, bgColor,
282 } else {
283 auto glyph = MakeDrawableRawStreamable(
284 Overlay(ProgMemRaster<Alpha4>(lb, left_data, color), 0, 0,
285 ProgMemRaster<Alpha4>(rb, right_data, color), 0, 0));
286 drawBordered(output, x, y, bgwidth, glyph, clip_box, bgColor,
288 }
289}
290
292 public:
293 GlyphPairIterator(const SmoothFont* font) : font_(font), swapped_(false) {}
294
295 const GlyphMetrics& left_metrics() const { return swapped_ ? m2_ : m1_; }
296
297 const GlyphMetrics& right_metrics() const { return swapped_ ? m1_ : m2_; }
298
299 const roo::byte* PROGMEM left_data() const {
300 return font_->glyph_data_begin_ +
301 (swapped_ ? data_offset_2_ : data_offset_1_);
302 }
303
304 const roo::byte* PROGMEM right_data() const {
305 return font_->glyph_data_begin_ +
306 (swapped_ ? data_offset_1_ : data_offset_2_);
307 }
308
309 bool left_compressed() const {
310 return swapped_ ? compressed_2_ : compressed_1_;
311 }
312 bool right_compressed() const {
313 return swapped_ ? compressed_1_ : compressed_2_;
314 }
315
316 void push(char32_t code) {
317 swapped_ = !swapped_;
318 if (is_space(code)) {
319 *mutable_right_metrics() =
320 GlyphMetrics(0, 0, -1, -1, font_->default_space_width_);
321 *mutable_right_data_offset() = 0;
322 return;
323 }
324 const roo::byte* PROGMEM glyph = font_->findGlyph(code);
325 if (glyph == nullptr) {
326 glyph = font_->findGlyph(font_->default_glyph_);
327 }
328 if (glyph == nullptr) {
329 *mutable_right_metrics() =
330 GlyphMetrics(0, 0, -1, -1, font_->default_space_width_);
331 *mutable_right_data_offset() = 0;
332 } else {
334 *mutable_right_metrics() = glyph_meta.readMetrics(
335 FontLayout::kHorizontal, mutable_right_compressed());
336 *mutable_right_data_offset() = glyph_meta.data_offset();
337 }
338 }
339
340 void pushNull() { swapped_ = !swapped_; }
341
342 private:
343 GlyphMetrics* mutable_right_metrics() { return swapped_ ? &m1_ : &m2_; }
344
345 long* mutable_right_data_offset() {
346 return swapped_ ? &data_offset_1_ : &data_offset_2_;
347 }
348
349 bool& mutable_right_compressed() {
350 return swapped_ ? compressed_1_ : compressed_2_;
351 }
352
353 const SmoothFont* font_;
354 bool swapped_;
355 GlyphMetrics m1_;
356 GlyphMetrics m2_;
357 long data_offset_1_;
358 long data_offset_2_;
359 bool compressed_1_;
360 bool compressed_2_;
361};
362
364 uint32_t size) const {
365 roo_io::Utf8Decoder decoder(utf8_data, size);
366 char32_t next_code;
367 if (!decoder.next(next_code)) {
368 // Nothing to draw.
369 return GlyphMetrics(0, 0, -1, -1, 0);
370 }
372 glyphs.push(next_code);
373 bool has_more;
374 int16_t advance = 0;
375 int16_t yMin = 32767;
376 int16_t yMax = -32768;
377 int16_t xMin = glyphs.right_metrics().lsb();
378 int16_t xMax = xMin;
379 do {
380 char32_t code = next_code;
381 has_more = decoder.next(next_code);
383 if (has_more) {
384 glyphs.push(next_code);
385 kern = kerning(code, next_code);
386 } else {
387 next_code = 0;
388 glyphs.pushNull();
389 kern = 0;
390 }
391 advance += (glyphs.left_metrics().advance() - kern);
392 const GlyphMetrics& metrics = glyphs.left_metrics();
393 if (yMax < metrics.glyphYMax()) {
394 yMax = metrics.glyphYMax();
395 }
396 if (yMin > metrics.glyphYMin()) {
397 yMin = metrics.glyphYMin();
398 }
399 // NOTE: need to do this at every glyph, because of possibly trailing
400 // spaces.
401 int16_t xm = advance - glyphs.left_metrics().rsb() - 1;
402 if (xm > xMax) {
403 xMax = xm;
404 }
405 } while (has_more);
406 return GlyphMetrics(xMin, yMin, xMax, yMax, advance);
407}
408
410 uint32_t size,
412 uint32_t offset,
413 uint32_t max_count) const {
414 roo_io::Utf8Decoder decoder(utf8_data, size);
415 char32_t next_code;
416 if (!decoder.next(next_code)) {
417 // Nothing to measure.
418 return 0;
419 }
423 glyphs.push(next_code);
424 bool has_more;
425 int16_t advance = 0;
426 do {
427 if (glyph_count >= max_count) return glyph_count;
428 char32_t code = next_code;
429 has_more = decoder.next(next_code);
431 if (has_more) {
432 glyphs.push(next_code);
433 kern = kerning(code, next_code);
434 } else {
435 next_code = 0;
436 glyphs.pushNull();
437 kern = 0;
438 }
439 if (glyph_idx >= offset) {
441 GlyphMetrics(glyphs.left_metrics().glyphXMin() + advance,
442 glyphs.left_metrics().glyphYMin(),
443 glyphs.left_metrics().glyphXMax() + advance,
444 glyphs.left_metrics().glyphYMax(),
445 glyphs.left_metrics().advance() + advance);
446 }
447 ++glyph_idx;
448 advance += (glyphs.left_metrics().advance() - kern);
449 } while (has_more);
450 return glyph_count;
451}
452
454 uint32_t size, Color color) const {
455 roo_io::Utf8Decoder decoder(utf8_data, size);
456 char32_t next_code;
457 if (!decoder.next(next_code)) {
458 // Nothing to draw.
459 return;
460 }
461 int16_t x = s.dx();
462 int16_t y = s.dy();
463 DisplayOutput& output = s.out();
464
466 glyphs.push(next_code);
468 if (glyphs.right_metrics().lsb() < 0) {
469 preadvanced = glyphs.right_metrics().lsb();
470 x += preadvanced;
471 }
472 bool has_more;
473 do {
474 char32_t code = next_code;
475 has_more = decoder.next(next_code);
477 if (has_more) {
478 glyphs.push(next_code);
479 kern = kerning(code, next_code);
480 } else {
481 next_code = 0;
482 glyphs.pushNull();
483 kern = 0;
484 }
485 if (s.fill_mode() == FillMode::kVisible) {
486 // No fill; simply draw and shift.
487 drawGlyphModeVisible(output, x - preadvanced, y, glyphs.left_metrics(),
488 glyphs.left_compressed(), glyphs.left_data(),
489 s.clip_box(), color, s.bgcolor(), s.blending_mode());
490 x += (glyphs.left_metrics().advance() - kern);
491 } else {
492 // General case. We may have two glyphs to worry about, and we may be
493 // pre-advanced. Let's determine our bounding box, taking the
494 // pre-advancement and kerning into account.
495 int16_t advance = glyphs.left_metrics().advance() - kern;
496 int16_t gap = 0;
497 if (has_more) {
498 gap = glyphs.left_metrics().rsb() + glyphs.right_metrics().lsb() - kern;
499 }
500 // Calculate the total width of a rectangle that we will need to fill with
501 // content (glyphs + background).
503 glyphs.left_metrics().glyphXMax() + 1 - preadvanced;
504 if (gap < 0) {
505 drawKernedGlyphsModeFill(
506 output, x, y, total_rect_width, glyphs.left_metrics(),
507 glyphs.left_compressed(), glyphs.left_data(), -preadvanced,
508 glyphs.right_metrics(), glyphs.right_compressed(),
509 glyphs.right_data(), advance - preadvanced,
510 Box::Intersect(s.clip_box(), Box(x, y - metrics().glyphYMax(),
511 x + total_rect_width - 1,
512 y - metrics().glyphYMin())),
513 color, s.bgcolor(), s.blending_mode());
514 } else {
515 // Glyphs do not overlap; can draw them one by one.
516 if (has_more) {
517 // Include the interim whitespace gap in the rectangle that we'll fill
518 // (the gap will be filled with background). This way, we draw longer
519 // horizontal strikes which translates to fewer SPI operations.
521 } else if (glyphs.left_metrics().rsb() > 0) {
522 // After the last character, fill up the right-side bearing.
523 total_rect_width += glyphs.left_metrics().rsb();
524 }
525 drawGlyphModeFill(
526 output, x, y, total_rect_width, glyphs.left_metrics(),
527 glyphs.left_compressed(), glyphs.left_data(), -preadvanced,
528 Box::Intersect(s.clip_box(), Box(x, y - metrics().glyphYMax(),
529 x + total_rect_width - 1,
530 y - metrics().glyphYMin())),
531 color, s.bgcolor(), s.blending_mode());
532 }
533 x += total_rect_width;
535 }
536 } while (has_more);
537}
538
540 Color color) const {
542 if (layout != FontLayout::kHorizontal) return;
543
544 int16_t x = s.dx();
545 int16_t y = s.dy();
546 DisplayOutput& output = s.out();
547
548 const roo::byte* PROGMEM glyph = findGlyph(code);
549 if (glyph == nullptr) {
550 if (is_space(code)) {
551 if (s.fill_mode() == FillMode::kExtents && default_space_width_ > 0) {
552 output.fillRect(
553 s.blending_mode(),
554 Box(x, y - metrics().glyphYMax(), x + default_space_width_ - 1,
555 y - metrics().glyphYMin()),
556 s.bgcolor());
557 }
558 return;
559 }
560 glyph = findGlyph(default_glyph_);
561 }
562 if (glyph == nullptr) return;
563
565 bool compressed;
568
570 if (glyph_metrics.lsb() < 0) {
572 x += preadvanced;
573 }
574
575 if (s.fill_mode() == FillMode::kVisible) {
576 drawGlyphModeVisible(output, x - preadvanced, y, glyph_metrics, compressed,
577 glyph_data_begin_ + reader.data_offset(), s.clip_box(),
578 color, s.bgcolor(), s.blending_mode());
579 return;
580 }
581
583 if (glyph_metrics.rsb() > 0) {
584 // For a standalone glyph, include trailing side-bearing in the fill area.
586 }
587 drawGlyphModeFill(
589 glyph_data_begin_ + reader.data_offset(), -preadvanced,
590 Box::Intersect(s.clip_box(),
591 Box(x, y - metrics().glyphYMax(),
592 x + total_rect_width - 1, y - metrics().glyphYMin())),
593 color, s.bgcolor(), s.blending_mode());
594}
595
597 GlyphMetrics* result) const {
598 const roo::byte* PROGMEM glyph = findGlyph(code);
599 if (glyph == nullptr) {
600 if (is_space(code)) {
601 *result = GlyphMetrics(0, 0, -1, -1,
603 ? default_space_width_
604 : 1 + metrics().linegap());
605 return true;
606 } else {
607 return false;
608 }
609 }
611 bool compressed;
612 *result = reader.readMetrics(layout, compressed);
613 return true;
614}
615
616int16_t SmoothFont::getKerning(char32_t left, char32_t right) const {
617 return kerning(left, right);
618}
619
620template <int encoding_bytes>
621char32_t read_unicode(const roo::byte* PROGMEM address);
622
623template <>
624inline char32_t read_unicode<1>(const roo::byte* PROGMEM address) {
625 return pgm_read_byte(address);
626}
627
628template <>
629inline char32_t read_unicode<2>(const roo::byte* PROGMEM address) {
630 return ((char32_t)pgm_read_byte(address) << 8) | pgm_read_byte(address + 1);
631}
632
633template <int encoding_bytes>
634const roo::byte* PROGMEM indexSearch(char32_t c, const roo::byte* PROGMEM data,
635 int glyph_size, int start, int stop) {
636 int pivot = (start + stop) / 2;
637 const roo::byte* PROGMEM pivot_ptr = data + (pivot * glyph_size);
639 if (c == pivot_value) return pivot_ptr;
640 if (start >= stop) return nullptr;
641 if (c < pivot_value)
644}
645
646const roo::byte* PROGMEM SmoothFont::findGlyph(char32_t code) const {
647 switch (encoding_bytes_) {
648 case 1:
649 return indexSearch<1>(code, glyph_metadata_begin_, glyph_metadata_size_,
650 0, glyph_count_);
651 case 2:
652 return indexSearch<2>(code, glyph_metadata_begin_, glyph_metadata_size_,
653 0, glyph_count_);
654 default:
655 return nullptr;
656 }
657}
658
659template <int encoding_bytes>
661 const roo::byte* PROGMEM data,
662 int kern_size, int start, int stop) {
663 int pivot = (start + stop) / 2;
664 const roo::byte* PROGMEM pivot_ptr = data + (pivot * kern_size);
667 uint32_t pivot_value = left << 16 | right;
668 if (lookup == pivot_value) {
669 return pivot_ptr;
670 }
671 if (start >= stop) {
672 return nullptr;
673 }
674 if (lookup < pivot_value)
676 pivot - 1);
678 stop);
679}
680
681const roo::byte* PROGMEM SmoothFont::findKernPair(char32_t left,
682 char32_t right) const {
683 uint32_t lookup = left << 16 | right;
684 switch (encoding_bytes_) {
685 case 1:
686 return kernIndexSearch<1>(lookup, glyph_kerning_begin_,
687 glyph_kerning_size_, 0, kerning_pairs_count_);
688 case 2:
689 return kernIndexSearch<2>(lookup, glyph_kerning_begin_,
690 glyph_kerning_size_, 0, kerning_pairs_count_);
691 default:
692 return nullptr;
693 }
694}
695
696int16_t SmoothFont::kerning(char32_t left, char32_t right) const {
697 const roo::byte* PROGMEM kern = findKernPair(left, right);
698 if (kern == 0) {
699 return 0;
700 } else {
701 return pgm_read_byte(kern + 2 * encoding_bytes_);
702 }
703}
704
705} // namespace roo_display
Axis-aligned integer rectangle.
Definition box.h:12
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
void fillRect(BlendingMode blending_mode, const Box &rect, Color color)
Fill a single rectangle. Invalidates the address window.
Definition device.h:135
FontMetricReader(roo_io::UnsafeGenericMemoryIterator< const roo::byte PROGMEM * > &reader, int font_metric_bytes)
Basic font metrics (ascent, descent, bounding box, and line spacing).
Definition font.h:24
int16_t linegap() const
Additional gap between lines.
Definition font.h:44
int16_t glyphYMin() const
Definition font.h:50
int16_t glyphYMax() const
Definition font.h:52
Metadata describing a font's encoding and spacing behavior.
Definition font.h:73
void init(FontMetrics metrics, FontProperties properties)
Definition font.h:258
const FontMetrics & metrics() const
Return font metrics.
Definition font.h:188
Per-glyph metrics (bounding box and advance).
Definition font.h:139
GlyphPairIterator(const SmoothFont *font)
const roo::byte *PROGMEM right_data() const
const GlyphMetrics & left_metrics() const
const GlyphMetrics & right_metrics() const
const roo::byte *PROGMEM left_data() const
GlyphMetrics readMetrics(FontLayout layout, bool &compressed)
GlyphMetadataReader(const SmoothFont &font, const roo::byte *PROGMEM ptr)
Anti-aliased font implementation based on PROGMEM data.
Definition smooth_font.h:13
int16_t getKerning(char32_t left, char32_t right) const override
Return kerning adjustment for a pair of code points.
SmoothFont(const U *font_data PROGMEM)
Legacy constructor for fonts using uint8_t byte data.
Definition smooth_font.h:19
void drawHorizontalString(const Surface &s, const char *utf8_data, uint32_t size, Color color) const override
Draw a UTF-8 string horizontally.
uint32_t getHorizontalStringGlyphMetrics(const char *utf8_data, uint32_t size, GlyphMetrics *result, uint32_t offset, uint32_t max_count) const override
Return metrics for consecutive glyphs in the UTF-8 string.
GlyphMetrics getHorizontalStringMetrics(const char *utf8_data, uint32_t size) const override
Return metrics of the specified UTF-8 string as if it were a single glyph.
bool getGlyphMetrics(char32_t code, FontLayout layout, GlyphMetrics *result) const override
Retrieve glyph metrics for a code point and layout.
void drawGlyph(const Surface &s, char32_t code, FontLayout layout, Color color) const override
Draw a single glyph.
Low-level handle used to draw to an underlying device.
Definition drawable.h:60
@ CHECK
Definition inflate.h:47
Defines 140 opaque HTML named colors.
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.
bool is_space(char32_t code)
DrawableRawStreamable< RawStreamable > MakeDrawableRawStreamable(RawStreamable streamable)
FontLayout
Glyph layout direction.
Definition font.h:16
char32_t read_unicode< 1 >(const roo::byte *PROGMEM address)
const roo::byte *PROGMEM kernIndexSearch(uint32_t lookup, const roo::byte *PROGMEM data, int kern_size, int start, int stop)
static int8_t readByte(const roo::byte *PROGMEM ptr)
char32_t read_unicode(const roo::byte *PROGMEM address)
const roo::byte *PROGMEM indexSearch(char32_t c, const roo::byte *PROGMEM data, int glyph_size, int start, int stop)
static int16_t readWord(const roo::byte *PROGMEM ptr)
internal::Superposition< Bg, Fg > Overlay(Bg bg, int16_t bg_x, int16_t bg_y, Fg fg, int16_t fg_x, int16_t fg_y)
static const uint8_t font[] PROGMEM
@ kVisible
Fully transparent pixels do not need to be filled.
@ kExtents
Fill the entire extents box (possibly with fully transparent pixels).
void streamToSurface(const Surface &s, RawStreamable streamable)
char32_t read_unicode< 2 >(const roo::byte *PROGMEM address)
#define pgm_read_byte(addr)
Definition progmem.h:14
#define PROGMEM
Definition progmem.h:10
Color bgcolor
Definition smooth.cpp:889
BlendingMode blending_mode
Definition smooth.cpp:888