ESPHome 2025.12.1
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"
6
7namespace esphome {
8namespace font {
9static const char *const TAG = "font";
10
11#ifdef USE_LVGL_FONT
12const uint8_t *Font::get_glyph_bitmap(const lv_font_t *font, uint32_t unicode_letter) {
13 auto *fe = (Font *) font->dsc;
14 const auto *gd = fe->get_glyph_data_(unicode_letter);
15 if (gd == nullptr) {
16 return nullptr;
17 }
18 return gd->data;
19}
20
21bool Font::get_glyph_dsc_cb(const lv_font_t *font, lv_font_glyph_dsc_t *dsc, uint32_t unicode_letter, uint32_t next) {
22 auto *fe = (Font *) font->dsc;
23 const auto *gd = fe->get_glyph_data_(unicode_letter);
24 if (gd == nullptr) {
25 return false;
26 }
27 dsc->adv_w = gd->advance;
28 dsc->ofs_x = gd->offset_x;
29 dsc->ofs_y = fe->height_ - gd->height - gd->offset_y - fe->lv_font_.base_line;
30 dsc->box_w = gd->width;
31 dsc->box_h = gd->height;
32 dsc->is_placeholder = 0;
33 dsc->bpp = fe->get_bpp();
34 return true;
35}
36
37const Glyph *Font::get_glyph_data_(uint32_t unicode_letter) {
38 if (unicode_letter == this->last_letter_ && this->last_letter_ != 0)
39 return this->last_data_;
40 auto *glyph = this->find_glyph(unicode_letter);
41 if (glyph == nullptr) {
42 return nullptr;
43 }
44 this->last_data_ = glyph;
45 this->last_letter_ = unicode_letter;
46 return glyph;
47}
48#endif
49
60static uint32_t extract_unicode_codepoint(const char *utf8_str, size_t *length) {
61 // Safely cast to uint8_t* for correct bitwise operations on bytes
62 const uint8_t *current = reinterpret_cast<const uint8_t *>(utf8_str);
63 uint32_t code_point = 0;
64 uint8_t c1 = *current++;
65
66 // check for end of string
67 if (c1 == 0) {
68 *length = 0;
69 return 0;
70 }
71
72 // --- 1-Byte Sequence: 0xxxxxxx (ASCII) ---
73 if (c1 < 0x80) {
74 // Valid ASCII byte.
75 code_point = c1;
76 // Optimization: No need to check for continuation bytes.
77 }
78 // --- 2-Byte Sequence: 110xxxxx 10xxxxxx ---
79 else if ((c1 & 0xE0) == 0xC0) {
80 uint8_t c2 = *current++;
81
82 // Error Check 1: Check if c2 is a valid continuation byte (10xxxxxx)
83 if ((c2 & 0xC0) != 0x80) {
84 *length = 0;
85 return 0;
86 }
87
88 code_point = (c1 & 0x1F) << 6;
89 code_point |= (c2 & 0x3F);
90
91 // Error Check 2: Overlong check (2-byte must be > 0x7F)
92 if (code_point <= 0x7F) {
93 *length = 0;
94 return 0;
95 }
96 }
97 // --- 3-Byte Sequence: 1110xxxx 10xxxxxx 10xxxxxx ---
98 else if ((c1 & 0xF0) == 0xE0) {
99 uint8_t c2 = *current++;
100 uint8_t c3 = *current++;
101
102 // Error Check 1: Check continuation bytes
103 if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80)) {
104 *length = 0;
105 return 0;
106 }
107
108 code_point = (c1 & 0x0F) << 12;
109 code_point |= (c2 & 0x3F) << 6;
110 code_point |= (c3 & 0x3F);
111
112 // Error Check 2: Overlong check (3-byte must be > 0x7FF)
113 // Also check for surrogates (0xD800-0xDFFF)
114 if (code_point <= 0x7FF || (code_point >= 0xD800 && code_point <= 0xDFFF)) {
115 *length = 0;
116 return 0;
117 }
118 }
119 // --- 4-Byte Sequence: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx ---
120 else if ((c1 & 0xF8) == 0xF0) {
121 uint8_t c2 = *current++;
122 uint8_t c3 = *current++;
123 uint8_t c4 = *current++;
124
125 // Error Check 1: Check continuation bytes
126 if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80) || ((c4 & 0xC0) != 0x80)) {
127 *length = 0;
128 return 0;
129 }
130
131 code_point = (c1 & 0x07) << 18;
132 code_point |= (c2 & 0x3F) << 12;
133 code_point |= (c3 & 0x3F) << 6;
134 code_point |= (c4 & 0x3F);
135
136 // Error Check 2: Overlong check (4-byte must be > 0xFFFF)
137 // Also check for valid Unicode range (must be <= 0x10FFFF)
138 if (code_point <= 0xFFFF || code_point > 0x10FFFF) {
139 *length = 0;
140 return 0;
141 }
142 }
143 // --- Invalid leading byte (e.g., 10xxxxxx or 11111xxx) ---
144 else {
145 *length = 0;
146 return 0;
147 }
148 *length = current - reinterpret_cast<const uint8_t *>(utf8_str);
149 return code_point;
150}
151
152Font::Font(const Glyph *data, int data_nr, int baseline, int height, int descender, int xheight, int capheight,
153 uint8_t bpp)
154 : glyphs_(ConstVector(data, data_nr)),
155 baseline_(baseline),
156 height_(height),
157 descender_(descender),
158 linegap_(height - baseline - descender),
159 xheight_(xheight),
160 capheight_(capheight),
161 bpp_(bpp) {
162#ifdef USE_LVGL_FONT
163 this->lv_font_.dsc = this;
164 this->lv_font_.line_height = this->get_height();
165 this->lv_font_.base_line = this->lv_font_.line_height - this->get_baseline();
166 this->lv_font_.get_glyph_dsc = get_glyph_dsc_cb;
167 this->lv_font_.get_glyph_bitmap = get_glyph_bitmap;
168 this->lv_font_.subpx = LV_FONT_SUBPX_NONE;
169 this->lv_font_.underline_position = -1;
170 this->lv_font_.underline_thickness = 1;
171#endif
172}
173
174const Glyph *Font::find_glyph(uint32_t codepoint) const {
175 int lo = 0;
176 int hi = this->glyphs_.size() - 1;
177 while (lo != hi) {
178 int mid = (lo + hi + 1) / 2;
179 if (this->glyphs_[mid].is_less_or_equal(codepoint)) {
180 lo = mid;
181 } else {
182 hi = mid - 1;
183 }
184 }
185 auto *result = &this->glyphs_[lo];
186 if (result->code_point == codepoint)
187 return result;
188 return nullptr;
189}
190
191#ifdef USE_DISPLAY
192void Font::measure(const char *str, int *width, int *x_offset, int *baseline, int *height) {
193 *baseline = this->baseline_;
194 *height = this->height_;
195 int min_x = 0;
196 bool has_char = false;
197 int x = 0;
198 for (;;) {
199 size_t length;
200 auto code_point = extract_unicode_codepoint(str, &length);
201 if (length == 0)
202 break;
203 str += length;
204 auto *glyph = this->find_glyph(code_point);
205 if (glyph == nullptr) {
206 // Unknown char, skip
207 if (!this->glyphs_.empty())
208 x += this->glyphs_[0].advance;
209 continue;
210 }
211
212 if (!has_char) {
213 min_x = glyph->offset_x;
214 } else {
215 min_x = std::min(min_x, x + glyph->offset_x);
216 }
217 x += glyph->advance;
218
219 has_char = true;
220 }
221 *x_offset = min_x;
222 *width = x - min_x;
223}
224
225void Font::print(int x_start, int y_start, display::Display *display, Color color, const char *text, Color background) {
226 int x_at = x_start;
227 for (;;) {
228 size_t length;
229 auto code_point = extract_unicode_codepoint(text, &length);
230 if (length == 0)
231 break;
232 text += length;
233 auto *glyph = this->find_glyph(code_point);
234 if (glyph == nullptr) {
235 // Unknown char, skip
236 ESP_LOGW(TAG, "Codepoint 0x%08" PRIx32 " not found in font", code_point);
237 if (!this->glyphs_.empty()) {
238 uint8_t glyph_width = this->glyphs_[0].advance;
239 display->rectangle(x_at, y_start, glyph_width, this->height_, color);
240 x_at += glyph_width;
241 }
242 continue;
243 }
244
245 const uint8_t *data = glyph->data;
246 const int max_x = x_at + glyph->offset_x + glyph->width;
247 const int max_y = y_start + glyph->offset_y + glyph->height;
248
249 uint8_t bitmask = 0;
250 uint8_t pixel_data = 0;
251 uint8_t bpp_max = (1 << this->bpp_) - 1;
252 auto diff_r = (float) color.r - (float) background.r;
253 auto diff_g = (float) color.g - (float) background.g;
254 auto diff_b = (float) color.b - (float) background.b;
255 auto diff_w = (float) color.w - (float) background.w;
256 auto b_r = (float) background.r;
257 auto b_g = (float) background.g;
258 auto b_b = (float) background.b;
259 auto b_w = (float) background.w;
260 for (int glyph_y = y_start + glyph->offset_y; glyph_y != max_y; glyph_y++) {
261 for (int glyph_x = x_at + glyph->offset_x; glyph_x != max_x; glyph_x++) {
262 uint8_t pixel = 0;
263 for (uint8_t bit_num = 0; bit_num != this->bpp_; bit_num++) {
264 if (bitmask == 0) {
265 pixel_data = progmem_read_byte(data++);
266 bitmask = 0x80;
267 }
268 pixel <<= 1;
269 if ((pixel_data & bitmask) != 0)
270 pixel |= 1;
271 bitmask >>= 1;
272 }
273 if (pixel == bpp_max) {
274 display->draw_pixel_at(glyph_x, glyph_y, color);
275 } else if (pixel != 0) {
276 auto on = (float) pixel / (float) bpp_max;
277 auto blended = Color((uint8_t) (diff_r * on + b_r), (uint8_t) (diff_g * on + b_g),
278 (uint8_t) (diff_b * on + b_b), (uint8_t) (diff_w * on + b_w));
279 display->draw_pixel_at(glyph_x, glyph_y, blended);
280 }
281 }
282 }
283 x_at += glyph->advance;
284 }
285}
286#endif
287} // namespace font
288} // namespace esphome
Lightweight read-only view over a const array stored in RODATA (will typically be in flash memory) Av...
Definition helpers.h:118
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:101
void draw_pixel_at(int x, int y)
Set a single pixel at the specified coordinates to default color.
Definition display.h:336
lv_font_t lv_font_
Definition font.h:92
void measure(const char *str, int *width, int *x_offset, int *baseline, int *height) override
Definition font.cpp:192
const Glyph * find_glyph(uint32_t codepoint) const
Definition font.cpp:174
void print(int x_start, int y_start, display::Display *display, Color color, const char *text, Color background) override
Definition font.cpp:225
static const uint8_t * get_glyph_bitmap(const lv_font_t *font, uint32_t unicode_letter)
Definition font.cpp:12
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:21
int get_height()
Definition font.h:69
uint32_t last_letter_
Definition font.h:96
const Glyph * get_glyph_data_(uint32_t unicode_letter)
Definition font.cpp:37
int get_baseline()
Definition font.h:68
const Glyph * last_data_
Definition font.h:97
uint8_t bpp_
Definition font.h:90
ConstVector< Glyph > glyphs_
Definition font.h:83
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:152
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
uint8_t progmem_read_byte(const uint8_t *addr)
Definition core.cpp:70
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