Allow saving of zero quality JPEG images

This commit is contained in:
Andrew Murray 2020-02-21 22:05:44 +11:00
parent f87505cbd2
commit a8c0794107
5 changed files with 13 additions and 9 deletions

View File

@ -299,6 +299,10 @@ class TestFileJpeg(PillowTestCase):
assert_image(im1, im2.mode, im2.size) assert_image(im1, im2.mode, im2.size)
self.assertGreaterEqual(im1.bytes, im2.bytes) self.assertGreaterEqual(im1.bytes, im2.bytes)
im3 = self.roundtrip(hopper(), quality=0)
assert_image(im1, im3.mode, im3.size)
self.assertGreater(im2.bytes, im3.bytes)
def test_smooth(self): def test_smooth(self):
im1 = self.roundtrip(hopper()) im1 = self.roundtrip(hopper())
im2 = self.roundtrip(hopper(), smooth=100) im2 = self.roundtrip(hopper(), smooth=100)

View File

@ -302,7 +302,7 @@ The :py:meth:`~PIL.Image.Image.open` method may set the following
The :py:meth:`~PIL.Image.Image.save` method supports the following options: The :py:meth:`~PIL.Image.Image.save` method supports the following options:
**quality** **quality**
The image quality, on a scale from 1 (worst) to 95 (best). The default is The image quality, on a scale from 0 (worst) to 95 (best). The default is
75. Values above 95 should be avoided; 100 disables portions of the JPEG 75. Values above 95 should be avoided; 100 disables portions of the JPEG
compression algorithm, and results in large files with hardly any gain in compression algorithm, and results in large files with hardly any gain in
image quality. image quality.

View File

@ -616,17 +616,17 @@ 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", 0) quality = info.get("quality", -1)
subsampling = info.get("subsampling", -1) subsampling = info.get("subsampling", -1)
qtables = info.get("qtables") qtables = info.get("qtables")
if quality == "keep": if quality == "keep":
quality = 0 quality = -1
subsampling = "keep" subsampling = "keep"
qtables = "keep" qtables = "keep"
elif quality in presets: elif quality in presets:
preset = presets[quality] preset = presets[quality]
quality = 0 quality = -1
subsampling = preset.get("subsampling", -1) subsampling = preset.get("subsampling", -1)
qtables = preset.get("quantization") qtables = preset.get("quantization")
elif not isinstance(quality, int): elif not isinstance(quality, int):
@ -749,8 +749,8 @@ def _save(im, fp, filename):
# CMYK can be bigger # CMYK can be bigger
if im.mode == "CMYK": if im.mode == "CMYK":
bufsize = 4 * im.size[0] * im.size[1] bufsize = 4 * im.size[0] * im.size[1]
# keep sets quality to 0, but the actual value may be high. # keep sets quality to -1, but the actual value may be high.
elif quality >= 95 or quality == 0: elif quality >= 95 or quality == -1:
bufsize = 2 * im.size[0] * im.size[1] bufsize = 2 * im.size[0] * im.size[1]
else: else:
bufsize = im.size[0] * im.size[1] bufsize = im.size[0] * im.size[1]

View File

@ -67,7 +67,7 @@ typedef struct {
/* CONFIGURATION */ /* CONFIGURATION */
/* Quality (1-100, 0 means default) */ /* Quality (0-100, -1 means default) */
int quality; int quality;
/* Progressive mode */ /* Progressive mode */

View File

@ -153,7 +153,7 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
int i; int i;
int quality = 100; int quality = 100;
int last_q = 0; int last_q = 0;
if (context->quality > 0) { if (context->quality != -1) {
quality = context->quality; quality = context->quality;
} }
for (i = 0; i < context->qtablesLen; i++) { for (i = 0; i < context->qtablesLen; i++) {
@ -171,7 +171,7 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
for (i = last_q; i < context->cinfo.num_components; i++) { for (i = last_q; i < context->cinfo.num_components; i++) {
context->cinfo.comp_info[i].quant_tbl_no = last_q; context->cinfo.comp_info[i].quant_tbl_no = last_q;
} }
} else if (context->quality > 0) { } else if (context->quality != -1) {
jpeg_set_quality(&context->cinfo, context->quality, 1); jpeg_set_quality(&context->cinfo, context->quality, 1);
} }