From 2bb498b96359c115477cedd15c81e6429699081c Mon Sep 17 00:00:00 2001 From: Michael Giacomelli Date: Mon, 28 Sep 2026 22:34:37 -0400 Subject: [PATCH] jpeg: fix decoding of grayscale images with 2x2 sampling A single-component (grayscale) scan is non-interleaved, so its MCU is one 8x8 block regardless of the sampling factors in the frame header (T.81 A.2.2). Some encoders write H=2,V=2 for the lone component, which sent both the imageviewer plugin decoder and the core loader down the 4:2:0 path: 6 blocks per 16x16 MCU, the image treated as colour, and the entropy data overrun. Force 1x1 sampling for single-component frames when parsing SOF0 so these images use the 4:4:4 layout with one block per MCU. Also add the missing else in fix_headers() in the core loader, matching the plugin. Tested on a Sansa e200 with both the plugin and the core loader, and the plugin in the e200 simulator and built for PC and for ARM (qemu). Fixes FS#13749. Co-Authored-By: Claude Opus 5.5 Change-Id: Iac2f925aab8cd602930470dca5da7dfb44e15961 --- apps/plugins/imageviewer/jpeg/jpeg_decoder.c | 7 +++++++ apps/recorder/jpeg_load.c | 9 ++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/apps/plugins/imageviewer/jpeg/jpeg_decoder.c b/apps/plugins/imageviewer/jpeg/jpeg_decoder.c index 9726639084..00acb94b95 100644 --- a/apps/plugins/imageviewer/jpeg/jpeg_decoder.c +++ b/apps/plugins/imageviewer/jpeg/jpeg_decoder.c @@ -538,6 +538,13 @@ int process_markers(unsigned char* p_src, long size, struct jpeg* p_jpeg) return -3; /* Unsupported SOF0 subsampling */ } p_jpeg->blocks = n; + /* A single-component scan is non-interleaved: the MCU is one + 8x8 block regardless of the sampling factors (T.81 A.2.2) */ + if (n == 1) + { + p_jpeg->frameheader[0].horizontal_sampling = 1; + p_jpeg->frameheader[0].vertical_sampling = 1; + } } break; diff --git a/apps/recorder/jpeg_load.c b/apps/recorder/jpeg_load.c index c85d9965b4..6cf0a9640e 100644 --- a/apps/recorder/jpeg_load.c +++ b/apps/recorder/jpeg_load.c @@ -1049,6 +1049,13 @@ static int process_markers(struct jpeg* p_jpeg) return -3; /* Unsupported SOF0 subsampling */ } p_jpeg->blocks = n; + /* A single-component scan is non-interleaved: the MCU is one + 8x8 block regardless of the sampling factors (T.81 A.2.2) */ + if (n == 1) + { + p_jpeg->frameheader[0].horizontal_sampling = 1; + p_jpeg->frameheader[0].vertical_sampling = 1; + } } break; @@ -1476,7 +1483,7 @@ INLINE void fix_headers(struct jpeg* p_jpeg) p_jpeg->subsample_y[1] = 1; p_jpeg->subsample_y[2] = 1; } - if (p_jpeg->frameheader[0].horizontal_sampling == 1 + else if (p_jpeg->frameheader[0].horizontal_sampling == 1 && p_jpeg->frameheader[0].vertical_sampling == 2) { /* 4:2:2 vertically subsampled */ p_jpeg->store_pos[1] = 2; /* block positions are mirrored */