src.nth.io/

summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorLuke Hoersten <[email protected]>2026-06-14 18:08:50 -0500
committerLuke Hoersten <[email protected]>2026-06-14 18:08:50 -0500
commit11ad249e4675cec22e924af83feaf0eba31d72e7 (patch)
tree8b27bd240e82fa07884a9b71ac56145cc8535229 /main
parentcbc4904aa35191f59aff93df5ed36f09f4cfa943 (diff)
M5 colors fixed: JPEG byte order + DPI memory layout
Two channel-swap bugs were stacked, hiding each other on symmetric pixels (white text on black background) and showing up only on saturated colour JPEGs: 1. jpeg_decoder: rgb_order was JPEG_DEC_RGB_ELEMENT_ORDER_RGB which empirically emits the RGB565 word in big-endian byte order (the IDF header comment "color component in big endian" is correct; the enum's "RGB" name is misleading). Our painter reads native LE uint16_t, so the bytes were always swapped. Flipped to _BGR which emits LE — verified by dumping decoded bytes for solid red/green/blue JPEGs. 2. display: rgb565_to_rgb888 wrote bytes [R, G, B], but the ESP32-P4 DSI engine + TC358762 + Pi panel expect [B, G, R] in memory despite the "RGB888" label. (MIPI DSI defines bit order, not byte position in memory.) Verified with an embedded solid-blue test JPEG: with [R, G, B] the panel showed solid red; with [B, G, R] it shows solid blue. Diagnosed by embedding a 480x800 blue test JPEG in firmware, decoding + dumping the buffer at boot, and reading the actual bytes over USB serial (ethernet was disconnected mid-investigation). The temporary self-test + debug dump are removed; the fixes themselves are tiny.
Diffstat (limited to 'main')
-rw-r--r--main/display.c12
-rw-r--r--main/jpeg_decoder.c11
2 files changed, 13 insertions, 10 deletions
diff --git a/main/display.c b/main/display.c
index c37ea52..13babdb 100644
--- a/main/display.c
+++ b/main/display.c
@@ -424,13 +424,17 @@ esp_err_t display_wake(void)
return err;
}
-// RGB565 → RGB888 in R,G,B byte order. TC358762's LCDCTRL = RGB888 path
-// expects channel-order matching the DSI 24bpp packing.
+// RGB565 → RGB888 in B,G,R byte order. The ESP32-P4 DSI engine + TC358762
+// bridge pipeline expects BGR on the wire for the panel's RGB channels —
+// writing R first paints pure blue as solid red on the panel (verified
+// with the M5 self-test: decoded buffer holds 0x001F, panel showed red).
+// Symmetric pixels (white text on black) don't expose this; saturated
+// channel inputs do.
static inline void rgb565_to_rgb888(uint16_t px, uint8_t *out)
{
- out[0] = (uint8_t)(((px >> 11) & 0x1F) * 255 / 31); // R
+ out[0] = (uint8_t)(( px & 0x1F) * 255 / 31); // B
out[1] = (uint8_t)(((px >> 5) & 0x3F) * 255 / 63); // G
- out[2] = (uint8_t)(( px & 0x1F) * 255 / 31); // B
+ out[2] = (uint8_t)(((px >> 11) & 0x1F) * 255 / 31); // R
}
esp_err_t display_fill(uint16_t rgb565)
diff --git a/main/jpeg_decoder.c b/main/jpeg_decoder.c
index 259af65..f2a7d92 100644
--- a/main/jpeg_decoder.c
+++ b/main/jpeg_decoder.c
@@ -70,12 +70,11 @@ esp_err_t jpeg_decoder_decode(size_t jpeg_len,
jpeg_decode_cfg_t dec_cfg = {
.output_format = JPEG_DECODE_OUT_FORMAT_RGB565,
- .rgb_order = JPEG_DEC_RGB_ELEMENT_ORDER_RGB,
- // TODO: known color bug on M5 — solid red renders correctly but solid
- // green renders ~black and solid blue renders green. Likely a JPEG
- // colorspace / channel-permutation issue, not a byte-order one
- // (swapping to BGR turns red into blue, confirming RGB is the right
- // element ordering). Needs follow-up.
+ // Empirically: _RGB emits the RGB565 word in big-endian byte order
+ // (red 0xF800 → bytes f8 00) which our LE uint16 painter reads
+ // swapped. _BGR emits little-endian to match (despite the
+ // misleading "BGR" name) — verified by the decode-byte dump.
+ .rgb_order = JPEG_DEC_RGB_ELEMENT_ORDER_BGR,
.conv_std = JPEG_YUV_RGB_CONV_STD_BT601,
};