diff --git a/esphome/components/epaper_spi/epaper_spi.cpp b/esphome/components/epaper_spi/epaper_spi.cpp index 7b962d59ea8..d8ead388183 100644 --- a/esphome/components/epaper_spi/epaper_spi.cpp +++ b/esphome/components/epaper_spi/epaper_spi.cpp @@ -468,7 +468,7 @@ void HOT EPaperBase::draw_pixels_at(int x_start, int y_start, int w, int h, cons ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) { size_t contiguous_length; uint8_t *contiguous = this->buffer_.get_span(0, contiguous_length); - if (!this->buffer_is_mono() || this->get_clipping().is_set() || bitness != COLOR_BITNESS_565 || + if (!this->buffer_is_mono_() || this->get_clipping().is_set() || bitness != COLOR_BITNESS_565 || contiguous_length != this->buffer_length_) { Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, x_pad); return; diff --git a/esphome/components/epaper_spi/epaper_spi.h b/esphome/components/epaper_spi/epaper_spi.h index 384b9cb0181..37f9a1449a2 100644 --- a/esphome/components/epaper_spi/epaper_spi.h +++ b/esphome/components/epaper_spi/epaper_spi.h @@ -38,15 +38,18 @@ class EPaperBase : public Display, spi::DATA_RATE_2MHZ> { public: EPaperBase(const char *name, uint16_t width, uint16_t height, const uint8_t *init_sequence = nullptr, - size_t init_sequence_length = 0, DisplayType display_type = DISPLAY_TYPE_BINARY) + size_t init_sequence_length = 0, DisplayType display_type = DISPLAY_TYPE_BINARY, + uint8_t bits_per_pixel = 1, uint8_t planes = 1) : name_(name), width_(width), + row_width_((width * bits_per_pixel + 7) / 8), height_(height), + bits_per_pixel_(bits_per_pixel), + planes_(planes), init_sequence_(init_sequence), init_sequence_length_(init_sequence_length), - display_type_(display_type) { - this->row_width_ = (this->width_ + 7) / 8; // width of a row in bytes - } + display_type_(display_type), + buffer_length_((size_t) this->row_width_ * height * planes) {} void set_dc_pin(GPIOPin *dc_pin) { dc_pin_ = dc_pin; } float get_setup_priority() const override; void set_reset_pin(GPIOPin *reset) { this->reset_pin_ = reset; } @@ -134,8 +137,8 @@ class EPaperBase : public Display, bool rotate_coordinates_(int &x, int &y); void draw_rgb565_block_(uint8_t *buffer, int x_start, int y_start, int w, int h, const uint8_t *ptr, ColorOrder order, bool big_endian, int x_offset, int y_offset, int x_pad); - // Whether the buffer holds 1 bit per pixel as draw_pixel_at() writes it, so draw_pixels_at() can write it directly - virtual bool buffer_is_mono() const { return false; } + // Whether the buffer is a single plane of 1 bit per pixel, as draw_pixel_at() writes it + bool buffer_is_mono_() const { return this->bits_per_pixel_ == 1 && this->planes_ == 1; } /** * Methods that must be implemented by concrete classes to control the display @@ -173,13 +176,15 @@ class EPaperBase : public Display, // properties initialised in the constructor const char *name_; uint16_t width_; - uint16_t row_width_; // width of a row in bytes + uint16_t row_width_; // width of a row of one plane in bytes uint16_t height_; + uint8_t bits_per_pixel_; + uint8_t planes_; // the buffer holds this many planes of row_width_ * height_ bytes, one after another const uint8_t *init_sequence_; size_t init_sequence_length_; DisplayType display_type_; - size_t buffer_length_{}; + size_t buffer_length_; size_t current_data_index_{}; // used by data transfer to track progress split_buffer::SplitBuffer buffer_{}; GPIOPin *dc_pin_{}; diff --git a/esphome/components/epaper_spi/epaper_spi_4bpp.h b/esphome/components/epaper_spi/epaper_spi_4bpp.h index 6eebe24c91f..3843752f858 100644 --- a/esphome/components/epaper_spi/epaper_spi_4bpp.h +++ b/esphome/components/epaper_spi/epaper_spi_4bpp.h @@ -15,9 +15,7 @@ class EPaper4bpp : public EPaperBase { public: EPaper4bpp(const char *name, uint16_t width, uint16_t height, const uint8_t *init_sequence, size_t init_sequence_length) - : EPaperBase(name, width, height, init_sequence, init_sequence_length, DISPLAY_TYPE_COLOR) { - this->buffer_length_ = width * height / 2; // 2 pixels per byte - } + : EPaperBase(name, width, height, init_sequence, init_sequence_length, DISPLAY_TYPE_COLOR, 4) {} void fill(Color color) override; void clear() override; diff --git a/esphome/components/epaper_spi/epaper_spi_inkplate2.h b/esphome/components/epaper_spi/epaper_spi_inkplate2.h index 657eca47590..09926db4ad8 100644 --- a/esphome/components/epaper_spi/epaper_spi_inkplate2.h +++ b/esphome/components/epaper_spi/epaper_spi_inkplate2.h @@ -9,10 +9,7 @@ class EPaperInkplate2 final : public EPaperBase { public: EPaperInkplate2(const char *name, uint16_t width, uint16_t height, const uint8_t *init_sequence, size_t init_sequence_length) - : EPaperBase(name, width, height, init_sequence, init_sequence_length, DISPLAY_TYPE_COLOR) { - // Dual-plane buffer: black/white plane followed by red plane, 1 bit per pixel each. - this->buffer_length_ = this->row_width_ * this->height_ * 2; - } + : EPaperBase(name, width, height, init_sequence, init_sequence_length, DISPLAY_TYPE_COLOR, 1, 2) {} void fill(Color color) override; void clear() override; diff --git a/esphome/components/epaper_spi/epaper_spi_jd79660.h b/esphome/components/epaper_spi/epaper_spi_jd79660.h index 4e488fe93ec..522950dc77b 100644 --- a/esphome/components/epaper_spi/epaper_spi_jd79660.h +++ b/esphome/components/epaper_spi/epaper_spi_jd79660.h @@ -27,12 +27,9 @@ class EPaperJD79660 final : public EPaperBase { public: EPaperJD79660(const char *name, uint16_t width, uint16_t height, const uint8_t *init_sequence, size_t init_sequence_length, const uint8_t *fast_update, uint16_t fast_update_length) - : EPaperBase(name, width, height, init_sequence, init_sequence_length, DISPLAY_TYPE_COLOR), + : EPaperBase(name, width, height, init_sequence, init_sequence_length, DISPLAY_TYPE_COLOR, 2), fast_update_(fast_update), - fast_update_length_(fast_update_length) { - this->row_width_ = (width + 3) / 4; // Fix base class calc (2bpp instead of 1bpp) - this->buffer_length_ = this->row_width_ * height; - } + fast_update_length_(fast_update_length) {} void fill(Color color) override; diff --git a/esphome/components/epaper_spi/epaper_spi_mono.h b/esphome/components/epaper_spi/epaper_spi_mono.h index 0f570149de4..4dc54ae60ba 100644 --- a/esphome/components/epaper_spi/epaper_spi_mono.h +++ b/esphome/components/epaper_spi/epaper_spi_mono.h @@ -9,13 +9,10 @@ namespace esphome::epaper_spi { class EPaperMono : public EPaperBase { public: EPaperMono(const char *name, uint16_t width, uint16_t height, const uint8_t *init_sequence, - size_t init_sequence_length, DisplayType display_type = DISPLAY_TYPE_BINARY) - : EPaperBase(name, width, height, init_sequence, init_sequence_length, display_type) { - this->buffer_length_ = (width + 7) / 8 * height; // 8 pixels per byte, rounded up - } + size_t init_sequence_length, DisplayType display_type = DISPLAY_TYPE_BINARY, uint8_t bits_per_pixel = 1) + : EPaperBase(name, width, height, init_sequence, init_sequence_length, display_type, bits_per_pixel) {} protected: - bool buffer_is_mono() const override { return true; } void refresh_screen(bool partial) override; void power_on() override {} void power_off() override{}; diff --git a/esphome/components/epaper_spi/epaper_spi_ssd1677.cpp b/esphome/components/epaper_spi/epaper_spi_ssd1677.cpp index a79bbc3dc91..ffc5d65a1ed 100644 --- a/esphome/components/epaper_spi/epaper_spi_ssd1677.cpp +++ b/esphome/components/epaper_spi/epaper_spi_ssd1677.cpp @@ -58,9 +58,9 @@ bool HOT EPaperSSD1677::transfer_data() { // A full update ignores 0x26, and the copy may not hold a real frame yet (first update after // boot): send the new frame to both planes. const bool send_copy = this->plane_ == 0 && this->update_count_ != 0; - // A mono buffer already holds the frame as a RAM plane does, so it can be sent as it is; a deeper - // buffer (the four-level model) returns false and packs the plane in plane_row(). - const bool direct = send_copy || this->buffer_is_mono(); + // A 1 bit per pixel buffer already holds the frame as a RAM plane does, so it can be sent as it is; a + // deeper buffer (the four-level model, 2 bits per pixel) packs the plane in plane_row() instead. + const bool direct = send_copy || this->buffer_is_mono_(); const auto &source = send_copy ? this->sent_ : this->buffer_; const size_t row_length = this->plane_row_length_(); const size_t plane_length = row_length * this->height_; diff --git a/esphome/components/epaper_spi/epaper_spi_ssd1677.h b/esphome/components/epaper_spi/epaper_spi_ssd1677.h index b8c93a1ecb8..d8481b712c7 100644 --- a/esphome/components/epaper_spi/epaper_spi_ssd1677.h +++ b/esphome/components/epaper_spi/epaper_spi_ssd1677.h @@ -23,8 +23,8 @@ namespace esphome::epaper_spi { class EPaperSSD1677 : public EPaperMono { public: EPaperSSD1677(const char *name, uint16_t width, uint16_t height, const uint8_t *init_sequence, - size_t init_sequence_length, DisplayType display_type = DISPLAY_TYPE_BINARY) - : EPaperMono(name, width, height, init_sequence, init_sequence_length, display_type) {} + size_t init_sequence_length, DisplayType display_type = DISPLAY_TYPE_BINARY, uint8_t bits_per_pixel = 1) + : EPaperMono(name, width, height, init_sequence, init_sequence_length, display_type, bits_per_pixel) {} void setup() override; diff --git a/esphome/components/epaper_spi/epaper_spi_ssd1677_gray4.h b/esphome/components/epaper_spi/epaper_spi_ssd1677_gray4.h index f21ab3eb069..e44fda37c2a 100644 --- a/esphome/components/epaper_spi/epaper_spi_ssd1677_gray4.h +++ b/esphome/components/epaper_spi/epaper_spi_ssd1677_gray4.h @@ -29,16 +29,12 @@ class EPaperSSD1677Gray4 : public EPaperSSD1677 { public: EPaperSSD1677Gray4(const char *name, uint16_t width, uint16_t height, const uint8_t *init_sequence, size_t init_sequence_length) - : EPaperSSD1677(name, width, height, init_sequence, init_sequence_length, DISPLAY_TYPE_GRAYSCALE) { - this->row_width_ = (width + 3) / 4; // 4 pixels per byte - this->buffer_length_ = (size_t) this->row_width_ * height; - } + : EPaperSSD1677(name, width, height, init_sequence, init_sequence_length, DISPLAY_TYPE_GRAYSCALE, 2) {} void fill(Color color) override; void draw_pixel_at(int x, int y, Color color) override; protected: - bool buffer_is_mono() const override { return false; } // A partial update if partial updates are enabled and this is not a full one. bool is_partial_push_() const { return this->update_count_ != 0 && this->sent_.is_valid(); } void plane_row(size_t y, uint8_t *out) override; diff --git a/esphome/components/epaper_spi/epaper_spi_t133a01.h b/esphome/components/epaper_spi/epaper_spi_t133a01.h index 5d356f90b01..7c14876bafd 100644 --- a/esphome/components/epaper_spi/epaper_spi_t133a01.h +++ b/esphome/components/epaper_spi/epaper_spi_t133a01.h @@ -22,9 +22,7 @@ class EPaperT133A01 : public EPaperBase { public: EPaperT133A01(const char *name, uint16_t width, uint16_t height, const uint8_t *init_sequence, size_t init_sequence_length) - : EPaperBase(name, width, height, init_sequence, init_sequence_length, DISPLAY_TYPE_COLOR) { - this->buffer_length_ = (size_t) width * height / 2; // 2 pixels per byte at 4bpp - } + : EPaperBase(name, width, height, init_sequence, init_sequence_length, DISPLAY_TYPE_COLOR, 4) {} void set_cs_pins(GPIOPin *cs, GPIOPin *cs1) { this->cs_pin_ = cs; diff --git a/esphome/components/epaper_spi/epaper_spi_uc8179.h b/esphome/components/epaper_spi/epaper_spi_uc8179.h index b12ed54ef6d..fda4297802b 100644 --- a/esphome/components/epaper_spi/epaper_spi_uc8179.h +++ b/esphome/components/epaper_spi/epaper_spi_uc8179.h @@ -32,12 +32,9 @@ class EPaperUC8179 final : public EPaperBase { public: EPaperUC8179(const char *name, uint16_t width, uint16_t height, const uint8_t *init_sequence, size_t init_sequence_length) - : EPaperBase(name, width, height, init_sequence, init_sequence_length, DISPLAY_TYPE_BINARY) { - this->buffer_length_ = this->row_width_ * height; - } + : EPaperBase(name, width, height, init_sequence, init_sequence_length, DISPLAY_TYPE_BINARY) {} protected: - bool buffer_is_mono() const override { return true; } bool initialise(bool partial) override; bool transfer_data() override; void refresh_screen(bool partial) override; diff --git a/esphome/components/epaper_spi/epaper_waveshare_bwr.h b/esphome/components/epaper_spi/epaper_waveshare_bwr.h index a090faa14d2..d34f05cbb2b 100644 --- a/esphome/components/epaper_spi/epaper_waveshare_bwr.h +++ b/esphome/components/epaper_spi/epaper_waveshare_bwr.h @@ -22,9 +22,7 @@ class EPaperWaveshareBWR : public EPaperBase { public: EPaperWaveshareBWR(const char *name, uint16_t width, uint16_t height, const uint8_t *init_sequence, size_t init_sequence_length) - : EPaperBase(name, width, height, init_sequence, init_sequence_length, DISPLAY_TYPE_BINARY) { - this->buffer_length_ = this->row_width_ * height * 2; - } + : EPaperBase(name, width, height, init_sequence, init_sequence_length, DISPLAY_TYPE_BINARY, 1, 2) {} void fill(Color color) override; diff --git a/esphome/components/epaper_spi/epaper_weact_3c.h b/esphome/components/epaper_spi/epaper_weact_3c.h index a31c2be8176..d3c89851962 100644 --- a/esphome/components/epaper_spi/epaper_weact_3c.h +++ b/esphome/components/epaper_spi/epaper_weact_3c.h @@ -18,9 +18,7 @@ class EPaperWeAct3C : public EPaperBase { public: EPaperWeAct3C(const char *name, uint16_t width, uint16_t height, const uint8_t *init_sequence, size_t init_sequence_length) - : EPaperBase(name, width, height, init_sequence, init_sequence_length, DISPLAY_TYPE_BINARY) { - this->buffer_length_ = this->row_width_ * height * 2; - } + : EPaperBase(name, width, height, init_sequence, init_sequence_length, DISPLAY_TYPE_BINARY, 1, 2) {} void fill(Color color) override; void clear() override; diff --git a/tests/components/epaper_spi/display/test_buffer_layout.cpp b/tests/components/epaper_spi/display/test_buffer_layout.cpp new file mode 100644 index 00000000000..34ffecc28e5 --- /dev/null +++ b/tests/components/epaper_spi/display/test_buffer_layout.cpp @@ -0,0 +1,49 @@ +#include + +#include "esphome/components/epaper_spi/epaper_spi_ssd1677_gray4.h" +#include "esphome/components/epaper_spi/epaper_spi_ssd1683.h" +#include "esphome/components/epaper_spi/epaper_spi_t133a01.h" +#include "esphome/components/epaper_spi/epaper_waveshare_bwr.h" +#include "esphome/components/epaper_spi/epaper_weact_3c.h" + +namespace esphome::epaper_spi::testing { +namespace { + +template class Layout : public Driver { + public: + Layout(uint16_t width, uint16_t height) : Driver("test", width, height, nullptr, 0) {} + uint16_t row_width() const { return this->row_width_; } + size_t buffer_length() const { return this->buffer_length_; } + bool is_mono() const { return this->buffer_is_mono_(); } +}; + +/// Each driver's bits per pixel and plane count give the row width and buffer size its drawing code expects, and only +/// a single 1 bit per pixel plane takes the direct drawing paths. +TEST(EPaperBufferLayout, SizesFollowBitsPerPixelAndPlanes) { + Layout mono(21, 3); + EXPECT_EQ(mono.row_width(), 3); + EXPECT_EQ(mono.buffer_length(), 9u); + EXPECT_TRUE(mono.is_mono()); + + Layout gray(21, 3); + EXPECT_EQ(gray.row_width(), 6); + EXPECT_EQ(gray.buffer_length(), 18u); + EXPECT_FALSE(gray.is_mono()); + + Layout t133(20, 3); + EXPECT_EQ(t133.row_width(), 10); + EXPECT_EQ(t133.buffer_length(), 30u); + EXPECT_FALSE(t133.is_mono()); + + Layout weact(21, 3); + EXPECT_EQ(weact.row_width(), 3); + EXPECT_EQ(weact.buffer_length(), 18u); + EXPECT_FALSE(weact.is_mono()); + + Layout bwr(21, 3); + EXPECT_EQ(bwr.buffer_length(), 18u); + EXPECT_FALSE(bwr.is_mono()); +} + +} // namespace +} // namespace esphome::epaper_spi::testing