13 static const char *
const TAG =
"display";
19 if (this->
is_set() && (this->
w >= (-2 * horizontal)) && (this->
h >= (-2 * vertical))) {
20 this->
x = this->
x - horizontal;
21 this->
y = this->
y - vertical;
22 this->
w = this->
w + (2 * horizontal);
23 this->
h = this->h + (2 * vertical);
34 if (this->
x > rect.
x) {
35 this->
w = this->
w + (this->
x - rect.
x);
38 if (this->
y > rect.
y) {
39 this->
h = this->
h + (this->
y - rect.
y);
42 if (this->
x2() < rect.
x2()) {
43 this->
w = rect.
x2() - this->
x;
45 if (this->
y2() < rect.
y2()) {
46 this->
h = rect.
y2() - this->
y;
54 if (this->
x2() > rect.
x2()) {
55 this->
w = rect.
x2() - this->
x;
57 if (this->
x < rect.
x) {
58 this->
w = this->
w + (this->
x - rect.
x);
61 if (this->
y2() > rect.
y2()) {
62 this->
h = rect.
y2() - this->
y;
64 if (this->
y < rect.
y) {
65 this->
h = this->
h + (this->
y - rect.
y);
72 return (rect.
x == this->x) && (rect.
w == this->
w) && (rect.
y == this->y) && (rect.
h == this->
h);
80 return ((test_x >= this->
x) && (test_x <= this->
x2()) && (test_y >= this->
y) && (test_y <= this->
y2()));
82 return ((test_x >= 0) && (test_x <= this->
w) && (test_y >= 0) && (test_y <= this->
h));
91 return ((rect.
x <= this->x2()) && (rect.
x2() >= this->
x) && (rect.
y <= this->y2()) && (rect.
y2() >= this->
y));
93 return ((rect.
x <= this->w) && (rect.
w >= 0) && (rect.
y <= this->h) && (rect.
h >= 0));
99 ESP_LOGI(TAG,
"%s [%3d,%3d,%3d,%3d] (%3d,%3d)", prefix.c_str(), this->
x, this->
y, this->
w, this->
h, this->
x2(),
102 ESP_LOGI(TAG,
"%s ** IS NOT SET **", prefix.c_str());
107 this->buffer_ = allocator.
allocate(buffer_length);
108 if (this->buffer_ ==
nullptr) {
109 ESP_LOGE(TAG,
"Could not allocate buffer for display!");
118 switch (this->rotation_) {
121 return this->get_height_internal();
125 return this->get_width_internal();
129 switch (this->rotation_) {
132 return this->get_height_internal();
136 return this->get_width_internal();
141 if (!this->get_clipping().
inside(x, y))
144 switch (this->rotation_) {
149 x = this->get_width_internal() - x - 1;
152 x = this->get_width_internal() - x - 1;
153 y = this->get_height_internal() - y - 1;
157 y = this->get_height_internal() - y - 1;
160 this->draw_absolute_pixel_internal(x, y, color);
164 const int32_t dx = abs(x2 - x1), sx = x1 < x2 ? 1 : -1;
165 const int32_t dy = -abs(y2 - y1), sy = y1 < y2 ? 1 : -1;
166 int32_t err = dx + dy;
169 this->draw_pixel_at(x1, y1, color);
170 if (x1 == x2 && y1 == y2)
172 int32_t e2 = 2 * err;
185 for (
int i = x; i < x + width; i++)
186 this->draw_pixel_at(i, y, color);
190 for (
int i = y; i < y + height; i++)
191 this->draw_pixel_at(x, i, color);
194 this->horizontal_line(x1, y1, width, color);
195 this->horizontal_line(x1, y1 + height - 1, width, color);
196 this->vertical_line(x1, y1, height, color);
197 this->vertical_line(x1 + width - 1, y1, height, color);
201 for (
int i = y1; i < y1 + height; i++) {
202 this->horizontal_line(x1, i, width, color);
208 int err = 2 - 2 * radius;
212 this->draw_pixel_at(center_x - dx, center_xy + dy, color);
213 this->draw_pixel_at(center_x + dx, center_xy + dy, color);
214 this->draw_pixel_at(center_x + dx, center_xy - dy, color);
215 this->draw_pixel_at(center_x - dx, center_xy - dy, color);
219 if (-dx == dy && e2 <= dx) {
229 int dx = -int32_t(radius);
231 int err = 2 - 2 * radius;
235 this->draw_pixel_at(center_x - dx, center_y + dy, color);
236 this->draw_pixel_at(center_x + dx, center_y + dy, color);
237 this->draw_pixel_at(center_x + dx, center_y - dy, color);
238 this->draw_pixel_at(center_x - dx, center_y - dy, color);
239 int hline_width = 2 * (-dx) + 1;
240 this->horizontal_line(center_x + dx, center_y + dy, hline_width, color);
241 this->horizontal_line(center_x + dx, center_y - dy, hline_width, color);
245 if (-dx == dy && e2 <= dx) {
256 int x_start, y_start;
258 this->get_text_bounds(x, y, text, font, align, &x_start, &y_start, &width, &height);
262 while (text[i] !=
'\0') {
267 ESP_LOGW(TAG,
"Encountered character without representation in font: '%c'", text[i]);
269 uint8_t glyph_width = font->
get_glyphs()[0].glyph_data_->width;
270 for (
int glyph_x = 0; glyph_x < glyph_width; glyph_x++) {
271 for (
int glyph_y = 0; glyph_y < height; glyph_y++)
272 this->draw_pixel_at(glyph_x + x_at, glyph_y + y_start, color);
282 int scan_x1, scan_y1, scan_width, scan_height;
283 glyph.
scan_area(&scan_x1, &scan_y1, &scan_width, &scan_height);
285 for (
int glyph_x = scan_x1; glyph_x < scan_x1 + scan_width; glyph_x++) {
286 for (
int glyph_y = scan_y1; glyph_y < scan_y1 + scan_height; glyph_y++) {
288 this->draw_pixel_at(glyph_x + x_at, glyph_y + y_start, color);
300 int ret = vsnprintf(buffer,
sizeof(buffer), format, arg);
302 this->print(x, y, font, color, align, buffer);
308 for (
int img_x = 0; img_x < image->
get_width(); img_x++) {
309 for (
int img_y = 0; img_y < image->
get_height(); img_y++) {
310 this->draw_pixel_at(x + img_x, y + img_y, image->
get_pixel(img_x, img_y) ? color_on : color_off);
315 for (
int img_x = 0; img_x < image->
get_width(); img_x++) {
316 for (
int img_y = 0; img_y < image->
get_height(); img_y++) {
322 for (
int img_x = 0; img_x < image->
get_width(); img_x++) {
323 for (
int img_y = 0; img_y < image->
get_height(); img_y++) {
324 this->draw_pixel_at(x + img_x, y + img_y, image->
get_color_pixel(img_x, img_y));
329 for (
int img_x = 0; img_x < image->
get_width(); img_x++) {
330 for (
int img_y = 0; img_y < image->
get_height(); img_y++) {
332 this->draw_pixel_at(x + img_x, y + img_y, color_on);
337 for (
int img_x = 0; img_x < image->
get_width(); img_x++) {
338 for (
int img_y = 0; img_y < image->
get_height(); img_y++) {
339 this->draw_pixel_at(x + img_x, y + img_y, image->
get_rgb565_pixel(img_x, img_y));
355 qr_code->
draw(
this, x, y, color_on, scale);
357 #endif // USE_QR_CODE 360 int *width,
int *height) {
361 int x_offset, baseline;
362 font->
measure(text, width, &x_offset, &baseline, height);
364 auto x_align =
TextAlign(
int(align) & 0x18);
365 auto y_align =
TextAlign(
int(align) & 0x07);
372 *x1 = x - (*width) / 2;
389 *y1 = y - (*height) / 2;
401 this->print(x, y, font, COLOR_ON, align, text);
408 va_start(arg, format);
409 this->vprintf_(x, y, font, color, align, format, arg);
414 va_start(arg, format);
420 va_start(arg, format);
421 this->vprintf_(x, y, font, COLOR_ON, align, format, arg);
426 va_start(arg, format);
432 for (
auto *page : pages)
433 page->set_parent(
this);
435 for (uint32_t i = 0; i < pages.size() - 1; i++) {
436 pages[i]->set_next(pages[i + 1]);
437 pages[i + 1]->set_prev(pages[i]);
439 pages[0]->set_prev(pages[pages.size() - 1]);
440 pages[pages.size() - 1]->set_next(pages[0]);
441 this->show_page(pages[0]);
444 this->previous_page_ = this->page_;
446 if (this->previous_page_ != this->page_) {
447 for (
auto *t : on_page_change_triggers_)
448 t->process(this->previous_page_, this->page_);
454 if (this->auto_clear_enabled_) {
457 if (this->page_ !=
nullptr) {
458 this->page_->get_writer()(*this);
459 }
else if (this->writer_.has_value()) {
460 (*this->writer_)(*
this);
463 while (is_clipping()) {
468 if ((this->from_ ==
nullptr || this->from_ == from) && (this->to_ ==
nullptr || this->to_ == to))
469 this->trigger(from, to);
475 size_t ret = time.
strftime(buffer,
sizeof(buffer), format);
477 this->print(x, y, font, color, align, buffer);
483 this->strftime(x, y, font, COLOR_ON, align, format, time);
491 if (!this->clipping_rectangle_.empty()) {
492 Rect r = this->clipping_rectangle_.back();
495 this->clipping_rectangle_.push_back(rect);
498 if (this->clipping_rectangle_.empty()) {
499 ESP_LOGE(TAG,
"clear: Clipping is not set.");
501 this->clipping_rectangle_.pop_back();
505 if (this->clipping_rectangle_.empty()) {
506 ESP_LOGE(TAG,
"add: Clipping is not set.");
508 this->clipping_rectangle_.back().extend(add_rect);
512 if (this->clipping_rectangle_.empty()) {
513 ESP_LOGE(TAG,
"add: Clipping is not set.");
515 this->clipping_rectangle_.back().shrink(add_rect);
519 if (this->clipping_rectangle_.empty()) {
522 return this->clipping_rectangle_.back();
526 const int x_data = x - this->glyph_data_->offset_x;
527 const int y_data = y - this->glyph_data_->offset_y;
528 if (x_data < 0 || x_data >= this->glyph_data_->width || y_data < 0 || y_data >= this->glyph_data_->height)
530 const uint32_t width_8 = ((this->glyph_data_->width + 7u) / 8u) * 8u;
531 const uint32_t pos = x_data + y_data * width_8;
532 return progmem_read_byte(this->glyph_data_->data + (pos / 8u)) & (0x80 >> (pos % 8u));
538 for (uint32_t i = 0;; i++) {
539 if (this->glyph_data_->a_char[i] ==
'\0')
543 if (this->glyph_data_->a_char[i] > str[i])
545 if (this->glyph_data_->a_char[i] < str[i])
552 for (uint32_t i = 0;; i++) {
553 if (this->glyph_data_->a_char[i] ==
'\0')
555 if (str[i] != this->glyph_data_->a_char[i])
562 *x1 = this->glyph_data_->offset_x;
563 *y1 = this->glyph_data_->offset_y;
564 *width = this->glyph_data_->width;
565 *height = this->glyph_data_->height;
569 int hi = this->glyphs_.size() - 1;
571 int mid = (lo + hi + 1) / 2;
572 if (this->glyphs_[mid].compare_to(str)) {
578 *match_length = this->glyphs_[lo].match_length(str);
579 if (*match_length <= 0)
583 void Font::measure(
const char *str,
int *width,
int *x_offset,
int *baseline,
int *height) {
584 *baseline = this->baseline_;
585 *height = this->height_;
588 bool has_char =
false;
590 while (str[i] !=
'\0') {
592 int glyph_n = this->match_next_glyph(str + i, &match_length);
595 if (!this->get_glyphs().empty())
596 x += this->get_glyphs()[0].glyph_data_->width;
601 const Glyph &glyph = this->glyphs_[glyph_n];
615 Font::Font(
const GlyphData *data,
int data_nr,
int baseline,
int height) : baseline_(baseline), height_(height) {
617 for (
int i = 0; i < data_nr; ++i)
618 glyphs_.emplace_back(&data[i]);
622 if (x < 0 || x >= this->width_ || y < 0 || y >= this->
height_)
624 const uint32_t width_8 = ((this->width_ + 7u) / 8u) * 8u;
625 const uint32_t pos = x + y * width_8;
629 if (x < 0 || x >= this->width_ || y < 0 || y >= this->
height_)
631 const uint32_t pos = (x + y * this->width_) * 3;
635 return Color(color32);
638 if (x < 0 || x >= this->width_ || y < 0 || y >= this->
height_)
640 const uint32_t pos = (x + y * this->width_) * 2;
643 auto r = (rgb565 & 0xF800) >> 11;
644 auto g = (rgb565 & 0x07E0) >> 5;
645 auto b = rgb565 & 0x001F;
646 return Color((r << 3) | (r >> 2), (g << 2) | (g >> 4), (b << 3) | (b >> 2));
649 if (x < 0 || x >= this->width_ || y < 0 || y >= this->
height_)
651 const uint32_t pos = (x + y * this->width_);
653 return Color(gray | gray << 8 | gray << 16 | gray << 24);
659 : width_(width),
height_(height), type_(type), data_start_(data_start) {}
663 if (x < 0 || x >= this->
width_ || y < 0 || y >= this->
height_)
665 const uint32_t width_8 = ((this->
width_ + 7u) / 8u) * 8u;
666 const uint32_t frame_index = this->
height_ * width_8 * this->current_frame_;
667 if (frame_index >= (uint32_t)(this->
width_ * this->
height_ * this->animation_frame_count_))
669 const uint32_t pos = x + y * width_8 + frame_index;
673 if (x < 0 || x >= this->
width_ || y < 0 || y >= this->
height_)
675 const uint32_t frame_index = this->
width_ * this->
height_ * this->current_frame_;
676 if (frame_index >= (uint32_t)(this->
width_ * this->
height_ * this->animation_frame_count_))
678 const uint32_t pos = (x + y * this->
width_ + frame_index) * 3;
682 return Color(color32);
685 if (x < 0 || x >= this->
width_ || y < 0 || y >= this->
height_)
687 const uint32_t frame_index = this->
width_ * this->
height_ * this->current_frame_;
688 if (frame_index >= (uint32_t)(this->
width_ * this->
height_ * this->animation_frame_count_))
690 const uint32_t pos = (x + y * this->
width_ + frame_index) * 2;
693 auto r = (rgb565 & 0xF800) >> 11;
694 auto g = (rgb565 & 0x07E0) >> 5;
695 auto b = rgb565 & 0x001F;
696 return Color((r << 3) | (r >> 2), (g << 2) | (g >> 4), (b << 3) | (b >> 2));
699 if (x < 0 || x >= this->
width_ || y < 0 || y >= this->
height_)
701 const uint32_t frame_index = this->
width_ * this->
height_ * this->current_frame_;
702 if (frame_index >= (uint32_t)(this->
width_ * this->
height_ * this->animation_frame_count_))
704 const uint32_t pos = (x + y * this->
width_ + frame_index);
706 return Color(gray | gray << 8 | gray << 16 | gray << 24);
709 :
Image(data_start, width, height, type), current_frame_(0), animation_frame_count_(animation_frame_count) {}
726 unsigned abs_frame = abs(frame);
void printf(int x, int y, Font *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.
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.
void void void void void strftime(int x, int y, Font *font, Color color, TextAlign align, const char *format, time::ESPTime time) __attribute__((format(strftime
Evaluate the strftime-format format and print the result with the anchor point at [x...
virtual Color get_grayscale_pixel(int x, int y) const
void show_page(DisplayPage *page)
void end_clipping()
Reset the invalidation region.
void draw(display::DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Color color, int scale)
Font(const GlyphData *data, int data_nr, int baseline, int height)
Construct the font with the given glyphs.
void set_pages(std::vector< DisplayPage *> pages)
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.
Image(const uint8_t *data_start, int width, int height, ImageType type)
void scan_area(int *x1, int *y1, int *width, int *height) const
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...
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...
void set_next(DisplayPage *next)
void set_rotation(DisplayRotation rotation)
Internal method to set the display rotation with.
Color get_rgb565_pixel(int x, int y) const override
std::vector< Glyph, ExternalRAMAllocator< Glyph > > glyphs_
void clear()
Clear the entire screen by filling it with OFF pixels.
int16_t x
X coordinate of corner.
void shrink_clipping(Rect rect)
substract a rectangular region to the invalidation region
const Color COLOR_OFF(0, 0, 0, 0)
Turn the pixel OFF.
An STL allocator that uses SPI RAM.
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...
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.
virtual Color get_color_pixel(int x, int y) const
void start_clipping(Rect rect)
Set the clipping rectangle for further drawing.
int16_t h
Height of region.
bool get_pixel(int x, int y) const
DisplayPage(display_writer_t writer)
void vprintf_(int x, int y, Font *font, Color color, TextAlign align, const char *format, va_list arg)
const uint8_t * data_start_
int16_t y
Y coordinate of corner.
void print(int x, int y, Font *font, Color color, TextAlign align, const char *text)
Print text with the anchor point at [x,y] with font.
void set_frame(int frame)
Selects a specific frame within the animation.
const display_writer_t & get_writer() const
void init_internal_(uint32_t buffer_length)
bool compare_to(const char *str) const
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.
ImageType get_type() const
int animation_frame_count_
virtual void fill(Color color)
Fill the entire screen with the given color.
Color get_grayscale_pixel(int x, int y) const override
TextAlign
TextAlign is used to tell the display class how to position a piece of text.
void measure(const char *str, int *width, int *x_offset, int *baseline, int *height)
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.
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.
A more user-friendly version of struct tm from time.h.
void process(DisplayPage *from, DisplayPage *to)
int match_length(const char *str) const
Color get_color_pixel(int x, int y) const override
int match_next_glyph(const char *str, int *match_length)
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...
int get_animation_frame_count() const
bool inside(Rect rect, bool absolute=true)
Application App
Global storage of Application pointer - only one Application can exist.
bool is_set() ALWAYS_INLINE
Y coordinate of corner.
void set_writer(display_writer_t &&writer)
Internal method to set the display writer lambda.
void void void void void void void void void image(int x, int y, Image *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.
Rect get_clipping()
Get the current the clipping rectangle.
void info(const std::string &prefix="rect info:")
void swap(optional< T > &x, optional< T > &y)
virtual int get_current_frame() const
const std::vector< Glyph, ExternalRAMAllocator< Glyph > > & get_glyphs() const
uint8_t progmem_read_byte(const uint8_t *addr)
const Color COLOR_ON(255, 255, 255, 255)
Turn the pixel ON.
int16_t y2()
X coordinate of corner.
void draw(display::DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Color color)
void get_text_bounds(int x, int y, const char *text, Font *font, TextAlign align, int *x1, int *y1, int *width, int *height)
Get the text bounds of the given string.
virtual Color get_rgb565_pixel(int x, int y) const
const GlyphData * glyph_data_
const char * get_char() const
int get_current_frame() const override
void expand(int16_t horizontal, int16_t vertical)
std::function< void(DisplayBuffer &)> display_writer_t
void extend_clipping(Rect rect)
Add a rectangular region to the invalidation region.
void draw_legend(display::DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Color color)
void set_parent(DisplayBuffer *parent)
void draw_pixel_at(int x, int y, Color color=COLOR_ON)
Set a single pixel at the specified coordinates to the given color.
Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, ImageType type)
int get_height()
Get the height of the image in pixels with rotation applied.
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+...
virtual bool get_pixel(int x, int y) const
int16_t w
Width of region.
void set_prev(DisplayPage *prev)
int get_width()
Get the width of the image in pixels with rotation applied.
bool get_pixel(int x, int y) const override