From c2fe53c5fa2133019f07a95fa397acd2b6465485 Mon Sep 17 00:00:00 2001 From: Benjamin Gilbert Date: Mon, 4 Dec 2023 07:31:24 -0600 Subject: [PATCH] Fail if chroma subsampling selected when writing RGB JPEG The user presumably doesn't intend to subsample the green and blue channels. --- Tests/test_file_jpeg.py | 12 +++++++++--- docs/handbook/image-file-formats.rst | 3 +++ src/libImaging/JpegEncode.c | 10 ++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 748c07119ba..75851e4773e 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -447,9 +447,15 @@ def getsampling(im): im = self.roundtrip(hopper(), subsampling=subsampling) assert getsampling(im) == (2, 2, 1, 1, 1, 1) - # RGB colorspace, no subsampling by default - im = self.roundtrip(hopper(), keep_rgb=True) - assert getsampling(im) == (1, 1, 1, 1, 1, 1) + # RGB colorspace + for subsampling in (-1, 0, "4:4:4"): + # "4:4:4" doesn't really make sense for RGB, but the conversion + # to an integer happens at a higher level + im = self.roundtrip(hopper(), keep_rgb=True, subsampling=subsampling) + assert getsampling(im) == (1, 1, 1, 1, 1, 1) + for subsampling in (1, "4:2:2", 2, "4:2:0", 3): + with pytest.raises(OSError): + self.roundtrip(hopper(), keep_rgb=True, subsampling=subsampling) with pytest.raises(TypeError): self.roundtrip(hopper(), subsampling="1:1:1") diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 20310dedd35..2324c997d58 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -491,6 +491,9 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: If this option is present and true, those images will be stored as RGB instead. + When this option is enabled, attempting to chroma-subsample RGB images + with the ``subsampling`` option will result in an error. + .. versionadded:: 10.2.0 **subsampling** diff --git a/src/libImaging/JpegEncode.c b/src/libImaging/JpegEncode.c index 2946cc530a1..fc9966235c3 100644 --- a/src/libImaging/JpegEncode.c +++ b/src/libImaging/JpegEncode.c @@ -144,6 +144,16 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) { #ifdef JCS_EXTENSIONS case JCS_EXT_RGBX: #endif + switch (context->subsampling) { + case -1: /* Default */ + case 0: /* No subsampling */ + break; + default: + /* Would subsample the GB channels, which + doesn't make sense */ + state->errcode = IMAGING_CODEC_CONFIG; + return -1; + } jpeg_set_colorspace(&context->cinfo, JCS_RGB); break; default: