From ce060a8e8bc7714220f5747d70de9b3ab55b34e6 Mon Sep 17 00:00:00 2001 From: scaramallion Date: Sun, 24 Mar 2024 15:40:11 +1100 Subject: [PATCH 1/5] Fix 9-bit JPEG 2000 images not using I;16 --- Tests/images/9bit.j2k | Bin 0 -> 9412 bytes Tests/test_file_jpeg2k.py | 6 ++++++ src/PIL/Jpeg2KImagePlugin.py | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 Tests/images/9bit.j2k diff --git a/Tests/images/9bit.j2k b/Tests/images/9bit.j2k new file mode 100644 index 0000000000000000000000000000000000000000..174f565fc64f2791a9b4aef3cc09240b402ddafe GIT binary patch literal 9412 zcmV;#Bs<&xPybN>DF6Tf004jh004jh0000000000004jh004jh00000000000SEyB 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 None: hdr = out.read(2) length = _binary.i16be(hdr) out.seek(length - 2, os.SEEK_CUR) + +def test_9bit(): + # Test 9-bit images are decoded to I;16 + with Image.open("Tests/images/9bit.j2k") as im: + assert im.mode == "I;16" + assert im.size == (128, 128) diff --git a/src/PIL/Jpeg2KImagePlugin.py b/src/PIL/Jpeg2KImagePlugin.py index 27f8fbbd0b5..925a9148cf3 100644 --- a/src/PIL/Jpeg2KImagePlugin.py +++ b/src/PIL/Jpeg2KImagePlugin.py @@ -114,7 +114,7 @@ def _parse_codestream(fp): size = (xsiz - xosiz, ysiz - yosiz) if csiz == 1: - if (yrsiz[0] & 0x7F) > 8: + if (yrsiz[0] & 0x7F) + 1 > 8: mode = "I;16" else: mode = "L" From 901c13c4b195f2e03a8779fe8d54e0e00551b579 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 24 Mar 2024 04:47:24 +0000 Subject: [PATCH 2/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- Tests/test_file_jpeg2k.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index 00eeb0cf9cc..ecdda104ad0 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -447,6 +447,7 @@ def test_plt_marker() -> None: length = _binary.i16be(hdr) out.seek(length - 2, os.SEEK_CUR) + def test_9bit(): # Test 9-bit images are decoded to I;16 with Image.open("Tests/images/9bit.j2k") as im: From 1f182ce8da03b375986c6bdf78f1337e3872bd0b Mon Sep 17 00:00:00 2001 From: scaramallion Date: Mon, 25 Mar 2024 17:39:21 +1100 Subject: [PATCH 3/5] Fix parsing the J2K header --- src/PIL/Jpeg2KImagePlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/Jpeg2KImagePlugin.py b/src/PIL/Jpeg2KImagePlugin.py index 925a9148cf3..d5734feac51 100644 --- a/src/PIL/Jpeg2KImagePlugin.py +++ b/src/PIL/Jpeg2KImagePlugin.py @@ -110,11 +110,11 @@ def _parse_codestream(fp): xrsiz = [None] * csiz yrsiz = [None] * csiz for i in range(csiz): - ssiz[i], xrsiz[i], yrsiz[i] = struct.unpack_from(">BBB", siz, 36 + 3 * i) + ssiz[i], xrsiz[i], yrsiz[i] = struct.unpack_from(">BBB", siz, 38 + 3 * i) size = (xsiz - xosiz, ysiz - yosiz) if csiz == 1: - if (yrsiz[0] & 0x7F) + 1 > 8: + if (ssiz[0] & 0x7F) + 1 > 8: mode = "I;16" else: mode = "L" From 0f49eaf23041e7b27c3a7b8d3b62433b2178fa08 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 25 Mar 2024 18:23:52 +1100 Subject: [PATCH 4/5] Simplified code --- src/PIL/Jpeg2KImagePlugin.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/PIL/Jpeg2KImagePlugin.py b/src/PIL/Jpeg2KImagePlugin.py index d5734feac51..be000c351b6 100644 --- a/src/PIL/Jpeg2KImagePlugin.py +++ b/src/PIL/Jpeg2KImagePlugin.py @@ -106,14 +106,10 @@ def _parse_codestream(fp): lsiz, rsiz, xsiz, ysiz, xosiz, yosiz, _, _, _, _, csiz = struct.unpack_from( ">HHIIIIIIIIH", siz ) - ssiz = [None] * csiz - xrsiz = [None] * csiz - yrsiz = [None] * csiz - for i in range(csiz): - ssiz[i], xrsiz[i], yrsiz[i] = struct.unpack_from(">BBB", siz, 38 + 3 * i) size = (xsiz - xosiz, ysiz - yosiz) if csiz == 1: + ssiz = struct.unpack_from(">B", siz, 38) if (ssiz[0] & 0x7F) + 1 > 8: mode = "I;16" else: From 7ca9bddf938537c4f4621e80c9212456b83be414 Mon Sep 17 00:00:00 2001 From: scaramallion Date: Mon, 25 Mar 2024 20:13:45 +1100 Subject: [PATCH 5/5] Update Tests/test_file_jpeg2k.py Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- Tests/test_file_jpeg2k.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index ecdda104ad0..81f75cc72cf 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -449,7 +449,6 @@ def test_plt_marker() -> None: def test_9bit(): - # Test 9-bit images are decoded to I;16 with Image.open("Tests/images/9bit.j2k") as im: assert im.mode == "I;16" assert im.size == (128, 128)