[display] Always treat 888 pixel blocks as b,g,r in memory (#20799)

This commit is contained in:
Clyde Stubbs
2026-10-15 20:49:50 +11:00
committed by GitHub
parent 7b08e738d3
commit d3faafc578
7 changed files with 21 additions and 37 deletions
+2 -1
View File
@@ -357,7 +357,8 @@ class Display : public PollingComponent {
* \param ptr A pointer to the start of the data to be copied
* \param order The ordering of the colors
* \param bitness Defines the number of bits and their format for each pixel
* \param big_endian True if 16 bit values are stored big-endian
* \param big_endian True if 16 bit values are stored big-endian. Ignored for 888, which is always b,g,r
* in memory for RGB order, as LVGL and ESPHome images store it.
* \param x_offset The initial x-offset into the source buffer.
* \param y_offset The initial y-offset into the source buffer.
* \param x_pad How many pixels are in each line after the end of the pixels to be copied.
@@ -23,9 +23,8 @@ class ColorUtil {
return (ptr[idx] << 8) + ptr[idx + 1];
return ptr[idx] + (ptr[idx + 1] << 8);
} else if constexpr (BITNESS == COLOR_BITNESS_888) {
// Always the LVGL RGB888 layout, b,g,r in memory for RGB order; byte order applies to 565 only
const size_t idx = index * 3;
if (big_endian)
return (ptr[idx + 0] << 16) + (ptr[idx + 1] << 8) + ptr[idx + 2];
return ptr[idx + 0] + (ptr[idx + 1] << 8) + (ptr[idx + 2] << 16);
} else {
return ptr[index];
+4 -2
View File
@@ -160,7 +160,8 @@ void HOT HUB75Display::draw_pixels_at(int x_start, int y_start, int w, int h, co
// 24-bit: 3 bytes per pixel, tightly packed
format = Hub75PixelFormat::RGB888;
bytes_per_pixel = 3;
// Note: 24-bit is always RGB order in LVGL
// ESPHome RGB order 888 is b,g,r in memory, which hub75 calls BGR
color_order = (order == ColorOrder::COLOR_ORDER_RGB) ? Hub75ColorOrder::BGR : Hub75ColorOrder::RGB;
#else
ESP_LOGE(TAG, "Unsupported LV_COLOR_DEPTH: %d", LV_COLOR_DEPTH);
return;
@@ -169,7 +170,8 @@ void HOT HUB75Display::draw_pixels_at(int x_start, int y_start, int w, int h, co
// Non-LVGL mode: standard 24-bit RGB888
format = Hub75PixelFormat::RGB888;
bytes_per_pixel = 3;
color_order = (order == ColorOrder::COLOR_ORDER_RGB) ? Hub75ColorOrder::RGB : Hub75ColorOrder::BGR;
// ESPHome RGB order 888 is b,g,r in memory, which hub75 calls BGR
color_order = (order == ColorOrder::COLOR_ORDER_RGB) ? Hub75ColorOrder::BGR : Hub75ColorOrder::RGB;
#endif
} else {
ESP_LOGE(TAG, "Unsupported bitness: %d", static_cast<int>(bitness));
+1 -4
View File
@@ -987,10 +987,7 @@ void HOT IT8951Display::draw_pixels_at(int x_start, int y_start, int w, int h, c
}
case COLOR_BITNESS_888: {
const size_t i = source_idx * 3;
color_value =
big_endian
? (static_cast<uint32_t>(ptr[i]) << 16) | (static_cast<uint32_t>(ptr[i + 1]) << 8) | ptr[i + 2]
: ptr[i] | (static_cast<uint32_t>(ptr[i + 1]) << 8) | (static_cast<uint32_t>(ptr[i + 2]) << 16);
color_value = ptr[i] | (static_cast<uint32_t>(ptr[i + 1]) << 8) | (static_cast<uint32_t>(ptr[i + 2]) << 16);
break;
}
default:
+9 -21
View File
@@ -297,15 +297,10 @@ void MipiDsi::draw_pixel_at(int x, int y, Color color) {
break;
}
case display::COLOR_BITNESS_888:
if (this->color_mode_ == display::COLOR_ORDER_BGR) {
this->buffer_[pos * 3] = color.b;
this->buffer_[pos * 3 + 1] = color.g;
this->buffer_[pos * 3 + 2] = color.r;
} else {
this->buffer_[pos * 3] = color.r;
this->buffer_[pos * 3 + 1] = color.g;
this->buffer_[pos * 3 + 2] = color.b;
}
// The panel applies the colour order, so the buffer keeps the standard b,g,r layout
this->buffer_[pos * 3] = color.b;
this->buffer_[pos * 3 + 1] = color.g;
this->buffer_[pos * 3 + 2] = color.r;
break;
case display::COLOR_BITNESS_332:
break;
@@ -341,19 +336,12 @@ void MipiDsi::fill(Color color) {
}
case display::COLOR_BITNESS_888:
if (this->color_mode_ == display::COLOR_ORDER_BGR) {
for (size_t i = 0; i != this->width_ * this->height_; i++) {
this->buffer_[i * 3 + 0] = color.b;
this->buffer_[i * 3 + 1] = color.g;
this->buffer_[i * 3 + 2] = color.r;
}
} else {
for (size_t i = 0; i != this->width_ * this->height_; i++) {
this->buffer_[i * 3 + 0] = color.r;
this->buffer_[i * 3 + 1] = color.g;
this->buffer_[i * 3 + 2] = color.b;
}
for (size_t i = 0; i != this->width_ * this->height_; i++) {
this->buffer_[i * 3 + 0] = color.b;
this->buffer_[i * 3 + 1] = color.g;
this->buffer_[i * 3 + 2] = color.r;
}
break;
default:
break;
-3
View File
@@ -39,8 +39,6 @@ class MipiDsi final : public display::Display {
public:
MipiDsi(size_t width, size_t height, display::ColorBitness color_depth, uint8_t pixel_mode)
: width_(width), height_(height), color_depth_(color_depth), pixel_mode_(pixel_mode) {}
display::ColorOrder get_color_mode() { return this->color_mode_; }
void set_color_mode(display::ColorOrder color_mode) { this->color_mode_ = color_mode; }
void set_invert_colors(bool invert_colors) { this->invert_colors_ = invert_colors; }
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; }
@@ -102,7 +100,6 @@ class MipiDsi final : public display::Display {
uint8_t lanes_{2}; // 1, 2, 3 or 4 lanes
bool invert_colors_{};
display::ColorOrder color_mode_{display::COLOR_ORDER_BGR};
display::ColorBitness color_depth_;
uint8_t pixel_mode_{};
@@ -62,8 +62,7 @@ static void expect_walks_match(ColorBitness bitness, int bytes_per_pixel, bool b
TEST(DrawPixelsAt, ColumnWalkMatchesRowWalk332) { expect_walks_match(COLOR_BITNESS_332, 1, true); }
TEST(DrawPixelsAt, ColumnWalkMatchesRowWalk565BigEndian) { expect_walks_match(COLOR_BITNESS_565, 2, true); }
TEST(DrawPixelsAt, ColumnWalkMatchesRowWalk565LittleEndian) { expect_walks_match(COLOR_BITNESS_565, 2, false); }
TEST(DrawPixelsAt, ColumnWalkMatchesRowWalk888BigEndian) { expect_walks_match(COLOR_BITNESS_888, 3, true); }
TEST(DrawPixelsAt, ColumnWalkMatchesRowWalk888LittleEndian) { expect_walks_match(COLOR_BITNESS_888, 3, false); }
TEST(DrawPixelsAt, ColumnWalkMatchesRowWalk888) { expect_walks_match(COLOR_BITNESS_888, 3, false); }
// One known pixel per bitness and byte order, so a decode that swapped bytes or channels
// could not pass the walk comparison above by being wrong the same way twice.
@@ -81,8 +80,9 @@ TEST(DrawPixelsAt, Golden565BigEndian) {
TEST(DrawPixelsAt, Golden565LittleEndian) {
expect_golden(COLOR_BITNESS_565, {0xE0, 0x07}, false, Color(0, 255, 0)); // 0x07E0: pure green
}
TEST(DrawPixelsAt, Golden888BigEndian) {
expect_golden(COLOR_BITNESS_888, {0x10, 0x20, 0x30}, true, Color(0x10, 0x20, 0x30));
// 888 is always b,g,r in memory for RGB order, whatever the byte order flag says
TEST(DrawPixelsAt, Golden888IgnoresBigEndian) {
expect_golden(COLOR_BITNESS_888, {0x10, 0x20, 0x30}, true, Color(0x30, 0x20, 0x10));
}
TEST(DrawPixelsAt, Golden888LittleEndian) {
expect_golden(COLOR_BITNESS_888, {0x10, 0x20, 0x30}, false, Color(0x30, 0x20, 0x10));