From de26f78eb13401bed2d87de44f34be49a778d0cf Mon Sep 17 00:00:00 2001 From: scaramallion Date: Tue, 11 May 2021 20:01:35 +1000 Subject: [PATCH 01/12] Add 'mct' and 'use_jp2' options for J2K saving --- Tests/test_file_jpeg2k.py | 31 ++++++++++++++++++++++++++++ docs/handbook/image-file-formats.rst | 15 +++++++++++--- src/PIL/Jpeg2KImagePlugin.py | 5 +++++ src/encode.c | 5 ++++- src/libImaging/Jpeg2K.h | 3 +++ src/libImaging/Jpeg2KEncode.c | 3 +++ 6 files changed, 58 insertions(+), 4 deletions(-) diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index b5ea6d0a026..1d224e1c82f 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -209,6 +209,37 @@ def test_layers(): assert_image_similar(im, test_card, 0.4) +def test_use_jp2(): + out = BytesIO() + test_card.save(out, "JPEG2000", use_jp2=False) + out.seek(0) + assert out.read(2) == b"\xff\x4f" + + +def test_mct(): + # Three component + for val in (0, 1): + out = BytesIO() + test_card.save(out, "JPEG2000", mct=val, use_jp2=False) + out.seek(0) + with Image.open(out) as im: + im.load() + assert_image_similar(im, test_card, 1.0e-3) + assert out.getvalue()[59] == val + + # Single component + for val in (0, 1): + out = BytesIO() + with Image.open("Tests/images/16bit.cropped.jp2") as jp2: + jp2.save(out, "JPEG2000", mct=val, use_jp2=False) + + out.seek(0) + with Image.open(out) as im: + im.load() + assert_image_similar(im, jp2, 1.0e-3) + assert out.getvalue()[53] == 0 + + def test_rgba(): # Arrange with Image.open("Tests/images/rgb_trns_ycbc.j2k") as j2k: diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index d1d3bb71725..3bb48115d6d 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -502,9 +502,14 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: and must be greater than the code-block size. **irreversible** - If ``True``, use the lossy Irreversible Color Transformation - followed by DWT 9-7. Defaults to ``False``, which means to use the - Reversible Color Transformation with DWT 5-3. + If ``True``, use the lossy discrete waveform transformation DWT 9-7. + Defaults to ``False``, which uses the lossless DWT 5-3. + +**mct** + If ``0`` then don't use multiple component transformation when encoding, + otherwise use ``1`` to apply to components 0, 1 and 2. MCT works best + with a ``mode`` of ``RGB`` and is only available for 3 component image + data. **progression** Controls the progression order; must be one of ``"LRCP"``, ``"RLCP"``, @@ -524,6 +529,10 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: for compliant 4K files, *at least one* of the dimensions must match 4096 x 2160. +**use_jp2** + If ``False`` then don't wrap the codestream in the JP2 file format when + saving. Defaults to ``True``. + .. note:: To enable JPEG 2000 support, you need to build and install the OpenJPEG diff --git a/src/PIL/Jpeg2KImagePlugin.py b/src/PIL/Jpeg2KImagePlugin.py index 4f4ee8f55b1..504043b479f 100644 --- a/src/PIL/Jpeg2KImagePlugin.py +++ b/src/PIL/Jpeg2KImagePlugin.py @@ -320,6 +320,10 @@ def _save(im, fp, filename): irreversible = info.get("irreversible", False) progression = info.get("progression", "LRCP") cinema_mode = info.get("cinema_mode", "no") + mct = info.get("mct", 0) + + kind = "jp2" if info.get("use_jp2", True) else "j2k" + fd = -1 if hasattr(fp, "fileno"): @@ -340,6 +344,7 @@ def _save(im, fp, filename): irreversible, progression, cinema_mode, + mct, fd, ) diff --git a/src/encode.c b/src/encode.c index a52d48f624a..c89a4179213 100644 --- a/src/encode.c +++ b/src/encode.c @@ -1187,11 +1187,12 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) { OPJ_PROG_ORDER prog_order; char *cinema_mode = "no"; OPJ_CINEMA_MODE cine_mode; + char *mct = 0; Py_ssize_t fd = -1; if (!PyArg_ParseTuple( args, - "ss|OOOsOnOOOssn", + "ss|OOOsOnOOOssbn", &mode, &format, &offset, @@ -1205,6 +1206,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) { &irreversible, &progression, &cinema_mode, + &mct, &fd)) { return NULL; } @@ -1302,6 +1304,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) { context->irreversible = PyObject_IsTrue(irreversible); context->progression = prog_order; context->cinema_mode = cine_mode; + context->mct = mct; return (PyObject *)encoder; } diff --git a/src/libImaging/Jpeg2K.h b/src/libImaging/Jpeg2K.h index f749ecfb21c..d030b0c439e 100644 --- a/src/libImaging/Jpeg2K.h +++ b/src/libImaging/Jpeg2K.h @@ -82,6 +82,9 @@ typedef struct { /* Compression style */ int irreversible; + /* Set multiple component transformation */ + char mct; + /* Progression order (LRCP/RLCP/RPCL/PCRL/CPRL) */ OPJ_PROG_ORDER progression; diff --git a/src/libImaging/Jpeg2KEncode.c b/src/libImaging/Jpeg2KEncode.c index 86cd7d5af26..fe5511ba5cb 100644 --- a/src/libImaging/Jpeg2KEncode.c +++ b/src/libImaging/Jpeg2KEncode.c @@ -435,6 +435,9 @@ j2k_encode_entry(Imaging im, ImagingCodecState state) { } params.irreversible = context->irreversible; + if (components == 3) { + params.tcp_mct = context->mct; + } params.prog_order = context->progression; From f8a74cbed17271cd0cce2ba8c66486a5f3973c24 Mon Sep 17 00:00:00 2001 From: scaramallion Date: Tue, 11 May 2021 20:45:48 +1000 Subject: [PATCH 02/12] Fix priority when selecting j2k kind --- Tests/test_file_jpeg2k.py | 12 ++++++++++++ src/PIL/Jpeg2KImagePlugin.py | 11 ++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index 1d224e1c82f..faaf8e35569 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -215,6 +215,18 @@ def test_use_jp2(): out.seek(0) assert out.read(2) == b"\xff\x4f" + out = BytesIO() + out.name = "foo.j2k" + test_card.save(out, "JPEG2000", use_jp2=False) + out.seek(0) + assert out.read(2) == b"\xff\x4f" + + out = BytesIO() + out.name = "foo.jp2" + test_card.save(out, "JPEG2000", use_jp2=False) + out.seek(4) + assert out.read(2) != b"jP" + def test_mct(): # Three component diff --git a/src/PIL/Jpeg2KImagePlugin.py b/src/PIL/Jpeg2KImagePlugin.py index 504043b479f..98e93fea55b 100644 --- a/src/PIL/Jpeg2KImagePlugin.py +++ b/src/PIL/Jpeg2KImagePlugin.py @@ -290,13 +290,13 @@ def _accept(prefix): def _save(im, fp, filename): + # Get the keyword arguments + info = im.encoderinfo + if filename.endswith(".j2k"): kind = "j2k" else: - kind = "jp2" - - # Get the keyword arguments - info = im.encoderinfo + kind = "jp2" if info.get("use_jp2", True) else "j2k" offset = info.get("offset", None) tile_offset = info.get("tile_offset", None) @@ -321,9 +321,6 @@ def _save(im, fp, filename): progression = info.get("progression", "LRCP") cinema_mode = info.get("cinema_mode", "no") mct = info.get("mct", 0) - - kind = "jp2" if info.get("use_jp2", True) else "j2k" - fd = -1 if hasattr(fp, "fileno"): From 4e7f04179586ae241fd4b0b71146d58d0043fbf1 Mon Sep 17 00:00:00 2001 From: scaramallion Date: Sun, 16 May 2021 10:46:43 +1000 Subject: [PATCH 03/12] Rename parameter and add more tests --- Tests/test_file_jpeg2k.py | 57 ++++++++++++++++++++++++---- docs/handbook/image-file-formats.rst | 15 ++++---- src/PIL/Jpeg2KImagePlugin.py | 4 +- 3 files changed, 59 insertions(+), 17 deletions(-) diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index faaf8e35569..474ca1f0058 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -209,41 +209,82 @@ def test_layers(): assert_image_similar(im, test_card, 0.4) -def test_use_jp2(): +def test_no_jp2(): out = BytesIO() - test_card.save(out, "JPEG2000", use_jp2=False) + out.name = "foo.j2k" + test_card.save(out, "JPEG2000") + out.seek(0) + assert out.read(2) == b"\xff\x4f" + + out = BytesIO() + out.name = "foo.jp2" + test_card.save(out, "JPEG2000") + out.seek(4) + assert out.read(2) == b"jP" + + out = BytesIO() + test_card.save(out, "JPEG2000", no_jp2=True) + out.seek(0) + assert out.read(2) == b"\xff\x4f" + + out = BytesIO() + test_card.save(out, "JPEG2000", no_jp2=True) out.seek(0) assert out.read(2) == b"\xff\x4f" out = BytesIO() out.name = "foo.j2k" - test_card.save(out, "JPEG2000", use_jp2=False) + test_card.save(out, "JPEG2000", no_jp2=True) out.seek(0) assert out.read(2) == b"\xff\x4f" out = BytesIO() out.name = "foo.jp2" - test_card.save(out, "JPEG2000", use_jp2=False) + test_card.save(out, "JPEG2000", no_jp2=True) + out.seek(0) + assert out.read(2) == b"\xff\x4f" + + # Use the filename extension to determine format + out = BytesIO() + out.name = "foo.j2k" + test_card.save(out, "JPEG2000", no_jp2=False) + out.seek(0) + assert out.read(2) == b"\xff\x4f" + + out = BytesIO() + out.name = "foo.jp2" + test_card.save(out, "JPEG2000", no_jp2=False) + out.seek(4) + assert out.read(2) == b"jP" + + # Default to JP2 if no filename + out = BytesIO() + test_card.save(out, "JPEG2000", no_jp2=False) + out.seek(4) + assert out.read(2) == b"jP" + + out = BytesIO() + test_card.save(out, "JPEG2000", no_jp2=False) out.seek(4) - assert out.read(2) != b"jP" + assert out.read(2) == b"jP" def test_mct(): # Three component for val in (0, 1): out = BytesIO() - test_card.save(out, "JPEG2000", mct=val, use_jp2=False) + test_card.save(out, "JPEG2000", mct=val, no_jp2=True) out.seek(0) with Image.open(out) as im: im.load() assert_image_similar(im, test_card, 1.0e-3) assert out.getvalue()[59] == val - # Single component + # Single component should have MCT disabled for val in (0, 1): out = BytesIO() with Image.open("Tests/images/16bit.cropped.jp2") as jp2: - jp2.save(out, "JPEG2000", mct=val, use_jp2=False) + jp2.save(out, "JPEG2000", mct=val, no_jp2=True) out.seek(0) with Image.open(out) as im: diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 3bb48115d6d..f76cc82b748 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -506,10 +506,10 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: Defaults to ``False``, which uses the lossless DWT 5-3. **mct** - If ``0`` then don't use multiple component transformation when encoding, - otherwise use ``1`` to apply to components 0, 1 and 2. MCT works best - with a ``mode`` of ``RGB`` and is only available for 3 component image - data. + If ``1`` then apply multiple component transformation when encoding, + otherwise use ``0`` for no component transformation (default). MCT works + best with a ``mode`` of ``RGB`` and is only applied when the image data has + 3 components. **progression** Controls the progression order; must be one of ``"LRCP"``, ``"RLCP"``, @@ -529,9 +529,10 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: for compliant 4K files, *at least one* of the dimensions must match 4096 x 2160. -**use_jp2** - If ``False`` then don't wrap the codestream in the JP2 file format when - saving. Defaults to ``True``. +**no_jp2** + If ``True`` then don't wrap the raw codestream in the JP2 file format when + saving, otherwise the extension of the filename will be used to determine + the format (default). .. note:: diff --git a/src/PIL/Jpeg2KImagePlugin.py b/src/PIL/Jpeg2KImagePlugin.py index 98e93fea55b..fb5d70cee06 100644 --- a/src/PIL/Jpeg2KImagePlugin.py +++ b/src/PIL/Jpeg2KImagePlugin.py @@ -293,10 +293,10 @@ def _save(im, fp, filename): # Get the keyword arguments info = im.encoderinfo - if filename.endswith(".j2k"): + if filename.endswith(".j2k") or info.get("no_jp2", False): kind = "j2k" else: - kind = "jp2" if info.get("use_jp2", True) else "j2k" + kind = "jp2" offset = info.get("offset", None) tile_offset = info.get("tile_offset", None) From 74d7319c6681344ee982579058933645fe8491f5 Mon Sep 17 00:00:00 2001 From: scaramallion Date: Sun, 16 May 2021 10:57:30 +1000 Subject: [PATCH 04/12] Reword the handbook entries to make explicit reference to RCT and ICT --- docs/handbook/image-file-formats.rst | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index f76cc82b748..782331bfd0c 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -506,10 +506,12 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: Defaults to ``False``, which uses the lossless DWT 5-3. **mct** - If ``1`` then apply multiple component transformation when encoding, - otherwise use ``0`` for no component transformation (default). MCT works - best with a ``mode`` of ``RGB`` and is only applied when the image data has - 3 components. + If ``1`` then enable multiple component transformation when encoding, + otherwise use ``0`` for no component transformation (default). If MCT is + enabled and ``irreversible`` is ``True`` then the Reversible Color + Transformation will be applied, otherwise encoding will use the + Irreversible Color Transformation. MCT works best with a ``mode`` of + ``RGB`` and is only applicable when the image data has 3 components. **progression** Controls the progression order; must be one of ``"LRCP"``, ``"RLCP"``, From da7e331573238f2706409c059840818d9d958d4a Mon Sep 17 00:00:00 2001 From: scaramallion Date: Sun, 16 May 2021 10:59:10 +1000 Subject: [PATCH 05/12] Fix typo in handbook entry --- docs/handbook/image-file-formats.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 782331bfd0c..bbb56be1be3 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -508,9 +508,9 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: **mct** If ``1`` then enable multiple component transformation when encoding, otherwise use ``0`` for no component transformation (default). If MCT is - enabled and ``irreversible`` is ``True`` then the Reversible Color + enabled and ``irreversible`` is ``True`` then the Irreversible Color Transformation will be applied, otherwise encoding will use the - Irreversible Color Transformation. MCT works best with a ``mode`` of + Reversible Color Transformation. MCT works best with a ``mode`` of ``RGB`` and is only applicable when the image data has 3 components. **progression** From 462e3fdaa5364f49985e679e3e1da963e7bec248 Mon Sep 17 00:00:00 2001 From: scaramallion Date: Sun, 16 May 2021 11:39:24 +1000 Subject: [PATCH 06/12] Remove duplicate test --- Tests/test_file_jpeg2k.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index 474ca1f0058..a0d0e485fe9 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -263,11 +263,6 @@ def test_no_jp2(): out.seek(4) assert out.read(2) == b"jP" - out = BytesIO() - test_card.save(out, "JPEG2000", no_jp2=False) - out.seek(4) - assert out.read(2) == b"jP" - def test_mct(): # Three component From b5a59d88607f654f138d44716ac5dc5bceb788ca Mon Sep 17 00:00:00 2001 From: scaramallion Date: Sun, 16 May 2021 12:26:24 +1000 Subject: [PATCH 07/12] Remove redundant im.load() in tests --- Tests/test_file_jpeg2k.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index a0d0e485fe9..aab94813942 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -271,7 +271,6 @@ def test_mct(): test_card.save(out, "JPEG2000", mct=val, no_jp2=True) out.seek(0) with Image.open(out) as im: - im.load() assert_image_similar(im, test_card, 1.0e-3) assert out.getvalue()[59] == val @@ -283,7 +282,6 @@ def test_mct(): out.seek(0) with Image.open(out) as im: - im.load() assert_image_similar(im, jp2, 1.0e-3) assert out.getvalue()[53] == 0 From bab4effbceb22b670d0ec06618bd646ee167fd81 Mon Sep 17 00:00:00 2001 From: scaramallion Date: Sun, 16 May 2021 15:23:51 +1000 Subject: [PATCH 08/12] Fix s390x build failure --- src/encode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/encode.c b/src/encode.c index c89a4179213..b72840a8ed2 100644 --- a/src/encode.c +++ b/src/encode.c @@ -1187,7 +1187,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) { OPJ_PROG_ORDER prog_order; char *cinema_mode = "no"; OPJ_CINEMA_MODE cine_mode; - char *mct = 0; + char mct; Py_ssize_t fd = -1; if (!PyArg_ParseTuple( From 07be0d26baa2bb7dc4aaf1e678575e0de97073a4 Mon Sep 17 00:00:00 2001 From: scaramallion Date: Mon, 17 May 2021 07:09:13 +1000 Subject: [PATCH 09/12] Re-add explicit default MCT value --- src/encode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/encode.c b/src/encode.c index b72840a8ed2..72c7f64d0a3 100644 --- a/src/encode.c +++ b/src/encode.c @@ -1187,7 +1187,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) { OPJ_PROG_ORDER prog_order; char *cinema_mode = "no"; OPJ_CINEMA_MODE cine_mode; - char mct; + char mct = 0; Py_ssize_t fd = -1; if (!PyArg_ParseTuple( From c3e0fd1a7a801994ee067d36fbf7617f8061b31e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 27 May 2021 18:42:07 +1000 Subject: [PATCH 10/12] Moved getvalue asserts outside of image context managers --- Tests/test_file_jpeg2k.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index aab94813942..2da25fb3307 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -269,10 +269,10 @@ def test_mct(): for val in (0, 1): out = BytesIO() test_card.save(out, "JPEG2000", mct=val, no_jp2=True) - out.seek(0) + + assert out.getvalue()[59] == val with Image.open(out) as im: assert_image_similar(im, test_card, 1.0e-3) - assert out.getvalue()[59] == val # Single component should have MCT disabled for val in (0, 1): @@ -280,10 +280,9 @@ def test_mct(): with Image.open("Tests/images/16bit.cropped.jp2") as jp2: jp2.save(out, "JPEG2000", mct=val, no_jp2=True) - out.seek(0) + assert out.getvalue()[53] == 0 with Image.open(out) as im: assert_image_similar(im, jp2, 1.0e-3) - assert out.getvalue()[53] == 0 def test_rgba(): From 698f52916e41c40495ca8c3e80d3c94ce659f756 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 27 May 2021 19:02:04 +1000 Subject: [PATCH 11/12] Parametrized test --- Tests/test_file_jpeg2k.py | 71 +++++++++++---------------------------- 1 file changed, 19 insertions(+), 52 deletions(-) diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index 2da25fb3307..677a149810f 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -209,59 +209,26 @@ def test_layers(): assert_image_similar(im, test_card, 0.4) -def test_no_jp2(): - out = BytesIO() - out.name = "foo.j2k" - test_card.save(out, "JPEG2000") - out.seek(0) - assert out.read(2) == b"\xff\x4f" - - out = BytesIO() - out.name = "foo.jp2" - test_card.save(out, "JPEG2000") - out.seek(4) - assert out.read(2) == b"jP" - - out = BytesIO() - test_card.save(out, "JPEG2000", no_jp2=True) - out.seek(0) - assert out.read(2) == b"\xff\x4f" - - out = BytesIO() - test_card.save(out, "JPEG2000", no_jp2=True) - out.seek(0) - assert out.read(2) == b"\xff\x4f" - - out = BytesIO() - out.name = "foo.j2k" - test_card.save(out, "JPEG2000", no_jp2=True) - out.seek(0) - assert out.read(2) == b"\xff\x4f" - - out = BytesIO() - out.name = "foo.jp2" - test_card.save(out, "JPEG2000", no_jp2=True) - out.seek(0) - assert out.read(2) == b"\xff\x4f" - - # Use the filename extension to determine format - out = BytesIO() - out.name = "foo.j2k" - test_card.save(out, "JPEG2000", no_jp2=False) - out.seek(0) - assert out.read(2) == b"\xff\x4f" - - out = BytesIO() - out.name = "foo.jp2" - test_card.save(out, "JPEG2000", no_jp2=False) - out.seek(4) - assert out.read(2) == b"jP" - - # Default to JP2 if no filename +@pytest.mark.parametrize( + "name, args, offset, data", + ( + ("foo.j2k", {}, 0, b"\xff\x4f"), + ("foo.jp2", {}, 4, b"jP"), + (None, {"no_jp2": True}, 0, b"\xff\x4f"), + ("foo.j2k", {"no_jp2": True}, 0, b"\xff\x4f"), + ("foo.jp2", {"no_jp2": True}, 0, b"\xff\x4f"), + ("foo.j2k", {"no_jp2": False}, 0, b"\xff\x4f"), + ("foo.jp2", {"no_jp2": False}, 4, b"jP"), + ("foo.jp2", {"no_jp2": False}, 4, b"jP"), + ), +) +def test_no_jp2(name, args, offset, data): out = BytesIO() - test_card.save(out, "JPEG2000", no_jp2=False) - out.seek(4) - assert out.read(2) == b"jP" + if name: + out.name = name + test_card.save(out, "JPEG2000", **args) + out.seek(offset) + assert out.read(2) == data def test_mct(): From e0de5b67ce79afec168627251ccce36ef0be0a0e Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 31 Mar 2022 22:19:00 +0300 Subject: [PATCH 12/12] Document mct and no_jp2 options for saving JPEG 2000 --- docs/handbook/image-file-formats.rst | 4 ++++ docs/releasenotes/9.1.0.rst | 24 ++++++++++++++++++------ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index bbb56be1be3..f051749ad1b 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -513,6 +513,8 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: Reversible Color Transformation. MCT works best with a ``mode`` of ``RGB`` and is only applicable when the image data has 3 components. + .. versionadded:: 9.1.0 + **progression** Controls the progression order; must be one of ``"LRCP"``, ``"RLCP"``, ``"RPCL"``, ``"PCRL"``, ``"CPRL"``. The letters stand for Component, @@ -536,6 +538,8 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: saving, otherwise the extension of the filename will be used to determine the format (default). + .. versionadded:: 9.1.0 + .. note:: To enable JPEG 2000 support, you need to build and install the OpenJPEG diff --git a/docs/releasenotes/9.1.0.rst b/docs/releasenotes/9.1.0.rst index 5a0bdba3ef7..00900e35631 100644 --- a/docs/releasenotes/9.1.0.rst +++ b/docs/releasenotes/9.1.0.rst @@ -146,12 +146,24 @@ At present, the information within each block is merely returned as a dictionary "data" entry. This will allow more useful information to be added in the future without breaking backwards compatibility. -Added rawmode argument to Image.getpalette() -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -By default, :py:meth:`~PIL.Image.Image.getpalette` returns RGB data from the palette. -A ``rawmode`` argument has been added, to allow the mode to be chosen instead. ``None`` -can be used to return data in the current mode of the palette. +Added mct and no_jp2 options for saving JPEG 2000 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The :py:meth:`PIL.Image.Image.save` method now supports the following options for +JPEG 2000: + +**mct** + If ``1`` then enable multiple component transformation when encoding, + otherwise use ``0`` for no component transformation (default). If MCT is + enabled and ``irreversible`` is ``True`` then the Irreversible Color + Transformation will be applied, otherwise encoding will use the + Reversible Color Transformation. MCT works best with a ``mode`` of + ``RGB`` and is only applicable when the image data has 3 components. + +**no_jp2** + If ``True`` then don't wrap the raw codestream in the JP2 file format when + saving, otherwise the extension of the filename will be used to determine + the format (default). Added PyEncoder ^^^^^^^^^^^^^^^