ESPHome  2024.5.1
font.cpp
Go to the documentation of this file.
1 #include "font.h"
2 
3 #include "esphome/core/hal.h"
4 #include "esphome/core/log.h"
5 #include "esphome/core/color.h"
7 
8 namespace esphome {
9 namespace font {
10 
11 static const char *const TAG = "font";
12 
13 const uint8_t *Glyph::get_char() const { return this->glyph_data_->a_char; }
14 // Compare the char at the string position with this char.
15 // Return true if this char is less than or equal the other.
16 bool Glyph::compare_to(const uint8_t *str) const {
17  // 1 -> this->char_
18  // 2 -> str
19  for (uint32_t i = 0;; i++) {
20  if (this->glyph_data_->a_char[i] == '\0')
21  return true;
22  if (str[i] == '\0')
23  return false;
24  if (this->glyph_data_->a_char[i] > str[i])
25  return false;
26  if (this->glyph_data_->a_char[i] < str[i])
27  return true;
28  }
29  // this should not happen
30  return false;
31 }
32 int Glyph::match_length(const uint8_t *str) const {
33  for (uint32_t i = 0;; i++) {
34  if (this->glyph_data_->a_char[i] == '\0')
35  return i;
36  if (str[i] != this->glyph_data_->a_char[i])
37  return 0;
38  }
39  // this should not happen
40  return 0;
41 }
42 void Glyph::scan_area(int *x1, int *y1, int *width, int *height) const {
43  *x1 = this->glyph_data_->offset_x;
44  *y1 = this->glyph_data_->offset_y;
45  *width = this->glyph_data_->width;
46  *height = this->glyph_data_->height;
47 }
48 
49 Font::Font(const GlyphData *data, int data_nr, int baseline, int height, uint8_t bpp)
50  : baseline_(baseline), height_(height), bpp_(bpp) {
51  glyphs_.reserve(data_nr);
52  for (int i = 0; i < data_nr; ++i)
53  glyphs_.emplace_back(&data[i]);
54 }
55 int Font::match_next_glyph(const uint8_t *str, int *match_length) {
56  int lo = 0;
57  int hi = this->glyphs_.size() - 1;
58  while (lo != hi) {
59  int mid = (lo + hi + 1) / 2;
60  if (this->glyphs_[mid].compare_to(str)) {
61  lo = mid;
62  } else {
63  hi = mid - 1;
64  }
65  }
66  *match_length = this->glyphs_[lo].match_length(str);
67  if (*match_length <= 0)
68  return -1;
69  return lo;
70 }
71 void Font::measure(const char *str, int *width, int *x_offset, int *baseline, int *height) {
72  *baseline = this->baseline_;
73  *height = this->height_;
74  int i = 0;
75  int min_x = 0;
76  bool has_char = false;
77  int x = 0;
78  while (str[i] != '\0') {
79  int match_length;
80  int glyph_n = this->match_next_glyph((const uint8_t *) str + i, &match_length);
81  if (glyph_n < 0) {
82  // Unknown char, skip
83  if (!this->get_glyphs().empty())
84  x += this->get_glyphs()[0].glyph_data_->width;
85  i++;
86  continue;
87  }
88 
89  const Glyph &glyph = this->glyphs_[glyph_n];
90  if (!has_char) {
91  min_x = glyph.glyph_data_->offset_x;
92  } else {
93  min_x = std::min(min_x, x + glyph.glyph_data_->offset_x);
94  }
95  x += glyph.glyph_data_->width + glyph.glyph_data_->offset_x;
96 
97  i += match_length;
98  has_char = true;
99  }
100  *x_offset = min_x;
101  *width = x - min_x;
102 }
103 void Font::print(int x_start, int y_start, display::Display *display, Color color, const char *text, Color background) {
104  int i = 0;
105  int x_at = x_start;
106  int scan_x1, scan_y1, scan_width, scan_height;
107  while (text[i] != '\0') {
108  int match_length;
109  int glyph_n = this->match_next_glyph((const uint8_t *) text + i, &match_length);
110  if (glyph_n < 0) {
111  // Unknown char, skip
112  ESP_LOGW(TAG, "Encountered character without representation in font: '%c'", text[i]);
113  if (!this->get_glyphs().empty()) {
114  uint8_t glyph_width = this->get_glyphs()[0].glyph_data_->width;
115  display->filled_rectangle(x_at, y_start, glyph_width, this->height_, color);
116  x_at += glyph_width;
117  }
118 
119  i++;
120  continue;
121  }
122 
123  const Glyph &glyph = this->get_glyphs()[glyph_n];
124  glyph.scan_area(&scan_x1, &scan_y1, &scan_width, &scan_height);
125 
126  const uint8_t *data = glyph.glyph_data_->data;
127  const int max_x = x_at + scan_x1 + scan_width;
128  const int max_y = y_start + scan_y1 + scan_height;
129 
130  uint8_t bitmask = 0;
131  uint8_t pixel_data = 0;
132  uint8_t bpp_max = (1 << this->bpp_) - 1;
133  auto diff_r = (float) color.r - (float) background.r;
134  auto diff_g = (float) color.g - (float) background.g;
135  auto diff_b = (float) color.b - (float) background.b;
136  auto b_r = (float) background.r;
137  auto b_g = (float) background.g;
138  auto b_b = (float) background.g;
139  for (int glyph_y = y_start + scan_y1; glyph_y != max_y; glyph_y++) {
140  for (int glyph_x = x_at + scan_x1; glyph_x != max_x; glyph_x++) {
141  uint8_t pixel = 0;
142  for (int bit_num = 0; bit_num != this->bpp_; bit_num++) {
143  if (bitmask == 0) {
144  pixel_data = progmem_read_byte(data++);
145  bitmask = 0x80;
146  }
147  pixel <<= 1;
148  if ((pixel_data & bitmask) != 0)
149  pixel |= 1;
150  bitmask >>= 1;
151  }
152  if (pixel == bpp_max) {
153  display->draw_pixel_at(glyph_x, glyph_y, color);
154  } else if (pixel != 0) {
155  auto on = (float) pixel / (float) bpp_max;
156  auto blended =
157  Color((uint8_t) (diff_r * on + b_r), (uint8_t) (diff_g * on + b_g), (uint8_t) (diff_b * on + b_b));
158  display->draw_pixel_at(glyph_x, glyph_y, blended);
159  }
160  }
161  }
162  x_at += glyph.glyph_data_->width + glyph.glyph_data_->offset_x;
163 
164  i += match_length;
165  }
166 }
167 
168 } // namespace font
169 } // namespace esphome
Font(const GlyphData *data, int data_nr, int baseline, int height, uint8_t bpp=1)
Construct the font with the given glyphs.
Definition: font.cpp:49
void print(int x_start, int y_start, display::Display *display, Color color, const char *text, Color background) override
Definition: font.cpp:103
const uint8_t * a_char
Definition: font.h:13
const uint8_t * get_char() const
Definition: font.cpp:13
uint16_t x
Definition: tt21100.cpp:17
void filled_rectangle(int x1, int y1, int width, int height, Color color=COLOR_ON)
Fill a rectangle with the top left point at [x1,y1] and the bottom right point at [x1+width...
Definition: display.cpp:104
uint8_t g
Definition: color.h:18
int match_next_glyph(const uint8_t *str, int *match_length)
Definition: font.cpp:55
void measure(const char *str, int *width, int *x_offset, int *baseline, int *height) override
Definition: font.cpp:71
uint8_t bpp_
Definition: font.h:66
const std::vector< Glyph, ExternalRAMAllocator< Glyph > > & get_glyphs() const
Definition: font.h:60
uint8_t progmem_read_byte(const uint8_t *addr)
Definition: core.cpp:55
const uint8_t * data
Definition: font.h:14
uint8_t b
Definition: color.h:22
void draw_pixel_at(int x, int y)
Set a single pixel at the specified coordinates to default color.
Definition: display.h:225
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
uint8_t r
Definition: color.h:14
int match_length(const uint8_t *str) const
Definition: font.cpp:32
bool compare_to(const uint8_t *str) const
Definition: font.cpp:16
const GlyphData * glyph_data_
Definition: font.h:38
std::vector< Glyph, ExternalRAMAllocator< Glyph > > glyphs_
Definition: font.h:63
void scan_area(int *x1, int *y1, int *width, int *height) const
Definition: font.cpp:42