Fold the update flag into the flush, inline the rotation, write a buffered block straight out when not updating

This commit is contained in:
J. Nick Koston
2026-10-14 10:48:07 -10:00
parent 906a9b8db3
commit 2d6e3f42e6
7 changed files with 61 additions and 82 deletions
+10 -35
View File
@@ -183,7 +183,6 @@ void MipiDsi::update() {
if (this->auto_clear_enabled_) {
this->clear();
}
// Blocks drawn by the writer stay in the buffer until the one flush below
this->updating_ = true;
if (this->show_test_card_) {
this->test_card();
@@ -198,14 +197,9 @@ void MipiDsi::update() {
this->flush_dirty_();
}
// Outside update() nothing else would flush, inside it the end of update() does.
void MipiDsi::flush_now_() {
if (!this->updating_)
this->flush_dirty_();
}
void MipiDsi::flush_dirty_() {
if (this->buffer_ == nullptr || this->x_low_ > this->x_high_ || this->y_low_ > this->y_high_)
// Inside update() the one flush at its end sends everything
if (this->updating_ || this->buffer_ == nullptr || this->x_low_ > this->x_high_ || this->y_low_ > this->y_high_)
return;
ESP_LOGV(TAG, "x_low %d, y_low %d, x_high %d, y_high %d", this->x_low_, this->y_low_, this->x_high_, this->y_high_);
int w = this->x_high_ - this->x_low_ + 1;
@@ -223,9 +217,7 @@ void MipiDsi::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8
display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) {
if (w <= 0 || h <= 0)
return;
// Copy as is only when the bytes are laid out the way draw_pixel_at() writes them; an RGB
// ordered little endian 888 block is b,g,r in memory. Anything else, or a block drawn under a clipping
// region, goes per pixel.
// An RGB ordered little endian 888 block is b,g,r in memory, as the BGR colour mode writes it
const bool same_layout = bitness == display::COLOR_BITNESS_565
? big_endian == this->big_endian_
: big_endian == (this->color_mode_ == display::COLOR_ORDER_RGB);
@@ -234,12 +226,11 @@ void MipiDsi::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8
display::Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset,
x_pad);
this->mark_dirty_(x_start, y_start, w, h);
this->flush_now_();
this->flush_dirty_();
return;
}
if (this->buffer_ != nullptr) {
// With a buffer the block goes through it, or a flush of the old contents would paint over it
// Only the part on the panel
const int first_col = std::max(0, -x_start), end_col = std::min(w, static_cast<int>(this->width_) - x_start);
const int first_row = std::max(0, -y_start), end_row = std::min(h, static_cast<int>(this->height_) - y_start);
if (first_col >= end_col || first_row >= end_row)
@@ -251,8 +242,12 @@ void MipiDsi::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8
ptr + ((y_offset + y) * stride + x_offset + first_col) * bytes_per_pixel,
(end_col - first_col) * bytes_per_pixel);
}
this->mark_dirty_(x_start + first_col, y_start + first_row, end_col - first_col, end_row - first_row);
this->flush_now_();
if (this->updating_) {
this->mark_dirty_(x_start + first_col, y_start + first_row, end_col - first_col, end_row - first_row);
} else {
this->write_to_display_(x_start + first_col, y_start + first_row, end_col - first_col, end_row - first_row, ptr,
x_offset + first_col, y_offset + first_row, x_pad + w - end_col);
}
return;
}
this->write_to_display_(x_start, y_start, w, h, ptr, x_offset, y_offset, x_pad);
@@ -300,26 +295,6 @@ bool MipiDsi::check_buffer_() {
return true;
}
// Maps drawn coordinates to the buffer the way draw_pixel_at() stores them.
void MipiDsi::rotate_(int &x, int &y) const {
switch (this->rotation_) {
case display::DISPLAY_ROTATION_0_DEGREES:
break;
case display::DISPLAY_ROTATION_90_DEGREES:
std::swap(x, y);
x = this->width_ - x - 1;
break;
case display::DISPLAY_ROTATION_180_DEGREES:
x = this->width_ - x - 1;
y = this->height_ - y - 1;
break;
case display::DISPLAY_ROTATION_270_DEGREES:
std::swap(x, y);
y = this->height_ - y - 1;
break;
}
}
// Widen the dirty box to the drawn rectangle, even where the buffer already held the same values.
void MipiDsi::mark_dirty_(int x_start, int y_start, int w, int h) {
int x1 = x_start, y1 = y_start;
+22 -3
View File
@@ -7,6 +7,8 @@
#ifdef USE_ESP32_VARIANT_ESP32P4
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include <utility>
#include "esphome/core/application.h"
#include "esphome/core/log.h"
#include "esphome/core/gpio.h"
@@ -88,8 +90,25 @@ class MipiDsi final : public display::Display {
bool check_buffer_();
/// Send the region touched since the last flush to the panel.
void flush_dirty_();
void flush_now_();
void rotate_(int &x, int &y) const;
// Maps drawn coordinates to the buffer the way draw_pixel_at() stores them
void rotate_(int &x, int &y) const {
switch (this->rotation_) {
case display::DISPLAY_ROTATION_0_DEGREES:
break;
case display::DISPLAY_ROTATION_90_DEGREES:
std::swap(x, y);
x = this->width_ - x - 1;
break;
case display::DISPLAY_ROTATION_180_DEGREES:
x = this->width_ - x - 1;
y = this->height_ - y - 1;
break;
case display::DISPLAY_ROTATION_270_DEGREES:
std::swap(x, y);
y = this->height_ - y - 1;
break;
}
}
void mark_dirty_(int x_start, int y_start, int w, int h);
/// Pack a colour as the 565 value the panel's byte order expects in memory.
uint16_t to_panel_565_(Color color) const {
@@ -118,6 +137,7 @@ class MipiDsi final : public display::Display {
display::ColorOrder color_mode_{display::COLOR_ORDER_BGR};
display::ColorBitness color_depth_;
bool big_endian_{false};
bool updating_{false};
uint8_t pixel_mode_{};
esp_lcd_panel_handle_t handle_{};
@@ -125,7 +145,6 @@ class MipiDsi final : public display::Display {
esp_lcd_panel_io_handle_t io_handle_{};
SemaphoreHandle_t io_lock_{};
uint8_t *buffer_{nullptr};
bool updating_{false};
uint16_t x_low_{1};
uint16_t y_low_{1};
uint16_t x_high_{0};
+9 -34
View File
@@ -188,7 +188,6 @@ void MipiRgb::update() {
if (this->auto_clear_enabled_) {
this->clear();
}
// Blocks drawn by the writer stay in the buffer until the one flush below
this->updating_ = true;
if (this->show_test_card_) {
this->test_card();
@@ -203,14 +202,9 @@ void MipiRgb::update() {
this->flush_dirty_();
}
// Outside update() nothing else would flush, inside it the end of update() does.
void MipiRgb::flush_now_() {
if (!this->updating_)
this->flush_dirty_();
}
void MipiRgb::flush_dirty_() {
if (this->buffer_ == nullptr || this->x_low_ > this->x_high_ || this->y_low_ > this->y_high_)
// Inside update() the one flush at its end sends everything
if (this->updating_ || this->buffer_ == nullptr || this->x_low_ > this->x_high_ || this->y_low_ > this->y_high_)
return;
ESP_LOGV(TAG, "x_low %d, y_low %d, x_high %d, y_high %d", this->x_low_, this->y_low_, this->x_high_, this->y_high_);
int w = this->x_high_ - this->x_low_ + 1;
@@ -228,16 +222,13 @@ void MipiRgb::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8
display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) {
if (w <= 0 || h <= 0 || this->is_failed())
return;
// RGB565 in the configured byte order at rotation 0 copies as is; anything else, or a block drawn under a
// clipping region, goes per pixel
if (bitness != display::COLOR_BITNESS_565 || order != display::COLOR_ORDER_RGB || big_endian != this->big_endian_ ||
this->rotation_ != display::DISPLAY_ROTATION_0_DEGREES || this->is_clipping()) {
Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, x_pad);
this->mark_dirty_(x_start, y_start, w, h);
this->flush_now_();
this->flush_dirty_();
} else if (this->buffer_ != nullptr) {
// With a buffer the block goes through it, or a flush of the old contents would paint over it
// Only the part on the panel
const int first_col = std::max(0, -x_start), end_col = std::min(w, static_cast<int>(this->width_) - x_start);
const int first_row = std::max(0, -y_start), end_row = std::min(h, static_cast<int>(this->height_) - y_start);
if (first_col >= end_col || first_row >= end_row)
@@ -247,8 +238,12 @@ void MipiRgb::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8
memcpy(this->buffer_ + (y_start + y) * this->width_ + x_start + first_col,
ptr + ((y_offset + y) * stride + x_offset + first_col) * 2, (end_col - first_col) * 2);
}
this->mark_dirty_(x_start + first_col, y_start + first_row, end_col - first_col, end_row - first_row);
this->flush_now_();
if (this->updating_) {
this->mark_dirty_(x_start + first_col, y_start + first_row, end_col - first_col, end_row - first_row);
} else {
this->write_to_display_(x_start + first_col, y_start + first_row, end_col - first_col, end_row - first_row, ptr,
x_offset + first_col, y_offset + first_row, x_pad + w - end_col);
}
} else {
this->write_to_display_(x_start, y_start, w, h, ptr, x_offset, y_offset, x_pad);
}
@@ -291,26 +286,6 @@ bool MipiRgb::check_buffer_() {
return true;
}
// Maps drawn coordinates to the buffer the way draw_pixel_at() stores them.
void MipiRgb::rotate_(int &x, int &y) const {
switch (this->rotation_) {
case display::DISPLAY_ROTATION_0_DEGREES:
break;
case display::DISPLAY_ROTATION_90_DEGREES:
std::swap(x, y);
x = this->width_ - x - 1;
break;
case display::DISPLAY_ROTATION_180_DEGREES:
x = this->width_ - x - 1;
y = this->height_ - y - 1;
break;
case display::DISPLAY_ROTATION_270_DEGREES:
std::swap(x, y);
y = this->height_ - y - 1;
break;
}
}
// Widen the dirty box to the drawn rectangle, even where the buffer already held the same values.
void MipiRgb::mark_dirty_(int x_start, int y_start, int w, int h) {
int x1 = x_start, y1 = y_start;
+20 -3
View File
@@ -80,8 +80,25 @@ class MipiRgb : public display::Display {
}
/// Send the region touched since the last flush to the panel.
void flush_dirty_();
void flush_now_();
void rotate_(int &x, int &y) const;
// Maps drawn coordinates to the buffer the way draw_pixel_at() stores them
void rotate_(int &x, int &y) const {
switch (this->rotation_) {
case display::DISPLAY_ROTATION_0_DEGREES:
break;
case display::DISPLAY_ROTATION_90_DEGREES:
std::swap(x, y);
x = this->width_ - x - 1;
break;
case display::DISPLAY_ROTATION_180_DEGREES:
x = this->width_ - x - 1;
y = this->height_ - y - 1;
break;
case display::DISPLAY_ROTATION_270_DEGREES:
std::swap(x, y);
y = this->height_ - y - 1;
break;
}
}
void mark_dirty_(int x_start, int y_start, int w, int h);
void dump_pins_(uint8_t start, uint8_t end, const char *name, uint8_t offset);
void setup_enables_();
@@ -104,11 +121,11 @@ class MipiRgb : public display::Display {
bool invert_colors_{};
display::ColorOrder color_mode_{display::COLOR_ORDER_BGR};
bool big_endian_{true};
bool updating_{false};
size_t width_;
size_t height_;
uint16_t *buffer_{nullptr};
std::vector<GPIOPin *> enable_pins_{};
bool updating_{false};
uint16_t x_low_{1};
uint16_t y_low_{1};
uint16_t x_high_{0};
-3
View File
@@ -671,15 +671,12 @@ class MipiSpiBuffer
}
protected:
// Convert per pixel into the buffer
void draw_pixels_slow(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order,
display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset,
int x_pad) override {
display::Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset,
x_pad);
}
// Rotate the coordinates to match the display orientation.
// Convert a color to the buffer pixel format.
static BUFFERTYPE convert_color(const Color &color) {
if constexpr (BUFFERPIXEL == PIXEL_MODE_8) {
@@ -51,8 +51,6 @@ void RpiDpiRgb::draw_pixels_at(int x_start, int y_start, int w, int h, const uin
display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) {
if (w <= 0 || h <= 0)
return;
// Big endian RGB565 at rotation 0 copies as is; anything else, or a block drawn under a clipping region, goes
// per pixel
if (bitness != display::COLOR_BITNESS_565 || order != display::COLOR_ORDER_RGB || !big_endian ||
this->rotation_ != display::DISPLAY_ROTATION_0_DEGREES || this->is_clipping()) {
display::Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset,
-2
View File
@@ -54,8 +54,6 @@ void ST7701S::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8
display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) {
if (w <= 0 || h <= 0)
return;
// Big endian RGB565 at rotation 0 copies as is; anything else, or a block drawn under a clipping region, goes
// per pixel
if (bitness != display::COLOR_BITNESS_565 || order != display::COLOR_ORDER_RGB || !big_endian ||
this->rotation_ != display::DISPLAY_ROTATION_0_DEGREES || this->is_clipping()) {
display::Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset,