mirror of
https://github.com/esphome/esphome.git
synced 2025-11-17 15:26:01 +00:00
Merge branch 'dev' into integration
This commit is contained in:
@@ -486,6 +486,8 @@ class GlyphInfo:
|
|||||||
|
|
||||||
|
|
||||||
def glyph_to_glyphinfo(glyph, font, size, bpp):
|
def glyph_to_glyphinfo(glyph, font, size, bpp):
|
||||||
|
# Convert to 32 bit unicode codepoint
|
||||||
|
glyph = ord(glyph)
|
||||||
scale = 256 // (1 << bpp)
|
scale = 256 // (1 << bpp)
|
||||||
if not font.is_scalable:
|
if not font.is_scalable:
|
||||||
sizes = [pt_to_px(x.size) for x in font.available_sizes]
|
sizes = [pt_to_px(x.size) for x in font.available_sizes]
|
||||||
|
|||||||
@@ -6,42 +6,147 @@
|
|||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace font {
|
namespace font {
|
||||||
|
|
||||||
static const char *const TAG = "font";
|
static const char *const TAG = "font";
|
||||||
|
|
||||||
// Compare the char at the string position with this char.
|
#ifdef USE_LVGL_FONT
|
||||||
// Return true if this char is less than or equal the other.
|
const uint8_t *Font::get_glyph_bitmap(const lv_font_t *font, uint32_t unicode_letter) {
|
||||||
bool Glyph::compare_to(const uint8_t *str) const {
|
auto *fe = (Font *) font->dsc;
|
||||||
// 1 -> this->char_
|
const auto *gd = fe->get_glyph_data_(unicode_letter);
|
||||||
// 2 -> str
|
if (gd == nullptr) {
|
||||||
for (uint32_t i = 0;; i++) {
|
return nullptr;
|
||||||
if (this->a_char[i] == '\0')
|
}
|
||||||
return true;
|
return gd->data;
|
||||||
if (str[i] == '\0')
|
}
|
||||||
|
|
||||||
|
bool Font::get_glyph_dsc_cb(const lv_font_t *font, lv_font_glyph_dsc_t *dsc, uint32_t unicode_letter, uint32_t next) {
|
||||||
|
auto *fe = (Font *) font->dsc;
|
||||||
|
const auto *gd = fe->get_glyph_data_(unicode_letter);
|
||||||
|
if (gd == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
if (this->a_char[i] > str[i])
|
}
|
||||||
return false;
|
dsc->adv_w = gd->advance;
|
||||||
if (this->a_char[i] < str[i])
|
dsc->ofs_x = gd->offset_x;
|
||||||
|
dsc->ofs_y = fe->height_ - gd->height - gd->offset_y - fe->lv_font_.base_line;
|
||||||
|
dsc->box_w = gd->width;
|
||||||
|
dsc->box_h = gd->height;
|
||||||
|
dsc->is_placeholder = 0;
|
||||||
|
dsc->bpp = fe->get_bpp();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// this should not happen
|
|
||||||
return false;
|
const Glyph *Font::get_glyph_data_(uint32_t unicode_letter) {
|
||||||
|
if (unicode_letter == this->last_letter_ && this->last_letter_ != 0)
|
||||||
|
return this->last_data_;
|
||||||
|
auto *glyph = this->find_glyph(unicode_letter);
|
||||||
|
if (glyph == nullptr) {
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
int Glyph::match_length(const uint8_t *str) const {
|
this->last_data_ = glyph;
|
||||||
for (uint32_t i = 0;; i++) {
|
this->last_letter_ = unicode_letter;
|
||||||
if (this->a_char[i] == '\0')
|
return glyph;
|
||||||
return i;
|
}
|
||||||
if (str[i] != this->a_char[i])
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to extract a 32 bit Unicode codepoint from a UTF-8 string.
|
||||||
|
* If successful, return the codepoint and set the length to the number of bytes read.
|
||||||
|
* If the end of the string has been reached and a valid codepoint has not been found, return 0 and set the length to
|
||||||
|
* 0.
|
||||||
|
*
|
||||||
|
* @param utf8_str The input string
|
||||||
|
* @param length Pointer to length storage
|
||||||
|
* @return The extracted code point
|
||||||
|
*/
|
||||||
|
static uint32_t extract_unicode_codepoint(const char *utf8_str, size_t *length) {
|
||||||
|
// Safely cast to uint8_t* for correct bitwise operations on bytes
|
||||||
|
const uint8_t *current = reinterpret_cast<const uint8_t *>(utf8_str);
|
||||||
|
uint32_t code_point = 0;
|
||||||
|
uint8_t c1 = *current++;
|
||||||
|
|
||||||
|
// check for end of string
|
||||||
|
if (c1 == 0) {
|
||||||
|
*length = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// this should not happen
|
|
||||||
|
// --- 1-Byte Sequence: 0xxxxxxx (ASCII) ---
|
||||||
|
if (c1 < 0x80) {
|
||||||
|
// Valid ASCII byte.
|
||||||
|
code_point = c1;
|
||||||
|
// Optimization: No need to check for continuation bytes.
|
||||||
|
}
|
||||||
|
// --- 2-Byte Sequence: 110xxxxx 10xxxxxx ---
|
||||||
|
else if ((c1 & 0xE0) == 0xC0) {
|
||||||
|
uint8_t c2 = *current++;
|
||||||
|
|
||||||
|
// Error Check 1: Check if c2 is a valid continuation byte (10xxxxxx)
|
||||||
|
if ((c2 & 0xC0) != 0x80) {
|
||||||
|
*length = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
void Glyph::scan_area(int *x1, int *y1, int *width, int *height) const {
|
|
||||||
*x1 = this->offset_x;
|
code_point = (c1 & 0x1F) << 6;
|
||||||
*y1 = this->offset_y;
|
code_point |= (c2 & 0x3F);
|
||||||
*width = this->width;
|
|
||||||
*height = this->height;
|
// Error Check 2: Overlong check (2-byte must be > 0x7F)
|
||||||
|
if (code_point <= 0x7F) {
|
||||||
|
*length = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// --- 3-Byte Sequence: 1110xxxx 10xxxxxx 10xxxxxx ---
|
||||||
|
else if ((c1 & 0xF0) == 0xE0) {
|
||||||
|
uint8_t c2 = *current++;
|
||||||
|
uint8_t c3 = *current++;
|
||||||
|
|
||||||
|
// Error Check 1: Check continuation bytes
|
||||||
|
if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80)) {
|
||||||
|
*length = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
code_point = (c1 & 0x0F) << 12;
|
||||||
|
code_point |= (c2 & 0x3F) << 6;
|
||||||
|
code_point |= (c3 & 0x3F);
|
||||||
|
|
||||||
|
// Error Check 2: Overlong check (3-byte must be > 0x7FF)
|
||||||
|
// Also check for surrogates (0xD800-0xDFFF)
|
||||||
|
if (code_point <= 0x7FF || (code_point >= 0xD800 && code_point <= 0xDFFF)) {
|
||||||
|
*length = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// --- 4-Byte Sequence: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx ---
|
||||||
|
else if ((c1 & 0xF8) == 0xF0) {
|
||||||
|
uint8_t c2 = *current++;
|
||||||
|
uint8_t c3 = *current++;
|
||||||
|
uint8_t c4 = *current++;
|
||||||
|
|
||||||
|
// Error Check 1: Check continuation bytes
|
||||||
|
if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80) || ((c4 & 0xC0) != 0x80)) {
|
||||||
|
*length = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
code_point = (c1 & 0x07) << 18;
|
||||||
|
code_point |= (c2 & 0x3F) << 12;
|
||||||
|
code_point |= (c3 & 0x3F) << 6;
|
||||||
|
code_point |= (c4 & 0x3F);
|
||||||
|
|
||||||
|
// Error Check 2: Overlong check (4-byte must be > 0xFFFF)
|
||||||
|
// Also check for valid Unicode range (must be <= 0x10FFFF)
|
||||||
|
if (code_point <= 0xFFFF || code_point > 0x10FFFF) {
|
||||||
|
*length = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// --- Invalid leading byte (e.g., 10xxxxxx or 11111xxx) ---
|
||||||
|
else {
|
||||||
|
*length = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*length = current - reinterpret_cast<const uint8_t *>(utf8_str);
|
||||||
|
return code_point;
|
||||||
}
|
}
|
||||||
|
|
||||||
Font::Font(const Glyph *data, int data_nr, int baseline, int height, int descender, int xheight, int capheight,
|
Font::Font(const Glyph *data, int data_nr, int baseline, int height, int descender, int xheight, int capheight,
|
||||||
@@ -53,82 +158,93 @@ Font::Font(const Glyph *data, int data_nr, int baseline, int height, int descend
|
|||||||
linegap_(height - baseline - descender),
|
linegap_(height - baseline - descender),
|
||||||
xheight_(xheight),
|
xheight_(xheight),
|
||||||
capheight_(capheight),
|
capheight_(capheight),
|
||||||
bpp_(bpp) {}
|
bpp_(bpp) {
|
||||||
int Font::match_next_glyph(const uint8_t *str, int *match_length) const {
|
#ifdef USE_LVGL_FONT
|
||||||
|
this->lv_font_.dsc = this;
|
||||||
|
this->lv_font_.line_height = this->get_height();
|
||||||
|
this->lv_font_.base_line = this->lv_font_.line_height - this->get_baseline();
|
||||||
|
this->lv_font_.get_glyph_dsc = get_glyph_dsc_cb;
|
||||||
|
this->lv_font_.get_glyph_bitmap = get_glyph_bitmap;
|
||||||
|
this->lv_font_.subpx = LV_FONT_SUBPX_NONE;
|
||||||
|
this->lv_font_.underline_position = -1;
|
||||||
|
this->lv_font_.underline_thickness = 1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
const Glyph *Font::find_glyph(uint32_t codepoint) const {
|
||||||
int lo = 0;
|
int lo = 0;
|
||||||
int hi = this->glyphs_.size() - 1;
|
int hi = this->glyphs_.size() - 1;
|
||||||
while (lo != hi) {
|
while (lo != hi) {
|
||||||
int mid = (lo + hi + 1) / 2;
|
int mid = (lo + hi + 1) / 2;
|
||||||
if (this->glyphs_[mid].compare_to(str)) {
|
if (this->glyphs_[mid].is_less_or_equal(codepoint)) {
|
||||||
lo = mid;
|
lo = mid;
|
||||||
} else {
|
} else {
|
||||||
hi = mid - 1;
|
hi = mid - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*match_length = this->glyphs_[lo].match_length(str);
|
auto *result = &this->glyphs_[lo];
|
||||||
if (*match_length <= 0)
|
if (result->code_point == codepoint)
|
||||||
return -1;
|
return result;
|
||||||
return lo;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_DISPLAY
|
#ifdef USE_DISPLAY
|
||||||
void Font::measure(const char *str, int *width, int *x_offset, int *baseline, int *height) {
|
void Font::measure(const char *str, int *width, int *x_offset, int *baseline, int *height) {
|
||||||
*baseline = this->baseline_;
|
*baseline = this->baseline_;
|
||||||
*height = this->height_;
|
*height = this->height_;
|
||||||
int i = 0;
|
|
||||||
int min_x = 0;
|
int min_x = 0;
|
||||||
bool has_char = false;
|
bool has_char = false;
|
||||||
int x = 0;
|
int x = 0;
|
||||||
while (str[i] != '\0') {
|
for (;;) {
|
||||||
int match_length;
|
size_t length;
|
||||||
int glyph_n = this->match_next_glyph((const uint8_t *) str + i, &match_length);
|
auto code_point = extract_unicode_codepoint(str, &length);
|
||||||
if (glyph_n < 0) {
|
if (length == 0)
|
||||||
|
break;
|
||||||
|
str += length;
|
||||||
|
auto *glyph = this->find_glyph(code_point);
|
||||||
|
if (glyph == nullptr) {
|
||||||
// Unknown char, skip
|
// Unknown char, skip
|
||||||
if (!this->get_glyphs().empty())
|
if (!this->glyphs_.empty())
|
||||||
x += this->get_glyphs()[0].advance;
|
x += this->glyphs_[0].advance;
|
||||||
i++;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Glyph &glyph = this->glyphs_[glyph_n];
|
|
||||||
if (!has_char) {
|
if (!has_char) {
|
||||||
min_x = glyph.offset_x;
|
min_x = glyph->offset_x;
|
||||||
} else {
|
} else {
|
||||||
min_x = std::min(min_x, x + glyph.offset_x);
|
min_x = std::min(min_x, x + glyph->offset_x);
|
||||||
}
|
}
|
||||||
x += glyph.advance;
|
x += glyph->advance;
|
||||||
|
|
||||||
i += match_length;
|
|
||||||
has_char = true;
|
has_char = true;
|
||||||
}
|
}
|
||||||
*x_offset = min_x;
|
*x_offset = min_x;
|
||||||
*width = x - min_x;
|
*width = x - min_x;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Font::print(int x_start, int y_start, display::Display *display, Color color, const char *text, Color background) {
|
void Font::print(int x_start, int y_start, display::Display *display, Color color, const char *text, Color background) {
|
||||||
int i = 0;
|
|
||||||
int x_at = x_start;
|
int x_at = x_start;
|
||||||
int scan_x1, scan_y1, scan_width, scan_height;
|
for (;;) {
|
||||||
while (text[i] != '\0') {
|
size_t length;
|
||||||
int match_length;
|
auto code_point = extract_unicode_codepoint(text, &length);
|
||||||
int glyph_n = this->match_next_glyph((const uint8_t *) text + i, &match_length);
|
if (length == 0)
|
||||||
if (glyph_n < 0) {
|
break;
|
||||||
|
text += length;
|
||||||
|
auto *glyph = this->find_glyph(code_point);
|
||||||
|
if (glyph == nullptr) {
|
||||||
// Unknown char, skip
|
// Unknown char, skip
|
||||||
ESP_LOGW(TAG, "Encountered character without representation in font: '%c'", text[i]);
|
ESP_LOGW(TAG, "Codepoint 0x%08" PRIx32 " not found in font", code_point);
|
||||||
if (!this->get_glyphs().empty()) {
|
if (!this->glyphs_.empty()) {
|
||||||
uint8_t glyph_width = this->get_glyphs()[0].advance;
|
uint8_t glyph_width = this->glyphs_[0].advance;
|
||||||
display->filled_rectangle(x_at, y_start, glyph_width, this->height_, color);
|
display->rectangle(x_at, y_start, glyph_width, this->height_, color);
|
||||||
x_at += glyph_width;
|
x_at += glyph_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
i++;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Glyph &glyph = this->get_glyphs()[glyph_n];
|
const uint8_t *data = glyph->data;
|
||||||
glyph.scan_area(&scan_x1, &scan_y1, &scan_width, &scan_height);
|
const int max_x = x_at + glyph->offset_x + glyph->width;
|
||||||
|
const int max_y = y_start + glyph->offset_y + glyph->height;
|
||||||
const uint8_t *data = glyph.data;
|
|
||||||
const int max_x = x_at + scan_x1 + scan_width;
|
|
||||||
const int max_y = y_start + scan_y1 + scan_height;
|
|
||||||
|
|
||||||
uint8_t bitmask = 0;
|
uint8_t bitmask = 0;
|
||||||
uint8_t pixel_data = 0;
|
uint8_t pixel_data = 0;
|
||||||
@@ -141,10 +257,10 @@ void Font::print(int x_start, int y_start, display::Display *display, Color colo
|
|||||||
auto b_g = (float) background.g;
|
auto b_g = (float) background.g;
|
||||||
auto b_b = (float) background.b;
|
auto b_b = (float) background.b;
|
||||||
auto b_w = (float) background.w;
|
auto b_w = (float) background.w;
|
||||||
for (int glyph_y = y_start + scan_y1; glyph_y != max_y; glyph_y++) {
|
for (int glyph_y = y_start + glyph->offset_y; glyph_y != max_y; glyph_y++) {
|
||||||
for (int glyph_x = x_at + scan_x1; glyph_x != max_x; glyph_x++) {
|
for (int glyph_x = x_at + glyph->offset_x; glyph_x != max_x; glyph_x++) {
|
||||||
uint8_t pixel = 0;
|
uint8_t pixel = 0;
|
||||||
for (int bit_num = 0; bit_num != this->bpp_; bit_num++) {
|
for (uint8_t bit_num = 0; bit_num != this->bpp_; bit_num++) {
|
||||||
if (bitmask == 0) {
|
if (bitmask == 0) {
|
||||||
pixel_data = progmem_read_byte(data++);
|
pixel_data = progmem_read_byte(data++);
|
||||||
bitmask = 0x80;
|
bitmask = 0x80;
|
||||||
@@ -164,12 +280,9 @@ void Font::print(int x_start, int y_start, display::Display *display, Color colo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
x_at += glyph.advance;
|
x_at += glyph->advance;
|
||||||
|
|
||||||
i += match_length;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace font
|
} // namespace font
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|||||||
@@ -6,6 +6,9 @@
|
|||||||
#ifdef USE_DISPLAY
|
#ifdef USE_DISPLAY
|
||||||
#include "esphome/components/display/display.h"
|
#include "esphome/components/display/display.h"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_LVGL_FONT
|
||||||
|
#include <lvgl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace font {
|
namespace font {
|
||||||
@@ -14,9 +17,9 @@ class Font;
|
|||||||
|
|
||||||
class Glyph {
|
class Glyph {
|
||||||
public:
|
public:
|
||||||
constexpr Glyph(const char *a_char, const uint8_t *data, int advance, int offset_x, int offset_y, int width,
|
constexpr Glyph(uint32_t code_point, const uint8_t *data, int advance, int offset_x, int offset_y, int width,
|
||||||
int height)
|
int height)
|
||||||
: a_char(a_char),
|
: code_point(code_point),
|
||||||
data(data),
|
data(data),
|
||||||
advance(advance),
|
advance(advance),
|
||||||
offset_x(offset_x),
|
offset_x(offset_x),
|
||||||
@@ -24,24 +27,15 @@ class Glyph {
|
|||||||
width(width),
|
width(width),
|
||||||
height(height) {}
|
height(height) {}
|
||||||
|
|
||||||
const uint8_t *get_char() const { return reinterpret_cast<const uint8_t *>(this->a_char); }
|
bool is_less_or_equal(uint32_t other) const { return this->code_point <= other; }
|
||||||
|
|
||||||
bool compare_to(const uint8_t *str) const;
|
const uint32_t code_point;
|
||||||
|
|
||||||
int match_length(const uint8_t *str) const;
|
|
||||||
|
|
||||||
void scan_area(int *x1, int *y1, int *width, int *height) const;
|
|
||||||
|
|
||||||
const char *a_char;
|
|
||||||
const uint8_t *data;
|
const uint8_t *data;
|
||||||
int advance;
|
int advance;
|
||||||
int offset_x;
|
int offset_x;
|
||||||
int offset_y;
|
int offset_y;
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
|
|
||||||
protected:
|
|
||||||
friend Font;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Font
|
class Font
|
||||||
@@ -64,7 +58,7 @@ class Font
|
|||||||
Font(const Glyph *data, int data_nr, int baseline, int height, int descender, int xheight, int capheight,
|
Font(const Glyph *data, int data_nr, int baseline, int height, int descender, int xheight, int capheight,
|
||||||
uint8_t bpp = 1);
|
uint8_t bpp = 1);
|
||||||
|
|
||||||
int match_next_glyph(const uint8_t *str, int *match_length) const;
|
const Glyph *find_glyph(uint32_t codepoint) const;
|
||||||
|
|
||||||
#ifdef USE_DISPLAY
|
#ifdef USE_DISPLAY
|
||||||
void print(int x_start, int y_start, display::Display *display, Color color, const char *text,
|
void print(int x_start, int y_start, display::Display *display, Color color, const char *text,
|
||||||
@@ -79,6 +73,9 @@ class Font
|
|||||||
inline int get_xheight() { return this->xheight_; }
|
inline int get_xheight() { return this->xheight_; }
|
||||||
inline int get_capheight() { return this->capheight_; }
|
inline int get_capheight() { return this->capheight_; }
|
||||||
inline int get_bpp() { return this->bpp_; }
|
inline int get_bpp() { return this->bpp_; }
|
||||||
|
#ifdef USE_LVGL_FONT
|
||||||
|
const lv_font_t *get_lv_font() const { return &this->lv_font_; }
|
||||||
|
#endif
|
||||||
|
|
||||||
const ConstVector<Glyph> &get_glyphs() const { return glyphs_; }
|
const ConstVector<Glyph> &get_glyphs() const { return glyphs_; }
|
||||||
|
|
||||||
@@ -91,6 +88,14 @@ class Font
|
|||||||
int xheight_;
|
int xheight_;
|
||||||
int capheight_;
|
int capheight_;
|
||||||
uint8_t bpp_; // bits per pixel
|
uint8_t bpp_; // bits per pixel
|
||||||
|
#ifdef USE_LVGL_FONT
|
||||||
|
lv_font_t lv_font_{};
|
||||||
|
static const uint8_t *get_glyph_bitmap(const lv_font_t *font, uint32_t unicode_letter);
|
||||||
|
static bool get_glyph_dsc_cb(const lv_font_t *font, lv_font_glyph_dsc_t *dsc, uint32_t unicode_letter, uint32_t next);
|
||||||
|
const Glyph *get_glyph_data_(uint32_t unicode_letter);
|
||||||
|
uint32_t last_letter_{};
|
||||||
|
const Glyph *last_data_{};
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace font
|
} // namespace font
|
||||||
|
|||||||
@@ -52,15 +52,7 @@ from .schemas import (
|
|||||||
from .styles import add_top_layer, styles_to_code, theme_to_code
|
from .styles import add_top_layer, styles_to_code, theme_to_code
|
||||||
from .touchscreens import touchscreen_schema, touchscreens_to_code
|
from .touchscreens import touchscreen_schema, touchscreens_to_code
|
||||||
from .trigger import add_on_boot_triggers, generate_triggers
|
from .trigger import add_on_boot_triggers, generate_triggers
|
||||||
from .types import (
|
from .types import IdleTrigger, PlainTrigger, lv_font_t, lv_group_t, lv_style_t, lvgl_ns
|
||||||
FontEngine,
|
|
||||||
IdleTrigger,
|
|
||||||
PlainTrigger,
|
|
||||||
lv_font_t,
|
|
||||||
lv_group_t,
|
|
||||||
lv_style_t,
|
|
||||||
lvgl_ns,
|
|
||||||
)
|
|
||||||
from .widgets import (
|
from .widgets import (
|
||||||
LvScrActType,
|
LvScrActType,
|
||||||
Widget,
|
Widget,
|
||||||
@@ -244,7 +236,6 @@ async def to_code(configs):
|
|||||||
cg.add_global(lvgl_ns.using)
|
cg.add_global(lvgl_ns.using)
|
||||||
for font in helpers.esphome_fonts_used:
|
for font in helpers.esphome_fonts_used:
|
||||||
await cg.get_variable(font)
|
await cg.get_variable(font)
|
||||||
cg.new_Pvariable(ID(f"{font}_engine", True, type=FontEngine), MockObj(font))
|
|
||||||
default_font = config_0[df.CONF_DEFAULT_FONT]
|
default_font = config_0[df.CONF_DEFAULT_FONT]
|
||||||
if not lvalid.is_lv_font(default_font):
|
if not lvalid.is_lv_font(default_font):
|
||||||
add_define(
|
add_define(
|
||||||
@@ -256,7 +247,8 @@ async def to_code(configs):
|
|||||||
type=lv_font_t.operator("ptr").operator("const"),
|
type=lv_font_t.operator("ptr").operator("const"),
|
||||||
)
|
)
|
||||||
cg.new_variable(
|
cg.new_variable(
|
||||||
globfont_id, MockObj(await lvalid.lv_font.process(default_font))
|
globfont_id,
|
||||||
|
MockObj(await lvalid.lv_font.process(default_font), "->").get_lv_font(),
|
||||||
)
|
)
|
||||||
add_define("LV_FONT_DEFAULT", df.DEFAULT_ESPHOME_FONT)
|
add_define("LV_FONT_DEFAULT", df.DEFAULT_ESPHOME_FONT)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
#include "lvgl_esphome.h"
|
|
||||||
|
|
||||||
#ifdef USE_LVGL_FONT
|
|
||||||
namespace esphome {
|
|
||||||
namespace lvgl {
|
|
||||||
|
|
||||||
static const uint8_t *get_glyph_bitmap(const lv_font_t *font, uint32_t unicode_letter) {
|
|
||||||
auto *fe = (FontEngine *) font->dsc;
|
|
||||||
const auto *gd = fe->get_glyph_data(unicode_letter);
|
|
||||||
if (gd == nullptr)
|
|
||||||
return nullptr;
|
|
||||||
// esph_log_d(TAG, "Returning bitmap @ %X", (uint32_t)gd->data);
|
|
||||||
|
|
||||||
return gd->data;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool get_glyph_dsc_cb(const lv_font_t *font, lv_font_glyph_dsc_t *dsc, uint32_t unicode_letter, uint32_t next) {
|
|
||||||
auto *fe = (FontEngine *) font->dsc;
|
|
||||||
const auto *gd = fe->get_glyph_data(unicode_letter);
|
|
||||||
if (gd == nullptr)
|
|
||||||
return false;
|
|
||||||
dsc->adv_w = gd->advance;
|
|
||||||
dsc->ofs_x = gd->offset_x;
|
|
||||||
dsc->ofs_y = fe->height - gd->height - gd->offset_y - fe->baseline;
|
|
||||||
dsc->box_w = gd->width;
|
|
||||||
dsc->box_h = gd->height;
|
|
||||||
dsc->is_placeholder = 0;
|
|
||||||
dsc->bpp = fe->bpp;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
FontEngine::FontEngine(font::Font *esp_font) : font_(esp_font) {
|
|
||||||
this->bpp = esp_font->get_bpp();
|
|
||||||
this->lv_font_.dsc = this;
|
|
||||||
this->lv_font_.line_height = this->height = esp_font->get_height();
|
|
||||||
this->lv_font_.base_line = this->baseline = this->lv_font_.line_height - esp_font->get_baseline();
|
|
||||||
this->lv_font_.get_glyph_dsc = get_glyph_dsc_cb;
|
|
||||||
this->lv_font_.get_glyph_bitmap = get_glyph_bitmap;
|
|
||||||
this->lv_font_.subpx = LV_FONT_SUBPX_NONE;
|
|
||||||
this->lv_font_.underline_position = -1;
|
|
||||||
this->lv_font_.underline_thickness = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const lv_font_t *FontEngine::get_lv_font() { return &this->lv_font_; }
|
|
||||||
|
|
||||||
const font::Glyph *FontEngine::get_glyph_data(uint32_t unicode_letter) {
|
|
||||||
if (unicode_letter == last_letter_)
|
|
||||||
return this->last_data_;
|
|
||||||
uint8_t unicode[5];
|
|
||||||
memset(unicode, 0, sizeof unicode);
|
|
||||||
if (unicode_letter > 0xFFFF) {
|
|
||||||
unicode[0] = 0xF0 + ((unicode_letter >> 18) & 0x7);
|
|
||||||
unicode[1] = 0x80 + ((unicode_letter >> 12) & 0x3F);
|
|
||||||
unicode[2] = 0x80 + ((unicode_letter >> 6) & 0x3F);
|
|
||||||
unicode[3] = 0x80 + (unicode_letter & 0x3F);
|
|
||||||
} else if (unicode_letter > 0x7FF) {
|
|
||||||
unicode[0] = 0xE0 + ((unicode_letter >> 12) & 0xF);
|
|
||||||
unicode[1] = 0x80 + ((unicode_letter >> 6) & 0x3F);
|
|
||||||
unicode[2] = 0x80 + (unicode_letter & 0x3F);
|
|
||||||
} else if (unicode_letter > 0x7F) {
|
|
||||||
unicode[0] = 0xC0 + ((unicode_letter >> 6) & 0x1F);
|
|
||||||
unicode[1] = 0x80 + (unicode_letter & 0x3F);
|
|
||||||
} else {
|
|
||||||
unicode[0] = unicode_letter;
|
|
||||||
}
|
|
||||||
int match_length;
|
|
||||||
int glyph_n = this->font_->match_next_glyph(unicode, &match_length);
|
|
||||||
if (glyph_n < 0)
|
|
||||||
return nullptr;
|
|
||||||
this->last_data_ = &this->font_->get_glyphs()[glyph_n];
|
|
||||||
this->last_letter_ = unicode_letter;
|
|
||||||
return this->last_data_;
|
|
||||||
}
|
|
||||||
} // namespace lvgl
|
|
||||||
} // namespace esphome
|
|
||||||
#endif // USES_LVGL_FONT
|
|
||||||
@@ -493,6 +493,7 @@ class LvFont(LValidator):
|
|||||||
return LV_FONTS
|
return LV_FONTS
|
||||||
if is_lv_font(value):
|
if is_lv_font(value):
|
||||||
return lv_builtin_font(value)
|
return lv_builtin_font(value)
|
||||||
|
add_lv_use("font")
|
||||||
fontval = cv.use_id(Font)(value)
|
fontval = cv.use_id(Font)(value)
|
||||||
esphome_fonts_used.add(fontval)
|
esphome_fonts_used.add(fontval)
|
||||||
return requires_component("font")(fontval)
|
return requires_component("font")(fontval)
|
||||||
@@ -502,7 +503,9 @@ class LvFont(LValidator):
|
|||||||
async def process(self, value, args=()):
|
async def process(self, value, args=()):
|
||||||
if is_lv_font(value):
|
if is_lv_font(value):
|
||||||
return literal(f"&lv_font_{value}")
|
return literal(f"&lv_font_{value}")
|
||||||
return literal(f"{value}_engine->get_lv_font()")
|
if isinstance(value, str):
|
||||||
|
return literal(f"{value}")
|
||||||
|
return await super().process(value, args)
|
||||||
|
|
||||||
|
|
||||||
lv_font = LvFont()
|
lv_font = LvFont()
|
||||||
|
|||||||
@@ -50,6 +50,14 @@ static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BIT
|
|||||||
static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BITNESS_332;
|
static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BITNESS_332;
|
||||||
#endif // LV_COLOR_DEPTH
|
#endif // LV_COLOR_DEPTH
|
||||||
|
|
||||||
|
#ifdef USE_LVGL_FONT
|
||||||
|
inline void lv_obj_set_style_text_font(lv_obj_t *obj, const font::Font *font, lv_style_selector_t part) {
|
||||||
|
lv_obj_set_style_text_font(obj, font->get_lv_font(), part);
|
||||||
|
}
|
||||||
|
inline void lv_style_set_text_font(lv_style_t *style, const font::Font *font) {
|
||||||
|
lv_style_set_text_font(style, font->get_lv_font());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifdef USE_LVGL_IMAGE
|
#ifdef USE_LVGL_IMAGE
|
||||||
// Shortcut / overload, so that the source of an image can easily be updated
|
// Shortcut / overload, so that the source of an image can easily be updated
|
||||||
// from within a lambda.
|
// from within a lambda.
|
||||||
@@ -134,24 +142,6 @@ template<typename... Ts> class ObjUpdateAction : public Action<Ts...> {
|
|||||||
protected:
|
protected:
|
||||||
std::function<void(Ts...)> lamb_;
|
std::function<void(Ts...)> lamb_;
|
||||||
};
|
};
|
||||||
#ifdef USE_LVGL_FONT
|
|
||||||
class FontEngine {
|
|
||||||
public:
|
|
||||||
FontEngine(font::Font *esp_font);
|
|
||||||
const lv_font_t *get_lv_font();
|
|
||||||
|
|
||||||
const font::Glyph *get_glyph_data(uint32_t unicode_letter);
|
|
||||||
uint16_t baseline{};
|
|
||||||
uint16_t height{};
|
|
||||||
uint8_t bpp{};
|
|
||||||
|
|
||||||
protected:
|
|
||||||
font::Font *font_{};
|
|
||||||
uint32_t last_letter_{};
|
|
||||||
const font::Glyph *last_data_{};
|
|
||||||
lv_font_t lv_font_{};
|
|
||||||
};
|
|
||||||
#endif // USE_LVGL_FONT
|
|
||||||
#ifdef USE_LVGL_ANIMIMG
|
#ifdef USE_LVGL_ANIMIMG
|
||||||
void lv_animimg_stop(lv_obj_t *obj);
|
void lv_animimg_stop(lv_obj_t *obj);
|
||||||
#endif // USE_LVGL_ANIMIMG
|
#endif // USE_LVGL_ANIMIMG
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ lv_coord_t = cg.global_ns.namespace("lv_coord_t")
|
|||||||
lv_event_code_t = cg.global_ns.enum("lv_event_code_t")
|
lv_event_code_t = cg.global_ns.enum("lv_event_code_t")
|
||||||
lv_indev_type_t = cg.global_ns.enum("lv_indev_type_t")
|
lv_indev_type_t = cg.global_ns.enum("lv_indev_type_t")
|
||||||
lv_key_t = cg.global_ns.enum("lv_key_t")
|
lv_key_t = cg.global_ns.enum("lv_key_t")
|
||||||
FontEngine = lvgl_ns.class_("FontEngine")
|
|
||||||
PlainTrigger = esphome_ns.class_("Trigger<>", automation.Trigger.template())
|
PlainTrigger = esphome_ns.class_("Trigger<>", automation.Trigger.template())
|
||||||
DrawEndTrigger = esphome_ns.class_(
|
DrawEndTrigger = esphome_ns.class_(
|
||||||
"Trigger<uint32_t, uint32_t>", automation.Trigger.template(cg.uint32, cg.uint32)
|
"Trigger<uint32_t, uint32_t>", automation.Trigger.template(cg.uint32, cg.uint32)
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ lvgl:
|
|||||||
line_width: 8
|
line_width: 8
|
||||||
line_rounded: true
|
line_rounded: true
|
||||||
- id: date_style
|
- id: date_style
|
||||||
text_font: roboto10
|
text_font: !lambda return id(roboto10);
|
||||||
align: center
|
align: center
|
||||||
text_color: !lambda return color_id2;
|
text_color: !lambda return color_id2;
|
||||||
bg_opa: cover
|
bg_opa: cover
|
||||||
@@ -267,7 +267,7 @@ lvgl:
|
|||||||
snprintf(buf, sizeof(buf), "Setup: %d", 42);
|
snprintf(buf, sizeof(buf), "Setup: %d", 42);
|
||||||
return std::string(buf);
|
return std::string(buf);
|
||||||
align: top_mid
|
align: top_mid
|
||||||
text_font: space16
|
text_font: !lambda return id(space16);
|
||||||
- label:
|
- label:
|
||||||
id: chip_info_label
|
id: chip_info_label
|
||||||
# Test complex setup lambda (real-world pattern)
|
# Test complex setup lambda (real-world pattern)
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ touchscreen:
|
|||||||
|
|
||||||
lvgl:
|
lvgl:
|
||||||
- id: lvgl_0
|
- id: lvgl_0
|
||||||
|
default_font: space16
|
||||||
displays: sdl0
|
displays: sdl0
|
||||||
- id: lvgl_1
|
- id: lvgl_1
|
||||||
displays: sdl1
|
displays: sdl1
|
||||||
@@ -39,3 +40,8 @@ lvgl:
|
|||||||
text: Click ME
|
text: Click ME
|
||||||
on_click:
|
on_click:
|
||||||
logger.log: Clicked
|
logger.log: Clicked
|
||||||
|
|
||||||
|
font:
|
||||||
|
- file: "gfonts://Roboto"
|
||||||
|
id: space16
|
||||||
|
bpp: 4
|
||||||
|
|||||||
Reference in New Issue
Block a user