From 0411d524200893714fe8d4836e7b676e425e128d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= Date: Thu, 15 Jun 2023 01:15:46 +0200 Subject: [PATCH] display: add `BaseImage` and provide only `Image::get_pixel` method (#4932) --- esphome/components/display/display_buffer.cpp | 152 +++++++++--------- esphome/components/display/display_buffer.h | 33 ++-- 2 files changed, 101 insertions(+), 84 deletions(-) diff --git a/esphome/components/display/display_buffer.cpp b/esphome/components/display/display_buffer.cpp index ebeb4ce1bf..c8dc7b62e2 100644 --- a/esphome/components/display/display_buffer.cpp +++ b/esphome/components/display/display_buffer.cpp @@ -325,63 +325,8 @@ void DisplayBuffer::vprintf_(int x, int y, Font *font, Color color, TextAlign al this->print(x, y, font, color, align, buffer); } -void DisplayBuffer::image(int x, int y, Image *image, Color color_on, Color color_off) { - bool transparent = image->has_transparency(); - - switch (image->get_type()) { - case IMAGE_TYPE_BINARY: { - for (int img_x = 0; img_x < image->get_width(); img_x++) { - for (int img_y = 0; img_y < image->get_height(); img_y++) { - if (image->get_pixel(img_x, img_y)) { - this->draw_pixel_at(x + img_x, y + img_y, color_on); - } else if (!transparent) { - this->draw_pixel_at(x + img_x, y + img_y, color_off); - } - } - } - break; - } - case IMAGE_TYPE_GRAYSCALE: - for (int img_x = 0; img_x < image->get_width(); img_x++) { - for (int img_y = 0; img_y < image->get_height(); img_y++) { - auto color = image->get_grayscale_pixel(img_x, img_y); - if (color.w >= 0x80) { - this->draw_pixel_at(x + img_x, y + img_y, color); - } - } - } - break; - case IMAGE_TYPE_RGB565: - for (int img_x = 0; img_x < image->get_width(); img_x++) { - for (int img_y = 0; img_y < image->get_height(); img_y++) { - auto color = image->get_rgb565_pixel(img_x, img_y); - if (color.w >= 0x80) { - this->draw_pixel_at(x + img_x, y + img_y, color); - } - } - } - break; - case IMAGE_TYPE_RGB24: - for (int img_x = 0; img_x < image->get_width(); img_x++) { - for (int img_y = 0; img_y < image->get_height(); img_y++) { - auto color = image->get_color_pixel(img_x, img_y); - if (color.w >= 0x80) { - this->draw_pixel_at(x + img_x, y + img_y, color); - } - } - } - break; - case IMAGE_TYPE_RGBA: - for (int img_x = 0; img_x < image->get_width(); img_x++) { - for (int img_y = 0; img_y < image->get_height(); img_y++) { - auto color = image->get_rgba_pixel(img_x, img_y); - if (color.w >= 0x80) { - this->draw_pixel_at(x + img_x, y + img_y, color); - } - } - } - break; - } +void DisplayBuffer::image(int x, int y, BaseImage *image, Color color_on, Color color_off) { + image->draw(x, y, this, color_on, color_off); } #ifdef USE_GRAPH @@ -656,23 +601,91 @@ Font::Font(const GlyphData *data, int data_nr, int baseline, int height) : basel glyphs_.emplace_back(&data[i]); } -bool Image::get_pixel(int x, int y) const { +void Image::draw(int x, int y, DisplayBuffer *display, Color color_on, Color color_off) { + switch (type_) { + case IMAGE_TYPE_BINARY: { + for (int img_x = 0; img_x < width_; img_x++) { + for (int img_y = 0; img_y < height_; img_y++) { + if (this->get_binary_pixel_(img_x, img_y)) { + display->draw_pixel_at(x + img_x, y + img_y, color_on); + } else if (!this->transparent_) { + display->draw_pixel_at(x + img_x, y + img_y, color_off); + } + } + } + break; + } + case IMAGE_TYPE_GRAYSCALE: + for (int img_x = 0; img_x < width_; img_x++) { + for (int img_y = 0; img_y < height_; img_y++) { + auto color = this->get_grayscale_pixel_(img_x, img_y); + if (color.w >= 0x80) { + display->draw_pixel_at(x + img_x, y + img_y, color); + } + } + } + break; + case IMAGE_TYPE_RGB565: + for (int img_x = 0; img_x < width_; img_x++) { + for (int img_y = 0; img_y < height_; img_y++) { + auto color = this->get_rgb565_pixel_(img_x, img_y); + if (color.w >= 0x80) { + display->draw_pixel_at(x + img_x, y + img_y, color); + } + } + } + break; + case IMAGE_TYPE_RGB24: + for (int img_x = 0; img_x < width_; img_x++) { + for (int img_y = 0; img_y < height_; img_y++) { + auto color = this->get_rgb24_pixel_(img_x, img_y); + if (color.w >= 0x80) { + display->draw_pixel_at(x + img_x, y + img_y, color); + } + } + } + break; + case IMAGE_TYPE_RGBA: + for (int img_x = 0; img_x < width_; img_x++) { + for (int img_y = 0; img_y < height_; img_y++) { + auto color = this->get_rgba_pixel_(img_x, img_y); + if (color.w >= 0x80) { + display->draw_pixel_at(x + img_x, y + img_y, color); + } + } + } + break; + } +} +Color Image::get_pixel(int x, int y, Color color_on, Color color_off) const { if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_) - return false; + return color_off; + switch (this->type_) { + case IMAGE_TYPE_BINARY: + return this->get_binary_pixel_(x, y) ? color_on : color_off; + case IMAGE_TYPE_GRAYSCALE: + return this->get_grayscale_pixel_(x, y); + case IMAGE_TYPE_RGB565: + return this->get_rgb565_pixel_(x, y); + case IMAGE_TYPE_RGB24: + return this->get_rgb24_pixel_(x, y); + case IMAGE_TYPE_RGBA: + return this->get_rgba_pixel_(x, y); + default: + return color_off; + } +} +bool Image::get_binary_pixel_(int x, int y) const { const uint32_t width_8 = ((this->width_ + 7u) / 8u) * 8u; const uint32_t pos = x + y * width_8; return progmem_read_byte(this->data_start_ + (pos / 8u)) & (0x80 >> (pos % 8u)); } -Color Image::get_rgba_pixel(int x, int y) const { - if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_) - return Color::BLACK; +Color Image::get_rgba_pixel_(int x, int y) const { const uint32_t pos = (x + y * this->width_) * 4; return Color(progmem_read_byte(this->data_start_ + pos + 0), progmem_read_byte(this->data_start_ + pos + 1), progmem_read_byte(this->data_start_ + pos + 2), progmem_read_byte(this->data_start_ + pos + 3)); } -Color Image::get_color_pixel(int x, int y) const { - if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_) - return Color::BLACK; +Color Image::get_rgb24_pixel_(int x, int y) const { const uint32_t pos = (x + y * this->width_) * 3; Color color = Color(progmem_read_byte(this->data_start_ + pos + 0), progmem_read_byte(this->data_start_ + pos + 1), progmem_read_byte(this->data_start_ + pos + 2)); @@ -685,9 +698,7 @@ Color Image::get_color_pixel(int x, int y) const { } return color; } -Color Image::get_rgb565_pixel(int x, int y) const { - if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_) - return Color::BLACK; +Color Image::get_rgb565_pixel_(int x, int y) const { const uint32_t pos = (x + y * this->width_) * 2; uint16_t rgb565 = progmem_read_byte(this->data_start_ + pos + 0) << 8 | progmem_read_byte(this->data_start_ + pos + 1); @@ -703,9 +714,7 @@ Color Image::get_rgb565_pixel(int x, int y) const { } return color; } -Color Image::get_grayscale_pixel(int x, int y) const { - if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_) - return Color::BLACK; +Color Image::get_grayscale_pixel_(int x, int y) const { const uint32_t pos = (x + y * this->width_); const uint8_t gray = progmem_read_byte(this->data_start_ + pos); uint8_t alpha = (gray == 1 && transparent_) ? 0 : 0xFF; @@ -716,7 +725,6 @@ int Image::get_height() const { return this->height_; } ImageType Image::get_type() const { return this->type_; } Image::Image(const uint8_t *data_start, int width, int height, ImageType type) : width_(width), height_(height), type_(type), data_start_(data_start) {} -int Image::get_current_frame() const { return 0; } Animation::Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, ImageType type) : Image(data_start, width, height, type), diff --git a/esphome/components/display/display_buffer.h b/esphome/components/display/display_buffer.h index 33e74b8df4..0c31ac24d9 100644 --- a/esphome/components/display/display_buffer.h +++ b/esphome/components/display/display_buffer.h @@ -123,8 +123,8 @@ class Rect { void info(const std::string &prefix = "rect info:"); }; +class BaseImage; class Font; -class Image; class DisplayBuffer; class DisplayPage; class DisplayOnPageChangeTrigger; @@ -315,7 +315,7 @@ class DisplayBuffer { * @param color_on The color to replace in binary images for the on bits. * @param color_off The color to replace in binary images for the off bits. */ - void image(int x, int y, Image *image, Color color_on = COLOR_ON, Color color_off = COLOR_OFF); + void image(int x, int y, BaseImage *image, Color color_on = COLOR_ON, Color color_off = COLOR_OFF); #ifdef USE_GRAPH /** Draw the `graph` with the top-left corner at [x,y] to the screen. @@ -529,24 +529,33 @@ class Font { int height_; }; -class Image { +class BaseImage { + public: + virtual void draw(int x, int y, DisplayBuffer *display, Color color_on, Color color_off) = 0; + virtual int get_width() const = 0; + virtual int get_height() const = 0; +}; + +class Image : public BaseImage { public: Image(const uint8_t *data_start, int width, int height, ImageType type); - bool get_pixel(int x, int y) const; - Color get_color_pixel(int x, int y) const; - Color get_rgba_pixel(int x, int y) const; - Color get_rgb565_pixel(int x, int y) const; - Color get_grayscale_pixel(int x, int y) const; - int get_width() const; - int get_height() const; + Color get_pixel(int x, int y, Color color_on = COLOR_ON, Color color_off = COLOR_OFF) const; + int get_width() const override; + int get_height() const override; ImageType get_type() const; - virtual int get_current_frame() const; + void draw(int x, int y, DisplayBuffer *display, Color color_on, Color color_off) override; void set_transparency(bool transparent) { transparent_ = transparent; } bool has_transparency() const { return transparent_; } protected: + bool get_binary_pixel_(int x, int y) const; + Color get_rgb24_pixel_(int x, int y) const; + Color get_rgba_pixel_(int x, int y) const; + Color get_rgb565_pixel_(int x, int y) const; + Color get_grayscale_pixel_(int x, int y) const; + int width_; int height_; ImageType type_; @@ -559,7 +568,7 @@ class Animation : public Image { Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, ImageType type); uint32_t get_animation_frame_count() const; - int get_current_frame() const override; + int get_current_frame() const; void next_frame(); void prev_frame();