diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 6dab418bf..5afae0412 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -611,6 +611,24 @@ class TestFileJpeg: None ) ] + + for quality in range(101): + qtable_from_qtable_quality = self.roundtrip( + im, + qtables={0: standard_l_qtable, 1: standard_chrominance_qtable}, + quality=quality, + ).quantization + + qtable_from_quality = self.roundtrip(im, quality=quality).quantization + + if features.check_feature("libjpeg_turbo"): + assert qtable_from_qtable_quality == qtable_from_quality + else: + assert qtable_from_qtable_quality[0] == qtable_from_quality[0] + assert ( + qtable_from_qtable_quality[1][1:] == qtable_from_quality[1][1:] + ) + # list of qtable lists assert_image_similar( im, diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 83df2bd5c..03ee96c0f 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -557,6 +557,8 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: hardly any gain in image quality. The value ``keep`` is only valid for JPEG files and will retain the original image quality level, subsampling, and qtables. + For more information on how qtables are modified based on the quality parameter, + see the qtables section. **optimize** If present and true, indicates that the encoder should make an extra pass @@ -622,6 +624,11 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: range(len(keys))) of lists of 64 integers. There must be between 2 and 4 tables. + If a quality parameter is provided, the qtables will be adjusted accordingly. + By default, the qtables are based on a standard JPEG table with a quality of 50. + The qtable values will be reduced if the quality is higher than 50 and increased + if the quality is lower than 50. + .. versionadded:: 2.5.0 **streamtype** diff --git a/src/libImaging/JpegEncode.c b/src/libImaging/JpegEncode.c index 79a38e12f..972435ee1 100644 --- a/src/libImaging/JpegEncode.c +++ b/src/libImaging/JpegEncode.c @@ -175,18 +175,21 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) { /* Use custom quantization tables */ if (context->qtables) { int i; - int quality = 100; + int quality = 50; int last_q = 0; + boolean force_baseline = FALSE; if (context->quality != -1) { quality = context->quality; + force_baseline = TRUE; } + int scale_factor = jpeg_quality_scaling(quality); for (i = 0; i < context->qtablesLen; i++) { jpeg_add_quant_table( &context->cinfo, i, &context->qtables[i * DCTSIZE2], - quality, - FALSE + scale_factor, + force_baseline ); context->cinfo.comp_info[i].quant_tbl_no = i; last_q = i; @@ -195,7 +198,11 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) { // jpeg_set_defaults created two qtables internally, but we only // wanted one. jpeg_add_quant_table( - &context->cinfo, 1, &context->qtables[0], quality, FALSE + &context->cinfo, + 1, + &context->qtables[0], + scale_factor, + force_baseline ); } for (i = last_q; i < context->cinfo.num_components; i++) {