Added support for jpeg2000 comments and PLT marker segments

This commit is contained in:
Josh Ware 2023-01-19 11:37:14 +11:00
parent fcf5b7ef83
commit de43bc99c8
5 changed files with 84 additions and 0 deletions

View File

@ -1,5 +1,6 @@
import os
import re
import struct
from io import BytesIO
import pytest
@ -371,3 +372,46 @@ def test_crashes(test_file):
im.load()
except OSError:
pass
def test_custom_comment():
output_stream = BytesIO()
unique_comment = "This is a unique comment, which should be found below"
test_card.save(output_stream, "JPEG2000", comment=unique_comment)
output_stream.seek(0)
data = output_stream.read()
# Lazy method to determine if the comment is in the image generated
assert(bytes(unique_comment, "utf-8") in data)
def test_plt_marker():
# Search the start of the codesteam for the PLT box (id 0xFF58)
opj_version = re.search(r"(\d+\.\d+)\.\d+$", features.version_codec("jpg_2000"))
assert opj_version is not None
if float(opj_version[1]) >= 2.4:
out = BytesIO()
test_card.save(out, "JPEG2000", no_jp2=True, add_plt=True)
out.seek(0)
while True:
box_bytes = out.read(2)
if len(box_bytes) == 0:
# End of steam encounterd and no PLT or SOD
break
jp2_boxid = struct.unpack(">H", box_bytes)[0]
if jp2_boxid == 0xff4f:
# No length specifier for main header
continue
elif jp2_boxid == 0xff58:
# This is the PLT box we're looking for
return
elif jp2_boxid == 0xff93:
break
# SOD box encountered and no PLT, so it wasn't found
jp2_boxlength = struct.unpack(">H", out.read(2))[0]
out.seek(jp2_boxlength - 2, os.SEEK_CUR)
# The PLT box wasn't found
raise ValueError

View File

@ -589,6 +589,19 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
.. versionadded:: 9.1.0
**comment**
Adds a custom comment to the file, replacing the default
"Created by OpenJPEG version" comment.
.. versionadded:: 9.5.0
**add_plt**
If ``True`` then include a PLT (packet length, tile-part header) marker
segment in the produced file.
The default is to not include it.
.. versionadded:: 9.5.0
.. note::
To enable JPEG 2000 support, you need to build and install the OpenJPEG

View File

@ -328,6 +328,8 @@ def _save(im, fp, filename):
mct = info.get("mct", 0)
signed = info.get("signed", False)
fd = -1
comment = info.get("comment", None)
add_plt = info.get("add_plt", False)
if hasattr(fp, "fileno"):
try:
@ -350,6 +352,8 @@ def _save(im, fp, filename):
mct,
signed,
fd,
comment,
add_plt
)
ImageFile._save(im, fp, [("jpeg2k", (0, 0) + im.size, 0, kind)])

View File

@ -97,6 +97,12 @@ typedef struct {
/* PRIVATE CONTEXT (set by decoder) */
const char *error_msg;
/* Custom comment */
char * comment;
/* Include PLT marker segment */
int add_plt;
} JPEG2KENCODESTATE;
/*

View File

@ -439,6 +439,10 @@ j2k_encode_entry(Imaging im, ImagingCodecState state) {
params.tcp_mct = context->mct;
}
if (context->comment) {
params.cp_comment = context->comment;
}
params.prog_order = context->progression;
params.cp_cinema = context->cinema_mode;
@ -492,6 +496,14 @@ j2k_encode_entry(Imaging im, ImagingCodecState state) {
opj_set_warning_handler(codec, j2k_warn, context);
opj_setup_encoder(codec, &params, image);
/* Enabling PLT markers only supported in OpenJPEG 2.4.0 and up */
#if ((OPJ_VERSION_MAJOR == 2 && OPJ_VERSION_MINOR >= 4) || OPJ_VERSION_MAJOR > 2)
if (context->add_plt) {
const char * plt_option[2] = {"PLT=YES", NULL};
opj_encoder_set_extra_options(codec, plt_option);
}
#endif
/* Start encoding */
if (!opj_start_compress(codec, image, stream)) {
state->errcode = IMAGING_CODEC_BROKEN;
@ -624,7 +636,12 @@ ImagingJpeg2KEncodeCleanup(ImagingCodecState state) {
free((void *)context->error_msg);
}
if (context->comment) {
free((void *)context->comment);
}
context->error_msg = NULL;
context->comment = NULL;
return -1;
}