Skip to content

Commit

Permalink
Merge pull request #4918 from gofr/4825-jpeg-16-bit-qt
Browse files Browse the repository at this point in the history
  • Loading branch information
hugovk authored Oct 14, 2020
2 parents 04f1b38 + 938e251 commit 1cb3e2f
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 16 deletions.
Binary file added Tests/images/hopper_16bit_qtables.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions Tests/test_file_jpeg.py
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@ def _n_qtables_helper(n, test_file):
assert len(im.quantization) == n
reloaded = self.roundtrip(im, qtables="keep")
assert im.quantization == reloaded.quantization
assert reloaded.quantization[0].typecode == "B"

with Image.open("Tests/images/hopper.jpg") as im:
qtables = im.quantization
Expand Down Expand Up @@ -544,6 +545,30 @@ def _n_qtables_helper(n, test_file):
with pytest.raises(ValueError):
self.roundtrip(im, qtables=[[1, 2, 3, 4]])

def test_load_16bit_qtables(self):
with Image.open("Tests/images/hopper_16bit_qtables.jpg") as im:
assert len(im.quantization) == 2
assert len(im.quantization[0]) == 64
assert max(im.quantization[0]) > 255

def test_save_multiple_16bit_qtables(self):
with Image.open("Tests/images/hopper_16bit_qtables.jpg") as im:
im2 = self.roundtrip(im, qtables="keep")
assert im.quantization == im2.quantization

def test_save_single_16bit_qtable(self):
with Image.open("Tests/images/hopper_16bit_qtables.jpg") as im:
im2 = self.roundtrip(im, qtables={0: im.quantization[0]})
assert len(im2.quantization) == 1
assert im2.quantization[0] == im.quantization[0]

def test_save_low_quality_baseline_qtables(self):
with Image.open(TEST_FILE) as im:
im2 = self.roundtrip(im, quality=10)
assert len(im2.quantization) == 2
assert max(im2.quantization[0]) <= 255
assert max(im2.quantization[1]) <= 255

@pytest.mark.skipif(not djpeg_available(), reason="djpeg not available")
def test_load_djpeg(self):
with Image.open(TEST_FILE) as img:
Expand Down
25 changes: 13 additions & 12 deletions src/PIL/JpegImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import os
import struct
import subprocess
import sys
import tempfile
import warnings

Expand Down Expand Up @@ -234,25 +235,25 @@ def SOF(self, marker):

def DQT(self, marker):
#
# Define quantization table. Support baseline 8-bit tables
# only. Note that there might be more than one table in
# each marker.
# Define quantization table. Note that there might be more
# than one table in each marker.

# FIXME: The quantization tables can be used to estimate the
# compression quality.

n = i16(self.fp.read(2)) - 2
s = ImageFile._safe_read(self.fp, n)
while len(s):
if len(s) < 65:
raise SyntaxError("bad quantization table marker")
v = i8(s[0])
if v // 16 == 0:
self.quantization[v & 15] = array.array("B", s[1:65])
s = s[65:]
else:
return # FIXME: add code to read 16-bit tables!
# raise SyntaxError, "bad quantization table element size"
precision = 1 if (v // 16 == 0) else 2 # in bytes
qt_length = 1 + precision * 64
if len(s) < qt_length:
raise SyntaxError("bad quantization table marker")
data = array.array("B" if precision == 1 else "H", s[1:qt_length])
if sys.byteorder == "little" and precision > 1:
data.byteswap() # the values are always big-endian
self.quantization[v & 15] = data
s = s[qt_length:]


#
Expand Down Expand Up @@ -676,7 +677,7 @@ def validate_qtables(qtables):
try:
if len(table) != 64:
raise TypeError
table = array.array("B", table)
table = array.array("H", table)
except TypeError as e:
raise ValueError("Invalid quantization table") from e
else:
Expand Down
7 changes: 3 additions & 4 deletions src/libImaging/JpegEncode.c
Original file line number Diff line number Diff line change
Expand Up @@ -159,22 +159,21 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
quality = context->quality;
}
for (i = 0; i < context->qtablesLen; i++) {
// TODO: Should add support for none baseline
jpeg_add_quant_table(&context->cinfo, i, &context->qtables[i * DCTSIZE2],
quality, TRUE);
quality, FALSE);
context->cinfo.comp_info[i].quant_tbl_no = i;
last_q = i;
}
if (context->qtablesLen == 1) {
// jpeg_set_defaults created two qtables internally, but we only wanted one.
jpeg_add_quant_table(&context->cinfo, 1, &context->qtables[0],
quality, TRUE);
quality, FALSE);
}
for (i = last_q; i < context->cinfo.num_components; i++) {
context->cinfo.comp_info[i].quant_tbl_no = last_q;
}
} else if (context->quality != -1) {
jpeg_set_quality(&context->cinfo, context->quality, 1);
jpeg_set_quality(&context->cinfo, context->quality, TRUE);
}

/* Set subsampling options */
Expand Down

0 comments on commit 1cb3e2f

Please sign in to comment.