mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-11-04 09:57:43 +03:00 
			
		
		
		
	Added support for jpeg2000 comments and PLT marker segments
This commit is contained in:
		
							parent
							
								
									fcf5b7ef83
								
							
						
					
					
						commit
						de43bc99c8
					
				| 
						 | 
					@ -1,5 +1,6 @@
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
import re
 | 
					import re
 | 
				
			||||||
 | 
					import struct
 | 
				
			||||||
from io import BytesIO
 | 
					from io import BytesIO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pytest
 | 
					import pytest
 | 
				
			||||||
| 
						 | 
					@ -371,3 +372,46 @@ def test_crashes(test_file):
 | 
				
			||||||
                im.load()
 | 
					                im.load()
 | 
				
			||||||
            except OSError:
 | 
					            except OSError:
 | 
				
			||||||
                pass
 | 
					                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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -589,6 +589,19 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .. versionadded:: 9.1.0
 | 
					    .. 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::
 | 
					.. note::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   To enable JPEG 2000 support, you need to build and install the OpenJPEG
 | 
					   To enable JPEG 2000 support, you need to build and install the OpenJPEG
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -328,6 +328,8 @@ def _save(im, fp, filename):
 | 
				
			||||||
    mct = info.get("mct", 0)
 | 
					    mct = info.get("mct", 0)
 | 
				
			||||||
    signed = info.get("signed", False)
 | 
					    signed = info.get("signed", False)
 | 
				
			||||||
    fd = -1
 | 
					    fd = -1
 | 
				
			||||||
 | 
					    comment = info.get("comment", None)
 | 
				
			||||||
 | 
					    add_plt = info.get("add_plt", False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if hasattr(fp, "fileno"):
 | 
					    if hasattr(fp, "fileno"):
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
| 
						 | 
					@ -350,6 +352,8 @@ def _save(im, fp, filename):
 | 
				
			||||||
        mct,
 | 
					        mct,
 | 
				
			||||||
        signed,
 | 
					        signed,
 | 
				
			||||||
        fd,
 | 
					        fd,
 | 
				
			||||||
 | 
					        comment,
 | 
				
			||||||
 | 
					        add_plt
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ImageFile._save(im, fp, [("jpeg2k", (0, 0) + im.size, 0, kind)])
 | 
					    ImageFile._save(im, fp, [("jpeg2k", (0, 0) + im.size, 0, kind)])
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -97,6 +97,12 @@ typedef struct {
 | 
				
			||||||
    /* PRIVATE CONTEXT (set by decoder) */
 | 
					    /* PRIVATE CONTEXT (set by decoder) */
 | 
				
			||||||
    const char *error_msg;
 | 
					    const char *error_msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Custom comment */
 | 
				
			||||||
 | 
					    char * comment;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Include PLT marker segment */
 | 
				
			||||||
 | 
					    int add_plt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} JPEG2KENCODESTATE;
 | 
					} JPEG2KENCODESTATE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -439,6 +439,10 @@ j2k_encode_entry(Imaging im, ImagingCodecState state) {
 | 
				
			||||||
        params.tcp_mct = context->mct;
 | 
					        params.tcp_mct = context->mct;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (context->comment) {
 | 
				
			||||||
 | 
					        params.cp_comment = context->comment;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    params.prog_order = context->progression;
 | 
					    params.prog_order = context->progression;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    params.cp_cinema = context->cinema_mode;
 | 
					    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_set_warning_handler(codec, j2k_warn, context);
 | 
				
			||||||
    opj_setup_encoder(codec, ¶ms, image);
 | 
					    opj_setup_encoder(codec, ¶ms, 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 */
 | 
					    /* Start encoding */
 | 
				
			||||||
    if (!opj_start_compress(codec, image, stream)) {
 | 
					    if (!opj_start_compress(codec, image, stream)) {
 | 
				
			||||||
        state->errcode = IMAGING_CODEC_BROKEN;
 | 
					        state->errcode = IMAGING_CODEC_BROKEN;
 | 
				
			||||||
| 
						 | 
					@ -624,7 +636,12 @@ ImagingJpeg2KEncodeCleanup(ImagingCodecState state) {
 | 
				
			||||||
        free((void *)context->error_msg);
 | 
					        free((void *)context->error_msg);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (context->comment) {
 | 
				
			||||||
 | 
					        free((void *)context->comment);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    context->error_msg = NULL;
 | 
					    context->error_msg = NULL;
 | 
				
			||||||
 | 
					    context->comment = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return -1;
 | 
					    return -1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user