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 <noreply@anthropic.com>
Change-Id: Iac2f925aab8cd602930470dca5da7dfb44e15961
This commit is contained in:
Michael Giacomelli 2026-09-28 22:34:37 -04:00
parent 89c38484ea
commit 2bb498b963
2 changed files with 15 additions and 1 deletions

View file

@ -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;

View file

@ -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 */