src: PIL: JpegImagePlugin.py: make -1 and keep subsamplings identical for save

Make the -1 subsampling behave like "keep" for the save method.

The doc claims that -1 is equivalent to keep. It is actually equivalent
to 4:2:0 for some reason.

Note that -1 was representing both the -1 subsampling and the absence of
it. To persist the current behavior which is compatible with multiple
input file formats other than JPEG (which is not the case of the
subsampling parameter), make the absence of subsampling parameter equal
to -2.

get_sampling() remains unchanged since it is a public API.
Note that subsampling can be -1 and -2 in some cases, both results in an
image with a subsampling of 4:2:0.

Also removing faulty test case since subsampling -1 cannot be used on a
generated image but only JPEG file format.

Signed-off-by: Quentin Schulz <foss@0leil.net>
This commit is contained in:
Quentin Schulz 2020-04-20 21:06:47 +02:00
parent a0d8e550ec
commit 9bbba06129
3 changed files with 6 additions and 7 deletions

View File

@ -314,8 +314,6 @@ class TestFileJpeg:
return layer[0][1:3] + layer[1][1:3] + layer[2][1:3] return layer[0][1:3] + layer[1][1:3] + layer[2][1:3]
# experimental API # experimental API
im = self.roundtrip(hopper(), subsampling=-1) # default
assert getsampling(im) == (2, 2, 1, 1, 1, 1)
im = self.roundtrip(hopper(), subsampling=0) # 4:4:4 im = self.roundtrip(hopper(), subsampling=0) # 4:4:4
assert getsampling(im) == (1, 1, 1, 1, 1, 1) assert getsampling(im) == (1, 1, 1, 1, 1, 1)
im = self.roundtrip(hopper(), subsampling=1) # 4:2:2 im = self.roundtrip(hopper(), subsampling=1) # 4:2:2

View File

@ -334,7 +334,8 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
If present, the image will be stored with the provided raw EXIF data. If present, the image will be stored with the provided raw EXIF data.
**subsampling** **subsampling**
If present, sets the subsampling for the encoder. If present, sets the subsampling for the encoder. Otherwise, ``4:2:0`` is
the default subsampling.
* ``keep``: Only valid for JPEG files, will retain the original image setting. * ``keep``: Only valid for JPEG files, will retain the original image setting.
* ``4:4:4``, ``4:2:2``, ``4:2:0``: Specific sampling values * ``4:4:4``, ``4:2:2``, ``4:2:0``: Specific sampling values

View File

@ -618,7 +618,7 @@ def _save(im, fp, filename):
dpi = [round(x) for x in info.get("dpi", (0, 0))] dpi = [round(x) for x in info.get("dpi", (0, 0))]
quality = info.get("quality", -1) quality = info.get("quality", -1)
subsampling = info.get("subsampling", -1) subsampling = info.get("subsampling", -2)
qtables = info.get("qtables") qtables = info.get("qtables")
if quality == "keep": if quality == "keep":
@ -628,13 +628,13 @@ def _save(im, fp, filename):
elif quality in presets: elif quality in presets:
preset = presets[quality] preset = presets[quality]
quality = -1 quality = -1
subsampling = preset.get("subsampling", -1) subsampling = preset.get("subsampling", -2)
qtables = preset.get("quantization") qtables = preset.get("quantization")
elif not isinstance(quality, int): elif not isinstance(quality, int):
raise ValueError("Invalid quality setting") raise ValueError("Invalid quality setting")
else: else:
if subsampling in presets: if subsampling in presets:
subsampling = presets[subsampling].get("subsampling", -1) subsampling = presets[subsampling].get("subsampling", -2)
if isinstance(qtables, str) and qtables in presets: if isinstance(qtables, str) and qtables in presets:
qtables = presets[qtables].get("quantization") qtables = presets[qtables].get("quantization")
@ -648,7 +648,7 @@ def _save(im, fp, filename):
# For compatibility. Before Pillow 4.3, 4:1:1 actually meant 4:2:0. # For compatibility. Before Pillow 4.3, 4:1:1 actually meant 4:2:0.
# Set 4:2:0 if someone is still using that value. # Set 4:2:0 if someone is still using that value.
subsampling = 2 subsampling = 2
elif subsampling == "keep": elif subsampling == "keep" or subsampling == -1:
if im.format != "JPEG": if im.format != "JPEG":
raise ValueError("Cannot use 'keep' when original image is not a JPEG") raise ValueError("Cannot use 'keep' when original image is not a JPEG")
subsampling = get_sampling(im) subsampling = get_sampling(im)