Skip to content

Panel_M5UnitLCD: fix ARGB image writes, reads after image writes and the first draw after reset - #355

Merged
lovyan03 merged 1 commit into
m5stack:developfrom
ainyan03:unitlcd_fixes
Oct 14, 2026
Merged

lovyan03 merged 1 commit into
m5stack:developfrom
ainyan03:unitlcd_fixes

Conversation

@ainyan03

Copy link
Copy Markdown
Contributor

What changes

writeImageARGB drew only one row. It set a one-row window and sent the first w pixels of src_data, ignoring h and the source position. pushAlphaImage() passes the whole clipped rectangle, so only the first row appeared, and with clipping on the left or top the wrong pixels were sent. Rows are now sent in a window of h rows, each pixel indexed through src_x32 / src_y32 / src_bitwidth as the pixel copy functions do.

bgra8888_t images came out in the wrong colors. pushAlphaImage() accepts both argb8888_t and bgra8888_t, and every source pixel was byte-swapped as if it were argb8888_t. Sources tagged argb8888_4Byte (bgra8888_t) are now sent without the swap; every other source, including the one-row buffer of the anti-aliased rotate/zoom path, is sent as before.

Image data lost right after init(). After CMD_RESET the unit answers READ_ID and consumes a NOP at once, but its first drawing command takes about 250 ms more, and pixel data streamed in that period is dropped. An image drawn right after init() lost its rows after the first dozen (both raw ARGB and RLE images); short fill commands were not affected. The reset path now queues a one-pixel fill and waits, with the same buffer-count wait readRect() uses, until the unit has consumed it. init() with reset takes about 250 ms longer; init_without_reset() is unchanged.

readRect() returned zeros right after an image write. When the read used the same column range as the cached window, _set_window() skipped CASET, and the unit then answered the read with zeros. Reading a full-width row right after a full-width pushImage() or pushAlphaImage() therefore gave all zeros. readRect() now always sends the window; this costs a few bytes per read.

Checked

  • Hardware: Unit LCD on a DinMeter (ESP32-S3) Port B, 400 kHz unless noted. Images are opaque gradients, read back with readPixel() unless noted.
Case Before After
pushAlphaImage (argb8888_t) 40x30 at (10,10) 1160 / 1200 pixels wrong (only row 0 drawn) 0 / 1200
pushAlphaImage 40x30 at (-10,-8) (clipped left and top) 660 / 660 wrong 0 / 660
pushAlphaImage (bgra8888_t) 40x30 1200 / 1200 wrong colors 0 / 1200
pushImage (RGB565) 40x30 drawn right after init() 713 / 1200 wrong (rows from 12 missing) 0 / 1200
full-width image (RGB565 or ARGB), then readRect() of full-width rows read as all zeros 0 / 32400 (135x240)
pushAlphaImage 135x240 in one call at 1 MHz — 0 / 32400
  • Builds with Arduino 2.0.x (ESP-IDF 4.4) / 3.x and ESP-IDF 5.1 to 6.1 across ESP32, S3, C3, C5, C6, C61, H2 and P4.

…the first draw after reset

writeImageARGB set a one-row window and sent the first w pixels of src_data, ignoring h
and the source position. pushAlphaImage passes the whole clipped rectangle, so only the
first row was drawn, and with clipping on the left or top the wrong pixels were sent.
Rows are now sent in a window of h rows, each indexed through src_x32 / src_y32 and
src_bitwidth as the pixel copy functions do. They are streamed as one raw command without
checking the unit's buffer in between, as the single-row version did: the unit consumes
raw pixel data faster than the bus delivers it (a full 135x240 ARGB frame sent at 1 MHz
arrives intact).

writeImageARGB also byte-swapped every source pixel as if it were argb8888_t, while
pushAlphaImage accepts bgra8888_t too, which keeps the bytes in the reverse order, so
its colors came out wrong. Sources tagged argb8888_4Byte (bgra8888_t) are now sent
without the swap; every other source, including the one-row buffer of the anti-aliased
rotate/zoom path, keeps the previous swapped send.

After CMD_RESET the unit answers READ_ID and consumes a NOP at once, but its first
drawing command takes about 250 ms more. Pixel data streamed in that period is dropped:
an image drawn right after init() lost its rows after the first dozen (both raw ARGB and
RLE), while short fill commands were unaffected. The reset path now queues a one-pixel
fill and waits until the unit has consumed it, the same buffer-count wait readRect uses.
init() with reset takes about 250 ms longer; init_without_reset() is unchanged.

After an image write, a read whose column range equals the cached one returned zeros:
_set_window() skipped CASET because the range was unchanged, and the unit then answers
the read with zeros. Reading a full-width row right after a full-width pushImage() or
pushAlphaImage() therefore gave all zeros. readRect() now invalidates the window cache so
CASET and RASET are always sent; this costs a few bytes per read.
@lovyan03
lovyan03 merged commit f89b565 into m5stack:develop Oct 14, 2026
25 checks passed
@ainyan03
ainyan03 deleted the unitlcd_fixes branch October 14, 2026 08:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants