ESPHome  2023.11.6
display.cpp
Go to the documentation of this file.
1 #include "display.h"
2 
3 #include <utility>
4 
5 #include "esphome/core/log.h"
6 
7 namespace esphome {
8 namespace display {
9 
10 static const char *const TAG = "display";
11 
12 const Color COLOR_OFF(0, 0, 0, 0);
13 const Color COLOR_ON(255, 255, 255, 255);
14 
15 void Display::fill(Color color) { this->filled_rectangle(0, 0, this->get_width(), this->get_height(), color); }
16 void Display::clear() { this->fill(COLOR_OFF); }
17 void Display::set_rotation(DisplayRotation rotation) { this->rotation_ = rotation; }
18 void HOT Display::line(int x1, int y1, int x2, int y2, Color color) {
19  const int32_t dx = abs(x2 - x1), sx = x1 < x2 ? 1 : -1;
20  const int32_t dy = -abs(y2 - y1), sy = y1 < y2 ? 1 : -1;
21  int32_t err = dx + dy;
22 
23  while (true) {
24  this->draw_pixel_at(x1, y1, color);
25  if (x1 == x2 && y1 == y2)
26  break;
27  int32_t e2 = 2 * err;
28  if (e2 >= dy) {
29  err += dy;
30  x1 += sx;
31  }
32  if (e2 <= dx) {
33  err += dx;
34  y1 += sy;
35  }
36  }
37 }
38 void HOT Display::horizontal_line(int x, int y, int width, Color color) {
39  // Future: Could be made more efficient by manipulating buffer directly in certain rotations.
40  for (int i = x; i < x + width; i++)
41  this->draw_pixel_at(i, y, color);
42 }
43 void HOT Display::vertical_line(int x, int y, int height, Color color) {
44  // Future: Could be made more efficient by manipulating buffer directly in certain rotations.
45  for (int i = y; i < y + height; i++)
46  this->draw_pixel_at(x, i, color);
47 }
48 void Display::rectangle(int x1, int y1, int width, int height, Color color) {
49  this->horizontal_line(x1, y1, width, color);
50  this->horizontal_line(x1, y1 + height - 1, width, color);
51  this->vertical_line(x1, y1, height, color);
52  this->vertical_line(x1 + width - 1, y1, height, color);
53 }
54 void Display::filled_rectangle(int x1, int y1, int width, int height, Color color) {
55  // Future: Use vertical_line and horizontal_line methods depending on rotation to reduce memory accesses.
56  for (int i = y1; i < y1 + height; i++) {
57  this->horizontal_line(x1, i, width, color);
58  }
59 }
60 void HOT Display::circle(int center_x, int center_xy, int radius, Color color) {
61  int dx = -radius;
62  int dy = 0;
63  int err = 2 - 2 * radius;
64  int e2;
65 
66  do {
67  this->draw_pixel_at(center_x - dx, center_xy + dy, color);
68  this->draw_pixel_at(center_x + dx, center_xy + dy, color);
69  this->draw_pixel_at(center_x + dx, center_xy - dy, color);
70  this->draw_pixel_at(center_x - dx, center_xy - dy, color);
71  e2 = err;
72  if (e2 < dy) {
73  err += ++dy * 2 + 1;
74  if (-dx == dy && e2 <= dx) {
75  e2 = 0;
76  }
77  }
78  if (e2 > dx) {
79  err += ++dx * 2 + 1;
80  }
81  } while (dx <= 0);
82 }
83 void Display::filled_circle(int center_x, int center_y, int radius, Color color) {
84  int dx = -int32_t(radius);
85  int dy = 0;
86  int err = 2 - 2 * radius;
87  int e2;
88 
89  do {
90  this->draw_pixel_at(center_x - dx, center_y + dy, color);
91  this->draw_pixel_at(center_x + dx, center_y + dy, color);
92  this->draw_pixel_at(center_x + dx, center_y - dy, color);
93  this->draw_pixel_at(center_x - dx, center_y - dy, color);
94  int hline_width = 2 * (-dx) + 1;
95  this->horizontal_line(center_x + dx, center_y + dy, hline_width, color);
96  this->horizontal_line(center_x + dx, center_y - dy, hline_width, color);
97  e2 = err;
98  if (e2 < dy) {
99  err += ++dy * 2 + 1;
100  if (-dx == dy && e2 <= dx) {
101  e2 = 0;
102  }
103  }
104  if (e2 > dx) {
105  err += ++dx * 2 + 1;
106  }
107  } while (dx <= 0);
108 }
109 
110 void Display::print(int x, int y, BaseFont *font, Color color, TextAlign align, const char *text) {
111  int x_start, y_start;
112  int width, height;
113  this->get_text_bounds(x, y, text, font, align, &x_start, &y_start, &width, &height);
114  font->print(x_start, y_start, this, color, text);
115 }
116 void Display::vprintf_(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, va_list arg) {
117  char buffer[256];
118  int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
119  if (ret > 0)
120  this->print(x, y, font, color, align, buffer);
121 }
122 
123 void Display::image(int x, int y, BaseImage *image, Color color_on, Color color_off) {
124  this->image(x, y, image, ImageAlign::TOP_LEFT, color_on, color_off);
125 }
126 
127 void Display::image(int x, int y, BaseImage *image, ImageAlign align, Color color_on, Color color_off) {
128  auto x_align = ImageAlign(int(align) & (int(ImageAlign::HORIZONTAL_ALIGNMENT)));
129  auto y_align = ImageAlign(int(align) & (int(ImageAlign::VERTICAL_ALIGNMENT)));
130 
131  switch (x_align) {
132  case ImageAlign::RIGHT:
133  x -= image->get_width();
134  break;
136  x -= image->get_width() / 2;
137  break;
138  case ImageAlign::LEFT:
139  default:
140  break;
141  }
142 
143  switch (y_align) {
144  case ImageAlign::BOTTOM:
145  y -= image->get_height();
146  break;
148  y -= image->get_height() / 2;
149  break;
150  case ImageAlign::TOP:
151  default:
152  break;
153  }
154 
155  image->draw(x, y, this, color_on, color_off);
156 }
157 
158 #ifdef USE_GRAPH
159 void Display::graph(int x, int y, graph::Graph *graph, Color color_on) { graph->draw(this, x, y, color_on); }
160 void Display::legend(int x, int y, graph::Graph *graph, Color color_on) { graph->draw_legend(this, x, y, color_on); }
161 #endif // USE_GRAPH
162 
163 #ifdef USE_QR_CODE
164 void Display::qr_code(int x, int y, qr_code::QrCode *qr_code, Color color_on, int scale) {
165  qr_code->draw(this, x, y, color_on, scale);
166 }
167 #endif // USE_QR_CODE
168 
169 void Display::get_text_bounds(int x, int y, const char *text, BaseFont *font, TextAlign align, int *x1, int *y1,
170  int *width, int *height) {
171  int x_offset, baseline;
172  font->measure(text, width, &x_offset, &baseline, height);
173 
174  auto x_align = TextAlign(int(align) & 0x18);
175  auto y_align = TextAlign(int(align) & 0x07);
176 
177  switch (x_align) {
178  case TextAlign::RIGHT:
179  *x1 = x - *width;
180  break;
182  *x1 = x - (*width) / 2;
183  break;
184  case TextAlign::LEFT:
185  default:
186  // LEFT
187  *x1 = x;
188  break;
189  }
190 
191  switch (y_align) {
192  case TextAlign::BOTTOM:
193  *y1 = y - *height;
194  break;
195  case TextAlign::BASELINE:
196  *y1 = y - baseline;
197  break;
199  *y1 = y - (*height) / 2;
200  break;
201  case TextAlign::TOP:
202  default:
203  *y1 = y;
204  break;
205  }
206 }
207 void Display::print(int x, int y, BaseFont *font, Color color, const char *text) {
208  this->print(x, y, font, color, TextAlign::TOP_LEFT, text);
209 }
210 void Display::print(int x, int y, BaseFont *font, TextAlign align, const char *text) {
211  this->print(x, y, font, COLOR_ON, align, text);
212 }
213 void Display::print(int x, int y, BaseFont *font, const char *text) {
214  this->print(x, y, font, COLOR_ON, TextAlign::TOP_LEFT, text);
215 }
216 void Display::printf(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, ...) {
217  va_list arg;
218  va_start(arg, format);
219  this->vprintf_(x, y, font, color, align, format, arg);
220  va_end(arg);
221 }
222 void Display::printf(int x, int y, BaseFont *font, Color color, const char *format, ...) {
223  va_list arg;
224  va_start(arg, format);
225  this->vprintf_(x, y, font, color, TextAlign::TOP_LEFT, format, arg);
226  va_end(arg);
227 }
228 void Display::printf(int x, int y, BaseFont *font, TextAlign align, const char *format, ...) {
229  va_list arg;
230  va_start(arg, format);
231  this->vprintf_(x, y, font, COLOR_ON, align, format, arg);
232  va_end(arg);
233 }
234 void Display::printf(int x, int y, BaseFont *font, const char *format, ...) {
235  va_list arg;
236  va_start(arg, format);
237  this->vprintf_(x, y, font, COLOR_ON, TextAlign::TOP_LEFT, format, arg);
238  va_end(arg);
239 }
240 void Display::set_writer(display_writer_t &&writer) { this->writer_ = writer; }
241 void Display::set_pages(std::vector<DisplayPage *> pages) {
242  for (auto *page : pages)
243  page->set_parent(this);
244 
245  for (uint32_t i = 0; i < pages.size() - 1; i++) {
246  pages[i]->set_next(pages[i + 1]);
247  pages[i + 1]->set_prev(pages[i]);
248  }
249  pages[0]->set_prev(pages[pages.size() - 1]);
250  pages[pages.size() - 1]->set_next(pages[0]);
251  this->show_page(pages[0]);
252 }
254  this->previous_page_ = this->page_;
255  this->page_ = page;
256  if (this->previous_page_ != this->page_) {
257  for (auto *t : on_page_change_triggers_)
258  t->process(this->previous_page_, this->page_);
259  }
260 }
264  if (this->auto_clear_enabled_) {
265  this->clear();
266  }
267  if (this->page_ != nullptr) {
268  this->page_->get_writer()(*this);
269  } else if (this->writer_.has_value()) {
270  (*this->writer_)(*this);
271  }
272  this->clear_clipping_();
273 }
275  if ((this->from_ == nullptr || this->from_ == from) && (this->to_ == nullptr || this->to_ == to))
276  this->trigger(from, to);
277 }
278 void Display::strftime(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, ESPTime time) {
279  char buffer[64];
280  size_t ret = time.strftime(buffer, sizeof(buffer), format);
281  if (ret > 0)
282  this->print(x, y, font, color, align, buffer);
283 }
284 void Display::strftime(int x, int y, BaseFont *font, Color color, const char *format, ESPTime time) {
285  this->strftime(x, y, font, color, TextAlign::TOP_LEFT, format, time);
286 }
287 void Display::strftime(int x, int y, BaseFont *font, TextAlign align, const char *format, ESPTime time) {
288  this->strftime(x, y, font, COLOR_ON, align, format, time);
289 }
290 void Display::strftime(int x, int y, BaseFont *font, const char *format, ESPTime time) {
291  this->strftime(x, y, font, COLOR_ON, TextAlign::TOP_LEFT, format, time);
292 }
293 
295  if (!this->clipping_rectangle_.empty()) {
296  Rect r = this->clipping_rectangle_.back();
297  rect.shrink(r);
298  }
299  this->clipping_rectangle_.push_back(rect);
300 }
302  if (this->clipping_rectangle_.empty()) {
303  ESP_LOGE(TAG, "clear: Clipping is not set.");
304  } else {
305  this->clipping_rectangle_.pop_back();
306  }
307 }
309  if (this->clipping_rectangle_.empty()) {
310  ESP_LOGE(TAG, "add: Clipping is not set.");
311  } else {
312  this->clipping_rectangle_.back().extend(add_rect);
313  }
314 }
316  if (this->clipping_rectangle_.empty()) {
317  ESP_LOGE(TAG, "add: Clipping is not set.");
318  } else {
319  this->clipping_rectangle_.back().shrink(add_rect);
320  }
321 }
323  if (this->clipping_rectangle_.empty()) {
324  return Rect();
325  } else {
326  return this->clipping_rectangle_.back();
327  }
328 }
330 bool Display::clip(int x, int y) {
331  if (x < 0 || x >= this->get_width() || y < 0 || y >= this->get_height())
332  return false;
333  if (!this->get_clipping().inside(x, y))
334  return false;
335  return true;
336 }
337 bool Display::clamp_x_(int x, int w, int &min_x, int &max_x) {
338  min_x = std::max(x, 0);
339  max_x = std::min(x + w, this->get_width());
340 
341  if (!this->clipping_rectangle_.empty()) {
342  const auto &rect = this->clipping_rectangle_.back();
343  if (!rect.is_set())
344  return false;
345 
346  min_x = std::max(min_x, (int) rect.x);
347  max_x = std::min(max_x, (int) rect.x2());
348  }
349 
350  return min_x < max_x;
351 }
352 bool Display::clamp_y_(int y, int h, int &min_y, int &max_y) {
353  min_y = std::max(y, 0);
354  max_y = std::min(y + h, this->get_height());
355 
356  if (!this->clipping_rectangle_.empty()) {
357  const auto &rect = this->clipping_rectangle_.back();
358  if (!rect.is_set())
359  return false;
360 
361  min_y = std::max(min_y, (int) rect.y);
362  max_y = std::min(max_y, (int) rect.y2());
363  }
364 
365  return min_y < max_y;
366 }
367 
369 void DisplayPage::show() { this->parent_->show_page(this); }
370 void DisplayPage::show_next() { this->next_->show(); }
371 void DisplayPage::show_prev() { this->prev_->show(); }
372 void DisplayPage::set_parent(Display *parent) { this->parent_ = parent; }
373 void DisplayPage::set_prev(DisplayPage *prev) { this->prev_ = prev; }
374 void DisplayPage::set_next(DisplayPage *next) { this->next_ = next; }
375 const display_writer_t &DisplayPage::get_writer() const { return this->writer_; }
376 
377 } // namespace display
378 } // namespace esphome
void circle(int center_x, int center_xy, int radius, Color color=COLOR_ON)
Draw the outline of a circle centered around [center_x,center_y] with the radius radius with the give...
Definition: display.cpp:60
void horizontal_line(int x, int y, int width, Color color=COLOR_ON)
Draw a horizontal line from the point [x,y] to [x+width,y] with the given color.
Definition: display.cpp:38
optional< display_writer_t > writer_
Definition: display.h:492
std::vector< DisplayOnPageChangeTrigger * > on_page_change_triggers_
Definition: display.h:495
void set_pages(std::vector< DisplayPage *> pages)
Definition: display.cpp:241
bool clamp_x_(int x, int w, int &min_x, int &max_x)
Definition: display.cpp:337
size_t strftime(char *buffer, size_t buffer_len, const char *format)
Convert this ESPTime struct to a null-terminated c string buffer as specified by the format argument...
Definition: time.cpp:15
void get_text_bounds(int x, int y, const char *text, BaseFont *font, TextAlign align, int *x1, int *y1, int *width, int *height)
Get the text bounds of the given string.
Definition: display.cpp:169
void set_next(DisplayPage *next)
Definition: display.cpp:374
void set_parent(Display *parent)
Definition: display.cpp:372
uint16_t x
Definition: tt21100.cpp:17
A more user-friendly version of struct tm from time.h.
Definition: time.h:12
void set_rotation(DisplayRotation rotation)
Internal method to set the display rotation with.
Definition: display.cpp:17
const Color COLOR_OFF(0, 0, 0, 0)
Turn the pixel OFF.
Definition: display.h:149
void extend_clipping(Rect rect)
Add a rectangular region to the invalidation region.
Definition: display.cpp:308
STL namespace.
void shrink_clipping(Rect rect)
substract a rectangular region to the invalidation region
Definition: display.cpp:315
void filled_circle(int center_x, int center_y, int radius, Color color=COLOR_ON)
Fill a circle centered around [center_x,center_y] with the radius radius with the given color...
Definition: display.cpp:83
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:54
void printf(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format,...) __attribute__((format(printf
Evaluate the printf-format format and print the result with the anchor point at [x,y] with font.
Definition: display.cpp:216
virtual int get_width()=0
Get the width of the image in pixels with rotation applied.
bool has_value() const
Definition: optional.h:87
virtual void fill(Color color)
Fill the entire screen with the given color.
Definition: display.cpp:15
DisplayPage(display_writer_t writer)
Definition: display.cpp:368
Rect get_clipping() const
Get the current the clipping rectangle.
Definition: display.cpp:322
void qr_code(int x, int y, qr_code::QrCode *qr_code, Color color_on=COLOR_ON, int scale=1)
Draw the qr_code with the top-left corner at [x,y] to the screen.
Definition: display.cpp:164
bool clamp_y_(int y, int h, int &min_y, int &max_y)
Definition: display.cpp:352
virtual void draw(int x, int y, Display *display, Color color_on, Color color_off)=0
uint16_t y
Definition: tt21100.cpp:18
const display_writer_t & get_writer() const
Definition: display.cpp:375
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:48
TextAlign
TextAlign is used to tell the display class how to position a piece of text.
Definition: display.h:47
DisplayPage * page_
Definition: display.h:493
std::function< void(Display &)> display_writer_t
Definition: display.h:139
void process(DisplayPage *from, DisplayPage *to)
Definition: display.cpp:274
void start_clipping(Rect rect)
Set the clipping rectangle for further drawing.
Definition: display.cpp:294
ImageAlign
ImageAlign is used to tell the display class how to position a image.
Definition: display.h:97
void clear()
Clear the entire screen by filling it with OFF pixels.
Definition: display.cpp:16
void draw_legend(display::Display *buff, uint16_t x_offset, uint16_t y_offset, Color color)
Definition: graph.cpp:306
void line(int x1, int y1, int x2, int y2, Color color=COLOR_ON)
Draw a straight line from the point [x1,y1] to [x2,y2] with the given color.
Definition: display.cpp:18
void legend(int x, int y, graph::Graph *graph, Color color_on=COLOR_ON)
Draw the legend for graph with the top-left corner at [x,y] to the screen.
Definition: display.cpp:160
void draw(display::Display *buff, uint16_t x_offset, uint16_t y_offset, Color color, int scale)
Definition: qr_code.cpp:36
display_writer_t writer_
Definition: display.h:513
DisplayRotation rotation_
Definition: display.h:491
void end_clipping()
Reset the invalidation region.
Definition: display.cpp:301
DisplayPage * previous_page_
Definition: display.h:494
void vertical_line(int x, int y, int height, Color color=COLOR_ON)
Draw a vertical line from the point [x,y] to [x,y+width] with the given color.
Definition: display.cpp:43
const Color COLOR_ON(255, 255, 255, 255)
Turn the pixel ON.
Definition: display.h:151
void print(int x, int y, BaseFont *font, Color color, TextAlign align, const char *text)
Print text with the anchor point at [x,y] with font.
Definition: display.cpp:110
void vprintf_(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, va_list arg)
Definition: display.cpp:116
uint8_t h
Definition: bl0939.h:21
virtual int get_width() const =0
bool clip(int x, int y)
Check if pixel is within region of display.
Definition: display.cpp:330
void void void void void strftime(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, ESPTime time) __attribute__((format(strftime
Evaluate the strftime-format format and print the result with the anchor point at [x...
Definition: display.cpp:278
virtual void print(int x, int y, Display *display, Color color, const char *text)=0
void draw_pixel_at(int x, int y)
Set a single pixel at the specified coordinates to default color.
Definition: display.h:179
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
void draw(display::Display *buff, uint16_t x_offset, uint16_t y_offset, Color color)
Definition: graph.cpp:59
virtual void measure(const char *str, int *width, int *x_offset, int *baseline, int *height)=0
std::vector< Rect > clipping_rectangle_
Definition: display.h:497
void set_writer(display_writer_t &&writer)
Internal method to set the display writer lambda.
Definition: display.cpp:240
void graph(int x, int y, graph::Graph *graph, Color color_on=COLOR_ON)
Draw the graph with the top-left corner at [x,y] to the screen.
Definition: display.cpp:159
void shrink(Rect rect)
Definition: rect.cpp:42
void void void void void void void void void image(int x, int y, BaseImage *image, Color color_on=COLOR_ON, Color color_off=COLOR_OFF)
Draw the image with the top-left corner at [x,y] to the screen.
Definition: display.cpp:123
void show_page(DisplayPage *page)
Definition: display.cpp:253
void set_prev(DisplayPage *prev)
Definition: display.cpp:373
virtual int get_height() const =0
virtual int get_height()=0
Get the height of the image in pixels with rotation applied.