ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
font.cpp
Go to the documentation of this file.
1#include "font.h"
2
4#include "esphome/core/hal.h"
5#include "esphome/core/log.h"
7
8namespace esphome::font {
9ESPHOME_LOG_TAG(TAG, "font");
10
11#ifdef USE_LVGL_FONT
12static const uint8_t OPA4_TABLE[16] = {0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255};
13
14static const uint8_t OPA2_TABLE[4] = {0, 85, 170, 255};
15
16const void *Font::get_glyph_bitmap(lv_font_glyph_dsc_t *dsc, lv_draw_buf_t *draw_buf) {
17 const auto *font = dsc->resolved_font;
18 auto *const fe = (Font *) font->dsc;
19
20 const auto *gd = fe->get_glyph_data_(dsc->gid.index);
21 if (gd == nullptr) {
22 return nullptr;
23 }
24
25 const uint8_t *bitmap_in = gd->data;
26 uint8_t *bitmap_out_tmp = draw_buf->data;
27 int32_t i = 0;
28 int32_t x, y;
29 uint32_t stride = lv_draw_buf_width_to_stride(gd->width, LV_COLOR_FORMAT_A8);
30
31 switch (fe->get_bpp()) {
32 case 1: {
33 uint8_t mask = 0;
34 uint8_t byte = 0;
35 for (y = 0; y != gd->height; y++) {
36 for (x = 0; x != gd->width; x++) {
37 if (mask == 0) {
38 mask = 0x80;
39 byte = *bitmap_in++;
40 }
41 bitmap_out_tmp[x] = byte & mask ? 255 : 0;
42 mask >>= 1;
43 }
44 bitmap_out_tmp += stride;
45 }
46 } break;
47
48 case 2:
49 for (y = 0; y != gd->height; y++) {
50 for (x = 0; x != gd->width; x++, i++) {
51 switch (i & 0x3) {
52 default:
53 bitmap_out_tmp[x] = OPA2_TABLE[(*bitmap_in) >> 6];
54 break;
55 case 1:
56 bitmap_out_tmp[x] = OPA2_TABLE[((*bitmap_in) >> 4) & 0x3];
57 break;
58 case 2:
59 bitmap_out_tmp[x] = OPA2_TABLE[((*bitmap_in) >> 2) & 0x3];
60 break;
61 case 3:
62 bitmap_out_tmp[x] = OPA2_TABLE[((*bitmap_in) >> 0) & 0x3];
63 bitmap_in++;
64 }
65 }
66 bitmap_out_tmp += stride;
67 }
68 break;
69
70 case 4:
71 for (y = 0; y != gd->height; y++) {
72 for (x = 0; x != gd->width; x++, i++) {
73 i = i & 0x1;
74 if (i == 0) {
75 bitmap_out_tmp[x] = OPA4_TABLE[(*bitmap_in) >> 4];
76 } else if (i == 1) {
77 bitmap_out_tmp[x] = OPA4_TABLE[(*bitmap_in) & 0xF];
78 bitmap_in++;
79 }
80 }
81 bitmap_out_tmp += stride;
82 }
83 break;
84
85 case 8:
86 memcpy(bitmap_out_tmp, bitmap_in, gd->width * gd->height);
87 break;
88 default:
89 ESP_LOGD(TAG, "Unknown bpp: %d", fe->get_bpp());
90 break;
91 }
92 return draw_buf;
93}
94
95bool Font::get_glyph_dsc_cb(const lv_font_t *font, lv_font_glyph_dsc_t *dsc, uint32_t unicode_letter, uint32_t next) {
96 auto *fe = (Font *) font->dsc;
97 const auto *gd = fe->get_glyph_data_(unicode_letter);
98 if (gd == nullptr) {
99 return false;
100 }
101 dsc->adv_w = gd->advance;
102 dsc->ofs_x = gd->offset_x;
103 dsc->ofs_y = fe->height_ - gd->height - gd->offset_y - fe->lv_font_.base_line;
104 dsc->box_w = gd->width;
105 dsc->box_h = gd->height;
106 dsc->is_placeholder = 0;
107 dsc->format = (lv_font_glyph_format_t) fe->get_bpp();
108 dsc->gid.index = unicode_letter;
109 return true;
110}
111
112const Glyph *Font::get_glyph_data_(uint32_t unicode_letter) {
113 if (unicode_letter == this->last_letter_ && this->last_letter_ != 0)
114 return this->last_data_;
115 auto *glyph = this->find_glyph(unicode_letter);
116 if (glyph == nullptr) {
117 return nullptr;
118 }
119 this->last_data_ = glyph;
120 this->last_letter_ = unicode_letter;
121 return glyph;
122}
123#endif
124
125Font::Font(const Glyph *data, int data_nr, int baseline, int height, int descender, int xheight, int capheight,
126 uint8_t bpp)
127 : glyphs_(ConstVector(data, data_nr)),
128 baseline_(baseline),
129 height_(height),
130 descender_(descender),
131 linegap_(height - baseline - descender),
132 xheight_(xheight),
133 capheight_(capheight),
134 bpp_(bpp) {
135#ifdef USE_LVGL_FONT
136 this->lv_font_.dsc = this;
137 this->lv_font_.line_height = this->get_height();
138 this->lv_font_.base_line = this->lv_font_.line_height - this->get_baseline();
139 this->lv_font_.get_glyph_dsc = get_glyph_dsc_cb;
140 this->lv_font_.get_glyph_bitmap = get_glyph_bitmap;
141 this->lv_font_.subpx = LV_FONT_SUBPX_NONE;
142 this->lv_font_.underline_position = -1;
143 this->lv_font_.underline_thickness = 1;
144#endif
145}
146
147const Glyph *Font::find_glyph(uint32_t codepoint) const {
148 int lo = 0;
149 int hi = this->glyphs_.size() - 1;
150 while (lo != hi) {
151 int mid = (lo + hi + 1) / 2;
152 if (this->glyphs_[mid].is_less_or_equal(codepoint)) {
153 lo = mid;
154 } else {
155 hi = mid - 1;
156 }
157 }
158 auto *result = &this->glyphs_[lo];
159 if (result->code_point == codepoint)
160 return result;
161 return nullptr;
162}
163
164#ifdef USE_DISPLAY
165void Font::measure(const char *str, int *width, int *x_offset, int *baseline, int *height) {
166 *baseline = this->baseline_;
167 *height = this->height_;
168 int min_x = 0;
169 bool has_char = false;
170 int x = 0;
171 for (;;) {
172 size_t length;
173 auto code_point = unicode::extract_unicode_codepoint(str, &length);
174 if (length == 0)
175 break;
176 str += length;
177 auto *glyph = this->find_glyph(code_point);
178 if (glyph == nullptr) {
179 // Unknown char, skip
180 if (!this->glyphs_.empty())
181 x += this->glyphs_[0].advance;
182 continue;
183 }
184
185 if (!has_char) {
186 min_x = glyph->offset_x;
187 } else {
188 min_x = std::min(min_x, x + glyph->offset_x);
189 }
190 x += glyph->advance;
191
192 has_char = true;
193 }
194 *x_offset = min_x;
195 *width = x - min_x;
196}
197
198void Font::print(int x_start, int y_start, display::Display *display, Color color, const char *text, Color background) {
199 int x_at = x_start;
200 for (;;) {
201 size_t length;
202 auto code_point = unicode::extract_unicode_codepoint(text, &length);
203 if (length == 0)
204 break;
205 text += length;
206 auto *glyph = this->find_glyph(code_point);
207 if (glyph == nullptr) {
208 // Unknown char, skip
209 ESP_LOGW(TAG, "Codepoint 0x%08" PRIx32 " not found in font", code_point);
210 if (!this->glyphs_.empty()) {
211 // Full-width read: a narrowing byte load would fault on a PROGMEM table on ESP8266.
212 int glyph_width = this->glyphs_[0].advance;
213 display->rectangle(x_at, y_start, glyph_width, this->height_, color);
214 x_at += glyph_width;
215 }
216 continue;
217 }
218
219 const uint8_t *data = glyph->data;
220 const int max_x = x_at + glyph->offset_x + glyph->width;
221 const int max_y = y_start + glyph->offset_y + glyph->height;
222
223 uint8_t bitmask = 0;
224 uint8_t pixel_data = 0;
225 uint8_t bpp_max = (1 << this->bpp_) - 1;
226 auto diff_r = (float) color.r - (float) background.r;
227 auto diff_g = (float) color.g - (float) background.g;
228 auto diff_b = (float) color.b - (float) background.b;
229 auto diff_w = (float) color.w - (float) background.w;
230 auto b_r = (float) background.r;
231 auto b_g = (float) background.g;
232 auto b_b = (float) background.b;
233 auto b_w = (float) background.w;
234 for (int glyph_y = y_start + glyph->offset_y; glyph_y != max_y; glyph_y++) {
235 for (int glyph_x = x_at + glyph->offset_x; glyph_x != max_x; glyph_x++) {
236 uint8_t pixel = 0;
237 for (uint8_t bit_num = 0; bit_num != this->bpp_; bit_num++) {
238 if (bitmask == 0) {
239 pixel_data = progmem_read_byte(data++);
240 bitmask = 0x80;
241 }
242 pixel <<= 1;
243 if ((pixel_data & bitmask) != 0)
244 pixel |= 1;
245 bitmask >>= 1;
246 }
247 if (pixel == bpp_max) {
248 display->draw_pixel_at(glyph_x, glyph_y, color);
249 } else if (pixel != 0) {
250 auto on = (float) pixel / (float) bpp_max;
251 auto blended = Color((uint8_t) (diff_r * on + b_r), (uint8_t) (diff_g * on + b_g),
252 (uint8_t) (diff_b * on + b_b), (uint8_t) (diff_w * on + b_w));
253 display->draw_pixel_at(glyph_x, glyph_y, blended);
254 }
255 }
256 }
257 x_at += glyph->advance;
258 }
259}
260#endif
261} // namespace esphome::font
Lightweight read-only view over a const array stored in RODATA (will typically be in flash memory).
Definition helpers.h:130
void rectangle(int x1, int y1, int width, int height, Color color=COLOR_ON)
Draw the outline of a rectangle with the top left point at [x1,y1] and the bottom right point at [x1+...
Definition display.cpp:166
void draw_pixel_at(int x, int y)
Set a single pixel at the specified coordinates to default color.
Definition display.h:343
lv_font_t lv_font_
Definition font.h:105
void measure(const char *str, int *width, int *x_offset, int *baseline, int *height) override
Definition font.cpp:165
const Glyph * find_glyph(uint32_t codepoint) const
Definition font.cpp:147
void print(int x_start, int y_start, display::Display *display, Color color, const char *text, Color background) override
Definition font.cpp:198
static bool get_glyph_dsc_cb(const lv_font_t *font, lv_font_glyph_dsc_t *dsc, uint32_t unicode_letter, uint32_t next)
Definition font.cpp:95
int get_height()
Definition font.h:82
uint32_t last_letter_
Definition font.h:109
const Glyph * get_glyph_data_(uint32_t unicode_letter)
Definition font.cpp:112
int get_baseline()
Definition font.h:81
const Glyph * last_data_
Definition font.h:110
static const void * get_glyph_bitmap(lv_font_glyph_dsc_t *dsc, lv_draw_buf_t *draw_buf)
Definition font.cpp:16
ConstVector< Glyph > glyphs_
Definition font.h:96
Font(const Glyph *data, int data_nr, int baseline, int height, int descender, int xheight, int capheight, uint8_t bpp=1)
Construct the font with the given glyphs.
Definition font.cpp:125
ESPHOME_LOG_TAG(TAG, "font")
uint32_t extract_unicode_codepoint(const char *utf8_str, size_t *length)
Attempt to extract a 32 bit Unicode codepoint from a UTF-8 string.
Definition unicode.cpp:5
uint8_t progmem_read_byte(const uint8_t *addr)
Definition hal.h:51
static void uint32_t
uint8_t w
Definition color.h:42
uint8_t g
Definition color.h:34
uint8_t b
Definition color.h:38
uint8_t r
Definition color.h:30
uint16_t length
Definition tt21100.cpp:0
uint16_t x
Definition tt21100.cpp:5
uint16_t y
Definition tt21100.cpp:6