ESPHome  2024.12.2
font.cpp
Go to the documentation of this file.
1 #include "font.h"
2 
3 #include "esphome/core/color.h"
4 #include "esphome/core/hal.h"
5 #include "esphome/core/log.h"
6 
7 namespace esphome {
8 namespace font {
9 
10 static const char *const TAG = "font";
11 
12 const uint8_t *Glyph::get_char() const { return this->glyph_data_->a_char; }
13 // Compare the char at the string position with this char.
14 // Return true if this char is less than or equal the other.
15 bool Glyph::compare_to(const uint8_t *str) const {
16  // 1 -> this->char_
17  // 2 -> str
18  for (uint32_t i = 0;; i++) {
19  if (this->glyph_data_->a_char[i] == '\0')
20  return true;
21  if (str[i] == '\0')
22  return false;
23  if (this->glyph_data_->a_char[i] > str[i])
24  return false;
25  if (this->glyph_data_->a_char[i] < str[i])
26  return true;
27  }
28  // this should not happen
29  return false;
30 }
31 int Glyph::match_length(const uint8_t *str) const {
32  for (uint32_t i = 0;; i++) {
33  if (this->glyph_data_->a_char[i] == '\0')
34  return i;
35  if (str[i] != this->glyph_data_->a_char[i])
36  return 0;
37  }
38  // this should not happen
39  return 0;
40 }
41 void Glyph::scan_area(int *x1, int *y1, int *width, int *height) const {
42  *x1 = this->glyph_data_->offset_x;
43  *y1 = this->glyph_data_->offset_y;
44  *width = this->glyph_data_->width;
45  *height = this->glyph_data_->height;
46 }
47 
48 Font::Font(const GlyphData *data, int data_nr, int baseline, int height, uint8_t bpp)
49  : baseline_(baseline), height_(height), bpp_(bpp) {
50  glyphs_.reserve(data_nr);
51  for (int i = 0; i < data_nr; ++i)
52  glyphs_.emplace_back(&data[i]);
53 }
54 int Font::match_next_glyph(const uint8_t *str, int *match_length) {
55  int lo = 0;
56  int hi = this->glyphs_.size() - 1;
57  while (lo != hi) {
58  int mid = (lo + hi + 1) / 2;
59  if (this->glyphs_[mid].compare_to(str)) {
60  lo = mid;
61  } else {
62  hi = mid - 1;
63  }
64  }
65  *match_length = this->glyphs_[lo].match_length(str);
66  if (*match_length <= 0)
67  return -1;
68  return lo;
69 }
70 #ifdef USE_DISPLAY
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 diff_w = (float) color.w - (float) background.w;
137  auto b_r = (float) background.r;
138  auto b_g = (float) background.g;
139  auto b_b = (float) background.b;
140  auto b_w = (float) background.w;
141  for (int glyph_y = y_start + scan_y1; glyph_y != max_y; glyph_y++) {
142  for (int glyph_x = x_at + scan_x1; glyph_x != max_x; glyph_x++) {
143  uint8_t pixel = 0;
144  for (int bit_num = 0; bit_num != this->bpp_; bit_num++) {
145  if (bitmask == 0) {
146  pixel_data = progmem_read_byte(data++);
147  bitmask = 0x80;
148  }
149  pixel <<= 1;
150  if ((pixel_data & bitmask) != 0)
151  pixel |= 1;
152  bitmask >>= 1;
153  }
154  if (pixel == bpp_max) {
155  display->draw_pixel_at(glyph_x, glyph_y, color);
156  } else if (pixel != 0) {
157  auto on = (float) pixel / (float) bpp_max;
158  auto blended = Color((uint8_t) (diff_r * on + b_r), (uint8_t) (diff_g * on + b_g),
159  (uint8_t) (diff_b * on + b_b), (uint8_t) (diff_w * on + b_w));
160  display->draw_pixel_at(glyph_x, glyph_y, blended);
161  }
162  }
163  }
164  x_at += glyph.glyph_data_->width + glyph.glyph_data_->offset_x;
165 
166  i += match_length;
167  }
168 }
169 #endif
170 
171 } // namespace font
172 } // namespace esphome
const uint8_t * a_char
Definition: font.h:16
const uint8_t * get_char() const
Definition: font.cpp:12
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
uint8_t progmem_read_byte(const uint8_t *addr)
Definition: core.cpp:55
uint8_t w
Definition: color.h:26
const uint8_t * data
Definition: font.h:17
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:226
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
uint8_t r
Definition: color.h:14
int match_length(const uint8_t *str) const
Definition: font.cpp:31
bool compare_to(const uint8_t *str) const
Definition: font.cpp:15
const GlyphData * glyph_data_
Definition: font.h:41
void scan_area(int *x1, int *y1, int *width, int *height) const
Definition: font.cpp:41