Found while running tests/test_lvgl_jpeg_decode.py on the aggregator's CircuitPython 10.2.1 unix coverage build (2026-09-03). The LVGL path (our shim on CP's lib/tjpgd) matches jpegio's goldens on every frame; CP's built-in jpegio.JpegDecoder.decode(bitmap) does not, on odd widths.
Where: shared-module/jpegio/JpegDecoder.c in bitmap_output(), line 136 at 10.2.1:
int src_width = rect->right - rect->left + 1, src_pixel_stride = src_width /* in units of pixels! */, ...
.stride = src_pixel_stride / 2, /* in units of uint32_t */
For the right-edge MCU block of a 37-px-wide image the block is 5 px wide; 5 / 2 gives a stride of 2 words (4 px), so every row after the first in that block starts one pixel early. On odd_size_37x29.jpg 104 pixels differ, all in columns 32..36, rows 1..28. Even widths are unaffected (the last block is a whole number of words).
Workaround in this repo: the Bitmap witness in tests/test_lvgl_jpeg_decode.py (lines ~225-232) compares only left of the last block for odd widths and requires the difference to stay inside it; README notes it beside the test.
Upstream: this is a CircuitPython core bug, a one-line fix ((src_pixel_stride + 1) / 2, i.e. round up, since the source rows are already word-padded by TJpgDec's output). Candidate for the upstream-emissary when Brad wants a CP PR prepared; the fix would also need the same rounding wherever src_pixel_stride feeds common_hal_displayio_bitmap_blit. Not a blocker for jpegio Phase 2 (the LVGL path is correct).
Found while running
tests/test_lvgl_jpeg_decode.pyon the aggregator's CircuitPython 10.2.1 unix coverage build (2026-09-03). The LVGL path (our shim on CP'slib/tjpgd) matches jpegio's goldens on every frame; CP's built-injpegio.JpegDecoder.decode(bitmap)does not, on odd widths.Where:
shared-module/jpegio/JpegDecoder.cinbitmap_output(), line 136 at 10.2.1:For the right-edge MCU block of a 37-px-wide image the block is 5 px wide;
5 / 2gives a stride of 2 words (4 px), so every row after the first in that block starts one pixel early. Onodd_size_37x29.jpg104 pixels differ, all in columns 32..36, rows 1..28. Even widths are unaffected (the last block is a whole number of words).Workaround in this repo: the Bitmap witness in
tests/test_lvgl_jpeg_decode.py(lines ~225-232) compares only left of the last block for odd widths and requires the difference to stay inside it; README notes it beside the test.Upstream: this is a CircuitPython core bug, a one-line fix (
(src_pixel_stride + 1) / 2, i.e. round up, since the source rows are already word-padded by TJpgDec's output). Candidate for the upstream-emissary when Brad wants a CP PR prepared; the fix would also need the same rounding whereversrc_pixel_stridefeedscommon_hal_displayio_bitmap_blit. Not a blocker for jpegio Phase 2 (the LVGL path is correct).