7#include "roo_io/core/input_iterator.h"
8#include "roo_io/text/unicode.h"
9#include "roo_logging.h"
16 roo_io::UnsafeGenericMemoryIterator<const roo::byte PROGMEM*>&
reader,
21 return font_metric_bytes_ == 1 ? roo_io::ReadS8(reader_)
22 : roo_io::ReadBeS16(reader_);
26 roo_io::UnsafeGenericMemoryIterator<const roo::byte PROGMEM*>& reader_;
27 int font_metric_bytes_;
41 : font_(font), ptr_(
ptr + font.encoding_bytes_) {}
45 if (font_.font_metric_bytes_ == 1) {
63 glyphXMin, glyphYMin, glyphXMax, glyphYMax,
71 const roo::byte*
PROGMEM ptr = ptr_ + 5 * font_.font_metric_bytes_;
86 : glyph_count_(0), default_glyph_(0), default_space_width_(0) {
87 roo_io::UnsafeGenericMemoryIterator<const roo::byte PROGMEM*>
reader(
91 <<
"Unsupported version number: " <<
version;
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);
97 <<
"Unsupported character encoding: " << encoding_bytes_;
98 font_metric_bytes_ = roo_io::ReadU8(
reader);
100 <<
"Unsupported count of metric bytes: " << encoding_bytes_;
102 offset_bytes_ = roo_io::ReadU8(
reader);
104 <<
"Unsupported count of offset bytes: " << offset_bytes_;
106 compression_method_ = roo_io::ReadU8(
reader);
108 <<
"Unsupported compression method: " << compression_method_;
110 glyph_count_ = roo_io::ReadBeU16(
reader);
111 kerning_pairs_count_ = roo_io::ReadBeU16(
reader);
126 if (encoding_bytes_ == 1) {
127 default_glyph_ = roo_io::ReadU8(
reader);
129 default_glyph_ = roo_io::ReadBeU16(
reader);
133 glyph_metadata_size_ =
134 (5 * font_metric_bytes_) + offset_bytes_ + encoding_bytes_;
135 glyph_kerning_size_ = 2 * encoding_bytes_ + 1;
160 return code == 0x0020 ||
code == 0x00A0 ||
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,
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,
179 ProgMemRaster<Alpha4> glyph(
metrics.width(),
metrics.height(), data, color);
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,
192 if (bgColor.a() == 0xFF &&
200 Box inner = glyph.extents().translate(x, y);
205 if (outer.yMin() < inner.yMin()) {
208 Box(outer.xMin(), outer.yMin(), outer.xMax(), inner.yMin() - 1),
211 if (outer.xMin() < inner.xMin()) {
214 Box(outer.xMin(), inner.yMin(), inner.xMin() - 1, inner.yMax()),
220 if (outer.xMax() > inner.xMax()) {
223 Box(inner.xMax() + 1, inner.yMin(), outer.xMax(), inner.yMax()),
226 if (outer.yMax() > inner.yMax()) {
229 Box(outer.xMin(), inner.yMax() + 1, outer.xMax(), outer.yMax()),
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,
239 Box box = glyph_metrics.screen_extents().translate(offset, 0);
240 if (rle() && compressed) {
242 RleImage4bppxBiased<Alpha4>(box, data, color));
243 drawBordered(output, x, y, bgwidth, glyph, clip_box, bgColor,
249 drawBordered(output, x, y, bgwidth, glyph, clip_box, bgColor,
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) {
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) {
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) {
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,
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,
300 return font_->glyph_data_begin_ +
301 (swapped_ ? data_offset_2_ : data_offset_1_);
305 return font_->glyph_data_begin_ +
306 (swapped_ ? data_offset_1_ : data_offset_2_);
310 return swapped_ ? compressed_2_ : compressed_1_;
313 return swapped_ ? compressed_1_ : compressed_2_;
317 swapped_ = !swapped_;
319 *mutable_right_metrics() =
320 GlyphMetrics(0, 0, -1, -1, font_->default_space_width_);
321 *mutable_right_data_offset() = 0;
325 if (
glyph ==
nullptr) {
326 glyph = font_->findGlyph(font_->default_glyph_);
328 if (
glyph ==
nullptr) {
329 *mutable_right_metrics() =
330 GlyphMetrics(0, 0, -1, -1, font_->default_space_width_);
331 *mutable_right_data_offset() = 0;
334 *mutable_right_metrics() =
glyph_meta.readMetrics(
336 *mutable_right_data_offset() =
glyph_meta.data_offset();
343 GlyphMetrics* mutable_right_metrics() {
return swapped_ ? &m1_ : &m2_; }
345 long* mutable_right_data_offset() {
346 return swapped_ ? &data_offset_1_ : &data_offset_2_;
349 bool& mutable_right_compressed() {
350 return swapped_ ? compressed_1_ : compressed_2_;
353 const SmoothFont* font_;
442 glyphs.left_metrics().glyphYMin(),
444 glyphs.left_metrics().glyphYMax(),
468 if (
glyphs.right_metrics().lsb() < 0) {
489 s.clip_box(),
color,
s.bgcolor(),
s.blending_mode());
490 x += (
glyphs.left_metrics().advance() -
kern);
505 drawKernedGlyphsModeFill(
513 color,
s.bgcolor(),
s.blending_mode());
521 }
else if (
glyphs.left_metrics().rsb() > 0) {
531 color,
s.bgcolor(),
s.blending_mode());
548 const roo::byte* PROGMEM
glyph = findGlyph(
code);
549 if (
glyph ==
nullptr) {
554 Box(x, y -
metrics().glyphYMax(), x + default_space_width_ - 1,
560 glyph = findGlyph(default_glyph_);
562 if (
glyph ==
nullptr)
return;
578 color,
s.bgcolor(),
s.blending_mode());
593 color,
s.bgcolor(),
s.blending_mode());
598 const roo::byte* PROGMEM
glyph = findGlyph(
code);
599 if (
glyph ==
nullptr) {
603 ? default_space_width_
617 return kerning(left, right);
620template <
int encoding_
bytes>
633template <
int encoding_
bytes>
646const roo::byte*
PROGMEM SmoothFont::findGlyph(
char32_t code)
const {
647 switch (encoding_bytes_) {
649 return indexSearch<1>(
code, glyph_metadata_begin_, glyph_metadata_size_,
652 return indexSearch<2>(
code, glyph_metadata_begin_, glyph_metadata_size_,
659template <
int encoding_
bytes>
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_) {
686 return kernIndexSearch<1>(lookup, glyph_kerning_begin_,
687 glyph_kerning_size_, 0, kerning_pairs_count_);
689 return kernIndexSearch<2>(lookup, glyph_kerning_begin_,
690 glyph_kerning_size_, 0, kerning_pairs_count_);
696int16_t SmoothFont::kerning(
char32_t left,
char32_t right)
const {
697 const roo::byte* PROGMEM kern = findKernPair(left, right);
Axis-aligned integer rectangle.
static Box Intersect(const Box &a, const Box &b)
Return the intersection of two boxes (may be empty).
ARGB8888 color stored as a 32-bit unsigned integer.
The abstraction for drawing to a display.
void fillRect(BlendingMode blending_mode, const Box &rect, Color color)
Fill a single rectangle. Invalidates the address window.
FontMetricReader(roo_io::UnsafeGenericMemoryIterator< const roo::byte PROGMEM * > &reader, int font_metric_bytes)
Basic font metrics (ascent, descent, bounding box, and line spacing).
int16_t linegap() const
Additional gap between lines.
int16_t glyphYMin() const
int16_t glyphYMax() const
Metadata describing a font's encoding and spacing behavior.
void init(FontMetrics metrics, FontProperties properties)
const FontMetrics & metrics() const
Return font metrics.
Per-glyph metrics (bounding box and advance).
GlyphPairIterator(const SmoothFont *font)
const roo::byte *PROGMEM right_data() const
bool left_compressed() const
const GlyphMetrics & left_metrics() const
const GlyphMetrics & right_metrics() const
bool right_compressed() const
const roo::byte *PROGMEM left_data() const
Anti-aliased font implementation based on PROGMEM data.
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.
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.
Defines 140 opaque HTML named colors.
BlendingMode
Porter-Duff style blending modes.
@ 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.
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)
BlendingMode blending_mode