From d6b8f0f66655c01baa2176aeb38d2907099ceb99 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Wed, 12 Mar 2014 16:25:59 +0000 Subject: [PATCH 01/20] Added a JPEG 2000 decoder based on OpenJPEG. --- PIL/ImageFile.py | 2 +- PIL/Jpeg2KImagePlugin.py | 189 +++++++++++++++ PIL/__init__.py | 1 + _imaging.c | 4 + decode.c | 76 +++++- libImaging/Imaging.h | 18 ++ libImaging/Incremental.c | 366 +++++++++++++++++++++++++++++ libImaging/Jpeg2K.h | 44 ++++ libImaging/Jpeg2KDecode.c | 475 ++++++++++++++++++++++++++++++++++++++ setup.py | 13 +- 10 files changed, 1185 insertions(+), 3 deletions(-) create mode 100644 PIL/Jpeg2KImagePlugin.py create mode 100644 libImaging/Incremental.c create mode 100644 libImaging/Jpeg2K.h create mode 100644 libImaging/Jpeg2KDecode.c diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index a63fe757e..501e16b00 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -205,7 +205,7 @@ class ImageFile(Image.Image): else: raise IndexError(ie) - if not s: # truncated jpeg + if not s and not d.handles_eof: # truncated jpeg self.tile = [] # JpegDecode needs to clean things up here either way diff --git a/PIL/Jpeg2KImagePlugin.py b/PIL/Jpeg2KImagePlugin.py new file mode 100644 index 000000000..9acc71c4e --- /dev/null +++ b/PIL/Jpeg2KImagePlugin.py @@ -0,0 +1,189 @@ +# +# The Python Imaging Library +# $Id$ +# +# JPEG2000 file handling +# +# History: +# 2014-03-12 ajh Created +# +# Copyright (c) 2014 Coriolis Systems Limited +# Copyright (c) 2014 Alastair Houghton +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.1" + +from PIL import Image, ImageFile, _binary +import struct +import os +import io + +def _parse_codestream(fp): + """Parse the JPEG 2000 codestream to extract the size and component + count from the SIZ marker segment, returning a PIL (size, mode) tuple.""" + + hdr = fp.read(2) + lsiz = struct.unpack('>H', hdr)[0] + siz = hdr + fp.read(lsiz - 2) + lsiz, rsiz, xsiz, ysiz, xosiz, yosiz, xtsiz, ytsiz, \ + xtosiz, ytosiz, csiz \ + = struct.unpack('>HHIIIIIIIIH', siz[:38]) + ssiz = [None]*csiz + xrsiz = [None]*csiz + yrsiz = [None]*csiz + for i in range(csiz): + ssiz[i], xrsiz[i], yrsiz[i] \ + = struct.unpack('>BBB', siz[36 + 3 * i:39 + 3 * i]) + + size = (xsiz - xosiz, ysiz - yosiz) + if csiz == 1: + mode = 'L' + elif csiz == 2: + mode = 'LA' + elif csiz == 3: + mode = 'RGB' + elif csiz == 4: + mode == 'RGBA' + else: + mode = None + + return (size, mode) + +def _parse_jp2_header(fp): + """Parse the JP2 header box to extract size, component count and + color space information, returning a PIL (size, mode) tuple.""" + + # Find the JP2 header box + header = None + while True: + lbox, tbox = struct.unpack('>I4s', fp.read(8)) + if lbox == 1: + lbox = struct.unpack('>Q', fp.read(8))[0] + hlen = 16 + else: + hlen = 8 + + if tbox == b'jp2h': + header = fp.read(lbox - hlen) + break + else: + fp.seek(lbox - hlen, os.SEEK_CUR) + + if header is None: + raise SyntaxError('could not find JP2 header') + + size = None + mode = None + + hio = io.BytesIO(header) + while True: + lbox, tbox = struct.unpack('>I4s', hio.read(8)) + if lbox == 1: + lbox = struct.unpack('>Q', hio.read(8))[0] + hlen = 16 + else: + hlen = 8 + + content = hio.read(lbox - hlen) + + if tbox == b'ihdr': + height, width, nc, bpc, c, unkc, ipr \ + = struct.unpack('>IIHBBBB', content) + size = (width, height) + if unkc: + if nc == 1: + mode = 'L' + elif nc == 2: + mode = 'LA' + elif nc == 3: + mode = 'RGB' + elif nc == 4: + mode = 'RGBA' + break + elif tbox == b'colr': + meth, prec, approx = struct.unpack('>BBB', content[:3]) + if meth == 1: + cs = struct.unpack('>I', content[3:7])[0] + if cs == 16: # sRGB + if nc == 3: + mode = 'RGB' + elif nc == 4: + mode = 'RGBA' + break + elif cs == 17: # grayscale + if nc == 1: + mode = 'L' + elif nc == 2: + mode = 'LA' + break + elif cs == 18: # sYCC + if nc == 3: + mode = 'RGB' + elif nc == 4: + mode == 'RGBA' + break + + return (size, mode) + +## +# Image plugin for JPEG2000 images. + +class Jpeg2KImageFile(ImageFile.ImageFile): + format = "JPEG2000" + format_description = "JPEG 2000 (ISO 15444)" + + def _open(self): + sig = self.fp.read(4) + if sig == b'\xff\x4f\xff\x51': + self.codec = "j2k" + print 'Reading size/mode' + try: + self.size, self.mode = _parse_codestream(self.fp) + except Exception as e: + print e + print '%r, %r' % (self.size, self.mode) + else: + sig = sig + self.fp.read(8) + + if sig == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a': + self.codec = "jp2" + self.size, self.mode = _parse_jp2_header(self.fp) + else: + raise SyntaxError('not a JPEG 2000 file') + + if self.size is None or self.mode is None: + raise SyntaxError('unable to determine size/mode') + + self.reduce = 0 + self.layers = 0 + + self.tile = [('jpeg2k', (0, 0) + self.size, 0, + (self.codec, self.reduce, self.layers))] + + def load(self): + if self.reduce: + power = 1 << self.reduce + self.size = (self.size[0] / power, self.size[1] / power) + + ImageFile.ImageFile.load(self) + +def _accept(prefix): + return (prefix[:4] == b'\xff\x4f\xff\x51' + or prefix[:12] == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a') + +# ------------------------------------------------------------ +# Registry stuff + +Image.register_open("JPEG2000", Jpeg2KImageFile, _accept) + +Image.register_extension("JPEG2000", ".jp2") +Image.register_extension("JPEG2000", ".j2k") +Image.register_extension("JPEG2000", ".jpc") +Image.register_extension("JPEG2000", ".jpf") +Image.register_extension("JPEG2000", ".jpx") +Image.register_extension("JPEG2000", ".j2c") + +Image.register_mime("JPEG2000", "image/jp2") +Image.register_mime("JPEG2000", "image/jpx") diff --git a/PIL/__init__.py b/PIL/__init__.py index 18bd42a5f..b83220097 100644 --- a/PIL/__init__.py +++ b/PIL/__init__.py @@ -33,6 +33,7 @@ _plugins = ['ArgImagePlugin', 'ImtImagePlugin', 'IptcImagePlugin', 'JpegImagePlugin', + 'Jpeg2KImagePlugin', 'McIdasImagePlugin', 'MicImagePlugin', 'MpegImagePlugin', diff --git a/_imaging.c b/_imaging.c index 078961da4..84b25219b 100644 --- a/_imaging.c +++ b/_imaging.c @@ -3283,6 +3283,7 @@ extern PyObject* PyImaging_FliDecoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_GifDecoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_HexDecoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_JpegDecoderNew(PyObject* self, PyObject* args); +extern PyObject* PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_TiffLzwDecoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_MspDecoderNew(PyObject* self, PyObject* args); @@ -3351,6 +3352,9 @@ static PyMethodDef functions[] = { #ifdef HAVE_LIBJPEG {"jpeg_decoder", (PyCFunction)PyImaging_JpegDecoderNew, 1}, {"jpeg_encoder", (PyCFunction)PyImaging_JpegEncoderNew, 1}, +#endif +#ifdef HAVE_OPENJPEG + {"jpeg2k_decoder", (PyCFunction)PyImaging_Jpeg2KDecoderNew, 1}, #endif {"tiff_lzw_decoder", (PyCFunction)PyImaging_TiffLzwDecoderNew, 1}, #ifdef HAVE_LIBTIFF diff --git a/decode.c b/decode.c index f3ac60e51..359b99695 100644 --- a/decode.c +++ b/decode.c @@ -52,6 +52,7 @@ typedef struct { struct ImagingCodecStateInstance state; Imaging im; PyObject* lock; + int handles_eof; } ImagingDecoderObject; static PyTypeObject ImagingDecoderType; @@ -194,6 +195,12 @@ _setimage(ImagingDecoderObject* decoder, PyObject* args) return Py_None; } +static PyObject * +_get_handles_eof(ImagingDecoderObject *decoder) +{ + return PyBool_FromLong(decoder->handles_eof); +} + static struct PyMethodDef methods[] = { {"decode", (PyCFunction)_decode, 1}, {"cleanup", (PyCFunction)_decode_cleanup, 1}, @@ -201,6 +208,13 @@ static struct PyMethodDef methods[] = { {NULL, NULL} /* sentinel */ }; +static struct PyGetSetDef getseters[] = { + {"handles_eof", (getter)_get_handles_eof, NULL, + "True if this decoder expects to handle EOF itself.", + NULL}, + {NULL, NULL, NULL, NULL, NULL} /* sentinel */ +}; + static PyTypeObject ImagingDecoderType = { PyVarObject_HEAD_INIT(NULL, 0) "ImagingDecoder", /*tp_name*/ @@ -232,7 +246,7 @@ static PyTypeObject ImagingDecoderType = { 0, /*tp_iternext*/ methods, /*tp_methods*/ 0, /*tp_members*/ - 0, /*tp_getset*/ + getseters, /*tp_getset*/ }; /* -------------------------------------------------------------------- */ @@ -762,3 +776,63 @@ PyImaging_JpegDecoderNew(PyObject* self, PyObject* args) return (PyObject*) decoder; } #endif + +/* -------------------------------------------------------------------- */ +/* JPEG2000 */ +/* -------------------------------------------------------------------- */ + +#ifdef HAVE_OPENJPEG + +/* We better define this decoder last in this file, so the following + undef's won't mess things up for the Imaging library proper. */ +#undef UINT8 +#undef UINT16 +#undef UINT32 +#undef INT8 +#undef INT16 +#undef INT32 + +#include "Jpeg2K.h" + +PyObject* +PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args) +{ + ImagingDecoderObject* decoder; + JPEG2KSTATE *context; + + char* mode; + char* format; + OPJ_CODEC_FORMAT codec_format; + int reduce = 0; + int layers = 0; + if (!PyArg_ParseTuple(args, "ss|ii", &mode, &format, + &reduce, &layers)) + return NULL; + + if (strcmp (format, "j2k") == 0) + codec_format = OPJ_CODEC_J2K; + else if (strcmp (format, "jpt") == 0) + codec_format = OPJ_CODEC_JPT; + else if (strcmp (format, "jp2") == 0) + codec_format = OPJ_CODEC_JP2; + else + return NULL; + + decoder = PyImaging_DecoderNew(sizeof(JPEG2KSTATE)); + if (decoder == NULL) + return NULL; + + decoder->handles_eof = 1; + decoder->decode = ImagingJpeg2KDecode; + decoder->cleanup = ImagingJpeg2KDecodeCleanup; + + context = (JPEG2KSTATE *)decoder->state.context; + + strncpy(context->mode, mode, 8); + context->format = codec_format; + context->reduce = reduce; + context->layers = layers; + + return (PyObject*) decoder; +} +#endif /* HAVE_OPENJPEG */ diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h index b45dcbe23..7a7eb9c66 100644 --- a/libImaging/Imaging.h +++ b/libImaging/Imaging.h @@ -424,6 +424,11 @@ extern int ImagingJpegDecodeCleanup(ImagingCodecState state); extern int ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); #endif +#ifdef HAVE_OPENJPEG +extern int ImagingJpeg2KDecode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +extern int ImagingJpeg2KDecodeCleanup(ImagingCodecState state); +#endif extern int ImagingLzwDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); #ifdef HAVE_LIBTIFF @@ -497,6 +502,19 @@ struct ImagingCodecStateInstance { void *context; }; +/* Incremental decoding support */ +typedef struct ImagingIncrementalDecoderStruct *ImagingIncrementalDecoder; + +typedef int (*ImagingIncrementalDecoderEntry)(Imaging im, + ImagingCodecState state, + ImagingIncrementalDecoder decoder); + +extern ImagingIncrementalDecoder ImagingIncrementalDecoderCreate(ImagingIncrementalDecoderEntry decoder_entry, Imaging im, ImagingCodecState state); +extern void ImagingIncrementalDecoderDestroy(ImagingIncrementalDecoder decoder); +extern int ImagingIncrementalDecodeData(ImagingIncrementalDecoder decoder, UINT8 *buf, int bytes); +size_t ImagingIncrementalDecoderRead(ImagingIncrementalDecoder decoder, void *buffer, size_t bytes); +off_t ImagingIncrementalDecoderSkip(ImagingIncrementalDecoder decoder, off_t bytes); + /* Errcodes */ #define IMAGING_CODEC_END 1 #define IMAGING_CODEC_OVERRUN -1 diff --git a/libImaging/Incremental.c b/libImaging/Incremental.c new file mode 100644 index 000000000..0e81af7e8 --- /dev/null +++ b/libImaging/Incremental.c @@ -0,0 +1,366 @@ +/* + * The Python Imaging Library + * $Id$ + * + * incremental decoding adaptor. + * + * Copyright (c) 2014 Coriolis Systems Limited + * Copyright (c) 2014 Alastair Houghton + * + */ + +#include "Imaging.h" + +/* The idea behind this interface is simple: the actual decoding proceeds in + a thread, which is run in lock step with the main thread. Whenever the + ImagingIncrementalDecoderRead() call runs short on data, it suspends the + decoding thread and wakes the main thread. Conversely, the + ImagingIncrementalDecodeData() call suspends the main thread and wakes + the decoding thread, providing a buffer of data. + + The two threads are never running simultaneously, so there is no need for + any addition synchronisation measures outside of this file. + + Note also that we start the thread suspended (on Windows), or make it + immediately wait (other platforms), so that it's possible to initialise + things before the thread starts running. + + This interface is useful to allow PIL to interact efficiently with any + third-party imaging library that does not support suspendable reads; + one example is OpenJPEG (which is used for J2K support). The TIFF library + might also benefit from using this code. + + Note that if using this module, you want to set handles_eof on your + decoder to true. Why? Because otherwise ImageFile.load() will abort, + thinking that the image is truncated, whereas generally you want it to + pass the EOF condition (0 bytes to read) through to your code. */ + +#ifdef _WIN32 +#include +#include +#else +#include +#endif + +struct ImagingIncrementalStreamStruct { + UINT8 *buffer; + UINT8 *ptr; + UINT8 *end; +}; + +struct ImagingIncrementalDecoderStruct { +#ifdef _WIN32 + HANDLE hDecodeEvent; + HANDLE hDataEvent; + HANDLE hThread; +#else + pthread_mutex_t start_mutex; + pthread_cond_t start_cond; + pthread_mutex_t decode_mutex; + pthread_cond_t decode_cond; + pthread_mutex_t data_mutex; + pthread_cond_t data_cond; + pthread_t thread; +#endif + ImagingIncrementalDecoderEntry entry; + Imaging im; + ImagingCodecState state; + struct { + UINT8 *buffer; + UINT8 *ptr; + UINT8 *end; + } stream; + int started; + int result; +}; + +#if _WIN32 +static void __stdcall +incremental_thread(void *ptr) +{ + ImagingIncrementalDecoder decoder = (ImagingIncrementalDecoder)ptr; + + decoder->result = decoder->entry(decoder->im, decoder->state, decoder); + + SetEvent(decoder->hDecodeEvent); +} +#else +static void * +incremental_thread(void *ptr) +{ + ImagingIncrementalDecoder decoder = (ImagingIncrementalDecoder)ptr; + + decoder->result = decoder->entry(decoder->im, decoder->state, decoder); + + pthread_cond_signal(&decoder->decode_cond); + + return NULL; +} +#endif + +/** + * Create a new incremental decoder */ +ImagingIncrementalDecoder +ImagingIncrementalDecoderCreate(ImagingIncrementalDecoderEntry decoder_entry, + Imaging im, + ImagingCodecState state) +{ + ImagingIncrementalDecoder decoder = (ImagingIncrementalDecoder)malloc(sizeof(struct ImagingIncrementalDecoderStruct)); + + decoder->entry = decoder_entry; + decoder->im = im; + decoder->state = state; + decoder->result = 0; + decoder->stream.buffer = decoder->stream.ptr = decoder->stream.end = NULL; + decoder->started = 0; + + /* System specific set-up */ +#if _WIN32 + decoder->hDecodeEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + + if (!decoder->hDecodeEvent) { + free(decoder); + return NULL; + } + + decoder->hDataEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + + if (!decoder->hDataEvent) { + CloseHandle(decoder->hDecodeEvent); + free(decoder); + return NULL; + } + + decoder->hThread = _beginthreadex(NULL, 0, incremental_thread, decoder, + CREATE_SUSPENDED, NULL); + + if (!decoder->hThread) { + CloseHandle(decoder->hDecodeEvent); + CloseHandle(decoder->hDataEvent); + free(decoder); + return NULL; + } +#else + if (pthread_mutex_init(&decoder->start_mutex, NULL)) { + free (decoder); + return NULL; + } + + if (pthread_mutex_init(&decoder->decode_mutex, NULL)) { + pthread_mutex_destroy(&decoder->start_mutex); + free(decoder); + return NULL; + } + + if (pthread_mutex_init(&decoder->data_mutex, NULL)) { + pthread_mutex_destroy(&decoder->start_mutex); + pthread_mutex_destroy(&decoder->decode_mutex); + free(decoder); + return NULL; + } + + if (pthread_cond_init(&decoder->start_cond, NULL)) { + pthread_mutex_destroy(&decoder->start_mutex); + pthread_mutex_destroy(&decoder->decode_mutex); + pthread_mutex_destroy(&decoder->data_mutex); + free(decoder); + return NULL; + } + + if (pthread_cond_init(&decoder->decode_cond, NULL)) { + pthread_mutex_destroy(&decoder->start_mutex); + pthread_mutex_destroy(&decoder->decode_mutex); + pthread_mutex_destroy(&decoder->data_mutex); + pthread_cond_destroy(&decoder->start_cond); + free(decoder); + return NULL; + } + + if (pthread_cond_init(&decoder->data_cond, NULL)) { + pthread_mutex_destroy(&decoder->start_mutex); + pthread_mutex_destroy(&decoder->decode_mutex); + pthread_mutex_destroy(&decoder->data_mutex); + pthread_cond_destroy(&decoder->start_cond); + pthread_cond_destroy(&decoder->decode_cond); + free(decoder); + return NULL; + } + + if (pthread_create(&decoder->thread, NULL, incremental_thread, decoder)) { + pthread_mutex_destroy(&decoder->start_mutex); + pthread_mutex_destroy(&decoder->decode_mutex); + pthread_mutex_destroy(&decoder->data_mutex); + pthread_cond_destroy(&decoder->start_cond); + pthread_cond_destroy(&decoder->decode_cond); + pthread_cond_destroy(&decoder->data_cond); + free(decoder); + return NULL; + } +#endif + + return decoder; +} + +/** + * Destroy an incremental decoder */ +void +ImagingIncrementalDecoderDestroy(ImagingIncrementalDecoder decoder) +{ + if (!decoder->started) { +#ifdef _WIN32 + ResumeThread(decoder->hThread); +#else + pthread_cond_signal(&decoder->start_cond); +#endif + decoder->started = 1; + } + +#ifndef _WIN32 + pthread_mutex_lock(&decoder->data_mutex); +#endif + + decoder->stream.buffer = decoder->stream.ptr = decoder->stream.end = NULL; + +#ifdef _WIN32 + SetEvent(decoder->hDataEvent); + + WaitForSingleObject(decoder->hThread, INFINITE); + + CloseHandle(decoder->hThread); + CloseHandle(decoder->hDecodeEvent); + CloseHandle(decoder->hDataEvent); +#else + pthread_cond_signal(&decoder->data_cond); + pthread_mutex_unlock(&decoder->data_mutex); + + pthread_join(decoder->thread, NULL); + + pthread_mutex_destroy(&decoder->start_mutex); + pthread_mutex_destroy(&decoder->decode_mutex); + pthread_mutex_destroy(&decoder->data_mutex); + pthread_cond_destroy(&decoder->start_cond); + pthread_cond_destroy(&decoder->decode_cond); + pthread_cond_destroy(&decoder->data_cond); +#endif + free (decoder); +} + +/** + * Decode data using an incremental decoder */ +int +ImagingIncrementalDecodeData(ImagingIncrementalDecoder decoder, + UINT8 *buf, int bytes) +{ + if (!decoder->started) { +#ifdef _WIN32 + ResumeThread(decoder->hThread); +#else + pthread_cond_signal(&decoder->start_cond); +#endif + decoder->started = 1; + } + +#ifndef _WIN32 + pthread_mutex_lock(&decoder->data_mutex); +#endif + + decoder->stream.buffer = decoder->stream.ptr = buf; + decoder->stream.end = buf + bytes; + +#ifdef _WIN32 + SetEvent(decoder->hDataEvent); + WaitForSingleObject(decoder->hDecodeEvent); +#else + pthread_cond_signal(&decoder->data_cond); + pthread_mutex_unlock(&decoder->data_mutex); + + pthread_mutex_lock(&decoder->decode_mutex); + pthread_cond_wait(&decoder->decode_cond, &decoder->decode_mutex); + pthread_mutex_unlock(&decoder->decode_mutex); +#endif + + return decoder->result; +} + +size_t +ImagingIncrementalDecoderRead(ImagingIncrementalDecoder decoder, + void *buffer, size_t bytes) +{ + UINT8 *ptr = (UINT8 *)buffer; + size_t done = 0; + + pthread_mutex_lock(&decoder->data_mutex); + while (bytes) { + size_t todo = bytes; + size_t remaining = decoder->stream.end - decoder->stream.ptr; + + if (!remaining) { +#ifndef _WIN32 + pthread_mutex_lock(&decoder->decode_mutex); +#endif + decoder->result = (int)(decoder->stream.ptr - decoder->stream.buffer); +#if _WIN32 + SetEvent(decoder->hDecodeEvent); + WaitForSingleObject(decoder->hDataEvent); +#else + pthread_cond_signal(&decoder->decode_cond); + pthread_mutex_unlock(&decoder->decode_mutex); + pthread_cond_wait(&decoder->data_cond, &decoder->data_mutex); +#endif + + remaining = decoder->stream.end - decoder->stream.ptr; + } + if (todo > remaining) + todo = remaining; + + if (!todo) + break; + + memcpy (ptr, decoder->stream.ptr, todo); + decoder->stream.ptr += todo; + bytes -= todo; + done += todo; + ptr += todo; + } + pthread_mutex_unlock(&decoder->data_mutex); + + return done; +} + +off_t +ImagingIncrementalDecoderSkip(ImagingIncrementalDecoder decoder, + off_t bytes) +{ + off_t done = 0; + + while (bytes) { + off_t todo = bytes; + off_t remaining = decoder->stream.end - decoder->stream.ptr; + + if (!remaining) { + decoder->result = (int)(decoder->stream.ptr - decoder->stream.buffer); +#if _WIN32 + SetEvent(decoder->hDecodeEvent); + WaitForSingleObject(decoder->hDataEvent); +#else + pthread_cond_signal(&decoder->decode_cond); + pthread_mutex_lock(&decoder->data_mutex); + pthread_cond_wait(&decoder->data_cond, &decoder->data_mutex); +#endif + + remaining = decoder->stream.end - decoder->stream.ptr; + } + if (todo > remaining) + todo = remaining; + + if (!todo) + break; + + decoder->stream.ptr += todo; + bytes -= todo; + done += todo; + } + + return done; +} + diff --git a/libImaging/Jpeg2K.h b/libImaging/Jpeg2K.h new file mode 100644 index 000000000..269357296 --- /dev/null +++ b/libImaging/Jpeg2K.h @@ -0,0 +1,44 @@ +/* + * The Python Imaging Library + * $Id$ + * + * declarations for the OpenJPEG codec interface. + * + * Copyright (c) 2014 by Coriolis Systems Limited + * Copyright (c) 2014 by Alastair Houghton + */ + +#include + +/* -------------------------------------------------------------------- */ +/* Decoder */ + +typedef struct { + /* CONFIGURATION */ + + /* Output mode */ + char mode[8]; + + /* Specify the desired format */ + OPJ_CODEC_FORMAT format; + + /* Set to divide image resolution by 2**reduce. */ + int reduce; + + /* Set to limit the number of quality layers to decode (0 = all layers) */ + int layers; + + /* PRIVATE CONTEXT (set by decoder) */ + const char *error_msg; + + ImagingIncrementalDecoder decoder; + + opj_stream_t *stream; +} JPEG2KSTATE; + +/* + * Local Variables: + * c-basic-offset: 4 + * End: + * + */ diff --git a/libImaging/Jpeg2KDecode.c b/libImaging/Jpeg2KDecode.c new file mode 100644 index 000000000..e050c5216 --- /dev/null +++ b/libImaging/Jpeg2KDecode.c @@ -0,0 +1,475 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * decoder for JPEG2000 image data. + * + * history: + * 2014-03-12 ajh Created + * + * Copyright (c) 2014 Coriolis Systems Limited + * Copyright (c) 2014 Alastair Houghton + * + * See the README file for details on usage and redistribution. + */ + +#include "Imaging.h" + +#ifdef HAVE_OPENJPEG + +#include +#include "Jpeg2K.h" + +/* -------------------------------------------------------------------- */ +/* Error handler */ +/* -------------------------------------------------------------------- */ + +static void +j2k_error(const char *msg, void *client_data) +{ + JPEG2KSTATE *state = (JPEG2KSTATE *) client_data; + free((void *)state->error_msg); + state->error_msg = strdup(msg); +} + +/* -------------------------------------------------------------------- */ +/* Buffer input stream */ +/* -------------------------------------------------------------------- */ + +static OPJ_SIZE_T +j2k_read(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) +{ + ImagingIncrementalDecoder decoder = (ImagingIncrementalDecoder)p_user_data; + + return ImagingIncrementalDecoderRead(decoder, p_buffer, p_nb_bytes); +} + +static OPJ_SIZE_T +j2k_write(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) +{ + return OPJ_FALSE; +} + +static OPJ_OFF_T +j2k_skip(OPJ_OFF_T p_nb_bytes, void *p_user_data) +{ + ImagingIncrementalDecoder decoder = (ImagingIncrementalDecoder)p_user_data; + + return ImagingIncrementalDecoderSkip(decoder, p_nb_bytes); +} + +static OPJ_BOOL +j2k_seek(OPJ_OFF_T p_nb_bytes, void *p_user_data) +{ + // We *deliberately* don't implement this + return OPJ_FALSE; +} + +/* -------------------------------------------------------------------- */ +/* Unpackers */ +/* -------------------------------------------------------------------- */ + +typedef void (*j2k_unpacker_t)(opj_image_t *in, Imaging im); + +struct j2k_decode_unpacker { + const char *mode; + OPJ_COLOR_SPACE color_space; + unsigned components; + j2k_unpacker_t unpacker; +}; + +static inline +unsigned j2ku_shift(unsigned x, int n) +{ + if (n < 0) + return x >> -n; + else + return x << n; +} + +static void +j2ku_gray_l(opj_image_t *in, Imaging im) +{ + unsigned x0 = in->comps[0].x0, y0 = in->comps[0].y0; + unsigned w = in->comps[0].w, h = in->comps[0].h; + int shift = 8 - in->comps[0].prec; + int offset = in->comps[0].sgnd ? 1 << (in->comps[0].prec - 1) : 0; + unsigned x, y; + + if (shift < 0) + offset += 1 << (-shift - 1); + + for (y = 0; y < h; ++y) { + OPJ_INT32 *data = &in->comps[0].data[y * w]; + UINT8 *row = (UINT8 *)im->image[y0 + y] + x0; + for (x = 0; x < w; ++x) + *row++ = j2ku_shift(offset + *data++, shift); + } +} + +static void +j2ku_gray_rgb(opj_image_t *in, Imaging im) +{ + unsigned x0 = in->comps[0].x0, y0 = in->comps[0].y0; + unsigned w = in->comps[0].w, h = in->comps[0].h; + int shift = 8 - in->comps[0].prec; + int offset = in->comps[0].sgnd ? 1 << (in->comps[0].prec - 1) : 0; + unsigned x, y; + + if (shift < 0) + offset += 1 << (-shift - 1); + + for (y = 0; y < h; ++y) { + OPJ_INT32 *data = &in->comps[0].data[y * w]; + UINT8 *row = (UINT8 *)im->image[y0 + y] + x0; + for (x = 0; x < w; ++x) { + UINT8 byte = j2ku_shift(offset + *data++, shift); + row[0] = row[1] = row[2] = byte; + row[3] = 0xff; + row += 4; + } + } +} + +static void +j2ku_graya_la(opj_image_t *in, Imaging im) +{ + unsigned x0 = in->comps[0].x0, y0 = in->comps[0].y0; + unsigned w = in->comps[0].w, h = in->comps[0].h; + int shift = 8 - in->comps[0].prec; + int offset = in->comps[0].sgnd ? 1 << (in->comps[0].prec - 1) : 0; + int ashift = 8 - in->comps[1].prec; + int aoffset = in->comps[1].sgnd ? 1 << (in->comps[1].prec - 1) : 0; + unsigned x, y; + + if (shift < 0) + offset += 1 << (-shift - 1); + if (ashift < 0) + aoffset += 1 << (-ashift - 1); + + for (y = 0; y < h; ++y) { + OPJ_INT32 *data = &in->comps[0].data[y * w]; + OPJ_INT32 *adata = &in->comps[1].data[y * w]; + UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4; + for (x = 0; x < w; ++x) { + UINT8 byte = j2ku_shift(offset + *data++, shift); + row[0] = row[1] = row[2] = byte; + row[3] = (unsigned)(offset + *adata++) >> shift; + row += 4; + } + } +} + +static void +j2ku_srgb_rgb(opj_image_t *in, Imaging im) +{ + unsigned x0 = in->comps[0].x0, y0 = in->comps[0].y0; + unsigned w = in->comps[0].w, h = in->comps[0].h; + int shifts[3], offsets[3]; + unsigned n, x, y; + + for (n = 0; n < 3; ++n) { + shifts[n] = 8 - in->comps[n].prec; + offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0; + if (shifts[n] < 0) + offsets[n] += 1 << (-shifts[n] - 1); + } + + for (y = 0; y < h; ++y) { + OPJ_INT32 *data[3]; + UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4; + for (n = 0; n < 3; ++n) + data[n] = &in->comps[n].data[y * w]; + + for (x = 0; x < w; ++x) { + for (n = 0; n < 3; ++n) + row[n] = j2ku_shift(offsets[n] + *data[n]++, shifts[n]); + row[3] = 0xff; + row += 4; + } + } +} + +static void +j2ku_sycc_rgb(opj_image_t *in, Imaging im) +{ + unsigned x0 = in->comps[0].x0, y0 = in->comps[0].y0; + unsigned w = in->comps[0].w, h = in->comps[0].h; + int shifts[3], offsets[3]; + unsigned n, x, y; + + for (n = 0; n < 3; ++n) { + shifts[n] = 8 - in->comps[n].prec; + offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0; + if (shifts[n] < 0) + offsets[n] += 1 << (-shifts[n] - 1); + } + + for (y = 0; y < h; ++y) { + OPJ_INT32 *data[3]; + UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4; + UINT8 *row_start = row; + for (n = 0; n < 3; ++n) + data[n] = &in->comps[n].data[y * w]; + + for (x = 0; x < w; ++x) { + for (n = 0; n < 3; ++n) + row[n] = j2ku_shift(offsets[n] + *data[n]++, shifts[n]); + row[3] = 0xff; + row += 4; + } + ImagingConvertYCbCr2RGB(row_start, row_start, w); + } +} + +static void +j2ku_srgba_rgba(opj_image_t *in, Imaging im) +{ + unsigned x0 = in->comps[0].x0, y0 = in->comps[0].y0; + unsigned w = in->comps[0].w, h = in->comps[0].h; + int shifts[4], offsets[4]; + unsigned n, x, y; + + for (n = 0; n < 4; ++n) { + shifts[n] = 8 - in->comps[n].prec; + offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0; + if (shifts[n] < 0) + offsets[n] += 1 << (-shifts[n] - 1); + } + + for (y = 0; y < h; ++y) { + OPJ_INT32 *data[4]; + UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4; + for (n = 0; n < 4; ++n) + data[n] = &in->comps[n].data[y * w]; + + for (x = 0; x < w; ++x) { + for (n = 0; n < 4; ++n) + row[n] = j2ku_shift(offsets[n] + *data[n]++, shifts[n]); + row += 4; + } + } +} + +static const struct j2k_decode_unpacker j2k_unpackers[] = { + { "L", OPJ_CLRSPC_GRAY, 1, j2ku_gray_l }, + { "LA", OPJ_CLRSPC_GRAY, 2, j2ku_graya_la }, + { "RGB", OPJ_CLRSPC_GRAY, 1, j2ku_gray_rgb }, + { "RGB", OPJ_CLRSPC_GRAY, 2, j2ku_gray_rgb }, + { "RGB", OPJ_CLRSPC_SRGB, 3, j2ku_srgb_rgb }, + { "RGB", OPJ_CLRSPC_SYCC, 3, j2ku_sycc_rgb }, + { "RGBA", OPJ_CLRSPC_GRAY, 1, j2ku_gray_rgb }, + { "RGBA", OPJ_CLRSPC_GRAY, 2, j2ku_graya_la }, + { "RGBA", OPJ_CLRSPC_SRGB, 3, j2ku_srgb_rgb }, + { "RGBA", OPJ_CLRSPC_SYCC, 3, j2ku_sycc_rgb }, + { "RGBA", OPJ_CLRSPC_SRGB, 4, j2ku_srgba_rgba }, +}; + +/* -------------------------------------------------------------------- */ +/* Decoder */ +/* -------------------------------------------------------------------- */ + +enum { + J2K_STATE_START = 0, + J2K_STATE_DECODING = 1, + J2K_STATE_DONE = 2, + J2K_STATE_FAILED = 3, +}; + +static int +j2k_decode_entry(Imaging im, ImagingCodecState state, + ImagingIncrementalDecoder decoder) +{ + JPEG2KSTATE *context = (JPEG2KSTATE *) state->context; + opj_stream_t *stream; + opj_image_t *image; + opj_codec_t *codec; + opj_dparameters_t params; + OPJ_COLOR_SPACE color_space; + j2k_unpacker_t unpack; + unsigned n; + + stream = opj_stream_default_create(OPJ_TRUE); + + if (!stream) { + state->errcode = IMAGING_CODEC_BROKEN; + state->state = J2K_STATE_FAILED; + goto quick_exit; + } + + opj_stream_set_read_function(stream, j2k_read); + opj_stream_set_write_function(stream, j2k_write); + opj_stream_set_skip_function(stream, j2k_skip); + opj_stream_set_seek_function(stream, j2k_seek); + + opj_stream_set_user_data(stream, context->decoder); + + + /* Setup decompression context */ + context->error_msg = NULL; + + opj_set_default_decoder_parameters(¶ms); + params.cp_reduce = context->reduce; + params.cp_layer = context->layers; + + codec = opj_create_decompress(context->format); + + if (!codec) { + state->errcode = IMAGING_CODEC_BROKEN; + state->state = J2K_STATE_FAILED; + goto quick_exit; + } + + opj_set_error_handler(codec, j2k_error, context); + opj_setup_decoder(codec, ¶ms); + + if (!opj_read_header(stream, codec, &image)) { + state->errcode = IMAGING_CODEC_BROKEN; + state->state = J2K_STATE_FAILED; + goto quick_exit; + } + + /* Check that this image is something we can handle */ + if (image->numcomps < 1 || image->numcomps > 4 + || image->color_space == OPJ_CLRSPC_UNKNOWN) { + state->errcode = IMAGING_CODEC_BROKEN; + state->state = J2K_STATE_FAILED; + goto quick_exit; + } + + for (n = 1; n < image->numcomps; ++n) { + /* Check that the sample frequency is uniform */ + if (image->comps[0].dx != image->comps[n].dx + || image->comps[0].dy != image->comps[n].dy) { + state->errcode = IMAGING_CODEC_BROKEN; + state->state = J2K_STATE_FAILED; + goto quick_exit; + } + + /* Check that the bit depth is uniform */ + if (image->comps[0].prec != image->comps[n].prec) { + state->errcode = IMAGING_CODEC_BROKEN; + state->state = J2K_STATE_FAILED; + goto quick_exit; + } + } + + /* + Colorspace Number of components PIL mode + ------------------------------------------------------ + sRGB 3 RGB + sRGB 4 RGBA + gray 1 L or I + gray 2 LA + YCC 3 YCbCr + + + If colorspace is unspecified, we assume: + + Number of components Colorspace + ----------------------------------------- + 1 gray + 2 gray (+ alpha) + 3 sRGB + 4 sRGB (+ alpha) + + */ + + /* Find the correct unpacker */ + color_space = image->color_space; + + if (color_space == OPJ_CLRSPC_UNSPECIFIED) { + switch (image->numcomps) { + case 1: case 2: color_space = OPJ_CLRSPC_GRAY; break; + case 3: case 4: color_space = OPJ_CLRSPC_SRGB; break; + } + } + + for (n = 0; n < sizeof(j2k_unpackers) / sizeof (j2k_unpackers[0]); ++n) { + if (color_space == j2k_unpackers[n].color_space + && image->numcomps == j2k_unpackers[n].components + && strcmp (context->mode, j2k_unpackers[n].mode) == 0) { + unpack = j2k_unpackers[n].unpacker; + break; + } + } + + if (!unpack) { + state->errcode = IMAGING_CODEC_BROKEN; + state->state = J2K_STATE_FAILED; + goto quick_exit; + } + + /* Decode and unpack the image */ + if (!opj_decode(codec, stream, image) + || !opj_end_decompress(codec, stream)) { + state->errcode = IMAGING_CODEC_BROKEN; + state->state = J2K_STATE_FAILED; + goto quick_exit; + } + + unpack(image, im); + + quick_exit: + if (codec) + opj_destroy_codec(codec); + if (image) + opj_image_destroy(image); + if (stream) + opj_stream_destroy(stream); + + return -1; +} + +int +ImagingJpeg2KDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) +{ + JPEG2KSTATE *context = (JPEG2KSTATE *) state->context; + + if (state->state == J2K_STATE_DONE || state->state == J2K_STATE_FAILED) + return -1; + + if (state->state == J2K_STATE_START) { + context->decoder = ImagingIncrementalDecoderCreate(j2k_decode_entry, + im, state); + + if (!context->decoder) { + state->errcode = IMAGING_CODEC_BROKEN; + state->state = J2K_STATE_FAILED; + return -1; + } + + state->state = J2K_STATE_DECODING; + } + + return ImagingIncrementalDecodeData(context->decoder, buf, bytes); +} + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + +int +ImagingJpeg2KDecodeCleanup(ImagingCodecState state) { + JPEG2KSTATE *context = (JPEG2KSTATE *)state->context; + + if (context->decoder) + ImagingIncrementalDecoderDestroy(context->decoder); + + return -1; +} + +const char * +ImagingJpeg2KVersion(void) +{ + return opj_version(); +} + +#endif /* HAVE_OPENJPEG */ + +/* + * Local Variables: + * c-basic-offset: 4 + * End: + * + */ diff --git a/setup.py b/setup.py index 0d791c444..e948e1050 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,8 @@ _LIB_IMAGING = ( "QuantHeap", "PcdDecode", "PcxDecode", "PcxEncode", "Point", "RankFilter", "RawDecode", "RawEncode", "Storage", "SunRleDecode", "TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask", "XbmDecode", - "XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode") + "XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode", "Incremental", + "Jpeg2KDecode") def _add_directory(path, dir, where=None): @@ -98,6 +99,7 @@ class pil_build_ext(build_ext): class feature: zlib = jpeg = tiff = freetype = tcl = tk = lcms = webp = webpmux = None + jpeg2000 = None required = [] def require(self, feat): @@ -349,6 +351,11 @@ class pil_build_ext(build_ext): _find_library_file(self, "libjpeg")): feature.jpeg = "libjpeg" # alternative name + if feature.want('jpeg2000'): + if _find_include_file(self, "openjpeg-2.0/openjpeg.h"): + if _find_library_file(self, "openjp2"): + feature.jpeg2000 = "openjp2" + if feature.want('tiff'): if _find_library_file(self, "tiff"): feature.tiff = "tiff" @@ -430,6 +437,9 @@ class pil_build_ext(build_ext): if feature.jpeg: libs.append(feature.jpeg) defs.append(("HAVE_LIBJPEG", None)) + if feature.jpeg2000: + libs.append(feature.jpeg2000) + defs.append(("HAVE_OPENJPEG", None)) if feature.zlib: libs.append(feature.zlib) defs.append(("HAVE_LIBZ", None)) @@ -537,6 +547,7 @@ class pil_build_ext(build_ext): options = [ (feature.tcl and feature.tk, "TKINTER"), (feature.jpeg, "JPEG"), + (feature.jpeg2000, "OPENJPEG (JPEG2000)"), (feature.zlib, "ZLIB (PNG/ZIP)"), (feature.tiff, "LIBTIFF"), (feature.freetype, "FREETYPE2"), From 7dba77364a74cf12c71409f56af3f089d11dcce6 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Thu, 13 Mar 2014 11:57:47 +0000 Subject: [PATCH 02/20] Fixed rounding. --- PIL/Jpeg2KImagePlugin.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PIL/Jpeg2KImagePlugin.py b/PIL/Jpeg2KImagePlugin.py index 9acc71c4e..95d206a99 100644 --- a/PIL/Jpeg2KImagePlugin.py +++ b/PIL/Jpeg2KImagePlugin.py @@ -165,7 +165,9 @@ class Jpeg2KImageFile(ImageFile.ImageFile): def load(self): if self.reduce: power = 1 << self.reduce - self.size = (self.size[0] / power, self.size[1] / power) + adjust = power >> 1 + self.size = ((self.size[0] + adjust) / power, + (self.size[1] + adjust) / power) ImageFile.ImageFile.load(self) From 5b22b715cea0a2e3320ed6afa7d573b451ee2bae Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Thu, 13 Mar 2014 12:29:03 +0000 Subject: [PATCH 03/20] Fixed some bugs. --- PIL/Jpeg2KImagePlugin.py | 2 - libImaging/Incremental.c | 117 ++++++++++++++++++++++++++------------ libImaging/Jpeg2KDecode.c | 26 ++++++--- 3 files changed, 97 insertions(+), 48 deletions(-) diff --git a/PIL/Jpeg2KImagePlugin.py b/PIL/Jpeg2KImagePlugin.py index 95d206a99..62aced03e 100644 --- a/PIL/Jpeg2KImagePlugin.py +++ b/PIL/Jpeg2KImagePlugin.py @@ -138,12 +138,10 @@ class Jpeg2KImageFile(ImageFile.ImageFile): sig = self.fp.read(4) if sig == b'\xff\x4f\xff\x51': self.codec = "j2k" - print 'Reading size/mode' try: self.size, self.mode = _parse_codestream(self.fp) except Exception as e: print e - print '%r, %r' % (self.size, self.mode) else: sig = sig + self.fp.read(8) diff --git a/libImaging/Incremental.c b/libImaging/Incremental.c index 0e81af7e8..bb51d91c7 100644 --- a/libImaging/Incremental.c +++ b/libImaging/Incremental.c @@ -42,6 +42,14 @@ #include #endif +#define DEBUG_INCREMENTAL 0 + +#if DEBUG_INCREMENTAL +#define DEBUG(...) printf(__VA_ARGS__) +#else +#define DEBUG(...) +#endif + struct ImagingIncrementalStreamStruct { UINT8 *buffer; UINT8 *ptr; @@ -206,6 +214,8 @@ ImagingIncrementalDecoderCreate(ImagingIncrementalDecoderEntry decoder_entry, void ImagingIncrementalDecoderDestroy(ImagingIncrementalDecoder decoder) { + DEBUG("destroying\n"); + if (!decoder->started) { #ifdef _WIN32 ResumeThread(decoder->hThread); @@ -252,6 +262,8 @@ ImagingIncrementalDecodeData(ImagingIncrementalDecoder decoder, UINT8 *buf, int bytes) { if (!decoder->started) { + DEBUG("starting\n"); + #ifdef _WIN32 ResumeThread(decoder->hThread); #else @@ -260,6 +272,8 @@ ImagingIncrementalDecodeData(ImagingIncrementalDecoder decoder, decoder->started = 1; } + DEBUG("providing %p, %d\n", buf, bytes); + #ifndef _WIN32 pthread_mutex_lock(&decoder->data_mutex); #endif @@ -279,6 +293,8 @@ ImagingIncrementalDecodeData(ImagingIncrementalDecoder decoder, pthread_mutex_unlock(&decoder->decode_mutex); #endif + DEBUG("got result %d\n", decoder->result); + return decoder->result; } @@ -289,12 +305,74 @@ ImagingIncrementalDecoderRead(ImagingIncrementalDecoder decoder, UINT8 *ptr = (UINT8 *)buffer; size_t done = 0; + DEBUG("reading (want %llu bytes)\n", (unsigned long long)bytes); + +#ifndef _WIN32 pthread_mutex_lock(&decoder->data_mutex); +#endif while (bytes) { size_t todo = bytes; size_t remaining = decoder->stream.end - decoder->stream.ptr; if (!remaining) { + DEBUG("waiting for data\n"); + +#ifndef _WIN32 + pthread_mutex_lock(&decoder->decode_mutex); +#endif + decoder->result = (int)(decoder->stream.ptr - decoder->stream.buffer); +#if _WIN32 + SetEvent(decoder->hDecodeEvent); + WaitForSingleObject(decoder->hDataEvent); +#else + pthread_cond_signal(&decoder->decode_cond); + pthread_mutex_unlock(&decoder->decode_mutex); + pthread_cond_wait(&decoder->data_cond, &decoder->data_mutex); +#endif + + remaining = decoder->stream.end - decoder->stream.ptr; + + DEBUG("got %llu bytes\n", (unsigned long long)remaining); + } + if (todo > remaining) + todo = remaining; + + if (!todo) + break; + + memcpy (ptr, decoder->stream.ptr, todo); + decoder->stream.ptr += todo; + bytes -= todo; + done += todo; + ptr += todo; + } +#ifndef _WIN32 + pthread_mutex_unlock(&decoder->data_mutex); +#endif + + DEBUG("read total %llu bytes\n", (unsigned long long)done); + + return done; +} + +off_t +ImagingIncrementalDecoderSkip(ImagingIncrementalDecoder decoder, + off_t bytes) +{ + off_t done = 0; + + DEBUG("skipping (want %llu bytes)\n", (unsigned long long)bytes); + +#ifndef _WIN32 + pthread_mutex_lock(&decoder->data_mutex); +#endif + while (bytes) { + off_t todo = bytes; + off_t remaining = decoder->stream.end - decoder->stream.ptr; + + if (!remaining) { + DEBUG("waiting for data\n"); + #ifndef _WIN32 pthread_mutex_lock(&decoder->decode_mutex); #endif @@ -316,50 +394,15 @@ ImagingIncrementalDecoderRead(ImagingIncrementalDecoder decoder, if (!todo) break; - memcpy (ptr, decoder->stream.ptr, todo); decoder->stream.ptr += todo; bytes -= todo; done += todo; - ptr += todo; } +#ifndef _WIN32 pthread_mutex_unlock(&decoder->data_mutex); - - return done; -} - -off_t -ImagingIncrementalDecoderSkip(ImagingIncrementalDecoder decoder, - off_t bytes) -{ - off_t done = 0; - - while (bytes) { - off_t todo = bytes; - off_t remaining = decoder->stream.end - decoder->stream.ptr; - - if (!remaining) { - decoder->result = (int)(decoder->stream.ptr - decoder->stream.buffer); -#if _WIN32 - SetEvent(decoder->hDecodeEvent); - WaitForSingleObject(decoder->hDataEvent); -#else - pthread_cond_signal(&decoder->decode_cond); - pthread_mutex_lock(&decoder->data_mutex); - pthread_cond_wait(&decoder->data_cond, &decoder->data_mutex); #endif - remaining = decoder->stream.end - decoder->stream.ptr; - } - if (todo > remaining) - todo = remaining; - - if (!todo) - break; - - decoder->stream.ptr += todo; - bytes -= todo; - done += todo; - } + DEBUG("skipped total %llu bytes\n", (unsigned long long)done); return done; } diff --git a/libImaging/Jpeg2KDecode.c b/libImaging/Jpeg2KDecode.c index e050c5216..5c1d4cd42 100644 --- a/libImaging/Jpeg2KDecode.c +++ b/libImaging/Jpeg2KDecode.c @@ -17,7 +17,7 @@ #ifdef HAVE_OPENJPEG -#include +#include #include "Jpeg2K.h" /* -------------------------------------------------------------------- */ @@ -41,27 +41,35 @@ j2k_read(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) { ImagingIncrementalDecoder decoder = (ImagingIncrementalDecoder)p_user_data; - return ImagingIncrementalDecoderRead(decoder, p_buffer, p_nb_bytes); + size_t len = ImagingIncrementalDecoderRead(decoder, p_buffer, p_nb_bytes); + + return len ? len : (OPJ_SIZE_T)-1; } static OPJ_SIZE_T j2k_write(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) { - return OPJ_FALSE; + /* This should never happen */ + fprintf(stderr, "OpenJPEG has written to our read stream(!)"); + abort(); + return (OPJ_SIZE_T)-1; } static OPJ_OFF_T j2k_skip(OPJ_OFF_T p_nb_bytes, void *p_user_data) { ImagingIncrementalDecoder decoder = (ImagingIncrementalDecoder)p_user_data; + off_t pos = ImagingIncrementalDecoderSkip(decoder, p_nb_bytes); - return ImagingIncrementalDecoderSkip(decoder, p_nb_bytes); + return pos ? pos : (OPJ_OFF_T)-1; } static OPJ_BOOL j2k_seek(OPJ_OFF_T p_nb_bytes, void *p_user_data) { - // We *deliberately* don't implement this + /* This should never happen */ + fprintf(stderr, "OpenJPEG tried to seek our read stream(!)"); + abort(); return OPJ_FALSE; } @@ -281,12 +289,12 @@ j2k_decode_entry(Imaging im, ImagingCodecState state, ImagingIncrementalDecoder decoder) { JPEG2KSTATE *context = (JPEG2KSTATE *) state->context; - opj_stream_t *stream; - opj_image_t *image; - opj_codec_t *codec; + opj_stream_t *stream = NULL; + opj_image_t *image = NULL; + opj_codec_t *codec = NULL; opj_dparameters_t params; OPJ_COLOR_SPACE color_space; - j2k_unpacker_t unpack; + j2k_unpacker_t unpack = NULL; unsigned n; stream = opj_stream_default_create(OPJ_TRUE); From 9a4bff722f9f4dc9320b6ea0478ff8102ec503b3 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Thu, 13 Mar 2014 13:44:26 +0000 Subject: [PATCH 04/20] Decode tile-by-tile; saves memory and means we don't need to buffer the entire image in the OpenJPEG opj_image. --- libImaging/Jpeg2KDecode.c | 355 +++++++++++++++++++++++++++++--------- 1 file changed, 277 insertions(+), 78 deletions(-) diff --git a/libImaging/Jpeg2KDecode.c b/libImaging/Jpeg2KDecode.c index 5c1d4cd42..b503ffa44 100644 --- a/libImaging/Jpeg2KDecode.c +++ b/libImaging/Jpeg2KDecode.c @@ -20,6 +20,13 @@ #include #include "Jpeg2K.h" +typedef struct { + OPJ_UINT32 tile_index; + OPJ_UINT32 data_size; + OPJ_INT32 x0, y0, x1, y1; + OPJ_UINT32 nb_comps; +} JPEG2KTILEINFO; + /* -------------------------------------------------------------------- */ /* Error handler */ /* -------------------------------------------------------------------- */ @@ -77,7 +84,10 @@ j2k_seek(OPJ_OFF_T p_nb_bytes, void *p_user_data) /* Unpackers */ /* -------------------------------------------------------------------- */ -typedef void (*j2k_unpacker_t)(opj_image_t *in, Imaging im); +typedef void (*j2k_unpacker_t)(opj_image_t *in, + const JPEG2KTILEINFO *tileInfo, + const UINT8 *data, + Imaging im); struct j2k_decode_unpacker { const char *mode; @@ -96,102 +106,216 @@ unsigned j2ku_shift(unsigned x, int n) } static void -j2ku_gray_l(opj_image_t *in, Imaging im) +j2ku_gray_l(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, + const UINT8 *tiledata, Imaging im) { - unsigned x0 = in->comps[0].x0, y0 = in->comps[0].y0; - unsigned w = in->comps[0].w, h = in->comps[0].h; + unsigned x0 = tileinfo->x0, y0 = tileinfo->y0; + unsigned w = tileinfo->x1 - tileinfo->x0; + unsigned h = tileinfo->y1 - tileinfo->y0; + int shift = 8 - in->comps[0].prec; int offset = in->comps[0].sgnd ? 1 << (in->comps[0].prec - 1) : 0; + int csiz = (in->comps[0].prec + 7) >> 3; + unsigned x, y; + if (csiz == 3) + csiz = 4; + if (shift < 0) offset += 1 << (-shift - 1); - for (y = 0; y < h; ++y) { - OPJ_INT32 *data = &in->comps[0].data[y * w]; - UINT8 *row = (UINT8 *)im->image[y0 + y] + x0; - for (x = 0; x < w; ++x) - *row++ = j2ku_shift(offset + *data++, shift); - } -} - -static void -j2ku_gray_rgb(opj_image_t *in, Imaging im) -{ - unsigned x0 = in->comps[0].x0, y0 = in->comps[0].y0; - unsigned w = in->comps[0].w, h = in->comps[0].h; - int shift = 8 - in->comps[0].prec; - int offset = in->comps[0].sgnd ? 1 << (in->comps[0].prec - 1) : 0; - unsigned x, y; - - if (shift < 0) - offset += 1 << (-shift - 1); - - for (y = 0; y < h; ++y) { - OPJ_INT32 *data = &in->comps[0].data[y * w]; - UINT8 *row = (UINT8 *)im->image[y0 + y] + x0; - for (x = 0; x < w; ++x) { - UINT8 byte = j2ku_shift(offset + *data++, shift); - row[0] = row[1] = row[2] = byte; - row[3] = 0xff; - row += 4; + switch (csiz) { + case 1: + for (y = 0; y < h; ++y) { + const UINT8 *data = &tiledata[y * w]; + UINT8 *row = (UINT8 *)im->image[y0 + y] + x0; + for (x = 0; x < w; ++x) + *row++ = j2ku_shift(offset + *data++, shift); } + break; + case 2: + for (y = 0; y < h; ++y) { + const UINT16 *data = (const UINT16 *)&tiledata[2 * y * w]; + UINT8 *row = (UINT8 *)im->image[y0 + y] + x0; + for (x = 0; x < w; ++x) + *row++ = j2ku_shift(offset + *data++, shift); + } + break; + case 4: + for (y = 0; y < h; ++y) { + const UINT32 *data = (const UINT32 *)&tiledata[4 * y * w]; + UINT8 *row = (UINT8 *)im->image[y0 + y] + x0; + for (x = 0; x < w; ++x) + *row++ = j2ku_shift(offset + *data++, shift); + } + break; } } static void -j2ku_graya_la(opj_image_t *in, Imaging im) +j2ku_gray_rgb(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, + const UINT8 *tiledata, Imaging im) { - unsigned x0 = in->comps[0].x0, y0 = in->comps[0].y0; - unsigned w = in->comps[0].w, h = in->comps[0].h; + unsigned x0 = tileinfo->x0, y0 = tileinfo->y0; + unsigned w = tileinfo->x1 - tileinfo->x0; + unsigned h = tileinfo->y1 - tileinfo->y0; + int shift = 8 - in->comps[0].prec; int offset = in->comps[0].sgnd ? 1 << (in->comps[0].prec - 1) : 0; + int csiz = (in->comps[0].prec + 7) >> 3; + + unsigned x, y; + + if (shift < 0) + offset += 1 << (-shift - 1); + + if (csiz == 3) + csiz = 4; + + switch (csiz) { + case 1: + for (y = 0; y < h; ++y) { + const UINT8 *data = &tiledata[y * w]; + UINT8 *row = (UINT8 *)im->image[y0 + y] + x0; + for (x = 0; x < w; ++x) { + UINT8 byte = j2ku_shift(offset + *data++, shift); + row[0] = row[1] = row[2] = byte; + row[3] = 0xff; + row += 4; + } + } + break; + case 2: + for (y = 0; y < h; ++y) { + const UINT16 *data = (UINT16 *)&tiledata[2 * y * w]; + UINT8 *row = (UINT8 *)im->image[y0 + y] + x0; + for (x = 0; x < w; ++x) { + UINT8 byte = j2ku_shift(offset + *data++, shift); + row[0] = row[1] = row[2] = byte; + row[3] = 0xff; + row += 4; + } + } + break; + case 4: + for (y = 0; y < h; ++y) { + const UINT32 *data = (UINT32 *)&tiledata[4 * y * w]; + UINT8 *row = (UINT8 *)im->image[y0 + y] + x0; + for (x = 0; x < w; ++x) { + UINT8 byte = j2ku_shift(offset + *data++, shift); + row[0] = row[1] = row[2] = byte; + row[3] = 0xff; + row += 4; + } + } + break; + } +} + +static void +j2ku_graya_la(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, + const UINT8 *tiledata, Imaging im) +{ + unsigned x0 = tileinfo->x0, y0 = tileinfo->y0; + unsigned w = tileinfo->x1 - tileinfo->x0; + unsigned h = tileinfo->y1 - tileinfo->y0; + + int shift = 8 - in->comps[0].prec; + int offset = in->comps[0].sgnd ? 1 << (in->comps[0].prec - 1) : 0; + int csiz = (in->comps[0].prec + 7) >> 3; int ashift = 8 - in->comps[1].prec; int aoffset = in->comps[1].sgnd ? 1 << (in->comps[1].prec - 1) : 0; + int acsiz = (in->comps[1].prec + 7) >> 3; + const UINT8 *atiledata; + unsigned x, y; + if (csiz == 3) + csiz = 4; + if (acsiz == 3) + acsiz = 4; + if (shift < 0) offset += 1 << (-shift - 1); if (ashift < 0) aoffset += 1 << (-ashift - 1); + atiledata = tiledata + csiz * w * h; + for (y = 0; y < h; ++y) { - OPJ_INT32 *data = &in->comps[0].data[y * w]; - OPJ_INT32 *adata = &in->comps[1].data[y * w]; + const UINT8 *data = &tiledata[csiz * y * w]; + const UINT8 *adata = &atiledata[acsiz * y * w]; UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4; for (x = 0; x < w; ++x) { - UINT8 byte = j2ku_shift(offset + *data++, shift); + UINT32 word, aword; + + switch (csiz) { + case 1: word = *data++; break; + case 2: word = *(const UINT16 *)data; data += 2; break; + case 4: word = *(const UINT32 *)data; data += 4; break; + } + + switch (acsiz) { + case 1: aword = *adata++; break; + case 2: aword = *(const UINT16 *)adata; adata += 2; break; + case 4: aword = *(const UINT32 *)adata; adata += 4; break; + } + + UINT8 byte = j2ku_shift(offset + word, shift); row[0] = row[1] = row[2] = byte; - row[3] = (unsigned)(offset + *adata++) >> shift; + row[3] = (unsigned)(aoffset + aword) >> ashift; row += 4; } } } static void -j2ku_srgb_rgb(opj_image_t *in, Imaging im) +j2ku_srgb_rgb(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, + const UINT8 *tiledata, Imaging im) { - unsigned x0 = in->comps[0].x0, y0 = in->comps[0].y0; - unsigned w = in->comps[0].w, h = in->comps[0].h; - int shifts[3], offsets[3]; + unsigned x0 = tileinfo->x0, y0 = tileinfo->y0; + unsigned w = tileinfo->x1 - tileinfo->x0; + unsigned h = tileinfo->y1 - tileinfo->y0; + + int shifts[3], offsets[3], csiz[3]; + const UINT8 *cdata[3]; + const UINT8 *cptr = tiledata; unsigned n, x, y; for (n = 0; n < 3; ++n) { + cdata[n] = cptr; shifts[n] = 8 - in->comps[n].prec; offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0; + csiz[n] = (in->comps[n].prec + 7) >> 3; + + if (csiz[n] == 3) + csiz[n] = 4; + if (shifts[n] < 0) offsets[n] += 1 << (-shifts[n] - 1); + + cptr += csiz[n] * w * h; } for (y = 0; y < h; ++y) { - OPJ_INT32 *data[3]; + const UINT8 *data[3]; UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4; for (n = 0; n < 3; ++n) - data[n] = &in->comps[n].data[y * w]; + data[n] = &cdata[n][csiz[n] * y * w]; for (x = 0; x < w; ++x) { - for (n = 0; n < 3; ++n) - row[n] = j2ku_shift(offsets[n] + *data[n]++, shifts[n]); + for (n = 0; n < 3; ++n) { + UINT32 word; + + switch (csiz[n]) { + case 1: word = *data[n]++; break; + case 2: word = *(const UINT16 *)data[n]; data[n] += 2; break; + case 4: word = *(const UINT32 *)data[n]; data[n] += 4; break; + } + + row[n] = j2ku_shift(offsets[n] + word, shifts[n]); + } row[3] = 0xff; row += 4; } @@ -199,61 +323,106 @@ j2ku_srgb_rgb(opj_image_t *in, Imaging im) } static void -j2ku_sycc_rgb(opj_image_t *in, Imaging im) +j2ku_sycc_rgb(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, + const UINT8 *tiledata, Imaging im) { - unsigned x0 = in->comps[0].x0, y0 = in->comps[0].y0; - unsigned w = in->comps[0].w, h = in->comps[0].h; - int shifts[3], offsets[3]; + unsigned x0 = tileinfo->x0, y0 = tileinfo->y0; + unsigned w = tileinfo->x1 - tileinfo->x0; + unsigned h = tileinfo->y1 - tileinfo->y0; + + int shifts[3], offsets[3], csiz[3]; + const UINT8 *cdata[3]; + const UINT8 *cptr = tiledata; unsigned n, x, y; for (n = 0; n < 3; ++n) { + cdata[n] = cptr; shifts[n] = 8 - in->comps[n].prec; offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0; + csiz[n] = (in->comps[n].prec + 7) >> 3; + + if (csiz[n] == 3) + csiz[n] = 4; + if (shifts[n] < 0) offsets[n] += 1 << (-shifts[n] - 1); + + cptr += csiz[n] * w * h; } for (y = 0; y < h; ++y) { - OPJ_INT32 *data[3]; + const UINT8 *data[3]; UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4; UINT8 *row_start = row; for (n = 0; n < 3; ++n) - data[n] = &in->comps[n].data[y * w]; + data[n] = &cdata[n][csiz[n] * y * w]; for (x = 0; x < w; ++x) { - for (n = 0; n < 3; ++n) - row[n] = j2ku_shift(offsets[n] + *data[n]++, shifts[n]); + for (n = 0; n < 3; ++n) { + UINT32 word; + + switch (csiz[n]) { + case 1: word = *data[n]++; break; + case 2: word = *(const UINT16 *)data[n]; data[n] += 2; break; + case 4: word = *(const UINT32 *)data[n]; data[n] += 4; break; + } + + row[n] = j2ku_shift(offsets[n] + word, shifts[n]); + } row[3] = 0xff; row += 4; } + ImagingConvertYCbCr2RGB(row_start, row_start, w); } } static void -j2ku_srgba_rgba(opj_image_t *in, Imaging im) +j2ku_srgba_rgba(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, + const UINT8 *tiledata, Imaging im) { - unsigned x0 = in->comps[0].x0, y0 = in->comps[0].y0; - unsigned w = in->comps[0].w, h = in->comps[0].h; - int shifts[4], offsets[4]; + unsigned x0 = tileinfo->x0, y0 = tileinfo->y0; + unsigned w = tileinfo->x1 - tileinfo->x0; + unsigned h = tileinfo->y1 - tileinfo->y0; + + int shifts[4], offsets[4], csiz[4]; + const UINT8 *cdata[4]; + const UINT8 *cptr = tiledata; unsigned n, x, y; for (n = 0; n < 4; ++n) { + cdata[n] = cptr; shifts[n] = 8 - in->comps[n].prec; offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0; + csiz[n] = (in->comps[n].prec + 7) >> 3; + + if (csiz[n] == 3) + csiz[n] = 4; + if (shifts[n] < 0) offsets[n] += 1 << (-shifts[n] - 1); + + cptr += csiz[n] * w * h; } for (y = 0; y < h; ++y) { - OPJ_INT32 *data[4]; + const UINT8 *data[4]; UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4; for (n = 0; n < 4; ++n) - data[n] = &in->comps[n].data[y * w]; + data[n] = &cdata[n][csiz[n] * y * w]; for (x = 0; x < w; ++x) { - for (n = 0; n < 4; ++n) - row[n] = j2ku_shift(offsets[n] + *data[n]++, shifts[n]); + for (n = 0; n < 4; ++n) { + UINT32 word; + + switch (csiz[n]) { + case 1: word = *data[n]++; break; + case 2: word = *(const UINT16 *)data[n]; data[n] += 2; break; + case 4: word = *(const UINT32 *)data[n]; data[n] += 4; break; + } + + row[n] = j2ku_shift(offsets[n] + word, shifts[n]); + } row += 4; } } @@ -295,6 +464,7 @@ j2k_decode_entry(Imaging im, ImagingCodecState state, opj_dparameters_t params; OPJ_COLOR_SPACE color_space; j2k_unpacker_t unpack = NULL; + size_t buffer_size = 0; unsigned n; stream = opj_stream_default_create(OPJ_TRUE); @@ -353,13 +523,6 @@ j2k_decode_entry(Imaging im, ImagingCodecState state, state->state = J2K_STATE_FAILED; goto quick_exit; } - - /* Check that the bit depth is uniform */ - if (image->comps[0].prec != image->comps[n].prec) { - state->errcode = IMAGING_CODEC_BROKEN; - state->state = J2K_STATE_FAILED; - goto quick_exit; - } } /* @@ -408,15 +571,51 @@ j2k_decode_entry(Imaging im, ImagingCodecState state, goto quick_exit; } - /* Decode and unpack the image */ - if (!opj_decode(codec, stream, image) - || !opj_end_decompress(codec, stream)) { - state->errcode = IMAGING_CODEC_BROKEN; - state->state = J2K_STATE_FAILED; - goto quick_exit; - } + /* Decode the image tile-by-tile; this means we only need use as much + memory as is required for one tile's worth of components. */ + for (;;) { + JPEG2KTILEINFO tile_info; + OPJ_BOOL should_continue; - unpack(image, im); + if (!opj_read_tile_header(codec, + stream, + &tile_info.tile_index, + &tile_info.data_size, + &tile_info.x0, &tile_info.y0, + &tile_info.x1, &tile_info.y1, + &tile_info.nb_comps, + &should_continue)) { + state->errcode = IMAGING_CODEC_BROKEN; + state->state = J2K_STATE_FAILED; + goto quick_exit; + } + + if (!should_continue) + break; + + if (buffer_size < tile_info.data_size) { + UINT8 *new = realloc (state->buffer, tile_info.data_size); + if (!new) { + state->errcode = IMAGING_CODEC_MEMORY; + state->state = J2K_STATE_FAILED; + goto quick_exit; + } + state->buffer = new; + buffer_size = tile_info.data_size; + } + + if (!opj_decode_tile_data(codec, + tile_info.tile_index, + (OPJ_BYTE *)state->buffer, + tile_info.data_size, + stream)) { + state->errcode = IMAGING_CODEC_BROKEN; + state->state = J2K_STATE_FAILED; + goto quick_exit; + } + + unpack(image, &tile_info, state->buffer, im); + } quick_exit: if (codec) From aea0ec56b2b6caaa3690b9bf951703d2c74fd524 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Thu, 13 Mar 2014 13:48:15 +0000 Subject: [PATCH 05/20] Fixed a small bug. --- libImaging/Jpeg2KDecode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libImaging/Jpeg2KDecode.c b/libImaging/Jpeg2KDecode.c index b503ffa44..77615cce5 100644 --- a/libImaging/Jpeg2KDecode.c +++ b/libImaging/Jpeg2KDecode.c @@ -264,7 +264,7 @@ j2ku_graya_la(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, UINT8 byte = j2ku_shift(offset + word, shift); row[0] = row[1] = row[2] = byte; - row[3] = (unsigned)(aoffset + aword) >> ashift; + row[3] = j2ku_shift(aoffset + aword, ashift); row += 4; } } From 61fb89ec54818f4e2a4d35e9c4bc97ec7716e6b3 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Thu, 13 Mar 2014 18:27:16 +0000 Subject: [PATCH 06/20] Added a JPEG 2000 encoder. --- PIL/Jpeg2KImagePlugin.py | 30 ++- _imaging.c | 2 + decode.c | 24 +- encode.c | 135 ++++++++++ libImaging/Imaging.h | 25 +- libImaging/Incremental.c | 356 ++++++++++++++----------- libImaging/Jpeg2K.h | 50 +++- libImaging/Jpeg2KDecode.c | 40 +-- libImaging/Jpeg2KEncode.c | 548 ++++++++++++++++++++++++++++++++++++++ setup.py | 2 +- 10 files changed, 1004 insertions(+), 208 deletions(-) create mode 100644 libImaging/Jpeg2KEncode.c diff --git a/PIL/Jpeg2KImagePlugin.py b/PIL/Jpeg2KImagePlugin.py index 62aced03e..94d49f705 100644 --- a/PIL/Jpeg2KImagePlugin.py +++ b/PIL/Jpeg2KImagePlugin.py @@ -173,17 +173,29 @@ def _accept(prefix): return (prefix[:4] == b'\xff\x4f\xff\x51' or prefix[:12] == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a') +# ------------------------------------------------------------ +# Save support + +def _save(im, fp, filename): + if filename.endswith('.j2k'): + kind = 'j2k' + else: + kind = 'jp2' + + ImageFile._save(im, fp, [('jpeg2k', (0, 0)+im.size, 0, kind)]) + # ------------------------------------------------------------ # Registry stuff -Image.register_open("JPEG2000", Jpeg2KImageFile, _accept) +Image.register_open('JPEG2000', Jpeg2KImageFile, _accept) +Image.register_save('JPEG2000', _save) -Image.register_extension("JPEG2000", ".jp2") -Image.register_extension("JPEG2000", ".j2k") -Image.register_extension("JPEG2000", ".jpc") -Image.register_extension("JPEG2000", ".jpf") -Image.register_extension("JPEG2000", ".jpx") -Image.register_extension("JPEG2000", ".j2c") +Image.register_extension('JPEG2000', '.jp2') +Image.register_extension('JPEG2000', '.j2k') +Image.register_extension('JPEG2000', '.jpc') +Image.register_extension('JPEG2000', '.jpf') +Image.register_extension('JPEG2000', '.jpx') +Image.register_extension('JPEG2000', '.j2c') -Image.register_mime("JPEG2000", "image/jp2") -Image.register_mime("JPEG2000", "image/jpx") +Image.register_mime('JPEG2000', 'image/jp2') +Image.register_mime('JPEG2000', 'image/jpx') diff --git a/_imaging.c b/_imaging.c index 84b25219b..fe623e780 100644 --- a/_imaging.c +++ b/_imaging.c @@ -3300,6 +3300,7 @@ extern PyObject* PyImaging_ZipDecoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_EpsEncoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_GifEncoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_JpegEncoderNew(PyObject* self, PyObject* args); +extern PyObject* PyImaging_Jpeg2KEncoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_PcxEncoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_RawEncoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_XbmEncoderNew(PyObject* self, PyObject* args); @@ -3355,6 +3356,7 @@ static PyMethodDef functions[] = { #endif #ifdef HAVE_OPENJPEG {"jpeg2k_decoder", (PyCFunction)PyImaging_Jpeg2KDecoderNew, 1}, + {"jpeg2k_encoder", (PyCFunction)PyImaging_Jpeg2KEncoderNew, 1}, #endif {"tiff_lzw_decoder", (PyCFunction)PyImaging_TiffLzwDecoderNew, 1}, #ifdef HAVE_LIBTIFF diff --git a/decode.c b/decode.c index 359b99695..0bc64ec77 100644 --- a/decode.c +++ b/decode.c @@ -778,27 +778,18 @@ PyImaging_JpegDecoderNew(PyObject* self, PyObject* args) #endif /* -------------------------------------------------------------------- */ -/* JPEG2000 */ +/* JPEG 2000 */ /* -------------------------------------------------------------------- */ #ifdef HAVE_OPENJPEG -/* We better define this decoder last in this file, so the following - undef's won't mess things up for the Imaging library proper. */ -#undef UINT8 -#undef UINT16 -#undef UINT32 -#undef INT8 -#undef INT16 -#undef INT32 - #include "Jpeg2K.h" PyObject* PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args) { ImagingDecoderObject* decoder; - JPEG2KSTATE *context; + JPEG2KDECODESTATE *context; char* mode; char* format; @@ -809,16 +800,16 @@ PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args) &reduce, &layers)) return NULL; - if (strcmp (format, "j2k") == 0) + if (strcmp(format, "j2k") == 0) codec_format = OPJ_CODEC_J2K; - else if (strcmp (format, "jpt") == 0) + else if (strcmp(format, "jpt") == 0) codec_format = OPJ_CODEC_JPT; - else if (strcmp (format, "jp2") == 0) + else if (strcmp(format, "jp2") == 0) codec_format = OPJ_CODEC_JP2; else return NULL; - decoder = PyImaging_DecoderNew(sizeof(JPEG2KSTATE)); + decoder = PyImaging_DecoderNew(sizeof(JPEG2KDECODESTATE)); if (decoder == NULL) return NULL; @@ -826,9 +817,8 @@ PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args) decoder->decode = ImagingJpeg2KDecode; decoder->cleanup = ImagingJpeg2KDecodeCleanup; - context = (JPEG2KSTATE *)decoder->state.context; + context = (JPEG2KDECODESTATE *)decoder->state.context; - strncpy(context->mode, mode, 8); context->format = codec_format; context->reduce = reduce; context->layers = layers; diff --git a/encode.c b/encode.c index 8be44d8ec..979742a84 100644 --- a/encode.c +++ b/encode.c @@ -40,6 +40,7 @@ typedef struct { PyObject_HEAD int (*encode)(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); + int (*cleanup)(ImagingCodecState state); struct ImagingCodecStateInstance state; Imaging im; PyObject* lock; @@ -77,6 +78,9 @@ PyImaging_EncoderNew(int contextsize) /* Initialize encoder context */ encoder->state.context = context; + /* Most encoders don't need this */ + encoder->cleanup = NULL; + /* Target image */ encoder->lock = NULL; encoder->im = NULL; @@ -87,6 +91,8 @@ PyImaging_EncoderNew(int contextsize) static void _dealloc(ImagingEncoderObject* encoder) { + if (encoder->cleanup) + encoder->cleanup(&encoder->state); free(encoder->state.buffer); free(encoder->state.context); Py_XDECREF(encoder->lock); @@ -793,3 +799,132 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) #endif +/* -------------------------------------------------------------------- */ +/* JPEG 2000 */ +/* -------------------------------------------------------------------- */ + +#ifdef HAVE_OPENJPEG + +#include "Jpeg2K.h" + +static void +j2k_decode_coord_tuple(PyObject *tuple, int *x, int *y) +{ + *x = *y = 0; + + if (tuple && PyTuple_Check(tuple) && PyTuple_GET_SIZE(tuple) == 2) { + *x = (int)PyInt_AsLong(PyTuple_GET_ITEM(tuple, 0)); + *y = (int)PyInt_AsLong(PyTuple_GET_ITEM(tuple, 1)); + + if (*x < 0) + *x = 0; + if (*y < 0) + *y = 0; + } +} + +PyObject* +PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) +{ + ImagingEncoderObject *encoder; + JPEG2KENCODESTATE *context; + + char *mode; + char *format; + OPJ_CODEC_FORMAT codec_format; + PyObject *offset = NULL, *tile_offset = NULL, *tile_size = NULL; + char *quality_mode = "rates"; + PyObject *quality_layers = NULL; + int num_resolutions = 0; + PyObject *cblk_size = NULL; + int irreversible = 0; + char *progression = "LRCP"; + OPJ_PROG_ORDER prog_order; + char *cinema_mode = "no"; + OPJ_CINEMA_MODE cine_mode; + + if (!PyArg_ParseTuple(args, "ss|OOOsOiOpss", &mode, &format, + &offset, &tile_offset, &tile_size, + &quality_mode, &quality_layers, &num_resolutions, + &cblk_size, &irreversible, &progression, &cinema_mode)) + return NULL; + + if (strcmp (format, "j2k") == 0) + codec_format = OPJ_CODEC_J2K; + else if (strcmp (format, "jpt") == 0) + codec_format = OPJ_CODEC_JPT; + else if (strcmp (format, "jp2") == 0) + codec_format = OPJ_CODEC_JP2; + else + return NULL; + + if (strcmp(progression, "LRCP") == 0) + prog_order = OPJ_LRCP; + else if (strcmp(progression, "RLCP") == 0) + prog_order = OPJ_RLCP; + else if (strcmp(progression, "RPCL") == 0) + prog_order = OPJ_RPCL; + else if (strcmp(progression, "PCRL") == 0) + prog_order = OPJ_PCRL; + else if (strcmp(progression, "CPRL") == 0) + prog_order = OPJ_CPRL; + else + return NULL; + + if (strcmp(cinema_mode, "no") == 0) + cine_mode = OPJ_OFF; + else if (strcmp(cinema_mode, "cinema2k-24") == 0) + cine_mode = OPJ_CINEMA2K_24; + else if (strcmp(cinema_mode, "cinema2k-48") == 0) + cine_mode = OPJ_CINEMA2K_48; + else if (strcmp(cinema_mode, "cinema4k-24") == 0) + cine_mode = OPJ_CINEMA4K_24; + else + return NULL; + + encoder = PyImaging_EncoderNew(sizeof(JPEG2KENCODESTATE)); + if (!encoder) + return NULL; + + encoder->encode = ImagingJpeg2KEncode; + encoder->cleanup = ImagingJpeg2KEncodeCleanup; + + context = (JPEG2KENCODESTATE *)encoder->state.context; + + context->format = codec_format; + context->offset_x = context->offset_y = 0; + + j2k_decode_coord_tuple(offset, &context->offset_x, &context->offset_y); + j2k_decode_coord_tuple(tile_offset, + &context->tile_offset_x, + &context->tile_offset_y); + j2k_decode_coord_tuple(tile_size, + &context->tile_size_x, + &context->tile_size_y); + + if (quality_layers && PySequence_Check(quality_layers)) { + context->quality_is_in_db = strcmp (quality_mode, "dB") == 0; + context->quality_layers = quality_layers; + } + + context->num_resolutions = num_resolutions; + + j2k_decode_coord_tuple(cblk_size, + &context->cblk_width, + &context->cblk_height); + + context->irreversible = irreversible; + context->progression = prog_order; + context->cinema_mode = cine_mode; + + return (PyObject *)encoder; +} + +#endif + +/* + * Local Variables: + * c-basic-offset: 4 + * End: + * + */ diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h index 7a7eb9c66..1602622a2 100644 --- a/libImaging/Imaging.h +++ b/libImaging/Imaging.h @@ -428,6 +428,9 @@ extern int ImagingJpegEncode(Imaging im, ImagingCodecState state, extern int ImagingJpeg2KDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); extern int ImagingJpeg2KDecodeCleanup(ImagingCodecState state); +extern int ImagingJpeg2KEncode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +extern int ImagingJpeg2KEncodeCleanup(ImagingCodecState state); #endif extern int ImagingLzwDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); @@ -502,18 +505,20 @@ struct ImagingCodecStateInstance { void *context; }; -/* Incremental decoding support */ -typedef struct ImagingIncrementalDecoderStruct *ImagingIncrementalDecoder; +/* Incremental encoding/decoding support */ +typedef struct ImagingIncrementalCodecStruct *ImagingIncrementalCodec; -typedef int (*ImagingIncrementalDecoderEntry)(Imaging im, - ImagingCodecState state, - ImagingIncrementalDecoder decoder); +typedef int (*ImagingIncrementalCodecEntry)(Imaging im, + ImagingCodecState state, + ImagingIncrementalCodec codec); -extern ImagingIncrementalDecoder ImagingIncrementalDecoderCreate(ImagingIncrementalDecoderEntry decoder_entry, Imaging im, ImagingCodecState state); -extern void ImagingIncrementalDecoderDestroy(ImagingIncrementalDecoder decoder); -extern int ImagingIncrementalDecodeData(ImagingIncrementalDecoder decoder, UINT8 *buf, int bytes); -size_t ImagingIncrementalDecoderRead(ImagingIncrementalDecoder decoder, void *buffer, size_t bytes); -off_t ImagingIncrementalDecoderSkip(ImagingIncrementalDecoder decoder, off_t bytes); +extern ImagingIncrementalCodec ImagingIncrementalCodecCreate(ImagingIncrementalCodecEntry codec_entry, Imaging im, ImagingCodecState state); +extern void ImagingIncrementalCodecDestroy(ImagingIncrementalCodec codec); +extern int ImagingIncrementalCodecPushBuffer(ImagingIncrementalCodec codec, UINT8 *buf, int bytes); +extern size_t ImagingIncrementalCodecRead(ImagingIncrementalCodec codec, void *buffer, size_t bytes); +extern off_t ImagingIncrementalCodecSkip(ImagingIncrementalCodec codec, off_t bytes); +extern size_t ImagingIncrementalCodecWrite(ImagingIncrementalCodec codec, const void *buffer, size_t bytes); +extern size_t ImagingIncrementalCodecBytesInBuffer(ImagingIncrementalCodec codec); /* Errcodes */ #define IMAGING_CODEC_END 1 diff --git a/libImaging/Incremental.c b/libImaging/Incremental.c index bb51d91c7..45f064570 100644 --- a/libImaging/Incremental.c +++ b/libImaging/Incremental.c @@ -13,9 +13,9 @@ /* The idea behind this interface is simple: the actual decoding proceeds in a thread, which is run in lock step with the main thread. Whenever the - ImagingIncrementalDecoderRead() call runs short on data, it suspends the + ImagingIncrementalCodecRead() call runs short on data, it suspends the decoding thread and wakes the main thread. Conversely, the - ImagingIncrementalDecodeData() call suspends the main thread and wakes + ImagingIncrementalCodecPushBuffer() call suspends the main thread and wakes the decoding thread, providing a buffer of data. The two threads are never running simultaneously, so there is no need for @@ -50,27 +50,21 @@ #define DEBUG(...) #endif -struct ImagingIncrementalStreamStruct { - UINT8 *buffer; - UINT8 *ptr; - UINT8 *end; -}; - -struct ImagingIncrementalDecoderStruct { +struct ImagingIncrementalCodecStruct { #ifdef _WIN32 - HANDLE hDecodeEvent; + HANDLE hCodecEvent; HANDLE hDataEvent; HANDLE hThread; #else pthread_mutex_t start_mutex; pthread_cond_t start_cond; - pthread_mutex_t decode_mutex; - pthread_cond_t decode_cond; + pthread_mutex_t codec_mutex; + pthread_cond_t codec_cond; pthread_mutex_t data_mutex; pthread_cond_t data_cond; pthread_t thread; #endif - ImagingIncrementalDecoderEntry entry; + ImagingIncrementalCodecEntry entry; Imaging im; ImagingCodecState state; struct { @@ -84,222 +78,228 @@ struct ImagingIncrementalDecoderStruct { #if _WIN32 static void __stdcall -incremental_thread(void *ptr) +codec_thread(void *ptr) { - ImagingIncrementalDecoder decoder = (ImagingIncrementalDecoder)ptr; + ImagingIncrementalCodec codec = (ImagingIncrementalCodec)ptr; - decoder->result = decoder->entry(decoder->im, decoder->state, decoder); + codec->result = codec->entry(codec->im, codec->state, codec); - SetEvent(decoder->hDecodeEvent); + SetEvent(codec->hCodecEvent); } #else static void * -incremental_thread(void *ptr) +codec_thread(void *ptr) { - ImagingIncrementalDecoder decoder = (ImagingIncrementalDecoder)ptr; + ImagingIncrementalCodec codec = (ImagingIncrementalCodec)ptr; - decoder->result = decoder->entry(decoder->im, decoder->state, decoder); + codec->result = codec->entry(codec->im, codec->state, codec); - pthread_cond_signal(&decoder->decode_cond); + pthread_cond_signal(&codec->codec_cond); return NULL; } #endif /** - * Create a new incremental decoder */ -ImagingIncrementalDecoder -ImagingIncrementalDecoderCreate(ImagingIncrementalDecoderEntry decoder_entry, - Imaging im, - ImagingCodecState state) + * Create a new incremental codec */ +ImagingIncrementalCodec +ImagingIncrementalCodecCreate(ImagingIncrementalCodecEntry codec_entry, + Imaging im, + ImagingCodecState state) { - ImagingIncrementalDecoder decoder = (ImagingIncrementalDecoder)malloc(sizeof(struct ImagingIncrementalDecoderStruct)); + ImagingIncrementalCodec codec = (ImagingIncrementalCodec)malloc(sizeof(struct ImagingIncrementalCodecStruct)); - decoder->entry = decoder_entry; - decoder->im = im; - decoder->state = state; - decoder->result = 0; - decoder->stream.buffer = decoder->stream.ptr = decoder->stream.end = NULL; - decoder->started = 0; + codec->entry = codec_entry; + codec->im = im; + codec->state = state; + codec->result = 0; + codec->stream.buffer = codec->stream.ptr = codec->stream.end = NULL; + codec->started = 0; /* System specific set-up */ #if _WIN32 - decoder->hDecodeEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + codec->hCodecEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if (!decoder->hDecodeEvent) { - free(decoder); + if (!codec->hCodecEvent) { + free(codec); return NULL; } - decoder->hDataEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + codec->hDataEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if (!decoder->hDataEvent) { - CloseHandle(decoder->hDecodeEvent); - free(decoder); + if (!codec->hDataEvent) { + CloseHandle(codec->hCodecEvent); + free(codec); return NULL; } - decoder->hThread = _beginthreadex(NULL, 0, incremental_thread, decoder, + codec->hThread = _beginthreadex(NULL, 0, codec_thread, codec, CREATE_SUSPENDED, NULL); - if (!decoder->hThread) { - CloseHandle(decoder->hDecodeEvent); - CloseHandle(decoder->hDataEvent); - free(decoder); + if (!codec->hThread) { + CloseHandle(codec->hCodecEvent); + CloseHandle(codec->hDataEvent); + free(codec); return NULL; } #else - if (pthread_mutex_init(&decoder->start_mutex, NULL)) { - free (decoder); + if (pthread_mutex_init(&codec->start_mutex, NULL)) { + free (codec); return NULL; } - if (pthread_mutex_init(&decoder->decode_mutex, NULL)) { - pthread_mutex_destroy(&decoder->start_mutex); - free(decoder); + if (pthread_mutex_init(&codec->codec_mutex, NULL)) { + pthread_mutex_destroy(&codec->start_mutex); + free(codec); return NULL; } - if (pthread_mutex_init(&decoder->data_mutex, NULL)) { - pthread_mutex_destroy(&decoder->start_mutex); - pthread_mutex_destroy(&decoder->decode_mutex); - free(decoder); + if (pthread_mutex_init(&codec->data_mutex, NULL)) { + pthread_mutex_destroy(&codec->start_mutex); + pthread_mutex_destroy(&codec->codec_mutex); + free(codec); return NULL; } - if (pthread_cond_init(&decoder->start_cond, NULL)) { - pthread_mutex_destroy(&decoder->start_mutex); - pthread_mutex_destroy(&decoder->decode_mutex); - pthread_mutex_destroy(&decoder->data_mutex); - free(decoder); + if (pthread_cond_init(&codec->start_cond, NULL)) { + pthread_mutex_destroy(&codec->start_mutex); + pthread_mutex_destroy(&codec->codec_mutex); + pthread_mutex_destroy(&codec->data_mutex); + free(codec); return NULL; } - if (pthread_cond_init(&decoder->decode_cond, NULL)) { - pthread_mutex_destroy(&decoder->start_mutex); - pthread_mutex_destroy(&decoder->decode_mutex); - pthread_mutex_destroy(&decoder->data_mutex); - pthread_cond_destroy(&decoder->start_cond); - free(decoder); + if (pthread_cond_init(&codec->codec_cond, NULL)) { + pthread_mutex_destroy(&codec->start_mutex); + pthread_mutex_destroy(&codec->codec_mutex); + pthread_mutex_destroy(&codec->data_mutex); + pthread_cond_destroy(&codec->start_cond); + free(codec); return NULL; } - if (pthread_cond_init(&decoder->data_cond, NULL)) { - pthread_mutex_destroy(&decoder->start_mutex); - pthread_mutex_destroy(&decoder->decode_mutex); - pthread_mutex_destroy(&decoder->data_mutex); - pthread_cond_destroy(&decoder->start_cond); - pthread_cond_destroy(&decoder->decode_cond); - free(decoder); + if (pthread_cond_init(&codec->data_cond, NULL)) { + pthread_mutex_destroy(&codec->start_mutex); + pthread_mutex_destroy(&codec->codec_mutex); + pthread_mutex_destroy(&codec->data_mutex); + pthread_cond_destroy(&codec->start_cond); + pthread_cond_destroy(&codec->codec_cond); + free(codec); return NULL; } - if (pthread_create(&decoder->thread, NULL, incremental_thread, decoder)) { - pthread_mutex_destroy(&decoder->start_mutex); - pthread_mutex_destroy(&decoder->decode_mutex); - pthread_mutex_destroy(&decoder->data_mutex); - pthread_cond_destroy(&decoder->start_cond); - pthread_cond_destroy(&decoder->decode_cond); - pthread_cond_destroy(&decoder->data_cond); - free(decoder); + if (pthread_create(&codec->thread, NULL, codec_thread, codec)) { + pthread_mutex_destroy(&codec->start_mutex); + pthread_mutex_destroy(&codec->codec_mutex); + pthread_mutex_destroy(&codec->data_mutex); + pthread_cond_destroy(&codec->start_cond); + pthread_cond_destroy(&codec->codec_cond); + pthread_cond_destroy(&codec->data_cond); + free(codec); return NULL; } #endif - return decoder; + return codec; } /** - * Destroy an incremental decoder */ + * Destroy an incremental codec */ void -ImagingIncrementalDecoderDestroy(ImagingIncrementalDecoder decoder) +ImagingIncrementalCodecDestroy(ImagingIncrementalCodec codec) { DEBUG("destroying\n"); - if (!decoder->started) { + if (!codec->started) { #ifdef _WIN32 - ResumeThread(decoder->hThread); + ResumeThread(codec->hThread); #else - pthread_cond_signal(&decoder->start_cond); + pthread_cond_signal(&codec->start_cond); #endif - decoder->started = 1; + codec->started = 1; } #ifndef _WIN32 - pthread_mutex_lock(&decoder->data_mutex); + pthread_mutex_lock(&codec->data_mutex); #endif - decoder->stream.buffer = decoder->stream.ptr = decoder->stream.end = NULL; + codec->stream.buffer = codec->stream.ptr = codec->stream.end = NULL; #ifdef _WIN32 - SetEvent(decoder->hDataEvent); + SetEvent(codec->hDataEvent); - WaitForSingleObject(decoder->hThread, INFINITE); + WaitForSingleObject(codec->hThread, INFINITE); - CloseHandle(decoder->hThread); - CloseHandle(decoder->hDecodeEvent); - CloseHandle(decoder->hDataEvent); + CloseHandle(codec->hThread); + CloseHandle(codec->hCodecEvent); + CloseHandle(codec->hDataEvent); #else - pthread_cond_signal(&decoder->data_cond); - pthread_mutex_unlock(&decoder->data_mutex); + pthread_cond_signal(&codec->data_cond); + pthread_mutex_unlock(&codec->data_mutex); - pthread_join(decoder->thread, NULL); + pthread_join(codec->thread, NULL); - pthread_mutex_destroy(&decoder->start_mutex); - pthread_mutex_destroy(&decoder->decode_mutex); - pthread_mutex_destroy(&decoder->data_mutex); - pthread_cond_destroy(&decoder->start_cond); - pthread_cond_destroy(&decoder->decode_cond); - pthread_cond_destroy(&decoder->data_cond); + pthread_mutex_destroy(&codec->start_mutex); + pthread_mutex_destroy(&codec->codec_mutex); + pthread_mutex_destroy(&codec->data_mutex); + pthread_cond_destroy(&codec->start_cond); + pthread_cond_destroy(&codec->codec_cond); + pthread_cond_destroy(&codec->data_cond); #endif - free (decoder); + free (codec); } /** - * Decode data using an incremental decoder */ + * Push a data buffer for an incremental codec */ int -ImagingIncrementalDecodeData(ImagingIncrementalDecoder decoder, - UINT8 *buf, int bytes) +ImagingIncrementalCodecPushBuffer(ImagingIncrementalCodec codec, + UINT8 *buf, int bytes) { - if (!decoder->started) { + if (!codec->started) { DEBUG("starting\n"); #ifdef _WIN32 - ResumeThread(decoder->hThread); + ResumeThread(codec->hThread); #else - pthread_cond_signal(&decoder->start_cond); + pthread_cond_signal(&codec->start_cond); #endif - decoder->started = 1; + codec->started = 1; } DEBUG("providing %p, %d\n", buf, bytes); #ifndef _WIN32 - pthread_mutex_lock(&decoder->data_mutex); + pthread_mutex_lock(&codec->data_mutex); #endif - decoder->stream.buffer = decoder->stream.ptr = buf; - decoder->stream.end = buf + bytes; + codec->stream.buffer = codec->stream.ptr = buf; + codec->stream.end = buf + bytes; #ifdef _WIN32 - SetEvent(decoder->hDataEvent); - WaitForSingleObject(decoder->hDecodeEvent); + SetEvent(codec->hDataEvent); + WaitForSingleObject(codec->hCodecEvent); #else - pthread_cond_signal(&decoder->data_cond); - pthread_mutex_unlock(&decoder->data_mutex); + pthread_cond_signal(&codec->data_cond); + pthread_mutex_unlock(&codec->data_mutex); - pthread_mutex_lock(&decoder->decode_mutex); - pthread_cond_wait(&decoder->decode_cond, &decoder->decode_mutex); - pthread_mutex_unlock(&decoder->decode_mutex); + pthread_mutex_lock(&codec->codec_mutex); + pthread_cond_wait(&codec->codec_cond, &codec->codec_mutex); + pthread_mutex_unlock(&codec->codec_mutex); #endif - DEBUG("got result %d\n", decoder->result); + DEBUG("got result %d\n", codec->result); - return decoder->result; + return codec->result; } size_t -ImagingIncrementalDecoderRead(ImagingIncrementalDecoder decoder, +ImagingIncrementalCodecBytesInBuffer(ImagingIncrementalCodec codec) +{ + return codec->stream.ptr - codec->stream.buffer; +} + +size_t +ImagingIncrementalCodecRead(ImagingIncrementalCodec codec, void *buffer, size_t bytes) { UINT8 *ptr = (UINT8 *)buffer; @@ -308,29 +308,29 @@ ImagingIncrementalDecoderRead(ImagingIncrementalDecoder decoder, DEBUG("reading (want %llu bytes)\n", (unsigned long long)bytes); #ifndef _WIN32 - pthread_mutex_lock(&decoder->data_mutex); + pthread_mutex_lock(&codec->data_mutex); #endif while (bytes) { size_t todo = bytes; - size_t remaining = decoder->stream.end - decoder->stream.ptr; + size_t remaining = codec->stream.end - codec->stream.ptr; if (!remaining) { DEBUG("waiting for data\n"); #ifndef _WIN32 - pthread_mutex_lock(&decoder->decode_mutex); + pthread_mutex_lock(&codec->codec_mutex); #endif - decoder->result = (int)(decoder->stream.ptr - decoder->stream.buffer); + codec->result = (int)(codec->stream.ptr - codec->stream.buffer); #if _WIN32 - SetEvent(decoder->hDecodeEvent); - WaitForSingleObject(decoder->hDataEvent); + SetEvent(codec->hCodecEvent); + WaitForSingleObject(codec->hDataEvent); #else - pthread_cond_signal(&decoder->decode_cond); - pthread_mutex_unlock(&decoder->decode_mutex); - pthread_cond_wait(&decoder->data_cond, &decoder->data_mutex); + pthread_cond_signal(&codec->codec_cond); + pthread_mutex_unlock(&codec->codec_mutex); + pthread_cond_wait(&codec->data_cond, &codec->data_mutex); #endif - remaining = decoder->stream.end - decoder->stream.ptr; + remaining = codec->stream.end - codec->stream.ptr; DEBUG("got %llu bytes\n", (unsigned long long)remaining); } @@ -340,14 +340,14 @@ ImagingIncrementalDecoderRead(ImagingIncrementalDecoder decoder, if (!todo) break; - memcpy (ptr, decoder->stream.ptr, todo); - decoder->stream.ptr += todo; + memcpy (ptr, codec->stream.ptr, todo); + codec->stream.ptr += todo; bytes -= todo; done += todo; ptr += todo; } #ifndef _WIN32 - pthread_mutex_unlock(&decoder->data_mutex); + pthread_mutex_unlock(&codec->data_mutex); #endif DEBUG("read total %llu bytes\n", (unsigned long long)done); @@ -356,7 +356,7 @@ ImagingIncrementalDecoderRead(ImagingIncrementalDecoder decoder, } off_t -ImagingIncrementalDecoderSkip(ImagingIncrementalDecoder decoder, +ImagingIncrementalCodecSkip(ImagingIncrementalCodec codec, off_t bytes) { off_t done = 0; @@ -364,29 +364,29 @@ ImagingIncrementalDecoderSkip(ImagingIncrementalDecoder decoder, DEBUG("skipping (want %llu bytes)\n", (unsigned long long)bytes); #ifndef _WIN32 - pthread_mutex_lock(&decoder->data_mutex); + pthread_mutex_lock(&codec->data_mutex); #endif while (bytes) { off_t todo = bytes; - off_t remaining = decoder->stream.end - decoder->stream.ptr; + off_t remaining = codec->stream.end - codec->stream.ptr; if (!remaining) { DEBUG("waiting for data\n"); #ifndef _WIN32 - pthread_mutex_lock(&decoder->decode_mutex); + pthread_mutex_lock(&codec->codec_mutex); #endif - decoder->result = (int)(decoder->stream.ptr - decoder->stream.buffer); + codec->result = (int)(codec->stream.ptr - codec->stream.buffer); #if _WIN32 - SetEvent(decoder->hDecodeEvent); - WaitForSingleObject(decoder->hDataEvent); + SetEvent(codec->hCodecEvent); + WaitForSingleObject(codec->hDataEvent); #else - pthread_cond_signal(&decoder->decode_cond); - pthread_mutex_unlock(&decoder->decode_mutex); - pthread_cond_wait(&decoder->data_cond, &decoder->data_mutex); + pthread_cond_signal(&codec->codec_cond); + pthread_mutex_unlock(&codec->codec_mutex); + pthread_cond_wait(&codec->data_cond, &codec->data_mutex); #endif - remaining = decoder->stream.end - decoder->stream.ptr; + remaining = codec->stream.end - codec->stream.ptr; } if (todo > remaining) todo = remaining; @@ -394,12 +394,12 @@ ImagingIncrementalDecoderSkip(ImagingIncrementalDecoder decoder, if (!todo) break; - decoder->stream.ptr += todo; + codec->stream.ptr += todo; bytes -= todo; done += todo; } #ifndef _WIN32 - pthread_mutex_unlock(&decoder->data_mutex); + pthread_mutex_unlock(&codec->data_mutex); #endif DEBUG("skipped total %llu bytes\n", (unsigned long long)done); @@ -407,3 +407,59 @@ ImagingIncrementalDecoderSkip(ImagingIncrementalDecoder decoder, return done; } +size_t +ImagingIncrementalCodecWrite(ImagingIncrementalCodec codec, + const void *buffer, size_t bytes) +{ + const UINT8 *ptr = (const UINT8 *)buffer; + size_t done = 0; + + DEBUG("write (have %llu bytes)\n", (unsigned long long)bytes); + +#ifndef _WIN32 + pthread_mutex_lock(&codec->data_mutex); +#endif + while (bytes) { + size_t todo = bytes; + size_t remaining = codec->stream.end - codec->stream.ptr; + + if (!remaining) { + DEBUG("waiting for space\n"); + +#ifndef _WIN32 + pthread_mutex_lock(&codec->codec_mutex); +#endif + codec->result = (int)(codec->stream.ptr - codec->stream.buffer); +#if _WIN32 + SetEvent(codec->hCodecEvent); + WaitForSingleObject(codec->hDataEvent); +#else + pthread_cond_signal(&codec->codec_cond); + pthread_mutex_unlock(&codec->codec_mutex); + pthread_cond_wait(&codec->data_cond, &codec->data_mutex); +#endif + + remaining = codec->stream.end - codec->stream.ptr; + + DEBUG("got %llu bytes\n", (unsigned long long)remaining); + } + if (todo > remaining) + todo = remaining; + + if (!todo) + break; + + memcpy (codec->stream.ptr, ptr, todo); + codec->stream.ptr += todo; + bytes -= todo; + done += todo; + ptr += todo; + } +#ifndef _WIN32 + pthread_mutex_unlock(&codec->data_mutex); +#endif + + DEBUG("wrote total %llu bytes\n", (unsigned long long)done); + + return done; +} diff --git a/libImaging/Jpeg2K.h b/libImaging/Jpeg2K.h index 269357296..128302f19 100644 --- a/libImaging/Jpeg2K.h +++ b/libImaging/Jpeg2K.h @@ -12,13 +12,11 @@ /* -------------------------------------------------------------------- */ /* Decoder */ +/* -------------------------------------------------------------------- */ typedef struct { /* CONFIGURATION */ - /* Output mode */ - char mode[8]; - /* Specify the desired format */ OPJ_CODEC_FORMAT format; @@ -31,10 +29,50 @@ typedef struct { /* PRIVATE CONTEXT (set by decoder) */ const char *error_msg; - ImagingIncrementalDecoder decoder; + ImagingIncrementalCodec decoder; +} JPEG2KDECODESTATE; - opj_stream_t *stream; -} JPEG2KSTATE; +/* -------------------------------------------------------------------- */ +/* Encoder */ +/* -------------------------------------------------------------------- */ + +typedef struct { + /* CONFIGURATION */ + + /* Specify the desired format */ + OPJ_CODEC_FORMAT format; + + /* Image offset */ + int offset_x, offset_y; + + /* Tile information */ + int tile_offset_x, tile_offset_y; + int tile_size_x, tile_size_y; + + /* Quality layers (a sequence of numbers giving *either* rates or dB) */ + int quality_is_in_db; + PyObject *quality_layers; + + /* Number of resolutions (DWT decompositions + 1 */ + int num_resolutions; + + /* Code block size */ + int cblk_width, cblk_height; + + /* Compression style */ + int irreversible; + + /* Progression order (LRCP/RLCP/RPCL/PCRL/CPRL) */ + OPJ_PROG_ORDER progression; + + /* Cinema mode */ + OPJ_CINEMA_MODE cinema_mode; + + /* PRIVATE CONTEXT (set by decoder) */ + const char *error_msg; + + ImagingIncrementalCodec encoder; +} JPEG2KENCODESTATE; /* * Local Variables: diff --git a/libImaging/Jpeg2KDecode.c b/libImaging/Jpeg2KDecode.c index 77615cce5..c3254d889 100644 --- a/libImaging/Jpeg2KDecode.c +++ b/libImaging/Jpeg2KDecode.c @@ -34,7 +34,7 @@ typedef struct { static void j2k_error(const char *msg, void *client_data) { - JPEG2KSTATE *state = (JPEG2KSTATE *) client_data; + JPEG2KDECODESTATE *state = (JPEG2KDECODESTATE *) client_data; free((void *)state->error_msg); state->error_msg = strdup(msg); } @@ -46,9 +46,9 @@ j2k_error(const char *msg, void *client_data) static OPJ_SIZE_T j2k_read(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) { - ImagingIncrementalDecoder decoder = (ImagingIncrementalDecoder)p_user_data; + ImagingIncrementalCodec decoder = (ImagingIncrementalCodec)p_user_data; - size_t len = ImagingIncrementalDecoderRead(decoder, p_buffer, p_nb_bytes); + size_t len = ImagingIncrementalCodecRead(decoder, p_buffer, p_nb_bytes); return len ? len : (OPJ_SIZE_T)-1; } @@ -65,8 +65,8 @@ j2k_write(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) static OPJ_OFF_T j2k_skip(OPJ_OFF_T p_nb_bytes, void *p_user_data) { - ImagingIncrementalDecoder decoder = (ImagingIncrementalDecoder)p_user_data; - off_t pos = ImagingIncrementalDecoderSkip(decoder, p_nb_bytes); + ImagingIncrementalCodec decoder = (ImagingIncrementalCodec)p_user_data; + off_t pos = ImagingIncrementalCodecSkip(decoder, p_nb_bytes); return pos ? pos : (OPJ_OFF_T)-1; } @@ -455,9 +455,9 @@ enum { static int j2k_decode_entry(Imaging im, ImagingCodecState state, - ImagingIncrementalDecoder decoder) + ImagingIncrementalCodec decoder) { - JPEG2KSTATE *context = (JPEG2KSTATE *) state->context; + JPEG2KDECODESTATE *context = (JPEG2KDECODESTATE *) state->context; opj_stream_t *stream = NULL; opj_image_t *image = NULL; opj_codec_t *codec = NULL; @@ -482,7 +482,6 @@ j2k_decode_entry(Imaging im, ImagingCodecState state, opj_stream_set_user_data(stream, context->decoder); - /* Setup decompression context */ context->error_msg = NULL; @@ -559,7 +558,7 @@ j2k_decode_entry(Imaging im, ImagingCodecState state, for (n = 0; n < sizeof(j2k_unpackers) / sizeof (j2k_unpackers[0]); ++n) { if (color_space == j2k_unpackers[n].color_space && image->numcomps == j2k_unpackers[n].components - && strcmp (context->mode, j2k_unpackers[n].mode) == 0) { + && strcmp (im->mode, j2k_unpackers[n].mode) == 0) { unpack = j2k_unpackers[n].unpacker; break; } @@ -617,6 +616,14 @@ j2k_decode_entry(Imaging im, ImagingCodecState state, unpack(image, &tile_info, state->buffer, im); } + if (!opj_end_decompress(codec, stream)) { + state->errcode = IMAGING_CODEC_BROKEN; + state->state = J2K_STATE_FAILED; + goto quick_exit; + } + + state->state = J2K_STATE_DONE; + quick_exit: if (codec) opj_destroy_codec(codec); @@ -631,14 +638,14 @@ j2k_decode_entry(Imaging im, ImagingCodecState state, int ImagingJpeg2KDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) { - JPEG2KSTATE *context = (JPEG2KSTATE *) state->context; + JPEG2KDECODESTATE *context = (JPEG2KDECODESTATE *) state->context; if (state->state == J2K_STATE_DONE || state->state == J2K_STATE_FAILED) return -1; if (state->state == J2K_STATE_START) { - context->decoder = ImagingIncrementalDecoderCreate(j2k_decode_entry, - im, state); + context->decoder = ImagingIncrementalCodecCreate(j2k_decode_entry, + im, state); if (!context->decoder) { state->errcode = IMAGING_CODEC_BROKEN; @@ -649,7 +656,7 @@ ImagingJpeg2KDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) state->state = J2K_STATE_DECODING; } - return ImagingIncrementalDecodeData(context->decoder, buf, bytes); + return ImagingIncrementalCodecPushBuffer(context->decoder, buf, bytes); } /* -------------------------------------------------------------------- */ @@ -658,10 +665,13 @@ ImagingJpeg2KDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) int ImagingJpeg2KDecodeCleanup(ImagingCodecState state) { - JPEG2KSTATE *context = (JPEG2KSTATE *)state->context; + JPEG2KDECODESTATE *context = (JPEG2KDECODESTATE *)state->context; + + if (context->error_msg) + free ((void *)context->error_msg); if (context->decoder) - ImagingIncrementalDecoderDestroy(context->decoder); + ImagingIncrementalCodecDestroy(context->decoder); return -1; } diff --git a/libImaging/Jpeg2KEncode.c b/libImaging/Jpeg2KEncode.c new file mode 100644 index 000000000..75f40d58f --- /dev/null +++ b/libImaging/Jpeg2KEncode.c @@ -0,0 +1,548 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * decoder for JPEG2000 image data. + * + * history: + * 2014-03-12 ajh Created + * + * Copyright (c) 2014 Coriolis Systems Limited + * Copyright (c) 2014 Alastair Houghton + * + * See the README file for details on usage and redistribution. + */ + +#include "Imaging.h" + +#ifdef HAVE_OPENJPEG + +#include "Jpeg2K.h" + +#define CINEMA_24_CS_LENGTH 1302083 +#define CINEMA_48_CS_LENGTH 651041 +#define COMP_24_CS_MAX_LENGTH 1041666 +#define COMP_48_CS_MAX_LENGTH 520833 + +/* -------------------------------------------------------------------- */ +/* Error handler */ +/* -------------------------------------------------------------------- */ + +static void +j2k_error(const char *msg, void *client_data) +{ + JPEG2KENCODESTATE *state = (JPEG2KENCODESTATE *) client_data; + free((void *)state->error_msg); + state->error_msg = strdup(msg); +} + +/* -------------------------------------------------------------------- */ +/* Buffer output stream */ +/* -------------------------------------------------------------------- */ + +static OPJ_SIZE_T +j2k_read(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) +{ + /* This should never happen */ + fprintf (stderr, "OpenJPEG has read from our write stream(!)"); + abort(); + return (OPJ_SIZE_T)-1; +} + +static OPJ_SIZE_T +j2k_write(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) +{ + ImagingIncrementalCodec encoder = (ImagingIncrementalCodec)p_user_data; + size_t len = ImagingIncrementalCodecWrite(encoder, p_buffer, p_nb_bytes); + + return len ? len : (OPJ_SIZE_T)-1; +} + +static OPJ_OFF_T +j2k_skip(OPJ_OFF_T p_nb_bytes, void *p_user_data) +{ + ImagingIncrementalCodec decoder = (ImagingIncrementalCodec)p_user_data; + off_t pos = ImagingIncrementalCodecSkip(decoder, p_nb_bytes); + + return pos ? pos : (OPJ_OFF_T)-1; +} + +static OPJ_BOOL +j2k_seek(OPJ_OFF_T p_nb_bytes, void *p_user_data) +{ + /* This should never happen */ + fprintf(stderr, "OpenJPEG tried to seek our write stream(!)"); + abort(); + return OPJ_FALSE; +} + +/* -------------------------------------------------------------------- */ +/* Encoder */ +/* -------------------------------------------------------------------- */ + +typedef void (*j2k_pack_tile_t)(Imaging im, UINT8 *buf, + unsigned x0, unsigned y0, + unsigned w, unsigned h); + +static void +j2k_pack_l(Imaging im, UINT8 *buf, + unsigned x0, unsigned y0, unsigned w, unsigned h) +{ + UINT8 *ptr = buf; + for (unsigned y = 0; y < h; ++y) { + UINT8 *data = (UINT8 *)(im->image[y + y0] + x0); + for (unsigned x = 0; x < w; ++x) + *ptr++ = *data++; + } +} + +static void +j2k_pack_la(Imaging im, UINT8 *buf, + unsigned x0, unsigned y0, unsigned w, unsigned h) +{ + UINT8 *ptr = buf; + UINT8 *ptra = buf + w * h; + for (unsigned y = 0; y < h; ++y) { + UINT8 *data = (UINT8 *)(im->image[y + y0] + 4 * x0); + for (unsigned x = 0; x < w; ++x) { + *ptr++ = data[0]; + *ptra++ = data[3]; + data += 4; + } + } +} + +static void +j2k_pack_rgb(Imaging im, UINT8 *buf, + unsigned x0, unsigned y0, unsigned w, unsigned h) +{ + UINT8 *pr = buf; + UINT8 *pg = pr + w * h; + UINT8 *pb = pg + w * h; + for (unsigned y = 0; y < h; ++y) { + UINT8 *data = (UINT8 *)(im->image[y + y0] + 4 * x0); + for (unsigned x = 0; x < w; ++x) { + *pr++ = data[0]; + *pg++ = data[1]; + *pb++ = data[2]; + data += 4; + } + } +} + +static void +j2k_pack_rgba(Imaging im, UINT8 *buf, + unsigned x0, unsigned y0, unsigned w, unsigned h) +{ + UINT8 *pr = buf; + UINT8 *pg = pr + w * h; + UINT8 *pb = pg + w * h; + UINT8 *pa = pb + w * h; + for (unsigned y = 0; y < h; ++y) { + UINT8 *data = (UINT8 *)(im->image[y + y0] + 4 * x0); + for (unsigned x = 0; x < w; ++x) { + *pr++ = *data++; + *pg++ = *data++; + *pb++ = *data++; + *pa++ = *data++; + } + } +} + +enum { + J2K_STATE_START = 0, + J2K_STATE_ENCODING = 1, + J2K_STATE_DONE = 2, + J2K_STATE_FAILED = 3, +}; + +static void +j2k_set_cinema_params(Imaging im, int components, opj_cparameters_t *params) +{ + float rate; + unsigned n; + + /* These settings have been copied from opj_compress in the OpenJPEG + sources. */ + + params->tile_size_on = OPJ_FALSE; + params->cp_tdx = params->cp_tdy = 1; + params->tp_flag = 'C'; + params->tp_on = 1; + params->cp_tx0 = params->cp_ty0 = 0; + params->image_offset_x0 = params->image_offset_y0 = 0; + params->cblockw_init = 32; + params->cblockh_init = 32; + params->csty |= 0x01; + params->prog_order = OPJ_CPRL; + params->roi_compno = -1; + params->subsampling_dx = params->subsampling_dy = 1; + params->irreversible = 1; + + if (params->cp_cinema == OPJ_CINEMA4K_24) { + float max_rate = ((float)(components * im->xsize * im->ysize * 8) + / (CINEMA_24_CS_LENGTH * 8)); + + params->POC[0].tile = 1; + params->POC[0].resno0 = 0; + params->POC[0].compno0 = 0; + params->POC[0].layno1 = 1; + params->POC[0].resno1 = params->numresolution - 1; + params->POC[0].compno1 = 3; + params->POC[0].prg1 = OPJ_CPRL; + params->POC[1].tile = 1; + params->POC[1].resno0 = 0; + params->POC[1].compno0 = 0; + params->POC[1].layno1 = 1; + params->POC[1].resno1 = params->numresolution - 1; + params->POC[1].compno1 = 3; + params->POC[1].prg1 = OPJ_CPRL; + params->numpocs = 2; + + for (n = 0; n < params->tcp_numlayers; ++n) { + rate = 0; + if (params->tcp_rates[0] == 0) { + params->tcp_rates[n] = max_rate; + } else { + rate = ((float)(components * im->xsize * im->ysize * 8) + / (params->tcp_rates[n] * 8)); + if (rate > CINEMA_24_CS_LENGTH) + params->tcp_rates[n] = max_rate; + } + } + + params->max_comp_size = COMP_24_CS_MAX_LENGTH; + } else { + float max_rate = ((float)(components * im->xsize * im->ysize * 8) + / (CINEMA_48_CS_LENGTH * 8)); + + for (n = 0; n < params->tcp_numlayers; ++n) { + rate = 0; + if (params->tcp_rates[0] == 0) { + params->tcp_rates[n] = max_rate; + } else { + rate = ((float)(components * im->xsize * im->ysize * 8) + / (params->tcp_rates[n] * 8)); + if (rate > CINEMA_48_CS_LENGTH) + params->tcp_rates[n] = max_rate; + } + } + + params->max_comp_size = COMP_48_CS_MAX_LENGTH; + } +} + +static int +j2k_encode_entry(Imaging im, ImagingCodecState state, + ImagingIncrementalCodec encoder) +{ + JPEG2KENCODESTATE *context = (JPEG2KENCODESTATE *)state->context; + opj_stream_t *stream = NULL; + opj_image_t *image = NULL; + opj_codec_t *codec = NULL; + opj_cparameters_t params; + unsigned components; + OPJ_COLOR_SPACE color_space; + opj_image_cmptparm_t image_params[4]; + unsigned xsiz, ysiz; + unsigned tile_width, tile_height; + unsigned tiles_x, tiles_y, num_tiles; + unsigned x, y, tile_ndx; + j2k_pack_tile_t pack; + int ret = -1; + + stream = opj_stream_default_create(OPJ_FALSE); + + if (!stream) { + state->errcode = IMAGING_CODEC_BROKEN; + state->state = J2K_STATE_FAILED; + goto quick_exit; + } + + opj_stream_set_read_function(stream, j2k_read); + opj_stream_set_write_function(stream, j2k_write); + opj_stream_set_skip_function(stream, j2k_skip); + opj_stream_set_seek_function(stream, j2k_seek); + + opj_stream_set_user_data(stream, context->encoder); + + /* Setup an opj_image */ + if (strcmp (im->mode, "L") == 0) { + components = 1; + color_space = OPJ_CLRSPC_GRAY; + pack = j2k_pack_l; + } else if (strcmp (im->mode, "LA") == 0) { + components = 2; + color_space = OPJ_CLRSPC_GRAY; + pack = j2k_pack_la; + } else if (strcmp (im->mode, "RGB") == 0) { + components = 3; + color_space = OPJ_CLRSPC_SRGB; + pack = j2k_pack_rgb; + } else if (strcmp (im->mode, "YCbCr") == 0) { + components = 3; + color_space = OPJ_CLRSPC_SYCC; + pack = j2k_pack_rgb; + } else if (strcmp (im->mode, "RGBA") == 0) { + components = 4; + color_space = OPJ_CLRSPC_SRGB; + pack = j2k_pack_rgba; + } else { + state->errcode = IMAGING_CODEC_BROKEN; + state->state = J2K_STATE_FAILED; + goto quick_exit; + } + + for (unsigned n = 0; n < components; ++n) { + image_params[n].dx = image_params[n].dy = 1; + image_params[n].w = im->xsize; + image_params[n].h = im->ysize; + image_params[n].x0 = image_params[n].y0 = 0; + image_params[n].prec = 8; + image_params[n].bpp = 8; + image_params[n].sgnd = 0; + } + + image = opj_image_create(components, image_params, color_space); + + /* Setup compression context */ + context->error_msg = NULL; + + opj_set_default_encoder_parameters(¶ms); + + params.image_offset_x0 = context->offset_x; + params.image_offset_y0 = context->offset_y; + + if (context->tile_size_x && context->tile_size_y) { + params.tile_size_on = OPJ_TRUE; + params.cp_tx0 = context->tile_offset_x; + params.cp_ty0 = context->tile_offset_y; + params.cp_tdx = context->tile_size_x; + params.cp_tdy = context->tile_size_y; + + tile_width = params.cp_tdx; + tile_height = params.cp_tdy; + } else { + params.cp_tx0 = 0; + params.cp_ty0 = 0; + params.cp_tdx = 1; + params.cp_tdy = 1; + + tile_width = im->xsize; + tile_height = im->ysize; + } + + if (PySequence_Check(context->quality_layers)) { + Py_ssize_t len = PySequence_Length(context->quality_layers); + Py_ssize_t n; + float *pq; + + if (len) { + if (len > sizeof(params.tcp_rates) / sizeof(params.tcp_rates[0])) + len = sizeof(params.tcp_rates)/sizeof(params.tcp_rates[0]); + + params.tcp_numlayers = (int)len; + + if (context->quality_is_in_db) { + params.cp_disto_alloc = params.cp_fixed_alloc = 0; + params.cp_fixed_quality = 1; + pq = params.tcp_distoratio; + } else { + params.cp_disto_alloc = 1; + params.cp_fixed_alloc = params.cp_fixed_quality = 0; + pq = params.tcp_rates; + } + + for (n = 0; n < len; ++n) { + PyObject *obj = PySequence_ITEM(context->quality_layers, n); + pq[n] = PyFloat_AsDouble(obj); + } + } + } else { + params.tcp_numlayers = 1; + params.tcp_rates[0] = 0; + params.cp_disto_alloc = 1; + } + + if (context->num_resolutions) + params.numresolution = context->num_resolutions; + + if (context->cblk_width >= 4 && context->cblk_width <= 1024 + && context->cblk_height >= 4 && context->cblk_height <= 1024 + && context->cblk_width * context->cblk_height <= 4096) { + params.cblockw_init = context->cblk_width; + params.cblockh_init = context->cblk_height; + } + + params.irreversible = context->irreversible; + + params.prog_order = context->progression; + + params.cp_cinema = context->cinema_mode; + + switch (params.cp_cinema) { + case OPJ_OFF: + params.cp_rsiz = OPJ_STD_RSIZ; + break; + case OPJ_CINEMA2K_24: + case OPJ_CINEMA2K_48: + params.cp_rsiz = OPJ_CINEMA2K; + if (params.numresolution > 6) + params.numresolution = 6; + break; + case OPJ_CINEMA4K_24: + params.cp_rsiz = OPJ_CINEMA4K; + if (params.numresolution > 7) + params.numresolution = 7; + break; + } + + if (context->cinema_mode != OPJ_OFF) + j2k_set_cinema_params(im, components, ¶ms); + + /* Set up the reference grid in the image */ + image->x0 = params.image_offset_x0; + image->y0 = params.image_offset_y0; + image->x1 = xsiz = im->xsize + params.image_offset_x0; + image->y1 = ysiz = im->ysize + params.image_offset_y0; + + /* Create the compressor */ + codec = opj_create_compress(context->format); + + if (!codec) { + state->errcode = IMAGING_CODEC_BROKEN; + state->state = J2K_STATE_FAILED; + goto quick_exit; + } + + opj_set_error_handler(codec, j2k_error, context); + opj_setup_encoder(codec, ¶ms, image); + + /* Start encoding */ + if (!opj_start_compress(codec, image, stream)) { + state->errcode = IMAGING_CODEC_BROKEN; + state->state = J2K_STATE_FAILED; + goto quick_exit; + } + + /* Write each tile */ + tiles_x = (im->xsize + tile_width - 1) / tile_width; + tiles_y = (im->ysize + tile_height - 1) / tile_height; + num_tiles = tiles_x * tiles_y; + + state->buffer = malloc (tile_width * tile_height * components); + + tile_ndx = 0; + for (y = 0; y < tiles_y; ++y) { + unsigned ty0 = params.cp_ty0 + y * tile_height; + unsigned ty1 = ty0 + tile_height; + unsigned pixy, pixh; + + if (ty0 < params.image_offset_y0) + ty0 = params.image_offset_y0; + if (ty1 > ysiz) + ty1 = ysiz; + + pixy = ty0 - params.image_offset_y0; + pixh = ty1 - ty0; + + for (x = 0; x < tiles_x; ++x) { + unsigned tx0 = params.cp_tx0 + x * tile_width; + unsigned tx1 = tx0 + tile_width; + unsigned pixx, pixw; + unsigned data_size; + + if (tx0 < params.image_offset_x0) + tx0 = params.image_offset_x0; + if (tx1 > xsiz) + tx1 = xsiz; + + pixx = tx0 - params.image_offset_x0; + pixw = tx1 - tx0; + + pack(im, state->buffer, pixx, pixy, pixw, pixh); + + data_size = pixw * pixh * components; + + if (!opj_write_tile(codec, tile_ndx++, state->buffer, + data_size, stream)) { + state->errcode = IMAGING_CODEC_BROKEN; + state->state = J2K_STATE_FAILED; + goto quick_exit; + } + } + } + + if (!opj_end_compress(codec, stream)) { + state->errcode = IMAGING_CODEC_BROKEN; + state->state = J2K_STATE_FAILED; + goto quick_exit; + } + + state->errcode = IMAGING_CODEC_END; + state->state = J2K_STATE_DONE; + ret = (int)ImagingIncrementalCodecBytesInBuffer(encoder); + + quick_exit: + if (codec) + opj_destroy_codec(codec); + if (image) + opj_image_destroy(image); + if (stream) + opj_stream_destroy(stream); + + return ret; +} + +int +ImagingJpeg2KEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) +{ + JPEG2KENCODESTATE *context = (JPEG2KENCODESTATE *)state->context; + + if (state->state == J2K_STATE_DONE || state->state == J2K_STATE_FAILED) + return -1; + + if (state->state == J2K_STATE_START) { + context->encoder = ImagingIncrementalCodecCreate(j2k_encode_entry, + im, state); + + if (!context->encoder) { + state->errcode = IMAGING_CODEC_BROKEN; + state->state = J2K_STATE_FAILED; + return -1; + } + + state->state = J2K_STATE_ENCODING; + } + + return ImagingIncrementalCodecPushBuffer(context->encoder, buf, bytes); +} + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + +int +ImagingJpeg2KEncodeCleanup(ImagingCodecState state) { + JPEG2KENCODESTATE *context = (JPEG2KENCODESTATE *)state->context; + + if (context->quality_layers) + Py_DECREF(context->quality_layers); + + if (context->error_msg) + free ((void *)context->error_msg); + + if (context->encoder) + ImagingIncrementalCodecDestroy(context->encoder); + + return -1; +} + +#endif /* HAVE_OPENJPEG */ + +/* + * Local Variables: + * c-basic-offset: 4 + * End: + * + */ diff --git a/setup.py b/setup.py index e948e1050..8d3511e0c 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ _LIB_IMAGING = ( "RankFilter", "RawDecode", "RawEncode", "Storage", "SunRleDecode", "TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask", "XbmDecode", "XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode", "Incremental", - "Jpeg2KDecode") + "Jpeg2KDecode", "Jpeg2KEncode") def _add_directory(path, dir, where=None): From cb1f990a925c5a5e4cb38a4fdba631896474da1a Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 14 Mar 2014 11:21:08 +0000 Subject: [PATCH 07/20] Added seek support to make writing jp2 files work. Also added support for directly using an fd rather than relying on the Python loop, if we have a real fd. --- PIL/Jpeg2KImagePlugin.py | 38 +++++- decode.c | 6 +- encode.c | 12 +- libImaging/Imaging.h | 17 ++- libImaging/Incremental.c | 241 ++++++++++++++++++++++++++++++++++---- libImaging/Jpeg2K.h | 6 + libImaging/Jpeg2KDecode.c | 26 +--- libImaging/Jpeg2KEncode.c | 33 +++--- 8 files changed, 306 insertions(+), 73 deletions(-) diff --git a/PIL/Jpeg2KImagePlugin.py b/PIL/Jpeg2KImagePlugin.py index 94d49f705..6b1a36f3f 100644 --- a/PIL/Jpeg2KImagePlugin.py +++ b/PIL/Jpeg2KImagePlugin.py @@ -156,9 +156,13 @@ class Jpeg2KImageFile(ImageFile.ImageFile): self.reduce = 0 self.layers = 0 + fd = -1 + if hasattr(self.fp, "fileno"): + fd = self.fp.fileno() + self.tile = [('jpeg2k', (0, 0) + self.size, 0, - (self.codec, self.reduce, self.layers))] + (self.codec, self.reduce, self.layers, fd))] def load(self): if self.reduce: @@ -181,6 +185,38 @@ def _save(im, fp, filename): kind = 'j2k' else: kind = 'jp2' + + # Get the keyword arguments + info = im.encoderinfo + + offset = info.get('offset', None) + tile_offset = info.get('tile_offset', None) + tile_size = info.get('tile_size', None) + quality_mode = info.get('quality_mode', 'rates') + quality_layers = info.get('quality_layers', None) + num_resolutions = info.get('num_resolutions', 0) + cblk_size = info.get('codeblock_size', None) + irreversible = info.get('irreversible', False) + progression = info.get('progression', 'LRCP') + cinema_mode = info.get('cinema_mode', 'no') + fd = -1 + + if hasattr(fp, "fileno"): + fd = fp.fileno() + + im.encoderconfig = ( + offset, + tile_offset, + tile_size, + quality_mode, + quality_layers, + num_resolutions, + cblk_size, + irreversible, + progression, + cinema_mode, + fd + ) ImageFile._save(im, fp, [('jpeg2k', (0, 0)+im.size, 0, kind)]) diff --git a/decode.c b/decode.c index 0bc64ec77..f3eaa9a50 100644 --- a/decode.c +++ b/decode.c @@ -796,8 +796,9 @@ PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args) OPJ_CODEC_FORMAT codec_format; int reduce = 0; int layers = 0; - if (!PyArg_ParseTuple(args, "ss|ii", &mode, &format, - &reduce, &layers)) + int fd = -1; + if (!PyArg_ParseTuple(args, "ss|iii", &mode, &format, + &reduce, &layers, &fd)) return NULL; if (strcmp(format, "j2k") == 0) @@ -819,6 +820,7 @@ PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args) context = (JPEG2KDECODESTATE *)decoder->state.context; + context->fd = fd; context->format = codec_format; context->reduce = reduce; context->layers = layers; diff --git a/encode.c b/encode.c index 979742a84..52777cc0c 100644 --- a/encode.c +++ b/encode.c @@ -837,16 +837,18 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) PyObject *quality_layers = NULL; int num_resolutions = 0; PyObject *cblk_size = NULL; - int irreversible = 0; + PyObject *irreversible = NULL; char *progression = "LRCP"; OPJ_PROG_ORDER prog_order; char *cinema_mode = "no"; OPJ_CINEMA_MODE cine_mode; + int fd = -1; - if (!PyArg_ParseTuple(args, "ss|OOOsOiOpss", &mode, &format, + if (!PyArg_ParseTuple(args, "ss|OOOsOIOOssi", &mode, &format, &offset, &tile_offset, &tile_size, &quality_mode, &quality_layers, &num_resolutions, - &cblk_size, &irreversible, &progression, &cinema_mode)) + &cblk_size, &irreversible, &progression, &cinema_mode, + &fd)) return NULL; if (strcmp (format, "j2k") == 0) @@ -891,6 +893,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) context = (JPEG2KENCODESTATE *)encoder->state.context; + context->fd = fd; context->format = codec_format; context->offset_x = context->offset_y = 0; @@ -905,6 +908,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) if (quality_layers && PySequence_Check(quality_layers)) { context->quality_is_in_db = strcmp (quality_mode, "dB") == 0; context->quality_layers = quality_layers; + Py_INCREF(quality_layers); } context->num_resolutions = num_resolutions; @@ -913,7 +917,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) &context->cblk_width, &context->cblk_height); - context->irreversible = irreversible; + context->irreversible = PyObject_IsTrue(irreversible); context->progression = prog_order; context->cinema_mode = cine_mode; diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h index 1602622a2..26207d121 100644 --- a/libImaging/Imaging.h +++ b/libImaging/Imaging.h @@ -512,12 +512,23 @@ typedef int (*ImagingIncrementalCodecEntry)(Imaging im, ImagingCodecState state, ImagingIncrementalCodec codec); -extern ImagingIncrementalCodec ImagingIncrementalCodecCreate(ImagingIncrementalCodecEntry codec_entry, Imaging im, ImagingCodecState state); +enum { + INCREMENTAL_CODEC_READ = 1, + INCREMENTAL_CODEC_WRITE = 2 +}; + +enum { + INCREMENTAL_CODEC_NOT_SEEKABLE = 0, + INCREMENTAL_CODEC_SEEKABLE = 1 +}; + +extern ImagingIncrementalCodec ImagingIncrementalCodecCreate(ImagingIncrementalCodecEntry codec_entry, Imaging im, ImagingCodecState state, int read_or_write, int seekable, int fd); extern void ImagingIncrementalCodecDestroy(ImagingIncrementalCodec codec); extern int ImagingIncrementalCodecPushBuffer(ImagingIncrementalCodec codec, UINT8 *buf, int bytes); -extern size_t ImagingIncrementalCodecRead(ImagingIncrementalCodec codec, void *buffer, size_t bytes); +extern ssize_t ImagingIncrementalCodecRead(ImagingIncrementalCodec codec, void *buffer, size_t bytes); extern off_t ImagingIncrementalCodecSkip(ImagingIncrementalCodec codec, off_t bytes); -extern size_t ImagingIncrementalCodecWrite(ImagingIncrementalCodec codec, const void *buffer, size_t bytes); +extern ssize_t ImagingIncrementalCodecWrite(ImagingIncrementalCodec codec, const void *buffer, size_t bytes); +extern off_t ImagingIncrementalCodecSeek(ImagingIncrementalCodec codec, off_t bytes); extern size_t ImagingIncrementalCodecBytesInBuffer(ImagingIncrementalCodec codec); /* Errcodes */ diff --git a/libImaging/Incremental.c b/libImaging/Incremental.c index 45f064570..bc6a36b3e 100644 --- a/libImaging/Incremental.c +++ b/libImaging/Incremental.c @@ -35,6 +35,11 @@ thinking that the image is truncated, whereas generally you want it to pass the EOF condition (0 bytes to read) through to your code. */ +/* Additional complication: *Some* codecs need to seek; this is fine if + there is a file descriptor, but if we're buffering data it becomes + awkward. The incremental adaptor now contains code to handle these + two cases. */ + #ifdef _WIN32 #include #include @@ -64,18 +69,24 @@ struct ImagingIncrementalCodecStruct { pthread_cond_t data_cond; pthread_t thread; #endif - ImagingIncrementalCodecEntry entry; - Imaging im; - ImagingCodecState state; + ImagingIncrementalCodecEntry entry; + Imaging im; + ImagingCodecState state; struct { - UINT8 *buffer; - UINT8 *ptr; - UINT8 *end; + int fd; + UINT8 *buffer; /* Base of buffer */ + UINT8 *ptr; /* Current pointer in buffer */ + UINT8 *top; /* Highest point in buffer we've used */ + UINT8 *end; /* End of buffer */ } stream; - int started; - int result; + int read_or_write; + int seekable; + int started; + int result; }; +static void flush_stream(ImagingIncrementalCodec codec); + #if _WIN32 static void __stdcall codec_thread(void *ptr) @@ -84,6 +95,8 @@ codec_thread(void *ptr) codec->result = codec->entry(codec->im, codec->state, codec); + flush_stream(codec); + SetEvent(codec->hCodecEvent); } #else @@ -94,18 +107,54 @@ codec_thread(void *ptr) codec->result = codec->entry(codec->im, codec->state, codec); + flush_stream(codec); + + pthread_mutex_lock(&codec->codec_mutex); pthread_cond_signal(&codec->codec_cond); + pthread_mutex_unlock(&codec->codec_mutex); return NULL; } #endif +static void +flush_stream(ImagingIncrementalCodec codec) +{ + /* This is to flush data from the write buffer for a seekable write + codec. */ + if (codec->read_or_write != INCREMENTAL_CODEC_WRITE + || codec->state->errcode != IMAGING_CODEC_END + || !codec->seekable + || codec->stream.fd >= 0) + return; + + DEBUG("flushing data\n"); + + UINT8 *buffer = codec->stream.buffer; + size_t bytes = codec->stream.ptr - codec->stream.buffer; + + codec->state->errcode = 0; + codec->seekable = INCREMENTAL_CODEC_NOT_SEEKABLE; + codec->stream.buffer = codec->stream.ptr = codec->stream.end + = codec->stream.top = NULL; + + ImagingIncrementalCodecWrite(codec, buffer, bytes); + + codec->state->errcode = IMAGING_CODEC_END; + codec->result = (int)ImagingIncrementalCodecBytesInBuffer(codec); + + free(buffer); +} + /** * Create a new incremental codec */ ImagingIncrementalCodec ImagingIncrementalCodecCreate(ImagingIncrementalCodecEntry codec_entry, Imaging im, - ImagingCodecState state) + ImagingCodecState state, + int read_or_write, + int seekable, + int fd) { ImagingIncrementalCodec codec = (ImagingIncrementalCodec)malloc(sizeof(struct ImagingIncrementalCodecStruct)); @@ -113,8 +162,15 @@ ImagingIncrementalCodecCreate(ImagingIncrementalCodecEntry codec_entry, codec->im = im; codec->state = state; codec->result = 0; - codec->stream.buffer = codec->stream.ptr = codec->stream.end = NULL; + codec->stream.fd = fd; + codec->stream.buffer = codec->stream.ptr = codec->stream.end + = codec->stream.top = NULL; codec->started = 0; + codec->seekable = seekable; + codec->read_or_write = read_or_write; + + if (fd >= 0) + lseek(fd, 0, SEEK_SET); /* System specific set-up */ #if _WIN32 @@ -223,7 +279,11 @@ ImagingIncrementalCodecDestroy(ImagingIncrementalCodec codec) pthread_mutex_lock(&codec->data_mutex); #endif - codec->stream.buffer = codec->stream.ptr = codec->stream.end = NULL; + if (codec->seekable && codec->stream.fd < 0) + free (codec->stream.buffer); + + codec->stream.buffer = codec->stream.ptr = codec->stream.end + = codec->stream.top = NULL; #ifdef _WIN32 SetEvent(codec->hDataEvent); @@ -264,6 +324,22 @@ ImagingIncrementalCodecPushBuffer(ImagingIncrementalCodec codec, pthread_cond_signal(&codec->start_cond); #endif codec->started = 1; + + /* Wait for the thread to ask for data */ +#ifdef _WIN32 + WaitForSingleObject(codec->hCodecEvent); +#else + pthread_mutex_lock(&codec->codec_mutex); + pthread_cond_wait(&codec->codec_cond, &codec->codec_mutex); + pthread_mutex_unlock(&codec->codec_mutex); +#endif + } + + /* Codecs using an fd don't need data, so when we get here, we're done */ + if (codec->stream.fd >= 0) { + DEBUG("got result %d\n", codec->result); + + return codec->result; } DEBUG("providing %p, %d\n", buf, bytes); @@ -272,8 +348,30 @@ ImagingIncrementalCodecPushBuffer(ImagingIncrementalCodec codec, pthread_mutex_lock(&codec->data_mutex); #endif - codec->stream.buffer = codec->stream.ptr = buf; - codec->stream.end = buf + bytes; + if (codec->read_or_write == INCREMENTAL_CODEC_READ + && codec->seekable && codec->stream.fd < 0) { + /* In this specific case, we append to a buffer we allocate ourselves */ + size_t old_size = codec->stream.end - codec->stream.buffer; + size_t new_size = codec->stream.end - codec->stream.buffer + bytes; + UINT8 *new = (UINT8 *)realloc (codec->stream.buffer, new_size); + + if (!new) { + codec->state->errcode = IMAGING_CODEC_MEMORY; +#ifndef _WIN32 + pthread_mutex_unlock(&codec->data_mutex); +#endif + return -1; + } + + codec->stream.ptr = codec->stream.ptr - codec->stream.buffer + new; + codec->stream.end = new + new_size; + codec->stream.buffer = new; + + memcpy(new + old_size, buf, bytes); + } else { + codec->stream.buffer = codec->stream.ptr = buf; + codec->stream.end = buf + bytes; + } #ifdef _WIN32 SetEvent(codec->hDataEvent); @@ -298,15 +396,27 @@ ImagingIncrementalCodecBytesInBuffer(ImagingIncrementalCodec codec) return codec->stream.ptr - codec->stream.buffer; } -size_t +ssize_t ImagingIncrementalCodecRead(ImagingIncrementalCodec codec, void *buffer, size_t bytes) { UINT8 *ptr = (UINT8 *)buffer; size_t done = 0; + if (codec->read_or_write == INCREMENTAL_CODEC_WRITE) { + DEBUG("attempt to read from write codec\n"); + return -1; + } + DEBUG("reading (want %llu bytes)\n", (unsigned long long)bytes); + if (codec->stream.fd >= 0) { + off_t offset = lseek(codec->stream.fd, 0, SEEK_CUR); + ssize_t ret = read(codec->stream.fd, buffer, bytes); + DEBUG("read %lld bytes from fd at %lld\n", (long long)ret, (long long)offset); + return ret; + } + #ifndef _WIN32 pthread_mutex_lock(&codec->data_mutex); #endif @@ -331,9 +441,11 @@ ImagingIncrementalCodecRead(ImagingIncrementalCodec codec, #endif remaining = codec->stream.end - codec->stream.ptr; + codec->stream.top = codec->stream.end; DEBUG("got %llu bytes\n", (unsigned long long)remaining); } + if (todo > remaining) todo = remaining; @@ -357,12 +469,30 @@ ImagingIncrementalCodecRead(ImagingIncrementalCodec codec, off_t ImagingIncrementalCodecSkip(ImagingIncrementalCodec codec, - off_t bytes) + off_t bytes) { off_t done = 0; DEBUG("skipping (want %llu bytes)\n", (unsigned long long)bytes); + /* In write mode, explicitly fill with zeroes */ + if (codec->read_or_write == INCREMENTAL_CODEC_WRITE) { + static const UINT8 zeroes[256] = { 0 }; + off_t done = 0; + while (bytes) { + size_t todo = bytes > 256 ? 256 : bytes; + ssize_t written = ImagingIncrementalCodecWrite(codec, zeroes, todo); + if (written <= 0) + break; + done += written; + bytes -= written; + } + return done; + } + + if (codec->stream.fd >= 0) + return lseek(codec->stream.fd, bytes, SEEK_CUR); + #ifndef _WIN32 pthread_mutex_lock(&codec->data_mutex); #endif @@ -388,6 +518,7 @@ ImagingIncrementalCodecSkip(ImagingIncrementalCodec codec, remaining = codec->stream.end - codec->stream.ptr; } + if (todo > remaining) todo = remaining; @@ -407,15 +538,23 @@ ImagingIncrementalCodecSkip(ImagingIncrementalCodec codec, return done; } -size_t +ssize_t ImagingIncrementalCodecWrite(ImagingIncrementalCodec codec, const void *buffer, size_t bytes) { const UINT8 *ptr = (const UINT8 *)buffer; size_t done = 0; + if (codec->read_or_write == INCREMENTAL_CODEC_READ) { + DEBUG("attempt to write from read codec\n"); + return -1; + } + DEBUG("write (have %llu bytes)\n", (unsigned long long)bytes); + if (codec->stream.fd >= 0) + return write(codec->stream.fd, buffer, bytes); + #ifndef _WIN32 pthread_mutex_lock(&codec->data_mutex); #endif @@ -424,20 +563,40 @@ ImagingIncrementalCodecWrite(ImagingIncrementalCodec codec, size_t remaining = codec->stream.end - codec->stream.ptr; if (!remaining) { - DEBUG("waiting for space\n"); + if (codec->seekable && codec->stream.fd < 0) { + /* In this case, we maintain the stream buffer ourselves */ + size_t old_size = codec->stream.top - codec->stream.buffer; + size_t new_size = (old_size + bytes + 65535) & ~65535; + UINT8 *new = (UINT8 *)realloc(codec->stream.buffer, new_size); + + if (!new) { + codec->state->errcode = IMAGING_CODEC_MEMORY; +#ifndef _WIN32 + pthread_mutex_unlock(&codec->data_mutex); +#endif + return done == 0 ? -1 : done; + } + + codec->stream.ptr = codec->stream.ptr - codec->stream.buffer + new; + codec->stream.buffer = new; + codec->stream.end = new + new_size; + codec->stream.top = new + old_size; + } else { + DEBUG("waiting for space\n"); #ifndef _WIN32 - pthread_mutex_lock(&codec->codec_mutex); + pthread_mutex_lock(&codec->codec_mutex); #endif - codec->result = (int)(codec->stream.ptr - codec->stream.buffer); + codec->result = (int)(codec->stream.ptr - codec->stream.buffer); #if _WIN32 - SetEvent(codec->hCodecEvent); - WaitForSingleObject(codec->hDataEvent); + SetEvent(codec->hCodecEvent); + WaitForSingleObject(codec->hDataEvent); #else - pthread_cond_signal(&codec->codec_cond); - pthread_mutex_unlock(&codec->codec_mutex); - pthread_cond_wait(&codec->data_cond, &codec->data_mutex); + pthread_cond_signal(&codec->codec_cond); + pthread_mutex_unlock(&codec->codec_mutex); + pthread_cond_wait(&codec->data_cond, &codec->data_mutex); #endif + } remaining = codec->stream.end - codec->stream.ptr; @@ -455,6 +614,10 @@ ImagingIncrementalCodecWrite(ImagingIncrementalCodec codec, done += todo; ptr += todo; } + + if (codec->stream.ptr > codec->stream.top) + codec->stream.top = codec->stream.ptr; + #ifndef _WIN32 pthread_mutex_unlock(&codec->data_mutex); #endif @@ -463,3 +626,33 @@ ImagingIncrementalCodecWrite(ImagingIncrementalCodec codec, return done; } + +off_t +ImagingIncrementalCodecSeek(ImagingIncrementalCodec codec, + off_t bytes) +{ + DEBUG("seeking (going to %llu bytes)\n", (unsigned long long)bytes); + + if (codec->stream.fd >= 0) + return lseek(codec->stream.fd, bytes, SEEK_SET); + + if (!codec->seekable) { + DEBUG("attempt to seek non-seekable stream\n"); + return -1; + } + + if (bytes < 0) { + DEBUG("attempt to seek before stream start\n"); + return -1; + } + + off_t buffered = codec->stream.top - codec->stream.buffer; + + if (bytes <= buffered) { + DEBUG("seek within buffer\n"); + codec->stream.ptr = codec->stream.buffer + bytes; + return bytes; + } + + return buffered + ImagingIncrementalCodecSkip(codec, bytes - buffered); +} diff --git a/libImaging/Jpeg2K.h b/libImaging/Jpeg2K.h index 128302f19..ba4b53a7f 100644 --- a/libImaging/Jpeg2K.h +++ b/libImaging/Jpeg2K.h @@ -17,6 +17,9 @@ typedef struct { /* CONFIGURATION */ + /* File descriptor, if available; otherwise, -1 */ + int fd; + /* Specify the desired format */ OPJ_CODEC_FORMAT format; @@ -39,6 +42,9 @@ typedef struct { typedef struct { /* CONFIGURATION */ + /* File descriptor, if available; otherwise, -1 */ + int fd; + /* Specify the desired format */ OPJ_CODEC_FORMAT format; diff --git a/libImaging/Jpeg2KDecode.c b/libImaging/Jpeg2KDecode.c index c3254d889..9c4e16b1f 100644 --- a/libImaging/Jpeg2KDecode.c +++ b/libImaging/Jpeg2KDecode.c @@ -53,15 +53,6 @@ j2k_read(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) return len ? len : (OPJ_SIZE_T)-1; } -static OPJ_SIZE_T -j2k_write(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) -{ - /* This should never happen */ - fprintf(stderr, "OpenJPEG has written to our read stream(!)"); - abort(); - return (OPJ_SIZE_T)-1; -} - static OPJ_OFF_T j2k_skip(OPJ_OFF_T p_nb_bytes, void *p_user_data) { @@ -71,15 +62,6 @@ j2k_skip(OPJ_OFF_T p_nb_bytes, void *p_user_data) return pos ? pos : (OPJ_OFF_T)-1; } -static OPJ_BOOL -j2k_seek(OPJ_OFF_T p_nb_bytes, void *p_user_data) -{ - /* This should never happen */ - fprintf(stderr, "OpenJPEG tried to seek our read stream(!)"); - abort(); - return OPJ_FALSE; -} - /* -------------------------------------------------------------------- */ /* Unpackers */ /* -------------------------------------------------------------------- */ @@ -476,9 +458,7 @@ j2k_decode_entry(Imaging im, ImagingCodecState state, } opj_stream_set_read_function(stream, j2k_read); - opj_stream_set_write_function(stream, j2k_write); opj_stream_set_skip_function(stream, j2k_skip); - opj_stream_set_seek_function(stream, j2k_seek); opj_stream_set_user_data(stream, context->decoder); @@ -623,6 +603,7 @@ j2k_decode_entry(Imaging im, ImagingCodecState state, } state->state = J2K_STATE_DONE; + state->errcode = IMAGING_CODEC_END; quick_exit: if (codec) @@ -645,7 +626,10 @@ ImagingJpeg2KDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) if (state->state == J2K_STATE_START) { context->decoder = ImagingIncrementalCodecCreate(j2k_decode_entry, - im, state); + im, state, + INCREMENTAL_CODEC_READ, + INCREMENTAL_CODEC_NOT_SEEKABLE, + context->fd); if (!context->decoder) { state->errcode = IMAGING_CODEC_BROKEN; diff --git a/libImaging/Jpeg2KEncode.c b/libImaging/Jpeg2KEncode.c index 75f40d58f..f5feaf72e 100644 --- a/libImaging/Jpeg2KEncode.c +++ b/libImaging/Jpeg2KEncode.c @@ -40,15 +40,6 @@ j2k_error(const char *msg, void *client_data) /* Buffer output stream */ /* -------------------------------------------------------------------- */ -static OPJ_SIZE_T -j2k_read(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) -{ - /* This should never happen */ - fprintf (stderr, "OpenJPEG has read from our write stream(!)"); - abort(); - return (OPJ_SIZE_T)-1; -} - static OPJ_SIZE_T j2k_write(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) { @@ -61,8 +52,8 @@ j2k_write(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) static OPJ_OFF_T j2k_skip(OPJ_OFF_T p_nb_bytes, void *p_user_data) { - ImagingIncrementalCodec decoder = (ImagingIncrementalCodec)p_user_data; - off_t pos = ImagingIncrementalCodecSkip(decoder, p_nb_bytes); + ImagingIncrementalCodec encoder = (ImagingIncrementalCodec)p_user_data; + off_t pos = ImagingIncrementalCodecSkip(encoder, p_nb_bytes); return pos ? pos : (OPJ_OFF_T)-1; } @@ -70,10 +61,10 @@ j2k_skip(OPJ_OFF_T p_nb_bytes, void *p_user_data) static OPJ_BOOL j2k_seek(OPJ_OFF_T p_nb_bytes, void *p_user_data) { - /* This should never happen */ - fprintf(stderr, "OpenJPEG tried to seek our write stream(!)"); - abort(); - return OPJ_FALSE; + ImagingIncrementalCodec encoder = (ImagingIncrementalCodec)p_user_data; + off_t pos = ImagingIncrementalCodecSeek(encoder, p_nb_bytes); + + return pos == p_nb_bytes; } /* -------------------------------------------------------------------- */ @@ -259,7 +250,6 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, goto quick_exit; } - opj_stream_set_read_function(stream, j2k_read); opj_stream_set_write_function(stream, j2k_write); opj_stream_set_skip_function(stream, j2k_skip); opj_stream_set_seek_function(stream, j2k_seek); @@ -499,12 +489,19 @@ ImagingJpeg2KEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) { JPEG2KENCODESTATE *context = (JPEG2KENCODESTATE *)state->context; - if (state->state == J2K_STATE_DONE || state->state == J2K_STATE_FAILED) + if (state->state == J2K_STATE_FAILED) return -1; if (state->state == J2K_STATE_START) { + int seekable = (context->format != OPJ_CODEC_J2K + ? INCREMENTAL_CODEC_SEEKABLE + : INCREMENTAL_CODEC_NOT_SEEKABLE); + context->encoder = ImagingIncrementalCodecCreate(j2k_encode_entry, - im, state); + im, state, + INCREMENTAL_CODEC_WRITE, + seekable, + context->fd); if (!context->encoder) { state->errcode = IMAGING_CODEC_BROKEN; From 5cb73c94e947f8567159618b609227a2900f1082 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 14 Mar 2014 14:35:09 +0000 Subject: [PATCH 08/20] Fixed some Windows issues. --- libImaging/Incremental.c | 14 ++++++++------ setup.py | 13 ++++++++++++- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/libImaging/Incremental.c b/libImaging/Incremental.c index bc6a36b3e..b3a1cb00a 100644 --- a/libImaging/Incremental.c +++ b/libImaging/Incremental.c @@ -88,7 +88,7 @@ struct ImagingIncrementalCodecStruct { static void flush_stream(ImagingIncrementalCodec codec); #if _WIN32 -static void __stdcall +static unsigned int __stdcall codec_thread(void *ptr) { ImagingIncrementalCodec codec = (ImagingIncrementalCodec)ptr; @@ -98,6 +98,8 @@ codec_thread(void *ptr) flush_stream(codec); SetEvent(codec->hCodecEvent); + + return 0; } #else static void * @@ -327,7 +329,7 @@ ImagingIncrementalCodecPushBuffer(ImagingIncrementalCodec codec, /* Wait for the thread to ask for data */ #ifdef _WIN32 - WaitForSingleObject(codec->hCodecEvent); + WaitForSingleObject(codec->hCodecEvent, INFINITE); #else pthread_mutex_lock(&codec->codec_mutex); pthread_cond_wait(&codec->codec_cond, &codec->codec_mutex); @@ -375,7 +377,7 @@ ImagingIncrementalCodecPushBuffer(ImagingIncrementalCodec codec, #ifdef _WIN32 SetEvent(codec->hDataEvent); - WaitForSingleObject(codec->hCodecEvent); + WaitForSingleObject(codec->hCodecEvent, INFINITE); #else pthread_cond_signal(&codec->data_cond); pthread_mutex_unlock(&codec->data_mutex); @@ -433,7 +435,7 @@ ImagingIncrementalCodecRead(ImagingIncrementalCodec codec, codec->result = (int)(codec->stream.ptr - codec->stream.buffer); #if _WIN32 SetEvent(codec->hCodecEvent); - WaitForSingleObject(codec->hDataEvent); + WaitForSingleObject(codec->hDataEvent, INFINITE); #else pthread_cond_signal(&codec->codec_cond); pthread_mutex_unlock(&codec->codec_mutex); @@ -509,7 +511,7 @@ ImagingIncrementalCodecSkip(ImagingIncrementalCodec codec, codec->result = (int)(codec->stream.ptr - codec->stream.buffer); #if _WIN32 SetEvent(codec->hCodecEvent); - WaitForSingleObject(codec->hDataEvent); + WaitForSingleObject(codec->hDataEvent, INFINITE); #else pthread_cond_signal(&codec->codec_cond); pthread_mutex_unlock(&codec->codec_mutex); @@ -590,7 +592,7 @@ ImagingIncrementalCodecWrite(ImagingIncrementalCodec codec, codec->result = (int)(codec->stream.ptr - codec->stream.buffer); #if _WIN32 SetEvent(codec->hCodecEvent); - WaitForSingleObject(codec->hDataEvent); + WaitForSingleObject(codec->hDataEvent, INFINITE); #else pthread_cond_signal(&codec->codec_cond); pthread_mutex_unlock(&codec->codec_mutex); diff --git a/setup.py b/setup.py index 8d3511e0c..7d387b822 100644 --- a/setup.py +++ b/setup.py @@ -89,6 +89,7 @@ NAME = 'Pillow' VERSION = '2.3.0' TCL_ROOT = None JPEG_ROOT = None +JPEG2K_ROOT = None ZLIB_ROOT = None TIFF_ROOT = None FREETYPE_ROOT = None @@ -152,7 +153,7 @@ class pil_build_ext(build_ext): # # add configured kits - for root in (TCL_ROOT, JPEG_ROOT, TIFF_ROOT, ZLIB_ROOT, + for root in (TCL_ROOT, JPEG_ROOT, JPEG2K_ROOT, TIFF_ROOT, ZLIB_ROOT, FREETYPE_ROOT, LCMS_ROOT): if isinstance(root, type(())): lib_root, include_root = root @@ -323,6 +324,16 @@ class pil_build_ext(build_ext): _add_directory(library_dirs, "/usr/lib") _add_directory(include_dirs, "/usr/include") + # on Windows, look for the OpenJPEG libraries in the location that + # the official installed puts them + if sys.platform == "win32": + _add_directory(library_dirs, + os.path.join(os.environ.get("ProgramFiles", ""), + "OpenJPEG 2.0", "lib")) + _add_directory(include_dirs, + os.path.join(os.environ.get("ProgramFiles", ""), + "OpenJPEG 2.0", "include")) + # # insert new dirs *before* default libs, to avoid conflicts # between Python PYD stub libs and real libraries From 9a1b6966b51dc5b2e07d95faa27c0c433b66a088 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 14 Mar 2014 15:40:30 +0000 Subject: [PATCH 09/20] Added precinct size option. Also added the jp2klib_version symbol on the _imaging module. --- PIL/Jpeg2KImagePlugin.py | 2 ++ _imaging.c | 7 +++++++ encode.c | 10 +++++++--- libImaging/Jpeg2K.h | 3 +++ libImaging/Jpeg2KEncode.c | 9 +++++++++ 5 files changed, 28 insertions(+), 3 deletions(-) diff --git a/PIL/Jpeg2KImagePlugin.py b/PIL/Jpeg2KImagePlugin.py index 6b1a36f3f..26eec237d 100644 --- a/PIL/Jpeg2KImagePlugin.py +++ b/PIL/Jpeg2KImagePlugin.py @@ -196,6 +196,7 @@ def _save(im, fp, filename): quality_layers = info.get('quality_layers', None) num_resolutions = info.get('num_resolutions', 0) cblk_size = info.get('codeblock_size', None) + precinct_size = info.get('precinct_size', None) irreversible = info.get('irreversible', False) progression = info.get('progression', 'LRCP') cinema_mode = info.get('cinema_mode', 'no') @@ -212,6 +213,7 @@ def _save(im, fp, filename): quality_layers, num_resolutions, cblk_size, + precinct_size, irreversible, progression, cinema_mode, diff --git a/_imaging.c b/_imaging.c index fe623e780..987bd0383 100644 --- a/_imaging.c +++ b/_imaging.c @@ -3461,6 +3461,13 @@ setup_module(PyObject* m) { } #endif +#ifdef HAVE_OPENJPEG + { + extern const char *ImagingJpeg2KVersion(void); + PyDict_SetItemString(d, "jp2klib_version", PyUnicode_FromString(ImagingJpeg2KVersion())); + } +#endif + #ifdef HAVE_LIBZ /* zip encoding strategies */ PyModule_AddIntConstant(m, "DEFAULT_STRATEGY", Z_DEFAULT_STRATEGY); diff --git a/encode.c b/encode.c index 52777cc0c..0c444bfc3 100644 --- a/encode.c +++ b/encode.c @@ -836,7 +836,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) char *quality_mode = "rates"; PyObject *quality_layers = NULL; int num_resolutions = 0; - PyObject *cblk_size = NULL; + PyObject *cblk_size = NULL, *precinct_size = NULL; PyObject *irreversible = NULL; char *progression = "LRCP"; OPJ_PROG_ORDER prog_order; @@ -844,10 +844,11 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) OPJ_CINEMA_MODE cine_mode; int fd = -1; - if (!PyArg_ParseTuple(args, "ss|OOOsOIOOssi", &mode, &format, + if (!PyArg_ParseTuple(args, "ss|OOOsOIOOOssi", &mode, &format, &offset, &tile_offset, &tile_size, &quality_mode, &quality_layers, &num_resolutions, - &cblk_size, &irreversible, &progression, &cinema_mode, + &cblk_size, &precinct_size, + &irreversible, &progression, &cinema_mode, &fd)) return NULL; @@ -916,6 +917,9 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) j2k_decode_coord_tuple(cblk_size, &context->cblk_width, &context->cblk_height); + j2k_decode_coord_tuple(precinct_size, + &context->precinct_width, + &context->precinct_height); context->irreversible = PyObject_IsTrue(irreversible); context->progression = prog_order; diff --git a/libImaging/Jpeg2K.h b/libImaging/Jpeg2K.h index ba4b53a7f..be6e0770b 100644 --- a/libImaging/Jpeg2K.h +++ b/libImaging/Jpeg2K.h @@ -65,6 +65,9 @@ typedef struct { /* Code block size */ int cblk_width, cblk_height; + /* Precinct size */ + int precinct_width, precinct_height; + /* Compression style */ int irreversible; diff --git a/libImaging/Jpeg2KEncode.c b/libImaging/Jpeg2KEncode.c index f5feaf72e..6bf25def8 100644 --- a/libImaging/Jpeg2KEncode.c +++ b/libImaging/Jpeg2KEncode.c @@ -364,6 +364,15 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, params.cblockh_init = context->cblk_height; } + if (context->precinct_width >= 4 && context->precinct_height >= 4 + && context->precinct_width >= context->cblk_width + && context->precinct_height > context->cblk_height) { + params.prcw_init[0] = context->precinct_width; + params.prch_init[0] = context->precinct_height; + params.res_spec = 1; + params.csty |= 0x01; + } + params.irreversible = context->irreversible; params.prog_order = context->progression; From 168acabcb09c0d30d98874fa1b76a337cd5f5d0c Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 14 Mar 2014 16:38:07 +0000 Subject: [PATCH 10/20] Fixed some warnings. --- libImaging/Incremental.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libImaging/Incremental.c b/libImaging/Incremental.c index b3a1cb00a..8e42b2e64 100644 --- a/libImaging/Incremental.c +++ b/libImaging/Incremental.c @@ -413,9 +413,8 @@ ImagingIncrementalCodecRead(ImagingIncrementalCodec codec, DEBUG("reading (want %llu bytes)\n", (unsigned long long)bytes); if (codec->stream.fd >= 0) { - off_t offset = lseek(codec->stream.fd, 0, SEEK_CUR); ssize_t ret = read(codec->stream.fd, buffer, bytes); - DEBUG("read %lld bytes from fd at %lld\n", (long long)ret, (long long)offset); + DEBUG("read %lld bytes from fd\n", (long long)ret); return ret; } @@ -482,7 +481,7 @@ ImagingIncrementalCodecSkip(ImagingIncrementalCodec codec, static const UINT8 zeroes[256] = { 0 }; off_t done = 0; while (bytes) { - size_t todo = bytes > 256 ? 256 : bytes; + size_t todo = (size_t)(bytes > 256 ? 256 : bytes); ssize_t written = ImagingIncrementalCodecWrite(codec, zeroes, todo); if (written <= 0) break; From 5853d2aed02abbc225323d15e1a838e2d12b2fca Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 14 Mar 2014 16:38:30 +0000 Subject: [PATCH 11/20] Add a couple of unpackers. --- libImaging/Jpeg2KDecode.c | 57 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/libImaging/Jpeg2KDecode.c b/libImaging/Jpeg2KDecode.c index 9c4e16b1f..a39c700ed 100644 --- a/libImaging/Jpeg2KDecode.c +++ b/libImaging/Jpeg2KDecode.c @@ -410,6 +410,60 @@ j2ku_srgba_rgba(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, } } +static void +j2ku_sycca_rgba(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, + const UINT8 *tiledata, Imaging im) +{ + unsigned x0 = tileinfo->x0, y0 = tileinfo->y0; + unsigned w = tileinfo->x1 - tileinfo->x0; + unsigned h = tileinfo->y1 - tileinfo->y0; + + int shifts[4], offsets[4], csiz[4]; + const UINT8 *cdata[4]; + const UINT8 *cptr = tiledata; + unsigned n, x, y; + + for (n = 0; n < 4; ++n) { + cdata[n] = cptr; + shifts[n] = 8 - in->comps[n].prec; + offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0; + csiz[n] = (in->comps[n].prec + 7) >> 3; + + if (csiz[n] == 3) + csiz[n] = 4; + + if (shifts[n] < 0) + offsets[n] += 1 << (-shifts[n] - 1); + + cptr += csiz[n] * w * h; + } + + for (y = 0; y < h; ++y) { + const UINT8 *data[4]; + UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4; + UINT8 *row_start = row; + for (n = 0; n < 4; ++n) + data[n] = &cdata[n][csiz[n] * y * w]; + + for (x = 0; x < w; ++x) { + for (n = 0; n < 4; ++n) { + UINT32 word; + + switch (csiz[n]) { + case 1: word = *data[n]++; break; + case 2: word = *(const UINT16 *)data[n]; data[n] += 2; break; + case 4: word = *(const UINT32 *)data[n]; data[n] += 4; break; + } + + row[n] = j2ku_shift(offsets[n] + word, shifts[n]); + } + row += 4; + } + + ImagingConvertYCbCr2RGB(row_start, row_start, w); + } +} + static const struct j2k_decode_unpacker j2k_unpackers[] = { { "L", OPJ_CLRSPC_GRAY, 1, j2ku_gray_l }, { "LA", OPJ_CLRSPC_GRAY, 2, j2ku_graya_la }, @@ -417,11 +471,14 @@ static const struct j2k_decode_unpacker j2k_unpackers[] = { { "RGB", OPJ_CLRSPC_GRAY, 2, j2ku_gray_rgb }, { "RGB", OPJ_CLRSPC_SRGB, 3, j2ku_srgb_rgb }, { "RGB", OPJ_CLRSPC_SYCC, 3, j2ku_sycc_rgb }, + { "RGB", OPJ_CLRSPC_SRGB, 4, j2ku_srgb_rgb }, + { "RGB", OPJ_CLRSPC_SYCC, 4, j2ku_sycc_rgb }, { "RGBA", OPJ_CLRSPC_GRAY, 1, j2ku_gray_rgb }, { "RGBA", OPJ_CLRSPC_GRAY, 2, j2ku_graya_la }, { "RGBA", OPJ_CLRSPC_SRGB, 3, j2ku_srgb_rgb }, { "RGBA", OPJ_CLRSPC_SYCC, 3, j2ku_sycc_rgb }, { "RGBA", OPJ_CLRSPC_SRGB, 4, j2ku_srgba_rgba }, + { "RGBA", OPJ_CLRSPC_SYCC, 4, j2ku_sycca_rgba }, }; /* -------------------------------------------------------------------- */ From 646f0c21e5b5fff3edca6029d05dd30df7a7bc3f Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 14 Mar 2014 16:38:50 +0000 Subject: [PATCH 12/20] Updated CHANGES file. --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index abdf73b33..ac5247f63 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.4.0 (unreleased) ------------------ +- Added support for JPEG 2000 + [al45tair] + - Fixed saving mode P image as a PNG with transparency = palette color 0 [d-schmidt] From efd47ce63ba8cebc86eddf076e1fee00ed3a2811 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 14 Mar 2014 16:39:09 +0000 Subject: [PATCH 13/20] Added documentation for JPEG 2000. --- docs/handbook/image-file-formats.rst | 86 ++++++++++++++++++++++++++++ docs/plugins.rst | 8 +++ 2 files changed, 94 insertions(+) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 21cd615fc..913c47c76 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -153,6 +153,92 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: before building the Python Imaging Library. See the distribution README for details. +JPEG 2000 +^^^^^^^^^ + +PIL reads and writes JPEG 2000 files containing ``L``, ``LA``, ``RGB`` or +``RGBA`` data. It can also read files containing ``YCbCr`` data, which it +converts on read into ``RGB`` or ``RGBA`` depending on whether or not there is +an alpha channel. PIL supports JPEG 2000 raw codestreams (``.j2k`` files), as +well as boxed JPEG 2000 files (``.j2p`` or ``.jpx`` files). PIL does *not* +support files whose components have different sampling frequencies. + +When loading, if you set the ``mode`` on the image prior to the +:py:meth:`~PIL.Image.Image.load` method being invoked, you can ask PIL to +convert the image to either ``RGB`` or ``RGBA`` rather than choosing for +itself. It is also possible to set ``reduce`` to the number of resolutions to +discard (each one reduces the size of the resulting image by a factor of 2), +and ``layers`` to specify the number of quality layers to load. + +The :py:meth:`~PIL.Image.Image.save` method supports the following options: + +**offset** + The image offset, as a tuple of integers, e.g. (16, 16) + +**tile_offset** + The tile offset, again as a 2-tuple of integers. + +**tile_size** + The tile size as a 2-tuple. If not specified, or if set to None, the + image will be saved without tiling. + +**quality_mode** + Either `"rates"` or `"dB"` depending on the units you want to use to + specify image quality. + +**quality_layers** + A sequence of numbers, each of which represents either an approximate size + reduction (if quality mode is `"rates"`) or a signal to noise ratio value + in decibels. If not specified, defaults to a single layer of full quality. + +**num_resolutions** + The number of different image resolutions to be stored (which corresponds + to the number of Discrete Wavelet Transform decompositions plus one). + +**codeblock_size** + The code-block size as a 2-tuple. Minimum size is 4 x 4, maximum is 1024 x + 1024, with the additional restriction that no code-block may have more + than 4096 coefficients (i.e. the product of the two numbers must be no + greater than 4096). + +**precinct_size** + The precinct size as a 2-tuple. Must be a power of two along both axes, + 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. + +**progression** + Controls the progression order; must be one of ``"LRCP"``, ``"RLCP"``, + ``"RPCL"``, ``"PCRL"``, ``"CPRL"``. The letters stand for Component, + Position, Resolution and Layer respectively and control the order of + encoding, the idea being that e.g. an image encoded using LRCP mode can + have its quality layers decoded as they arrive at the decoder, while one + encoded using RLCP mode will have increasing resolutions decoded as they + arrive, and so on. + +**cinema_mode** + Set the encoder to produce output compliant with the digital cinema + specifications. The options here are ``"no"`` (the default), + ``"cinema2k-24"`` for 24fps 2K, ``"cinema2k-48"`` for 48fps 2K, and + ``"cinema4k-24"`` for 24fps 4K. Note that for compliant 2K files, + *at least one* of your image dimensions must match 2048 x 1080, while + for compliant 4K files, *at least one* of the dimensions must match + 4096 x 2160. + +.. note:: + + To enable JPEG 2000 support, you need to build and install the OpenJPEG + library, version 2.0.0 or higher, before building the Python Imaging + Library. + + Windows users can install the OpenJPEG binaries available on the + OpenJPEG website, but must add them to their PATH in order to use PIL (if + you fail to do this, you will get errors about not being able to load the + ``_imaging`` DLL). + MSP ^^^ diff --git a/docs/plugins.rst b/docs/plugins.rst index b92b500c1..001cee949 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -153,6 +153,14 @@ Plugin reference :undoc-members: :show-inheritance: +:mod:`Jpeg2KImagePlugin` Module +----------------------------- + +.. automodule:: PIL.Jpeg2KImagePlugin + :members: + :undoc-members: + :show-inheritance: + :mod:`McIdasImagePlugin` Module ------------------------------- From 6840278b7d3781d2fe4159e66c58c27d20588184 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 14 Mar 2014 16:49:29 +0000 Subject: [PATCH 14/20] Removed print statement. --- PIL/Jpeg2KImagePlugin.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/PIL/Jpeg2KImagePlugin.py b/PIL/Jpeg2KImagePlugin.py index 26eec237d..36e15b762 100644 --- a/PIL/Jpeg2KImagePlugin.py +++ b/PIL/Jpeg2KImagePlugin.py @@ -138,10 +138,7 @@ class Jpeg2KImageFile(ImageFile.ImageFile): sig = self.fp.read(4) if sig == b'\xff\x4f\xff\x51': self.codec = "j2k" - try: - self.size, self.mode = _parse_codestream(self.fp) - except Exception as e: - print e + self.size, self.mode = _parse_codestream(self.fp) else: sig = sig + self.fp.read(8) From 68fd58a7e292c61357e0cae8f9783f91e437d52a Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 14 Mar 2014 17:25:38 +0000 Subject: [PATCH 15/20] Initialize handles_eof (oops). --- decode.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/decode.c b/decode.c index f3eaa9a50..f1e4aeaba 100644 --- a/decode.c +++ b/decode.c @@ -94,6 +94,9 @@ PyImaging_DecoderNew(int contextsize) /* Initialize the cleanup function pointer */ decoder->cleanup = NULL; + /* Most decoders don't want to handle EOF themselves */ + decoder->handles_eof = 0; + return decoder; } From ac52886da123b03de1b9e17a72576055c7902eff Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 14 Mar 2014 11:35:23 -0700 Subject: [PATCH 16/20] Fixed compilation errors on linux, ref #547 --- libImaging/Jpeg2KEncode.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/libImaging/Jpeg2KEncode.c b/libImaging/Jpeg2KEncode.c index 6bf25def8..3652e023e 100644 --- a/libImaging/Jpeg2KEncode.c +++ b/libImaging/Jpeg2KEncode.c @@ -80,9 +80,10 @@ j2k_pack_l(Imaging im, UINT8 *buf, unsigned x0, unsigned y0, unsigned w, unsigned h) { UINT8 *ptr = buf; - for (unsigned y = 0; y < h; ++y) { + unsigned x,y; + for (y = 0; y < h; ++y) { UINT8 *data = (UINT8 *)(im->image[y + y0] + x0); - for (unsigned x = 0; x < w; ++x) + for (x = 0; x < w; ++x) *ptr++ = *data++; } } @@ -93,9 +94,10 @@ j2k_pack_la(Imaging im, UINT8 *buf, { UINT8 *ptr = buf; UINT8 *ptra = buf + w * h; - for (unsigned y = 0; y < h; ++y) { + unsigned x,y; + for (y = 0; y < h; ++y) { UINT8 *data = (UINT8 *)(im->image[y + y0] + 4 * x0); - for (unsigned x = 0; x < w; ++x) { + for (x = 0; x < w; ++x) { *ptr++ = data[0]; *ptra++ = data[3]; data += 4; @@ -110,9 +112,10 @@ j2k_pack_rgb(Imaging im, UINT8 *buf, UINT8 *pr = buf; UINT8 *pg = pr + w * h; UINT8 *pb = pg + w * h; - for (unsigned y = 0; y < h; ++y) { + unsigned x,y; + for (y = 0; y < h; ++y) { UINT8 *data = (UINT8 *)(im->image[y + y0] + 4 * x0); - for (unsigned x = 0; x < w; ++x) { + for (x = 0; x < w; ++x) { *pr++ = data[0]; *pg++ = data[1]; *pb++ = data[2]; @@ -129,9 +132,10 @@ j2k_pack_rgba(Imaging im, UINT8 *buf, UINT8 *pg = pr + w * h; UINT8 *pb = pg + w * h; UINT8 *pa = pb + w * h; - for (unsigned y = 0; y < h; ++y) { + unsigned x,y; + for (y = 0; y < h; ++y) { UINT8 *data = (UINT8 *)(im->image[y + y0] + 4 * x0); - for (unsigned x = 0; x < w; ++x) { + for (x = 0; x < w; ++x) { *pr++ = *data++; *pg++ = *data++; *pb++ = *data++; @@ -239,6 +243,7 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, unsigned tile_width, tile_height; unsigned tiles_x, tiles_y, num_tiles; unsigned x, y, tile_ndx; + unsigned n; j2k_pack_tile_t pack; int ret = -1; @@ -283,7 +288,7 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, goto quick_exit; } - for (unsigned n = 0; n < components; ++n) { + for (n = 0; n < components; ++n) { image_params[n].dx = image_params[n].dy = 1; image_params[n].w = im->xsize; image_params[n].h = im->ysize; From dfe6fff10f5d5457be53682bd50b73b2533451d8 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 14 Mar 2014 14:23:44 -0700 Subject: [PATCH 17/20] Eliminating warning for possibly uninitialized variable use --- libImaging/Jpeg2KDecode.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libImaging/Jpeg2KDecode.c b/libImaging/Jpeg2KDecode.c index a39c700ed..c9a631a75 100644 --- a/libImaging/Jpeg2KDecode.c +++ b/libImaging/Jpeg2KDecode.c @@ -230,7 +230,7 @@ j2ku_graya_la(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, const UINT8 *adata = &atiledata[acsiz * y * w]; UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4; for (x = 0; x < w; ++x) { - UINT32 word, aword; + UINT32 word = 0, aword = 0; switch (csiz) { case 1: word = *data++; break; @@ -288,7 +288,7 @@ j2ku_srgb_rgb(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, for (x = 0; x < w; ++x) { for (n = 0; n < 3; ++n) { - UINT32 word; + UINT32 word = 0; switch (csiz[n]) { case 1: word = *data[n]++; break; @@ -341,7 +341,7 @@ j2ku_sycc_rgb(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, for (x = 0; x < w; ++x) { for (n = 0; n < 3; ++n) { - UINT32 word; + UINT32 word = 0; switch (csiz[n]) { case 1: word = *data[n]++; break; @@ -395,7 +395,7 @@ j2ku_srgba_rgba(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, for (x = 0; x < w; ++x) { for (n = 0; n < 4; ++n) { - UINT32 word; + UINT32 word = 0; switch (csiz[n]) { case 1: word = *data[n]++; break; @@ -447,7 +447,7 @@ j2ku_sycca_rgba(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, for (x = 0; x < w; ++x) { for (n = 0; n < 4; ++n) { - UINT32 word; + UINT32 word = 0; switch (csiz[n]) { case 1: word = *data[n]++; break; From ac8ebccfa631a2390a5dd9cb175ae799e6413364 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Wed, 19 Mar 2014 12:16:14 +0000 Subject: [PATCH 18/20] Added tests and fixed a few bugs that the tests threw up. --- PIL/Jpeg2KImagePlugin.py | 19 ++++++++++++++++--- decode.c | 2 +- encode.c | 20 +++++++++++++++++++- libImaging/Incremental.c | 13 +++++++++++++ libImaging/Jpeg2KDecode.c | 30 ++++++++++++++++++++++-------- libImaging/Jpeg2KEncode.c | 9 ++++++--- selftest.py | 1 + 7 files changed, 78 insertions(+), 16 deletions(-) diff --git a/PIL/Jpeg2KImagePlugin.py b/PIL/Jpeg2KImagePlugin.py index 36e15b762..cd72107a8 100644 --- a/PIL/Jpeg2KImagePlugin.py +++ b/PIL/Jpeg2KImagePlugin.py @@ -153,11 +153,15 @@ class Jpeg2KImageFile(ImageFile.ImageFile): self.reduce = 0 self.layers = 0 + fd = -1 if hasattr(self.fp, "fileno"): - fd = self.fp.fileno() - + try: + fd = self.fp.fileno() + except: + fd = -1 + self.tile = [('jpeg2k', (0, 0) + self.size, 0, (self.codec, self.reduce, self.layers, fd))] @@ -167,6 +171,12 @@ class Jpeg2KImageFile(ImageFile.ImageFile): adjust = power >> 1 self.size = ((self.size[0] + adjust) / power, (self.size[1] + adjust) / power) + + if self.tile: + # Update the reduce and layers settings + t = self.tile[0] + t3 = (t[3][0], self.reduce, self.layers, t[3][3]) + self.tile = [(t[0], t[1], t[2], t3)] ImageFile.ImageFile.load(self) @@ -200,7 +210,10 @@ def _save(im, fp, filename): fd = -1 if hasattr(fp, "fileno"): - fd = fp.fileno() + try: + fd = fp.fileno() + except: + fd = -1 im.encoderconfig = ( offset, diff --git a/decode.c b/decode.c index f1e4aeaba..77038cc2c 100644 --- a/decode.c +++ b/decode.c @@ -827,7 +827,7 @@ PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args) context->format = codec_format; context->reduce = reduce; context->layers = layers; - + return (PyObject*) decoder; } #endif /* HAVE_OPENJPEG */ diff --git a/encode.c b/encode.c index 0c444bfc3..ecb6ba95e 100644 --- a/encode.c +++ b/encode.c @@ -885,7 +885,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) else return NULL; - encoder = PyImaging_EncoderNew(sizeof(JPEG2KENCODESTATE)); + encoder = PyImaging_EncoderNew(sizeof(JPEG2KENCODESTATE)); if (!encoder) return NULL; @@ -906,6 +906,24 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) &context->tile_size_x, &context->tile_size_y); + /* Error on illegal tile offsets */ + if (context->tile_size_x && context->tile_size_y) { + if (context->tile_offset_x <= context->offset_x - context->tile_size_x + || context->tile_offset_y <= context->offset_y - context->tile_size_y) { + PyErr_SetString(PyExc_ValueError, + "JPEG 2000 tile offset too small; top left tile must " + "intersect image area"); + } + + if (context->tile_offset_x > context->offset_x + || context->tile_offset_y > context->offset_y) { + PyErr_SetString(PyExc_ValueError, + "JPEG 2000 tile offset too large to cover image area"); + Py_DECREF(encoder); + return NULL; + } + } + if (quality_layers && PySequence_Check(quality_layers)) { context->quality_is_in_db = strcmp (quality_mode, "dB") == 0; context->quality_layers = quality_layers; diff --git a/libImaging/Incremental.c b/libImaging/Incremental.c index 8e42b2e64..9574d51a8 100644 --- a/libImaging/Incremental.c +++ b/libImaging/Incremental.c @@ -93,8 +93,12 @@ codec_thread(void *ptr) { ImagingIncrementalCodec codec = (ImagingIncrementalCodec)ptr; + DEBUG("Entering thread\n"); + codec->result = codec->entry(codec->im, codec->state, codec); + DEBUG("Leaving thread (%d)\n", codec->result); + flush_stream(codec); SetEvent(codec->hCodecEvent); @@ -107,8 +111,12 @@ codec_thread(void *ptr) { ImagingIncrementalCodec codec = (ImagingIncrementalCodec)ptr; + DEBUG("Entering thread\n"); + codec->result = codec->entry(codec->im, codec->state, codec); + DEBUG("Leaving thread (%d)\n", codec->result); + flush_stream(codec); pthread_mutex_lock(&codec->codec_mutex); @@ -335,6 +343,11 @@ ImagingIncrementalCodecPushBuffer(ImagingIncrementalCodec codec, pthread_cond_wait(&codec->codec_cond, &codec->codec_mutex); pthread_mutex_unlock(&codec->codec_mutex); #endif + if (codec->result < 0) { + DEBUG("got result %d\n", codec->result); + + return codec->result; + } } /* Codecs using an fd don't need data, so when we get here, we're done */ diff --git a/libImaging/Jpeg2KDecode.c b/libImaging/Jpeg2KDecode.c index a39c700ed..d68f23165 100644 --- a/libImaging/Jpeg2KDecode.c +++ b/libImaging/Jpeg2KDecode.c @@ -91,7 +91,7 @@ static void j2ku_gray_l(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, const UINT8 *tiledata, Imaging im) { - unsigned x0 = tileinfo->x0, y0 = tileinfo->y0; + unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0; unsigned w = tileinfo->x1 - tileinfo->x0; unsigned h = tileinfo->y1 - tileinfo->y0; @@ -139,7 +139,7 @@ static void j2ku_gray_rgb(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, const UINT8 *tiledata, Imaging im) { - unsigned x0 = tileinfo->x0, y0 = tileinfo->y0; + unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0; unsigned w = tileinfo->x1 - tileinfo->x0; unsigned h = tileinfo->y1 - tileinfo->y0; @@ -199,7 +199,7 @@ static void j2ku_graya_la(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, const UINT8 *tiledata, Imaging im) { - unsigned x0 = tileinfo->x0, y0 = tileinfo->y0; + unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0; unsigned w = tileinfo->x1 - tileinfo->x0; unsigned h = tileinfo->y1 - tileinfo->y0; @@ -256,7 +256,7 @@ static void j2ku_srgb_rgb(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, const UINT8 *tiledata, Imaging im) { - unsigned x0 = tileinfo->x0, y0 = tileinfo->y0; + unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0; unsigned w = tileinfo->x1 - tileinfo->x0; unsigned h = tileinfo->y1 - tileinfo->y0; @@ -308,7 +308,7 @@ static void j2ku_sycc_rgb(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, const UINT8 *tiledata, Imaging im) { - unsigned x0 = tileinfo->x0, y0 = tileinfo->y0; + unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0; unsigned w = tileinfo->x1 - tileinfo->x0; unsigned h = tileinfo->y1 - tileinfo->y0; @@ -363,7 +363,7 @@ static void j2ku_srgba_rgba(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, const UINT8 *tiledata, Imaging im) { - unsigned x0 = tileinfo->x0, y0 = tileinfo->y0; + unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0; unsigned w = tileinfo->x1 - tileinfo->x0; unsigned h = tileinfo->y1 - tileinfo->y0; @@ -414,7 +414,7 @@ static void j2ku_sycca_rgba(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, const UINT8 *tiledata, Imaging im) { - unsigned x0 = tileinfo->x0, y0 = tileinfo->y0; + unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0; unsigned w = tileinfo->x1 - tileinfo->x0; unsigned h = tileinfo->y1 - tileinfo->y0; @@ -517,7 +517,7 @@ j2k_decode_entry(Imaging im, ImagingCodecState state, opj_stream_set_read_function(stream, j2k_read); opj_stream_set_skip_function(stream, j2k_skip); - opj_stream_set_user_data(stream, context->decoder); + opj_stream_set_user_data(stream, decoder); /* Setup decompression context */ context->error_msg = NULL; @@ -650,6 +650,20 @@ j2k_decode_entry(Imaging im, ImagingCodecState state, goto quick_exit; } + /* Check the tile bounds; if the tile is outside the image area, + or if it has a negative width or height (i.e. the coordinates are + swapped), bail. */ + if (tile_info.x0 >= tile_info.x1 + || tile_info.y0 >= tile_info.y1 + || tile_info.x0 < image->x0 + || tile_info.y0 < image->y0 + || tile_info.x1 - image->x0 > im->xsize + || tile_info.y1 - image->y0 > im->ysize) { + state->errcode = IMAGING_CODEC_BROKEN; + state->state = J2K_STATE_FAILED; + goto quick_exit; + } + unpack(image, &tile_info, state->buffer, im); } diff --git a/libImaging/Jpeg2KEncode.c b/libImaging/Jpeg2KEncode.c index 6bf25def8..d013ffa7f 100644 --- a/libImaging/Jpeg2KEncode.c +++ b/libImaging/Jpeg2KEncode.c @@ -254,7 +254,7 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, opj_stream_set_skip_function(stream, j2k_skip); opj_stream_set_seek_function(stream, j2k_seek); - opj_stream_set_user_data(stream, context->encoder); + opj_stream_set_user_data(stream, encoder); /* Setup an opj_image */ if (strcmp (im->mode, "L") == 0) { @@ -425,8 +425,11 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, } /* Write each tile */ - tiles_x = (im->xsize + tile_width - 1) / tile_width; - tiles_y = (im->ysize + tile_height - 1) / tile_height; + tiles_x = (im->xsize + (params.image_offset_x0 - params.cp_tx0) + + tile_width - 1) / tile_width; + tiles_y = (im->ysize + (params.image_offset_y0 - params.cp_ty0) + + tile_height - 1) / tile_height; + num_tiles = tiles_x * tiles_y; state->buffer = malloc (tile_width * tile_height * components); diff --git a/selftest.py b/selftest.py index 1f905b9a7..248cb3937 100644 --- a/selftest.py +++ b/selftest.py @@ -192,6 +192,7 @@ if __name__ == "__main__": check_module("PIL CORE", "PIL._imaging") check_module("TKINTER", "PIL._imagingtk") check_codec("JPEG", "jpeg") + check_codec("JPEG 2000", "jpeg2k") check_codec("ZLIB (PNG/ZIP)", "zip") check_codec("LIBTIFF", "libtiff") check_module("FREETYPE2", "PIL._imagingft") From f1b79ec5c01fe3a2ee18fd22bf8b1ab492417f3c Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Wed, 19 Mar 2014 12:17:14 +0000 Subject: [PATCH 19/20] Really added the tests this time. --- Tests/images/test-card-lossless.jp2 | Bin 0 -> 134081 bytes Tests/images/test-card-lossy-tiled.jp2 | Bin 0 -> 74557 bytes Tests/images/test-card.png | Bin 0 -> 30776 bytes Tests/test_file_jpeg2k.py | 106 +++++++++++++++++++++++++ 4 files changed, 106 insertions(+) create mode 100644 Tests/images/test-card-lossless.jp2 create mode 100644 Tests/images/test-card-lossy-tiled.jp2 create mode 100644 Tests/images/test-card.png create mode 100644 Tests/test_file_jpeg2k.py diff --git a/Tests/images/test-card-lossless.jp2 b/Tests/images/test-card-lossless.jp2 new file mode 100644 index 0000000000000000000000000000000000000000..497b97b8daea1f52067f59d5983cab615da29267 GIT binary patch literal 134081 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

GFpb@-RI8MV9rh z%5Qs4@Q5&>dv?(rQkDv~3G06didFM}1SOrhM*tODLwFNRw*Y3yswmxw@TkPwaQa`o-J_udkP13J9cZxS7XLf$jVz9ow)h_rPxzEnVthZ( zu!34LaWtdH9TxDhcT-wLl4^7RjZ>6vK(~`J7wpS~&5X{Dz?S^ui@Ct1kBe6NI#=+- zG|L&WR>j%M>$**jZ=5RV)K?UGdR&EW3dFj7Ae`OJu*%|2_KippqGH#A*}Z)ZLTmAt zu^+EpX4UHtjvdi^SzKH%v3O$94?JMw1BdSOTF*4&lZkJPk$i+3x(y!=i3r^eaYF#p zla&6f3)#CZ8zp!#Dol*?NK%KXKdn~61#8fo&u9IUzg_dbkbqDD$ajGIE(hOdboE3B z&eI@#Ss)rjD|Ark5j}-*pS&Y^wO`>^aqAl$sieUt5@k>^iZO6`x$dw4Y$fI{?n$Sx z3zv+0`UlCwtc>Fy3=-LeW|K-+*0hvCE)+EBzya$}r@6SvAAJTkSN)*V*#HqTb7W+~ zKCFE+Ma7jgbHpBPPJ#d@b*YR+tXKDd9qi*r`mY~d{LRIz@UVo#ILkra8wQoL&Qf1F2It=UQxK8rLWT zN1YkHWPe|16e8A` z!^X(4)wOirfdx5zD345yNB$n;nY7~{U&~WAI^$sN^|UO7R>Gz zj8s;+Wd!067J^ab6WE2*zCqJR=RqUiaRMz)yFMCG$abA~Q{K=fPLPvYr0MQ7^_wVn zu<_X%r33`xq3gnM&Nt^NN2KZiQ>y9gO@L8hEVfcgOM?r!MtE+rjbt8VDT$z?!_wIOJ zHS`4D<_wnDg*dXB3xYNCtMwIjoe|nc6_Ha_i6;MR@ zKdu!kVe|}dz@`&agBP%oma{026E3qB>YZv*D>~^4*5Pk3PB0UF87PPBqvhdKZ}c?A zSS9!Ak@FxVHuT&x4C5CYFXD-Z>Pm3BG0S&1!KClK*zp;(1E?OQZ-N9MHRS8;{-DejuRCcpk$Z?4GK#DXFNz(d%y}_MrcY(vl+mUaRpRNd}+yC z>32Ldj>Hznco?v#L+otkg6!F(Nf;*apsKu^Ti(%VH)J#|*B;13I;sG}=m&`#*Zw)% zM19OEn%0Lo#*vlpJwbF=+;SQrpyCcF?V*^5mU*S!o&jYSZfhh-RapD_216)^*1o1q zA^ntCO`%gM^S2thZ*Q&pVe2ch6Kl{JvNFs{VfBX7235nt+Fr}TNb z@?|SehY88v*wJv|Aa&<@K`NDwcrv5Vng3txD`c6>y?zhwp!=ywzB_Djl1dMD(eXT0 z^AFYEH}V*OQpI96$%9$5EiD{Kx6~_^Q%la`{(XsrvN+(jxwwlr^WDPFyzwvLyC1A> z7|fFQ>RbLN$^pAqKo3X{IQK*zY_C4!YECK_>OT%IW@Kj}scN8)Ho9`OkKP>y+UW6Z zfTbx<(-=(`G;f9F1I<(x5c&dStP^IujJ9?D1jkMojr z6)vUc5A}Wke&GBOs-$#^4``x@RI}>iv-3&2DA$mOBXqxPov+KHG_a;n-&7uTVYQXl zV({w%%EiqH;Z*Yh$^)%ALMjwQvR7sr+#^B$O}sZBtgY(U&ecCT{y{Hy@Wo4R{I32dg{Xwg+8VB(O|^EJ;9dnsv?+eoDC z5i(Pfa&^fE&-R&ah5SdE9n71urz5A2#KCu0nx?#`=Er*K%b?Qf8RCPm48MJY!ekz- zj+@sI`RCl=I#vsmaq;WFVgH z^UA_f$6tHOwp#AG&1z! zhvev1Spa#JhsHZ4lnCkn5KL)@y$_e!s*>Y#sUoFtOl8ptCu=1#%`M4f{>Aj5I@Qc7 zg@(=Mgc*3qipOjnV-%WVvl;5jee?OD&B~J9=AKGo!U;D}q#HYqoNqCon4MPu20_xu zybgi4XgyZ6y=M(BcU!Ct=U|{fMDW#p$H^RkSTB7xXL*>(V;6dGs6hXQZ`u4Jp%^K6 zzHA6TOc$3~cV_fE(Ua<)c)}7<+5)d$!JgQ|mLokdH#4;(Rh0UUR1nVjG2aS404L{= z@@#wIF{jj2j8CI?#+LJUztHDW+U#??OHYO2mNTJ85(BnUWlYRz_Rx&BvM@eh!XKS2 z89ATTKfeB<1R5Cl$SL)dR54Gav-ph3Le%pGOmKZZRnWb~<9v=P1*sn5?4F7X&%fDyy?a$zvN@jS z%N!z^TxTKfcW!9;Hdp$-XkdJyh&>lUgf9)4N~VMbpaAFtW@%~sy7+A8FG9>JswOiRunOE*hiCK;axDI;F(kp)jk`t6o_W8?)Wh)IZhrbMF^ z567mnmw1k#!%%O39m7B7uJFRM!yaOL-1T+*Gm`14AuXIiLci{r%)`osdsG(Y2S`ANN0lpeKR2XF8=(7v9Ab(6TBn zw_0>A!htV@_H1C_j6z8{YE&UaE_|OyCIX&Zldto2{m0w`43q62fozvK91kRg!+f%& zesf)Q(lOX*BnDYC@%j6!c4Q2-Zk2eUNB48Hcb;I__&Y0dtC#ncpoQUOsdS!nHMS|C zLGH*MsEI!4NCOq_!JRSCnH;=P154(%^WRWtUP;S?Tymz+*0RY%m@Iet;@wo>#=947 za3Ql%i&=hL-fjg`m`H4V1cAAsy5YutWsk?}>B-{fum*^;J-|N4;W4nQ7-2uQI*~Tw zJCyQNrjfDVa5~WC;#iYg5X%W;(~mjibbY+GaTl}w88)lbhzE@DiBMCW-!+E>X{~<; zUr=1(>clgRw}54iKSYqYwz)ockOvM^&}sLAcOYG{;Oj*EvVpfdQO&0RSUDonh+xNj(v$WMlqP#|zk98HL zkA*&cv^_RCw=XCjEeqB~L8S+=26QDJzgZ_eX8^z&KzXdCJ5Rx*kM$5z6ve)RdIG&9 zY}2@F9H1?y*roFVzNV#`Ole^47~9$W{Xx*7i?6qGTojcV!yk|-#;`{x2Sjfl7OAu$ zn?cvUT@tT#?rd@AgR-YK0GYvJvnK$OcMb3#FJw^aLW8TlUp@?2u)~!d7U)run7&N1 zl;zCZ@*!+<#yhaA>T1~!5U8Mgzq)0L9>9G4l(F=uX?@(t3MoE6CdcEl*h$CV5K_w{ z^OBg39H?))SN?egQGrc^OfA8N*sIM)hmcoRnUYw_Dx(1>n+JP**p8r@dQm8K)qKb7 zFIpjDr2zB2j!C}Xm1D({*M&j#MSo0^QPKASI59BsRVx1MMMvA1jB(UJPOK)P2?qD$ zpV2~85r7D$$UBoN>b3TXJS8Wcx7{BHzx`WT-_daYz)MZAQ43W+8u#Mc*l37OI$%r` z7lB07g*&v3Q)t3YjLId3A0h%|VQxd7Qo;+=9n{m_i_>ch!AL#6Rl*&LIF0WDlY(@eb4OM5RP&}b_4S0IX>Tcw`a-(qBJpfvWi z>9BS>Ku==K`wHp$fz?R?>pqLPKW22pM2o1u#->LlJ6J;;!05gw9tNf#6@NA8e|Vls z_L0fdm|lPQ- zi_mWnvOrbA2(&*dcw8&-JV!&tGRV$6w{^r^-v$c9CDM;O*vvrJwDmL{jv^R5hXq=9 zV7fj&6iXrsR=Vb#-hq8MW1fn-FA-{h9*ygHc6*}`3KS!IsXU3+zu>33L9zX(_ z8w|MHOc!`$;3vN6kL`;Dme7lFt!O*Fq2GV8g)tza_8abq_1mEcAxeO&pNVkiEx@`3 z5(7Z?Ez9?6guv8T;N~5tL?`K1-iHO23Yw0UqXJJ_t z(Y9@Ib7tVS|8nt|m5sd8OO*Tmuo0~>6WsN5xq64E`}Rr)wh~A)?pE4^uCUKyz1SNa zgR4GNIHBg!1ubO1Tiu=Q>{E*0S?^2ZrO%q=BgG^aqdYSpDDrBB-+c5iazdahwZc$A z72>EjSb;WJDoskFt7_cG;!_;v$%NMAKpVvL?S-j9`DuOT%*|(2nfTOh^~8{1C_w>< zqo-{~;EXt$_GDItvC4>eYd|Ekp_N2Qznr#OPI6Eu!<%9v70H(+doAun`p=F!zFW<5 z$?W0!9$K{^f~yJ9MZxlVi!D0-cn{f`L{IZqU=HQ_69aQdpiBH#)ep6&=HEk~j+5{) zKf7n`Zk;z5?k@Lkxi^M>_%3mGegG@($~kI)dpm`YCRK@QVGN$~i!#IAQTkleZE5Cn zi(OD5H3D5YD)Nl2mJtbEMCR;jVWj_)$qx_sb1sp0$4Zpyk1=k>w0YdPUS@KUz)5Ax zpnUa#u$N?Qltm(R60cE{sgAlrIs`(~GiYpd_cP5}QlGCqBSjyxfWx)66xuKHDFFP8 z+tawJV_my_@+MP!2Am};5AaGiyL0ly04OO6Qn=(VbE))v9jMkg)P5Eq*ZRif=5QSU zg9GE^Ewz)~ZK?v`TMLJ+h62Se2m7wd4tr0Ugp!~NmZWDw2GIcE;z!!ASZ=ujirch} zkZSi(_GKlBF(}sE%`s?r4w21aMfJ?s79&1om?3?4vOkA{6O`K+*pt(g-_xWXzh47s zpNcqRE_H%)QehgyG~maupqv1r8e}~_x~#=0Zi5Mm3f!xjR8DkCP_43jJiWIi9xCl` zvR-%OOfw0u7PM?NCkaKM!>>?BV#KK$&3$qhec)XdGqHkdv}0y`$9Ef}_-0X6YppnBg#TRz5+@@N4Of z@Tz+mVSfdJpFc8eu+xNAom(%-*Nz2=d-2ao_w(E&II5zki3TSpr)5m@5+Pnz@5V0! z9JqG|3Y}wgmfN-R03`ke^z9#zmDUOq;4cfyfA@kTWt=2+hJPPG%;lBnWC{VRLf2I=26c0V{SGt zp7_gu=7r1vZtl`t(HT5*?jAJr;4=kJdZGwf8{EEUJWaC5R98_+@^&=M3R>~5@SR7R zZxhGuQ)Lc<#!s9V22VxovRwkRZAh(+V6U@jl$bl(3Rmqt z)%}6h5w@qV166}KWpX>#Q!l;s4AKR3>*l8Nwh z6W~Q^5H(u7a1KO5Z;H>z^CkTTB9GG7>(5ZNRFxn_yi19J6ZRBpd~&q1*#2|6(SPl& zdw0qEGRivWpjHC|FtA$?N`WW<_X0X)N!KZ1ne3w|-cmjmD6TW{zrmCW;94ZZ@$c;) zM6gC5R#O&WpjzrCd(o(AC2IEb$jM2?U67(Rh1!)16>5%&3|yavx`Bj+RAyICg@u;q z66;w9!cXV{V0y5waH9!!Jb7;cZ`6jx$uT5bUK!;pp}m6+c`Udn7wgVZL8zQ=26v#6 z4v&1>XMIpUIhhTm5z#el%*L8CjtrD)D;}n>Cc(REs|KPdT!szg@6u;Q+Fwi3mI(Zh zZj-!PdcPzJ2K~!W&3gQSH#OWkGbgftwfQ}l_&}yGOz1IdB#Ct|9Y5F-MHxRN zv}rTt3?wTETIMymOL_Sw*g3ml=OFjk8atXhWwGG=q2}MVRmT$;#<-SQ^-c$`e(0Jg zO~5RKyJ&r7vObBD|7yq)+G7U%?@s_tw-SxH6{5td3AlNjpUqolnMx%DNKL@4Rq=FWg_gj3A-y89AqoB-_hjiE z)QHaj<*c|DFkzH8kmn)4WL@00-%%shFK*;HZH}*~s+Dz7sGMMi41W4IF_x2d+s|GD zKpvhgoyCR1Xx$F!sB?=gRHQPu-#OYolUoXPH^~X-lNVzYVA3qH$&KP8_Jiz*=TI5zx^)?>ASIm6nPdd3Jol5Fm#S@ALmIhA5Dr1%3^=!QIZ!b zUE1&mT20>!u!E;D2XOC2I{j=vPHSyXP9#_6+uisvWh|sATKX0BV_9(TPzep%cXun8 z$g+_6F3DZUUh4ID)TYqL0*aPb?iQ_~q?d>{v#o{JGm)d^qLyeK;gZ6Zt%Fh>rYmM_ zG8nNYy`*J-IK+m4FDt-o26LobW>R+Tz~ULq-AYfD(Z#P)w#7MoM|r!0vqr}(1+yjz zeqP`M;29795Fx+wL?&qF&5lO2DsNsUYJ{St!e+nz=(~6>+3CxhyJwHBUb9ly18aRP z0Cs(S%W>=g=eF4pfIEKEYR^!`4p9`g;zrGd0~`#c67k3za(GRaV_i^y3I94QW}U!A zOj~P~j(t+IR9P(iYS>Jp4#dXhE<#b_S=0myvRz9XEvb;*BX46qtF^^+QYFLE`gz#!&%|N?Ka1H|80YB!}Vh=XVKAT z>f_{p8PG&+N{HUCNwc+8AJeBm@Cc0#!P<75LO7M^@8g1ZpB4SOLxop*m#fTVhkf>J zynttU0#{SuQA(6>ZWyQWt7)2IAL%Y$&-VTc`uP{YQi3o!m4T+HU8=We)J>}k0tR7G zzzHF%{J5v3-VL^kX_v3GG9D@=Jp{|*@N)0wkaDe;4{w)Ip;8G!c;t5 zHZx!aAd|G-ovvPSo^uur<%8?ulj=W`5YLz!;Bb_RJdesCElnIw0)ewG|8!k&Y!scq zUXfU&{-YMrV-4rG%uC5YKg;5k($Iu}3u4#-Pw~THw0k!2nH#Q8!k{A8l;B;mzP_Ck z{mFb>8N}GxC3wEb-P_1tRP%sh=enP(*i4y-s2qQEslWW@HGG)KJG_`z3FnW{M+sV= zBC3y-(uk%?0o^;7o;_NL+BE)(srDG`%nFPbEL?u^LPteub<0=m0p6NGWRLI}fl%(@ zcX8h63LAVW1WN`v6roFB+F{^68S}M8%qFDpg^@9KFjOX4mFn_}q_wZynAdMU%mB|y z*jbHU>T#=vCiz4*b@GMQn7mPQ^2V?zIILfs0ZFUYLlrrxYWb;ZvixwNjiS5O%>stHWNw!_&ZVsHM-fC|W7( zU)S5Pn&W~ohVoDk8?_dlYfK&-@f6+RJ2+IndYJAomij&-(xZ{_>n`(Nkz}R-BmpG) zr=$idt!MQMt6gT9I2#qmTWKBK>(rcQH3;yUUN<^EZ*J%yyz>vo++juiAD3xrs-?5Q zi7{4Vmsz|QL*;(UJFGQplbZ4IB0wagJZW_HG2}%3kiPy##OZAjOmjZA#g);~X|yK8 zI7~0s#dRW5_M(J~xYyp@tE=ny;Ez?2eF?U5U>=Ks>;@rY>R}+wGWk(@oG@ZhK3}*N zK$XwUH5r2wR3Ww@*#47+6Z-}ZG;;Lo8i$Zois89xBaud6$hA)j4QyxrNrJZ<(Okh{hlMsU{pj!piDomK}Wq55t=*D zM<&AVnQu{E-*;{~hTwxS+#M@=Nif&DC8do)k?!5b`d`zaSuo%kY2I*1R)k65IjPNfk{px+MXO>~*iR-1`2z7^}(lEFD`zycxC>%#0flly<9obd<(8`@|7yHP62xU+p z0HBkEL{)9;KHh6EjX`-wD+HrG%ln0$_Yt z(qPgWlrE)SJwSlcx2~U^j)y}B1`aF*kCj$XewhyEpLXL*uI{VU>e|@n1YFsIb}v*@ z>k&@zljMB#J3Sux@!Tvuwdr?8mUwh7y^_sx^h^Hzm7naad*N$EqJ^nw3;x_-zltnpa(@qcSXV9n=@}> z`erS&H4wGsldI1mao2!*z~&g8Tqh#mPYL-eN0mYd)gt{-7fFu-$J|N3Smr6I;<=T$ zt=M&OX!DjL4*PrlFJ-EYxZr$dSafBO$CJ3;apql%MvCfTHCTc>=&+m|zv+`VR2l}D zgh?Q}5{aqP0pftBOs=MeYA1i2x|4Eb+pX&ZL!wFhE2RZzvGn)i?TWjc6 zb#!;D`i@SGc0>C%187*9e&l}DHmva<9Aq(akI8g zL8ScFcGepM$aEdNRQTa9g+P1q$wlKvvRdA}I7f&qkpzVyS$t^MJk;G?k`R3NdVkqI zXb%oOTcJJR;TZ&Y$3=m?9H9|dXb-b~`q?Fb&e=jfBX1FjYu$HsuSh#Ppv;=JMaOgw zo>?`QOdSPvMouz8#<}D2Pq7N3e%KKUlmw3KupEe@KtQ_yGDMZykKg9WtQcfXxP`m1xKxwfH;v!5XEo(wAP!OZ^DdjIjXWwkpa;BRnfm zs9sihIIgr1lQ;+kcF6vwspnP?+`$^|U7i2hxYpX}Or%bcMwF$VHHZppQ@2X(`%W0k zio^iqyh9vjd@7)BA`Dp=#)l3ZHNX?{z;Tie+I$fCJy^*DWEXuEV#|17Lfbt&>vM>^ z3Hm(qIv_w58=EM;uO#EQ}Y;#bV%DB^j<(NUM})Px3!5A2p@Pf7#UA$ZJ_|7`GC zoX+o{cf*5-RG#Q*$J5_~I~pk56h(WhlR{DEqGeJtSw;7#zf3{w>F43jR65frlcgVM zTO@zjqU0rPsY+0tyEegwifC_N-I(Y11k6_V)~Wy+Y(fo79)7Ua$9S=YEeqWB-892$KuU zz-ARNh10y5I33L};9B6*8qFH&{gmtPEt3~8gRSQ6^zvBu<3+zbDXX+dn{yok#;&4% z=u(W)0#Z7G5fDa%<+Ti6`lxCL`j;WJ+n%7aL5}o?+1^wsW2N?$QseT^{qpW$_z!zh zP~jr(#J&%wCu6CCY@j5qcUSb9*Lq%Nw7Aa9Uu42wxrhE+U{&vngvc+G0Y_ zQFi2eLq71khJ(;oJe{E2Tem1}6j08737|2nGqS&)*5$8Oc(U8+O(sKwHD`l+(@#A0 zI64@&w;~lV35T#8jw3-XZ8W&%;M zhOfw~agH2nLX@dKFFdpiSB4rMkPI{@_GNtcJh@r%nNSWcLaR%PA%WOOJNUhL8^-QUQ*(_&#p&e8HbKRsD4X<4?QIq7yq_08zP=AMwh2bEgXXdL%Npxp z(`$8H!Hhh@ce^efqisW{9uy8Vbt;J&EKhY@IBRrNv%R>*Vu$FPXRm>&OMj;`Tv`IP zH^>)xBZ3_B!>_7b@}dmki*%?Zf#k%O3C84PO5Wkq+?dMpFhJr;(^~bcdYkj;61>KU z7o^SM+ycL<*u}tw5l|mcwAM^>lJ-#67Dcl(Y{n^jJx?@a!=v_Y+9 z(ALQ)Na?z2JY@EqC)98}ofiHM11CvNZQdsq;(Z1a=|-4$p8vv84S8=387WqhO>Wmu zgxO%rOr}g-Uj;rB&2XVt+&i81F4TE%A{pgUw`FM3S;q5?bj0m!6lQlIv;jusx44<5 zYi@nmFq+}A2cPxmvo*k%N=AfQ^TcQ%(Oo+|13>hKqh&9t}Wtnxt zNS)affAvEMy2UlRNO9RR9tQz}k|B%o7GnGTjUxn*Rl!K>!}0M`W>U)~Kq8XkR3iz_ zgyB2Zi}`s+U`1Uhn69Oe3*!k8Aheyr#xbGCZGnhm*G~{pstQ@9ZAeaibWwZIdvw>W zS0yq(zNNgnfC?l(8o!&Owp+nRJn;l(mAIMyZC%aP*z!o!5t>0?yoVBJl(J9%f|LuX zVDL7E71|D^<#cNsH;PzBZD&~e?}!rGqbbmc4~f>)cWO18jZA`3mYSpeo1k%f!nJ!; z*c`^W$Ilp)Yr2X=FyRzZI)XuFI8(iOcr*2*a)p(QdcE1CV~%TmDqkH7mw>h17)llXTzZVq?kI~0 zaX_R${|HJoQ&yxlO)M&zz8FC?^m074pf&zY7zZT*(fF+Fn00=^pqm*`)T;8_t6>{@ zuywODFf@_}F)3M$rb%QN63Xbpt5wTJRW1mA!iA^ju*BK8HT)VkRte@uYOb_}){K^l zQ~7E##M@z*=m62j*Gv^(CBURE-ThK6M@zt7!e%7Rl>(IBl@%VaJH&kX{#SU}c|YN4 z0b;Br+S`N*snvbSLpRO?syhM9>UlW|FkWag-JP;t7TQ#d>;HX7NnSvPfW028>TOw; zE*3?8QaALkIvXfD(%Ri^)}k~FXmF4anjSaLX<{Cs7mf2$Zms0YtNYFstOG8d!5@q} zl^8D{x@mu8ph3i2nM31AB&DN(B|GvXMH3OY5cDr7SLr4#I`wYN&N>=6q3!Lr^V zY+ha)$N_>)8@^!)qq#h9+5e~kH0i!GR6eEa@|+l^RGm8lPA!M`3=4A+vqtQ*ziXIM z)H`${7r0mnZV#XT-wcND9poJ@war1~JwCJ;jb7bop)%#tqSZwRk$T}g! znzccCsU3df^Foonb!l;bU%X1c(9b;~MqbB7PMX(aL(nV4`_~sA~bC3|*9#t&P6z$>wBWTc<6PrAAEDtu!Y6 zK@KrgQld-z5>*PQz?RVnpVjFB0Suk-y8cK*i3IH%1rYwyoLEu31x^g{u5z-*I;V!U zi|v!Vx|_lM91gi6w1TE`o(Q-$vl9Y~qs!cyAWm7HBdnI>rByX@j|8Q`8a~qrTqyoj z)uNlHC!y^8o4VJo+d7nG_)()F*Bv2I1%prbs)Gu0xH#bdFxKqzFPjw7rw(8DWWKMy z7p(PTcTWm}%GWuSX66<9w4JBAD_D74Q*A$LuN>g!wtsiO3=I`5J^tPB@&WIuB750x zqck1v5psNb;nDxL4}wW(NtLQ;Juo*BDgxflV`l@i&gZMvP!E4Sf1a1%{k>uWVCCHK z)Mm0KEFdszu``%s@{VV-IAJrPdoMo|&39>`0;tO;3nYS8ExW9}kA_ohF1pL>6vli? zkI6egPwHjH%&3O^t+Hp`aI|Y_$j+Da207OC$^qI&-(Y#eef+Om zlIzaWl<@^^i-4!|poX~3=%eVp#plM?0PalDSr`LHI|v=_VgKfl!QphUM?0X&HU}-v@l#Nvq|uq+#%Me(zIq6`*#RAFrkt&X zK6~1Z*rSd1n0lQhP?t-cEi8`*nazSL(FMELd$&K)3$UEpms{1O_5O z+6}FEyHSsaR8E(-#XDb_KmoO{LYTMHO6Uubsa0=jC=o2m{F@g7IxTc*t$_pz;!Nd!j}(WRo7Uq{Fn_;32f z0}YZvWTi~%*m*Yc)dg&GDTYPqicAfqz4#U$H6xH~J2WtkKe4)y8K24k$w z3v3u~Ky;y^e%eljzG%4*%0i$_AX3OD`oGpz0(?2ijYHZ!;$ zqc4Gxp^k|h)?(2jLt}*t)*UxM?fAlMdN51+wCM(Z#&CN6mm0A|h5a`uQkKH<584nU zb}+uXe_m-3Rc~}`?#!RMfy8(ye07AfXxeWXQ&K7SnL_W1=f(pdHwMV%6g|?^MBQ_T zWp*h=fow0xj_KC!-Y}?`msU?|rs{43hal6k-t)4WAur}2xWfBDFnb;MfHXe(JOSy4 zGc<=(I7P6}URmf#zD0sJzMGizQMOg@)I{MNRnWy3dsN0=Gka%R@uq6{JFP_=_HVjq z&jphH9(GAS+^f}=V9=Olz;i1RwsH;|t0~k}JLSyR{Z!YCvbVR}*l-$!wn{6|%Q$~p zQ7pY*ublaL&F_jdh6C_}OU}n<>DD$=0ekiQ^p_|#u*>%{ff6sBF)#E{D0pwudEzIr zJhZby{_aIm(=^?&zlQ-ht-n_Yzjl9fC{$z32uDh|)2a``m%s{tzR_$WDAsyL&brwJ z8^IclhsOo`7dEu0qUp52K%PBn=T!5-cTFf{ zRZV=#(9xd|f!MC_Mvf81UwvFdKSK!Rat%a&D-RGHlgE-hubMuxED_P?nhqbuk1TN< zWIg?N2g8_2DVUwx$&g|EidKmJ)dK7yVRWmTL2izMslxiVWqp5P(y4b}!&0dBCe=v&PE_dEChPGq}%3{=W+Mc-8iwntWTzQ6y5)P+|DpQ#mh2#XMeI7{~sM$YRAJ; zTD{@r?zq+Cbwtg8HSi5A0wo7xshM?5aBhFJ>!C0E10l5N6EGAQvfd%WJ6(*`gQE}9 z&fU~y1Y^-bl7*lzJLiJP$Z2siHu{H+(7vm>D(=V+l@oM(@Oa*K_pGFMp~N?tkA9mX$VTal9ca&L%<8wS?tiUV9uhu4 zBS%Yf#uG0o$&>e&ittcs*;UD}LRC;C2C`Ccu_>|TApH+0rqNtkZ77N)&DckBs>kKp zie@*$q>MpDiy+Sr4~jo*yxHx75Wlv36qX1*&eg zoq9IRNV>CMz1|G4A5$0Pp$7PKU`?YMzO%WD|FqvEu4n=PU;u|V$RV(a zH032Q29o5+PBWNkXqHp5hj|$8>UJc4_i(E)MYeYH$2eFXq7!QN4>OGVIvF2PioZ)I z?2;peyJGaJ6-^p8+`xUt$D4Ett}jwa#2A5zxC z!FU7xZ8BCAVK`iO>Zke($2O+#5S74~1_#nB-^j%i;RpGG&z_bFuy^*)>;0pq!>*L`{Rcn`tbBx%O3WCL4|qybqUN3RRLzovW~z2-1!loekM-fLRiQ=Dg@eHLC(%N zx72sx-Bjgb>j2v%a8=Ij z-%w9KtPa{2ZE!A48_CQn0`L4Z8e~99nDQAeHZH4PHtZxn@ z(~Kn-0Y}V%X^*{sjb#42j*)zcNxt(ORfRFsf`ZPV**lO3C|l4oJ8c0Mz9cDCAXCSZ z$5!J7ZiR3|UL?7G>EMl9T2PKxlUl+xo3VQ+0xHxc656d2w}M=oLCS%56i( zo*M9b#*&(K1iLW!nIt9+La9bCvy7H}**lZ5w^Mv2KxFz8=Zj=+bGUDvy@`&u@rVE13f>8o5A=u7#WK-yWF-7Ta_E&pJS-DcvZSLu^kw zVaf;N$ZDK}#VG;3D)*svnyw@=0l#c9#0?WeqFam%h1eVAPcY**l~jxfsUEdA$+ zWvdx~Dk=6sUaeHHD-hWnaqx3`0)y(mFxP|>#_t~E5;aBXTL4qpS-96Wj3H>THf@Ox5iTx${A z)BirZejZfUfK``iFfB?O(mAqc86L^2TZJy^nR8K3`+1B< z^p&I1=521b5wi>(!eUca%omi*i?HXt*RoDUxs!E}0H9Mpt>gGs3T?jE5b=6k{ z>Dq+--YwSKPn_FcFLT24#F?5dbK#~a7PzD8)MJ-78S+v_8b&i-9uu2~dbW$sq*HkU zBP{!r^NqF8uUoFfljI%-5fZrc!#LFWOc>o~@ebALm!$**?qq$;8!f@BYz$s8Z12Gm zP*u-84*mn&Ae9>u!F{kZbtxB;U79!akVs3_gxjobgK`)n} z@nED4hkUOgI*G|E$R+vhp>&!8TiWXaAu{l(jVZNVuu104oxy*(u*X3f!KfbVQ+~!9 zEoJ$=ZRZvs3rK4@ed0SA0`y|4LK{Ah1qY~$--{e&^``Qd|K5nHxqWVJFP!Z#QBWo( z=<^?~>AJv7Z#~q^jWCe69(t_&DCWfw_X8C@Jp1!Un9BScaEMoDm7*HRy*Jo7_U^(b z^Q6hYm6Je9p0NGSr9U51j<-I(8;O@SzJ#!;qtPkT?uI{Kls-#iy`N{j>kF9Q_!QHk zr~{!b`%y6`5}1A^i}DWv*2XaUlp!ReLQpV}?&}P)!2qiE7|ss0ae*0$QZvGR{QdAY zyKa%aeLdH^zb!fcD(SV_cxWUWo-Pc#zs)-qUq5Q0a0HBRSO9ysHskm2-|b+x4^EY) zZt72yqHJ&S?Vp@;pTK|ZjaVn=vHph&KNX4pqY%kK=EwaiwI=y()P@pe%g`?jGhr<8 zY-Y?o-bh;2rpMd)qD8&QN^4l4$Qdmokcupy$fZVZbeT0VO)D0~5R4|SE-mQ#L$bR4 zUTh7%Ph5GaY96-3uO`P`*yU1u4uZbm_x$_a`i%ZhtpiaA^KqeviHv{bDAN~8pDEEZ z-ah7{lWuQ7ej+B^H`%^WWeK?FhltT0J1Qxo$r{K+kJ0yJqAbvm){NqIWrMqVrTT$} zYSEz%@yM9r>Ge{3 zt~;ByRf#+qiJs~amVlg)cWZ=a$m?1&5u#Thh;#{yD+!kd$yYB9bZV!{Lynn&*~S1J z+}(|US$2*AbP~~F#E+<>G$5<@KrhOYHjB!?%nN zgS;cz`N@`F9B(_M@)JTo&{y}IKW3cX619;NZ|dJ#pRbBP=&X0zc1NvFds1%}ekt<% zoZgmx%>3N@vTv3@j@t|oC*MvFyZO20-DHZxLbcswm_s@@5h-^HuV*XcBlmwkU%2;` z-{xO;(4WL#Ela9)VBTl$D7r*?A}jO*k2_s%$yNFNER!p8L;Ib z!#kbBm>GUjyM!XXDD|Ea$LKSlcsoSb2Xot(8{*E`>Bch@s6vgnqik-iwP@(btTK-` z{#Xar1Kw|F$_#s227~E0IqL85Wle_jJJjH14DkNLK$y>#-yapKc%-|RpSp*v^oZkDNqLDly&b6gnkY%(Hz(lS zI&*IGqR60;$n`JW1Q|6m5RvHBtK#6@N~Gaqcrsrcb3yg;_f+>7$&3tcg0$`*HQ41_ zr$))B?LA?h|Kg>{+1awjb3C2E`|bO`iF90*9gyoLVt zrvD3K-3rJDxo+TuAUHSQ9uuo0St%;>+U!L@zPT*hAfe`2FsFCBUB z_1z1p%?E0M=n%TS@ohxqK9&{eDh{84y+SWh`Eze*EDo|Xw}y*e-2f@0r)eCkQ{ciH z;?#QaO2WW2$bLhTHWt4?+Akd4agh6_*8mHpq35jX!+htrZ#Samsu+|g2ya&BfF*whX*G899wL#9s+A&=kJvGOTp&JX^#WF_5llNBCL=LD z1q8>cLzK6w>aIRfnvFt|$)OWW-V3GXkB}KG#IoTwr$;RYSUj?Cf=~CMW1cWPeND!2YWz-zaAqpph}u(qpRh@;3Qr@ zj7UsM`ohJs=5QwDD9?PJFQTbngE)kyPin%b^=iqAURjSpPh+amz#C63+zbX81u68# z<~9ZLzrT4r=AJmLwlgFGQtkjC zxp1*L&W}0VYe=s@YQX)+^ry&qYI80@)_jIlj@*{T4Y@u4fqqzz+|sjO%9gE*ECQ7> zFeDIX<9{o6DUgb?Zot7%(K%9gODjM1l_(zN=)T!o){F}!Q|hi;;h3Xv2QH|v2zR&t zGawl&ikE#W$)T;51ktUGtmiUZoNXGZE`=r{M^p1J8<{M@sL?rI=E{GvEP9UmKT%qr ze|V4hPgk@@EYD)qJiXnT%qIxd1B6K_|2lyMYJXVL2g^cogq9;2VuoZeE ztM7BQeQ*wNSy1)TJ~b?8nQ)X#Jj2F3&Sqg<8g2~6oi+y#TjWtV3p)U>vBjwlb>UdEQ+{L-xJC?D1csh2@^|fT7BPGHZYa`@(2W!Y zfneUOt5Sb7Tq&cJUpN{-AVAQJJ-iDoljjjie*NCg3WLBRK0*^0YIjB!uBNzwb35LV zm>{Mw98fDWH1PgKfSjP$?JG5zf~R245|wyP_mbIBUaHKfeNxDC9`+?EUPjz{%7N4N zLCet^oN>l1R65~0wH4l-nMkX)o9QXgwP!~g7bIpF!@YeTvy9GpwHA?%;>rFK58f!} zCv?OM8q!>ZX>^^B%1?`nWjkY?Zd})nf{no}w>sK)sM22N5-M=|i}Py#5c`xjpd(Zy zg$Pj*do%?h?R=Ezw*_2dv*;Mc$FE2L%0xXW0tl$pPu|6tDxq#G?x8{>+3a~`rvG5r z{o40|@uHvsJ_WK<`V3~uYDiFjlObgu3r7}4!7@FX3P~$BBqU;x;>E!sWn8C4+Z<)q z(oTrau0WMamN=wOSO{t7VTD~IX7{PG{j^nk<7)FXbZ#%9d8&%e@lS$mOi`MQ4M{#; zvK!QMj>_Pk^9RZUwe^(&r1v5))#cR$g=adzG4uxq*Mx1I@-GB)tmZygeVCs82F9F* zZFLMMkm@+{W5SONnf#3)e#36O+W4gis{(BH7GzHDM}*+X1+xf+^0P~c)9$c5rAXVG zfHDNoR0Bst+ZlQ0H7+HT1f_qa$1#33L%&z;Z&5wW%3(B05lhG2Sw%C@g198xFg$|| z=>bcQG4E#w2GkK>)+c=-wh~P}u{+U%&so1B5YT63j>ruxR(3-%1H%keMS%^`4$9jq z4DU$5?l`*~gd$z#x!scMhg79O>*;Y7BuwQ^LJEp7jE$V32SM@y8^x(s+%+m2E2!hu zX(IrBZ?B(9%Xrl${lDQ#g5Q=APhd6EPjE6eJrnlN+&8>R$&8cgcnR>w7ek08Tb0y9 zb0~%uL38-9B!5!PWzc$xJr9%^#5{t_wDS`YTQRL}bhHfD;yl08%1Ihk;hD!^jf%Ac`JW<5*SD%TY6- zdCmG{1MKrmF8iSUsAr+QbZS?G90}cDVB!Y^yx>xg@$T9mj zgWcW#FuX*DgGGk|*nD4eWx}hD6z@?*(4F|C8R`&sUnwP8?PNcpTS4!Z-RyJRD3mlBjBO6g z8{+zZtbPQ1a2HE};U!1}^==To#X^?0lbGT!joN&B#sXkCP2-0{=<6y^Wwzz(QeqBM z@w5$t_6_|1J24)db0{{p7&kIBOYJ`?C$`fK*esR7q+1;{%X{ePf4wfnj9_et3u}S3 z{xXp*tdZaNB&@*BQ)})rBvycKV=veYHfI#j;cvVfodtBZX&16LWK~LdI~DEA48L{1 zCDC_ZuF=j)BrnW48<7JGjhhtWc0pcXL`6lfB|q0k%VR(w_Y%ys&;9^CUsl|_*IYVM z?-mq}k|q4Q+^hNpD+GsDo1*10bUc6ym_f3l@U}uQwa+5HQeucU=m^(6@lqRNraNd8G&t9N*<}% zmU5lDTnuZYmPemuKE`Dir3&GQj%HV)q{{GztZB(4BZO_7{%qr==Fa%soxV<7f z9tu0ZO#&Qu>)tsfIwZXjIoF2Q`lEw{V{tqNNT85z)l^OY3fWh#89t%W5JV1o3(ghJ zu*3Cb5)Pf#{@fh3V-3B1ubz>hz3-~&xB9q9wXt6A4=_j zMDD88IDVRF+_*mrEg;0W4g z@k94P5#BN9wy@hpA|PhD*fY~XG$E1{5EAK>m5y1})IK*)LoIj5Hq%Z8jyXT`tln}PEP2AWFdQK~rn2$l84D)Oa6D z-=&A>TZEEUi7;=Sa?!^`uwDh#%u*U!*=gL8sSH>qPBRz}(m0>0BS{KHKN~IOIOzr@ zpWh@Ph*t}I#G{DnQgI%PJe;S$COe58LIiOMk*Cus3-56lXzu{9%^z6R;+j-h?~D9I zW5z{SGu)!$HQ-^N2X?G_2oSK;QozRP0E*vkq9ZqqxPE^)f4j+!=cw9iHc-Cw=Az(R zL_=_TBKHJbuWC_NHmIKfUOj}0p!)q2;xA`2^2%58s$yK;H$~!cSfCTz{O}91d){8B}7(-gV)AR0!2uTF-AY)WK`=LA+!KHnqkCa*(fsJ`dfyiR9`H`Grp4_np5<9IDF0*MG_ zVWeQzaw?{h#0VA$8!33OirV1oQ+$X={>*oL1xy5tFV!qHmvBqwN(W^74_=NA^5ZAB z5c4bg@;&xXXgHIze3 zv*qf_;{g83`hS}O(pU7IdoRRpRO|J7V4<>wE+cNScvV8BHW}$mYZm8 zAw^DlAIC=4u;^3Mh&`6bp7QMRib_OYh5 z<&6RfJcB+BTPF9V<`5J0uGl@ye3i01T6QR{XEH`Tc{o?t0Wp{$TwGIUT-?iN&+Go2 zz@jqqQD+|?h}EXfW?*a+CI27Rhq{ZDJ6%m;y$-LEDQqUwvhh>zj7Qr`ti(GZyvVwr zLWMXm{#H$P6Igu*RMt$ipIF1fo)IXk>`I2Ne2bZ>^3oubg_yI`3)xvZ_hS|0w4o<= zrC$F0x2Ikj9ASqMpg!e_??&Pw!wFqY>e^jfIXJYXDwfO~E^{1&=2F2@b-uL05$uaUl4X8^7$4q}}WL<9Yb7^-coAYbatF#DraC zf$Q%$;+KP$*xp6}86>r3M8wyKn}fXrg|sxf zQ*0LUK5e_O0HyIDqT%t5{6!$6SFe>F&rDL~NIGNoIH6|d8iZ4wsSh>^YZg6Zwuu>Lc` zu_qtd_0i&lCK18&guwTOSphY08pO~#r0X`i5LUb7Ty1-Tw;7EPczAx}$mXnPYe@U~ z+OUBXgFZuIZqIkyYu%kghwu zRN&b=l`TUtAQQjkAQHh9^n=@o4JB=UPgkJCTUXIU`hqt25SDBu# z5^==QQ+CQw??*gQZi5surXGzv{1qPq&w}IdPayYQFz8u%za3QxgJteTTHfPQX)2+-=rn7*fTTI);7E4+8@8!MjLdcrc1>7 z1NdoJKCslE&ftm2sv21Bddc>gMA7fk>|Pz6f>U`EoS8auKBnA@L~%)+8*{MS&$Fu^ zVWSfU{~9+ox0vHslWzf96g18_DzFcfJy=|;SLYG^H|R)lZ;@k~x6IT|_E->CEq7GD z-3(pJ8LJe=1bEY}*Ng-zycG`vRCuthmIrPg&3UDbu5fxRUug~6w+6r9<1RaMpY*~v z{c7`{KTmwWrbr-O_jmq0$DdkYIG$qvMwT2;-(+bR`$eklC9@6Djn}| zygMVsdV_FhgC55luALFjvYLV$HiK0SM$lA$)NMv}Tpq%s#6|j0Ud~AMM>*I|B;L0< zboP%=D8djrQ9K{2huG?GAb}aCWhtzrkhpQYvd1o5Atz3z{f+ALbZke_L`Hj)X=;7L z5lQS=yJR_NkZBg7RKw(QB>UF!vMs{L%0N*3-#IznjyN#2HNj0&-eUMbb>pDsHhq zrkzQ+*LdYFG{7KpJ;5+TkHHy)7l(1Sp?qH@=mwpB7JnSo5d>RNjXz_o5Nk2tfEh2t zc*OuNiQxWCc#B>MOnN^~l@0hpQW}}{8*7w;j-F^n2BKsFm|+Ue%}Cn?kPYVT!CWH) z4nHS2ZaG~;mNH$(W;K{}Qa_j)IhgSTcspQR6yJLnvrCR#0TUhZTDM*2PX;L)*|jyT zRlwjfr-AaLFvx}f6ZF!R>w^OOe1zp@@3L_XibzH7C(FVsJ&n}gd!ukX*Yk!=i+I12 z?L#ExG5F^uboQU_E((GEq3mAC1qyDZR#cTgn0Ov5E?iC#Jin2RJ{($L@W?NXE4etp z9jOJ!+cqoF%(X@oCh4du?u(A>Y}0I{AmQ8O$|7+`!AR2pp|m%p0^orju5=n&gg6HB zQR`M9;uW5FyojUz>WJ~@mwHY}srtOKt|07RLHV7{lCDvyLh3Ul6ns*IL`99D@Xx5j z5=c{c*I5IYUn7-XoSITlq=Unk^$9z|Gd)H8jed?H-KOixKu+v`F95PAIiR2AKTLnq z=Dar`$%|0A*!iq`=NFi5x~u}Ypp?LKL)Z=;&yO>0-$;tHJm2Wu*;R@_ZyYUGiBOVSjK!cCae0(}ch+BQf zgNVp*_?lBz-#NcIcc@m>x^#MMS9^fG!9Vcazmr>-dOzn4iAxadQ2o<5=t6SQa=rs0 zEWz9ap)JFPd(-g&e3O|hwq8KKXe`+vc}>cPf!aYLu)EEWtRs zQlg=^me^9u&|$7_)H-OQBa_u}wA)0Ca{hu)e2t9(qFTTB1N?Mbmquc#e!;>kZqhTS z6Y_WF7mfvUE-0qjrT{R89uaFT7M~G=IgciAoS^z~Y7#yEgZmrmVec7E#KBzLm2hZy zg(q{@mbQp0Am_mvyM_Xbr~ebuBHU?8xs@1fDxEsFN%i|1R4ft}MKSqRD*6 z*p{UN6*(bGtfXEHtgRHoZ~$xf0ic6k!~4aK0X8x}j5iqd=io|3+AN=7L=HH)jqGSLm84zLa7d>brD@SDOWRT@<1Rh2ZF) zRJLX0vU1s64E5JK`^z#AjEluE1DoYmC zOz00F#$Yk3LAE$PdZA8mEOl4)@bS2Mc<+fzwYk;vX-T4Bt01Zkuym!~S>Vc6ERriu zW@23M35y@!M%4sLs&PD6-{At-zdbVE9bREB`ZTejkG3bMpg2|wzSfPsyd`!1(+wb{ zNlQu!P4KuLA?hA56q{9IK^*gX&b1vT#F}&^HF@wjBJFFwx8Bp?HSN*|mt{J1mDb2N7e%R5dx8r{nH zf4H`_fTE{j95#3}Fk*f^EX~qhbN>6DdqTU;)T%FGUxM^;nS$P3C^pr-bdY^LhJ`yK zAYWLIbL|S&gFO>G7OJa4B!g-~7X~$MRpj!Zes%O4|1oP0nVdk~pLHwWpYDu7EO5nv zw<*{>#`n+IGsLGd$?-~3m>89R)rK)lgU-0&8Kn;s^CGE)SOo6ynwkp9$>;eB&S0|k zHjLzcSn}bz%QLn+%KqqU7t(8XY{Xg_;g z1{eh(rEUGy{t#3(MSjx>q-zrSj{A+DKZIeG-Jn*V$7ulTfJJ^&M)jYGW8=_0t@6JD z{{oNYJ_-j>^8W&X4}#0!tNE@U>OYf0`lI2a_#gNgd=_tls?*&Kh=6~jQljefP2GV3f~1M;8);sp9|A}hSvGp47y<&vZ3Fm;o zY9cz*72vA_8KDmjR|sge6q$zQ$;_aeTbWFnfs(*seXvOQzQ)J|XatJxs zL{XPeSQR@aC?NtIGO2XkG?i$+>r%;DBU9&#OSR$!+>eIO$K=?)_luAL7rWrM+w||X ziL#M2X$L1UsZCxJ9PlS#?k4MrjCr6}2k}gI5j2Wg$JKzx`!&=e%PMpjaSmNu@-lO0 zeAGt!`~Gj&FsX~^y1EW-+zS0R63Q(;YrG(O~Zx>=%xp{Mk+_du_H?{?=4O0&KHb>1)Q>)lt z&l5Y@eDb2KoJWEmmt-~fWV$HKjlz~;;DJ*Vs{Cv~x;m=#AqYdeLRZP>O@vBY9KopO z*BY?v>#TX3KM#-!ebq_R{S6wJr`d0F4+gVYK<6J!RwM;njlrZrujku-pCVb)Zpx3} zgie)12i3v2yN-&s&*royfl)sOjifm|L*vbzH3-U8xu~N=Uc(|(!WQaAMy+=-tU>Lq zlL;txcc=0gm-pMU(Kxs;oYTcjvDxLpn=X~rH^@kaD7T|azc~MBM?EmuApCQ~YAEQ& z1APJaIdDNvib)QDI)1%jKs)F=s(sOzasZit!oy735@B*)Gs2l?>Rn|A_*t~fUj!&6 z8PPA~l;(IDfEM4i>n>#$k+B&aP!(Glz1#rlx0>>z_yZ@zpwsdAr?Pv^OcTCcs5c() z|5m9WGSZK+?~CM^X++yZS+D^R4B8LoHg|lPO5sxzT;#FML@jnxRrnZ%68?exK`rT| zsiqW#7S#CZmRZhZJ|3zaW_k;oUeBF{%Z276DcsC!aOI-S=cddiJ^o&H1c@cXaB1a4 z%gT4r=&V7y&-o>6v-*R9A+iUk`Cs4y+;$KMW2c|UZKEV~hx9(0g0AQ&@o5_&C8!u3 zg{bm)P#{0Yrwl@jdORA;I$p}}XbSJYLNmY}87>~ zTf_+7??Da+{jr+Wh5+P6agJrG2Pb~EJ_SYz zW)|~xM`rtL|7~+kA0Q775V1ucv*hPu7bp~pY6rn{v~Wh+vzf0ud@t@KBbP4r<}~RS ze3I9C^{@n84DSh?bVog(#*tAs$@j8MunRx6MibJzbbK2n_1m49{QEMG8guBOxL&S_ zhNq06F9uO*IO{uH?um6 zZ5%?e$xoCGIENNO6ay-)Af)ZM65YUo@fvS2ZJN|_SK1Ao2}l(aBWoT_Y#%$UX8?-H z1E@~JVHajZ1I*K`Xj5a8PXBZ!K7vgzD!_=hOTZCeCY0dMnbfZD4W;5A3)+Y8zaga<#$m&4NIt{+}oigGT7S2w4X{ zeu5uCPHhS<;idRs5H>=|Xj;CIbHEK8sUEc$MZWp7U!`jx%}?47kFSDO`!t68Z5N9D zDux+VLynRd?2;BcVm+c=Xz@ic|1dkGQw>SLTb;U(zj< z{$Lrmox+CJa^bJ{NA^i59A*zbrq12CuG4iI6K|kz!WFuzef9Pjv+vc~tq26zL{Ic< zMBFkXOqs&9h-SE_lcsfx94BHgTQCclhO0v>;42~Es281M>px#eZ6w)yWfeXECM+^G zLhalexu5yj)L06t4F?6oX<1%^>w6p#ESN|ZPt*MAUB%8pR(^E$q{~C750e5kAhy~U z=jr~y)f+$&yOEKfyYC-8|9#Jy`mNU={ordpD|-KD#mq(2-!X7vk@o8KiIj6$amEJ#fXB2SOIo3axx3McDAc_M2M^G6emm94Os~brg5wP|N03niA zHL`?_1%m^D7}n1$Kz4-K=t(5d+7617$VIdiy`| zIlkW(<*Fp+TZQ%!w}5xQ^-y(UT4TPK{6!CKwQtI_1?> zZ^6g%P&wAdRsel!Z%+{l4oosET=&EGtFIordP!f8R`{iYF)8VSD{U>XZEw?ciXk2Tkx}EJa zj(b6@XUe@(sr6o^>knFL+Lu-ROYINJJ_?UpxNm9S0&JHL>R%J$d{<3% zM~?WYy-o2>RO8|G3#jsshvBg?Tt|o?K%(^xcOxOcucoLU?lc7BiuteXj7C9SnMDKo zq0q~*)VY3FotYOpWe|sUI8Oxi@sV3<>e=L9?RDKFQ(N7CkX{bdQ(;K`hx1R!p+|OnL z@L&PK{Y{zLmNB@-d7)Qx%UU=Ge`e9J!^GeWXfMzpsc4oq`Ms+xweXRPTWcN&YI$mM z6y@3TW|8?Z!Ta21n#uHad&^BJx1{q+#d^KDL>eI3Y;yBUVKwro>-{!*!%1<{61> zWDXqoh$_RUQg&@#oS`VCGFw3E*c!TjPpUoAas^Z@V|iX` zxl;k|{au&{SQ$utdA%Q0_0Jug&y`-FYESM~CRHDCSz6`JDD`mt1MMx*u_oL1p^fvM zty+ZcQgtp89a!diQ0j>iJw$*aCp>G2#goeWoz$Aw=XbbcR67-DZAEr>SaHAAnZQaB zYoHp?-UYK{JB;@SY=n&_mJAOS#&xhWP0V1G9rNHiZ@1=n;$!iZ8Y@?S2%W;kB&r-v zr|Dt%9I=1c9=ldUtFpJ&5;Qi=eGL;T!B~Nc=EnbFW0qYQt8vIZ=eLyHm*Wt*bWE zu{y&Z=ej7gt6(H*PM?!tr6ped$tEWzUcxS(Znsab_q!13Q?{hP4LDrAB)dn9p$tocbN4e0sncsumD(@kQ@jhmq4Q?y@1`p(yIe{o7f z@+kGrqz$%+!A&i2)%Ixd>1P^Mx|*gl*X5=f-bhbZS)LBW?dk29&;t7D>x>9uNGfyv zh~~_yqKq@giUkI`(3PJmD3O_aHkiyu@hDS~u-+K=wP>=waic#Lk-MPUL-j9kla@pW z*xEA?k#r*uR~vlo+lZ-~;_|X~7|{`sl^p>4=oT2OcI~RrhJ5z%g*0XuRJXw7KsLl8 zNnwhyHo&fDkT-_wPJHrz1@B(nFjAhz;lN?x@^{(o%j13RsTvF#DCwx5EniXKNn&U% zj=0*5Ba$84epU`xubP7XYlPE}IuUlyVmOK}0>`7rJPueyB>3<&mlncW0AP~k!9>po zZc(907Ez?^33s7;3MN_ULEUT81Np>OM5e2}&9ZY~5<2GoK#vxkayWPSIVdVmpWAlG zsj1&8Bw-J04L}#8hQ4pjEYrPYb^C8sveu7ojDdt5j0%EGkw}z^&M0@dz&rnt4xurs0SRr8L4+ z&qnX+#lgg*;zis-4g1%rnoJUSFZ6!B0bNs_9?jEM<>8#z-_6mQn({a^HgH2a1h}kq z#GA)&l&WFzbEo>7U-C-Wx*akA4?Lghw&#)dq@ds%T zjN*;Q;9tC(Up&VlH z2N#v5H8<2a(htd0wxWR5x;%HvvOY49jGUXg8pq5kPvT!00v(qT|#@6eV_#Evqau6D^9mET#BgCu#d8)Viq}`FWr|d(@MjM~&vCXrY6m*g z5}K+1L`tLyD%tqI^#CHu6oh@}nRDLI_(>_ZSfs58z4&+v8;jP)g8xvHsmner0ER7& zUXyI2NDK7vDRmKF7$DtiwSW)SAn2o?+&50%h%5pE=2sOl_9x)w7 z8&1)d9mu=VJnU**2c-}enH-A{Wrvax-#`p;u%2ymjgbU-{@a*n@Y}QC?=l&EyRN~+ z$*?fqED-Q_Sa!TQx%lQkwl`|tTZR_1*Z&)hdi|ejdLTDj89>2O6Lf3%uG7= znymq>)%%2K@!gp%2J%gt9m5Mm4=M2MI25?iW znP-E~Jg+UK805Jr(zGGy<+b#zsT9t_=WS&%so|(9)b})v9FWOUb`dwFuj*b^Lul6o z#tSH{WT`mER_jY(CkrgH>L39UrZVKrPpp&K#o3v}Sd&h{v~& zhrX%T0DWaCbEP)r$`TLRzPofi&T``LCfDZwiV=>t^+;Fr)igr~e$FK(>0{Z6lv&oO z|2$0l@|VbC7c$UGhdK+|vJC+6KQ{cnyg&Ju4&!Yy^9+fk7BIi;Ro zdR2p$ewUvxv;xl%jXMB1g!dLa+o}B8a38IY%jqGN)E|*7)(cNj@NWWwrPU09yoUCt z>))wvTD}c?or~bFJ`TS4D<6Y&?!oGp+Na>KdDuHg^^eay)qSS-O!kBA4eblfE8B0Z zAA_WQT=#AEMvK{1>2inRl2Fo7vRyjUZ3gj6!9_CdjDp6_uwK5{-5;UQ20*| z(>T8s(mvk7zH6bCrBku-v6H zE7Zsp-k)Gglr}$&Qb87~eAoEI-4?VXR3>))XVc$&TK>pOV{WWawGEjhLWo9aWw?(M zd2)BH=x5*>x9jNdmXwM!wM!NR2Xt6FgLQtU?NWQK{}q_J8VQ}!)NLZHnr2)a`1LaP zQDJZ&3{eo}U4#f&gro?7H9~)~G*e_W^lDP+I)u{6Eqh=9Cpw9LAd2nSM};!l6-QjK zXkyg0ISqUHIIWdSwJPivyrpQ;A?$aJ;R|QQjX9-;c?waGIcX&(EQ3*a;A%t=n-{WE z1=w@h6RO+ug*;3-K!o>9B_9*PNnkD@bXrEi@XITKQj=W5bA#LJqerPmI(viK{=fQ= z81j}iV;9#*6S^+`;Ch`(B_m5HIVc;<6b^fgyGWq5bz!!%Xq3koxTDPn{r)oGj_z0@ ze37QTWmguGno@`=rhyEaeIVE||5a_3C6%MN45b)jE(1XBAih^kb~nybMx09$$F!J9 zxd45|%YT!7*jDpD-+g@}jQwrO(eJADvCiq%3f{N9oX-N$-R9HgD7OM1S&0Zv8CTb^ zbL>fx866rGLY`9B1)=15Gi3^ps?@lcX;aG(ztLtoIX2(_O{2dU=i4|o%SWEgjAZ%X zc~%9N1ZX))2HNQLieBGF?qu{|Ae|-+qhLFibww2unTS^`c%-UJN*L&ssFk;tdF>eq z@7ahF!Y>x_wRyN1c*dj^r33vmN52?7TgNUDo*Ku3pNpW{Iv zPjqK%i-+XNyhO8gyh8)nDDx)Ckc24Q`%>}T#Y5eW>svu2hyGsIfI#qTVy~{kXO<}h z()q+5!yx66WrJJ=bb$a$j{*!@HUy8{W+IQAq;J#|9FVn(omHEJfc|PS6!AlMntEaP zI%up1`$Q9_(~sD-MgIZLz@7l6VCWW~Pal-VsIn>1PnYomB8*-eP2eHHWCi@7pYj`Y z_m;1}jobflra4a%nAPZ7OvF6wAJY0i6)%ozL-b3)m2)7-&E~<)Qs&$56_&2lkmARg zX@E+jt$`yDU)5mDPQAS^z^6iS1Dx$mmKH5kIi` zjuUf&HE6vWSD0iBZGsgZLQM5&omJTiusvg*#pJ^7opK5Y3GxRq2m+KexdpXXmcE;E zu1V~0bP~ctkFqV|X%HwAL`6dfEDUn$8&b0in&cF>(qh32I<4=vjdje}w~3$zC}b)Q z@DPL58gZu}n?zIm>uS8LdU@)sDF+An>P06$y${6AB?`oLW(kKoP!pu~*zm5Fcu89v zJMIE$;^+55NRfDWkJhr_Xfrqq?bs3VP} zArx(*q~s-RZoqB*m*|cs)cTafUD+OA&0f4!Itz6{z5)6|IM$Rv3Z2YpgK#vP3!<;j zANQf8beC%K`uZ<1!&kFn3iXbMklMH8BxayHzfpU&hLNZrkz_A(_74DK9s{A;+_XpY zogVC7d;!FA2S``6h!Et7Yo>o-$==Z_p0XB6{q;@V(cG#0cR_U;y-l=rMoDNlW>G~_ zX&70MPMmSBOcKbnf7IiJCM0q=$La*)dN}mlLmZm(-JsdYpKfJwpk(Nc@S$$)g$<5L zp4)2l43@RIuHt78V!MM-{QhS-<dHjLb?s+vw6s!TCHye*)UYkAhWb#=?`^&qA674>2#nBR<2N7%fDKB zIfRtMP|0SLe9Soc2rl;V1xrr;1Dw~AflwAoxh+cx{5#>{5I_{IbsjDf2t%~6K2>ke z%FXnmPZJsR29Qd4c(X8?^r77;YH(<7N7N^)jKN|h+Z$2f4|dnw&Z?6`;3|wvJXu!0 zAiB~D@I}jNJv>MWRmj#LHt#OZt>b4grnR+Zs?{QM1WrMXcO!g(DS>ivcWI^_e2481 zH=Y-Xrhq^t07X3EzLaHFVUT|-No}M*Tw2|2^ckbVx~5!|29}>uF!cH2KHLR3DF>uY zr3LLki@q29SjY#NC_C)(jea(*8%q5*83=7YKP zj81WM@=Oe^;2~#QZaPq_QrWc@VVgfLWGI=-0DP`tIdD$GLG4Tls?Bd;b4l!g1Z7= z(?s=`uFE+X455&c3}$xgwCLwxc@faw7bDLfn>WLLM?k6cJqa9fi1Uh6=*P(6O*EUU z?q0Op>%b@|6(&)$iomg{gbU0#yk|yRbgU`;5gq5aC}Nxhh)ntOY?n(�KWjh2zH{ zi_t7pa9sJOedbPjDSmedVR--yGLSQn5H%t--g3fXaRkd*5tAaJA0zF_`pXi;GbEk? zdlqg|Ie$rPqcro6^yi+3ZJIj}oP7)i6-XW5+e!~pRkM$+Ky|pRydOp+t$x>*T?OYC zOlFmmhw`Ts9ZZ)d{_JaAYhfXRZ+ar13>*h97-BgX+hGIwh@@?95a79=lLAjH{X%2p*@6N(W{f z1yhAB@wH1^>+`=!MlaltX~W0WW6f0E*+9agCMSh9(E8z5MfI{4H#G+~#ce>UP2To$ zGMkjUiL#|^Sft=UlAN+fn71r7cE*GX;cd$6YYZp~LP4B?;MFu$P=(6pa}6i(6^Bg@ zoWJ`{kZdHy1l?co;}C^*N=*%G7@G06LMoH_1_rJ-F6onHIJR<|D*>b}lYv29&xb%V zNC}P}0LcLbh~&Bnbli5qli5*!ru9rT6!Ow{)V&ev@g%E3U zpX|wp1sdQ@%!q2zmbs*QnI5)qW0aLNP?dH7+o3X%IRR%TotY;AW{RCrj?`yMUw0wN z6@fBAk95PQ%8OE1ZlXeLFm6(y8Uc&JM_M2wF8Kag+|^EG^T){ktP#x=P+rhU7k`1ZL|Z*TpAiZE`0py$j+~U*HQU{^tP|0joh?PD(L) z-+fdD%3P1A35nn*Au4i?!_X>?*R?F5+_CKYbtBRtWttq$ee!nt{}Tjvn?eyZnl)xa z7Ri)0co2!AOh%bq+Z3^FIPM7DgKA5aw#O7c8@s7h5km_=2U9FYke1=h{U=!x3no>a zEIiBb_NjCa?`=I{@;k%doIM$;h|NWO3#i|tcLmm?yd#mgi$vW|{*U*PJ}mNX~P z@wcK~xauRJ2Xf=X7&Csry~Ynkq&#$gZMpwxTVi4xm3FNc?zM?zz-5iiIN9z$GK`3%q8u|#5E2SwH&apiK6{=UYX@!IUa7!#4HE47M zszTjfKehg_Rk}n+Ey1l?SH>Y9`m2G8WNuOr1%^ACC-0^&F_iQQiC>=65EFRv02~sq zB}9LW&>jXXN%p)-Y=(0>{TF>l_Y-_oP+I4WuU#Wp0T$-y*zO4D6o=O7S8djVp+MnD zTSq6!;x0;ZZC)-4e9GPSuTymh&Nl$!^3^jn^XzEj{1}4|^q+2XFY%L`ZXT-m9JN`* zwjW^iMdTxPd%#Hujs_5VR&^|uWdWE!Sh%4nbi)S3P7(`guUpG(rtk_FHvnTvj6qS; zrJ;rD8l31IHW2z1cU{Wk8!N74u?S|?V>Q8?Tu5^3T|N!kgBdMs40yauq7jONg&^V+ zVZg?93{m|_>&>C%SmI|AiHQ6kG0P>#>qlEOGy-9S2?{`((NzGxMot;j4E_6!&~lrx zCa3hnZSqDRP^<~ElA#gk&98j4Y18*$ex}JV{qo+p2tQLQDJ?P_?w#rxY0NFB&W%HG z+hOR(D+ui=f!VQYwzu4T7ehv&9e(q;Fdv;;2Ty2=_ZE^)7C&OxB~;@k+D>GiE>t*mmn6looW@1m^mUZlA#JIG(~dbt{4OqCOAVvS zNm{-j*HB5U4fyiqUzmR~lV$$XIcc|lW=L`CP>ZD7R^PRpj~B|M{y2Dj8f0^0w5USl zm>mS2BdCl5{tU~-RHLgq!!C#%HJ+_2(pOQ=K!8nzWN zhF<%t{OPzeWW8f_rcIYH8r!yQCmq{PI_g**+qP}nwr$(CZRhKL-e=~_oVov0)%L!2 z-Bq<#VK38sB0^hdL^sE!cMFu5%{abOgfwlQWgmNjs9@1wu?E=b<2PKp6Hy#O&2^@f z3;JWp5K)5Zb;p&KjXl6Y(Twfx<<^;RH4R)e;f1dmtFfcWSW%5)d9&Zgv%miEMzrWE zPZwH(Bt~Z94Z!y#w^H5CiUgLb4Fflv9ihZ<#ju5ti1*tpdEQ+T#n+Qohhfie>3S#nH z@P2$CrL)&zGlWFfw5R18wcL5~xeLj71lma*56TQdqSejsoR!E)9>61AD*a>}@Qq8{ zQ+m9tdGYOi*HDO#D~hTf{@b2Z;>2|_Y7SnIdZ*?6+DXig(oS0q4c%J7hSPUl-i|7^ zqfxwPj?d9|r|~M{A^0ead1yRmZf-6M&j-P^e-vSM!Gm}LM+ZXH0J@ScmGN7#SGw&n zS&xZFrIT>gHda{DDV*Fxp$`$Q-ZJXJ9gDXk&{sfcsO{6UjL0W6T3gk!`>`Yjc7w~4eEt0cM z?iZ)*)X?BDoR*0Dshfkd`fctM^7}Wc-}S;OSvU5 z!+F+xp1zLktChw^Zrnn!EEZ?!I6KceUpQ)?O?ElCdZKAYU9+wpciuUOHACc#{V}+0 z*$GgCB!4QaUvCcCsNEuf2mjA%Ebr5w<`_OZ0*zEP%K4r|(#PqfvwSEBvUY!=Dvh^x z$TxbBRE!VcoJ;8F0zyrMGLl5hvj~*^vsqQN@cn`3OdQEy8ha4)TenE26>Gt?cjcsb2*D-Va5_roz^;Nw+GMUf zKpY$Az{9Sh%N|pSq3EZ!T%(M841GszlhMnR+jdZO%6_@Qq*_&lp8HF8u|28Mi*eHF?Q!Th?+rSbXB+>oT4F%wrI^yAnFN9Vs}CtL3Lfz z$q`ot(81BPc@f^3=QM`2KHZ~-ZU-e;Q9Dx)&R2QRg!S%%J)A1UNJ2L%p&QD@Up_~? z$L~!SnH*kN2a&V=bf7|GpIuaTE4p2;=!A=)DWu3@zF6cI3PO8W!mVopaZ5I1H~MICL@v8O&Ki&QMmZ1 zwcd?q#^$irbf%U#=p36KiaMI~=c0#h?h9@&zeMX0nrmpET40y%&XucAV;bp!D<)n1 z0`So+uDyb$(Bl)NiAjrQQ6Kx5@~lCXX+;4f00H$=h*@{Q#A2BGDET(BXf0KyRC6K0 zIXl2(R2yWaQ}R$Q1<mF0L+6 z39%j>h~hWk51>qT?a&h%g0nFHlD=ZWZy6xZb=FV0hMCAW3sd0z+mTSbc@eyGaT1E1$Tt z!+7d}Zvn3+r`SXoFPRX1 zq{^ZV7P*a@^RAtTbgw}2L@H^X^I@xJH?}(h^!E1pc<0*%OJ2jq`qWs*$N>iDu^0a@XdZVA&-Gz zuvxmy>JUgq{%e*e6dM&Yqowh&dftt{*7!leCOMHXXD)xXY+$KZ#`In4-bT|E*9g4&7xnqRq8bI^j=EVlGndT97K^>(qIWe13oSz|w*ikKWy)@%65R4P0dED- zgoe#Ju^r!=);e@YUj-!~dF)$Ru$VZzGE-1X@1Ny0W!o8M76Czh zR+yiA3vonhGC*#Xv>;Tp#(y^XdtfD7gz{!Ssda~W^LKmW(H{3PiEmpoa}w6AY?B#LG&9E zZ?pQi`PzH?jfRQL_pT9qY0zHos%A;6D!Asp){-$V7T3Ln=G4ZpXX;V~$Avw7_RmBOg`>=71sE;o2A{ zXI z?W~8@jpK2;nhM^x=-yGsVaypiWzy$*t85Ws?M=$PEU)4}I$3FLX7h4Ej(_lV=%t8Q zc88;5%@kSLYs`n|3P81pc||A}0;ZPDUS*O$<>|ikse3ho71Z)Uj$ymvc&#wz@OwA^ zLEk&k?Yf5t;i`56 z47D2P8ERaJFKV=`L!2(<=r$N+qQ-=2-ZpyGYr}9Ft_GU$enYbwn)Yb%F;zP}DSY{4 zkm33L_bM58=2q8Smw__3B*ONae)1O$Re7Bc)lVlIrjIGOW5^$J+_rs9vWACtAe&Wj zqJg_^quF(9iA4!31B&NR+g3MRY|k9(!wrWHDvk}_8M18?fMAGB<0j2&H4_LlDqy6d zlXRS~gYldxq<&`Da}ud9X{WoEm5anUNE*?+Ih6b*u>Fa|dXPYF$;4ecM8Z-dzm49^ z)h00m?`0xMFD_=)Ugz<{Z7E{+wwr1t@v3_^#bf0(oeVAq58PIBXRpljL+65k{G72$ z&~9&c)sZ(quog32=nM2-SiHA7VSru+r!=Hm`5ZqH0!pV1L)Nd1QOzKN>aN-g1Wg`) zqEbrp<5c}j;ZZ{s4oMfNf3j&ZPxzl*YKPh*^F5D zoIq1+nx!tr3h3MT@a8)J?y{rfM78BwLQh4gjaGIm()bimZ8o#no+N4-uGlIxug|MwL%~&{5xQa#6@IT+!kM z&sesG=fn?(H))NGV?MzzTF`$~16PU*D`s#TL&DO5Y^KGbb%2#z&)8YZzrW1@|7Ijp%>{K zzA?4eKyzG4RF)rb$)M{`4y1^2lq{kttAhsPZBvdYJcT}i$D(*P9}QC zT)o8bb)>(-t|duv=H6AFtcTIWSwvi2H}y>1hJ(pJyQ7*-${K3%wfj&m3jq9T*CUI+ zMqKqJcM5UY1wEA2!qaREc-y_UaL_^D*C%4lJZ?LgxC25QkrJ06VS^UV>J1=QP_AWbyYKrPA? z7~G8^Qetx$qXagptm2ldmknjD(m?t3iy2J_;FH zq{^vY`qsE$^=yBKG@9^NknZ9XYwcQHSiTC~CcIG7r)}c;^e%F(MVaJWUJvxoGYjne zs)kcLR|U=}tcmP=weE&ijMf|=h(nnp3H6-!i29Y!mV+!ziEsHgW-g{OUPsLu<>w+{ zS=J{>7C;w4EWTYEmv9X^{rgzY(3ep-qnBSJzO6&B>4<#{w3FNy^1bV0qD%!kFiBB47=dmmwxVv9|`)X7o< z0~m4?w628;_^Tv;0=g4(F%Ux`-(w--{+?&W6_cuuyKCvovAD%4_!M@qw}|%=(;tkb z%_M+l2)aY)M4P;w$d1Y&e2b94Q#}@#K}Mk1d%{otI7Prj0&ns_3Om!8s#P2a!a_sZ zoy>q|bLsr8WQ2WjNOkM-bW@)ZWkWa+zzU>P*5=3X%>67~W8h`i#z1gDPtM~=ft@<; zQE2>8)=5eTi=1t&4Pf|NjkX0?t97r{LaRTr+F<}k{}Kz=e4W^fr_N9UtXtI!oz42J zo@*PIJSu!9Ir@C^ni;IoDSuTsjRyk!xEvcZV`B#Mp^jj*_2W!JIb3nVj3<3#H|Ym> z#jQIjqWg(+8ny}d@3Hj=WcF814j&YLk`Kr~uK;7$fKjo*>yeLR7x>I5+X@A4Qa z%K(o>uJdpDMuGB&JvEexypZ{!^zpku7q6+h^2~#QDx8WF7$>g1wPd#=D{#BFl4A8b z4A*y|+#l_biwH`J^OOE6Bg%4Y$1j>3Z=R70B5GgEW+OY}U}oGotJn)225Q+Tp6gGX zIZ3$&gN;!)U$O@I3a7aRiTCyf9c3ey$$wC<;*T*4reBW3DfAVH7Y>CZGdW_93{*`B z#HUEkT1Iww*!4+U@dYvpLXP1ba5v3b2Gw7E$Cej05?(A4xSdTMrq|rk0cm{9){vaxT8Pp8*jvAYdHE6cU;O zuz`hDhtZbCx6pA?7g=1x=37bNFS5d=<7g94@lhzjsoxy9X)@ZGqHSIw(_;QY`gpSA zD*+xZ8RUf8)5!+Sy|+1})hT(jo@o%f?0aNuIg=w5q4hQ^X}Wp=#*-$D+KB8NvD_N3z7$UGVc@#% zTP{eFLwfQ1UfL46u~1R`@}6=De)7?TL35wYM`2gHl*?{>+`y-(@mwlPXzCNEH!^>m zkwJg44FYfkeQk4V)ed9%>F%|Jq(R@uo4TTO{>T8U!!p-nbO5a=bf}A{m_1nW*4-lol;63& zx*E86UOhX%6A6>>qjXn7!K8BEHK%S6BwT>mQwC(mmexPHITFs$H4Ok=Itg@hQ^c*; z(neRn)g6!jxgI<0&kvfvmtLPoegvB`hCsEXcwPqa#s!$R%)aL5Or83=OxWg4HX&BRc?j<4g2;`{D$k;_?IRGcYC z=oSL_myl_hJUD<*qc_p3Ra-94yobCi20@(DX5}ZZ(8icDaU)-QeWsz{rafFahsS6f z(j9(N`%#!`5X28E27y}JY?MI4z(##TO(5j&Eo<1Tl+EqF`YWd7J_}%~e6tlEikQ2f zLbZeV`MI7huo&DTpp{)wLh55q@u2Bufj!8et+|%1gbp3Dq4NAP3{I>VB29&?lR)Mv zQgOxlswN6ouWg!6S+ha%N&DqMMk%P#!3$fvvc7V(*Bt zVzgKokpR-;VP_Cb!WQH) zIKT)ZQ}+Eg>geb-CXu)@SuRFG_d7e{ukk5>R70m}Hv~lfVECa3YAg!;&DZ1PF}FXVR(Ez+S0AkcU0KGjWVAK^XwjYA3BMbrvQI?iABVG!u>LCgH2i*`Ggvv%x6 zM?#3=Z_tY87T=}yv_%P+8ZS>NETi*{Ft0^*c)QG@h`0?Ut!-Rs8+u^vj-S|B7*B7M zQEt{XERULF!q!nfQ%rUwaL=HSskFh^gc z3eRC`?=`Z-BH?i=g4ZK++RM$xVY0_gFwjn;BDD4_{JkX)hLhnnQ#)xde)XGf3H;}_ zeK2BF;N{{4Jytb61YB1#ta`s)<{arNhRTeEk?ai!nuT`G=!Grc99LCtRCo3!WWgYDBkvGZ)09ro@&mQfjX(|i| z9uV5|M0eX5U!jv6`URbkuyK+NV44=diwF%)2-=(*@ji>w6^JxtJiLQzrwW%9v(!UE$MDpW-oR9(cX$B8pf zpm#GQaPCcRu#NGF{AD5bSq;t>FSYQ#82fAPyORahxes->wC;=;X2TiZvoQV<*{K=U z2iaC*2WWlm!sW6gm~bqOsWk^I6Bd2N$Cs4v;B?XG50UtfTew+WihRJHNuk@@O2-T) z;oe_(P)N^=XyBsX{xmcfqoUCw)j#91ApjjVMRD8?*b}qwxhvSw^I}S4uVfvqJ47ue z!QTL7IzCAyXkp)ET1Lfm>5Fl$_lG9O9*qe#DCedk9`0={BD0QV?iQrf@ib%{j)6XW zG&v2GO5hNdWFtt=*BVYPjK!;sO3yx|D7!{{1p-breMFt!mDt49J1`l)@yQij9~%5q5zT{EdN45X$#BrHGlPHSo=7=qw$ zlPv&zgHoG%Lm8NZ#K)IFR!J|=_O1mkq#+^7=7ihk2 z90w=%vrS6;30g;iY48#)v&d`DMX9P(XV>7;4)rVyC!rfNxXLG)OaRtjnT1(ZfUGE@ zt&gbgvzws*oIpJ*(<2q+d@zkly^BG4beA7#)Xr5qm#q&d5!V){iI;TP35MJ9Y+mjh zov$}va3lcesFwd&-A5s-`BnZZFpZ0ud~)Im!E?Xa+C6VBwt1}8M_Uj=9jeTZOZltS zGPV?4`Ns$-s|SwhW5s~OPi*QVY!ViINzL5+)dCGC;i`OU_wh%A=QP)B`(y{hDYGA> z&NdJ-j;pWfEpzFu4sc_I>sLTW+M3V3;(P!=G%%q6KqPKVw>r((S+^Xf&T$ubPG!2BtzKw7 zi`$F2d>=#q{N*du8&Dpq=aYS zeSR#{i%>?!oi12t-_5~Dp{;{O);7Sse^CWd$&m3)UWwDF3*S#KJLCe+Yt)WH08cOa z4^mA&wrR9?Z*%lRTu!cD(kkAURj%8dW;7Y`eq4bJ#HVJuqF(#EtlKg&42Y8104FYt z2u~I(p!viKBeUz{jb1C<@p1QFFbp&oPCW6_cQ|+g^mP=~{vJ%7Z7n!mxSR~8#{w9~ zGlL=LHt7wli-~ci+^ebgFVprkZwQ~UAJ$;`7$&&E2`3R@fT6o^jfQ*NSa&Aw<%A{Cr)68LR+nL~|#Ocgl&D z)7gPIFS%J5rav`;#V$)+PGqk6Ec&Ejr#>nY9;79)lp5phDL<72;US9)S~fsUpK(IY z+DguYiq$0EVB~O<*36AZ5%MEv7|BU8p5l85tD(3lHP<`A%Z zJRDQeUokZNnL~F7RdYtCw?4n5QL5O_leuoj3F}=+B!kN;OFJtX3SdY!1cNqU^e$jA z>49G13Is@x(dqP@_S9@~eL^x1WbNmoW^-Nr;|XP>ANWTeM5%GzO8OIGmzyGyx5R{R zh(wQp=&@jo0c@IDe7kjPJw02K?b}PMNSF-(rR|I&GGv1(M(J9({>740)hT~C#nPSR zvEb`!qL$1n>dl0MZYMaktQ5Yr8qE7bH~QilUG=w2-(fDOZaWDgrk@dph>RwrL)5J`CovV4+C#+hNqT$17ZqjhT^Ceo!_q zdX}x0A#w?l+K~8YKc+xJ&zs-xyNu>-dJ8(Dn;;!wMI~Bv|F%2Q;+Z$ql%Rp4@t+)a zfEmDfk3BlC5|NTh`;;C`+wOO+cefski!XZ^uo85r2ckxCgh&jd5P3QtL3b6 z{8Cj-4=%`2l;RA#zyD+_xi*&HQ@mBoFey$b^xg>9mOUGF4AiSQmL;P5Ae63+)ylYD zE`XcmT60p{Wp=7G$0D~|ofNZRtY{jTlDjBC>bBK(dnm#%JQ4-n(1T3eFH!DOo-O>e zaq$(5nUAB=28eDJrr3o2#fMqRIr0li^ojRkc{LC;Z%82%u#zUp?F*;0_XBv!`xSBF z7~AT>c=j^snAKNU8Y!4j)Z-RI3BxV4I{ub-mQUZYhB#h#%MY4=U%eeX0Q$oHREvE2 zmM3`#CrH(C17_FwgU4Snh@H7H;fMirF1&I}Z9Ufn;`ZGDlK9R&a6WXjkgNO=$7g%P z-dtqJP3hIcvIok?%idt`-i=^@$X=9G_<$D13aH98IJD3Vyz%A}F z=2gk7nT^YnA%(gF@8y;j*pUhAw2!r?&VU1_KtNdYb43rJJ*;X^&GI(TmNq(%$6G{^{AP+k8f_u{F!Poh zLoGlULK_Vg10^Q5D?7dnF?WQkPP18Fq;(K)VedW_X$^uA?A!nyfzcWV0zP{_2&3JffNE;ks~hAZ+xthl?qrOtg;;~HfGtRYVq*<&h!c}ei>S1l+9Wzvbh|bVA0Q|w?+(tB5$ZXhFY~3#Mx19XuUC!-DH%R$Gc=g%m z0cQsAtEbl`2nDrTZb;-(AF?}@Q17BjYI^Y657bA@Z_HwB{G7UhZ>nleni<>@uO#%s z;gU_{FfS{3|2E?kq}|`c?YPvK)CQ-5Mj!+10|+Vi5y0(dEksVEliJO*mnGL~>?R|7%Qq=yxK){`n#c;w9c0pf4!fxI+)^1TRf)l-g2KF$Q9p5Ezw z!O8`-BneC{#nY?zGOsENx>L(P>N_#bD8<3V4Mvg~ULz#0iRlxaNMh3H)o%9gs}a-r z9A$WKkMBQjy%>U(K!t*P9x?Z#BZ~%DNIw zB!z7)Uqx-%05?)L^0`+&RGsX$EGNL+S)OdRg>0szrM%wba~N&RN9E3OWif+&zyhuP zb@uL5nUGKg0owp5A7|&2hwUG=0BBB9dASdH@IHGDS24i8M2b6oFYR(E03?-&XrYxf z%#OjbGRDOqC(*$lLA8?95I-w{-0}sj9yfrty3#s+Rn(TN$YhBp_-^t2IUb%UWy70E zM;OoxoS(Rv`@xb@IJM3AN(Zw9P4Dc$vuT!gz8&p<9IL{6%w(YH8-=Z6@S_Mc!z)5| zI@%Ne3aT~T)YInZ9?W4K6q3!yz9HdM;fxaHhDf+2Obl1@SK9u|NBsT85urAsJ^8uubK9J^5ue)h?~oA-Y#^*rq}mS1)a3#@7SSmjL{_ zuU1R2Adcb6@|uj-6Oo!=UMVyvO1|9|Z8sv~S zd=S(i+_+T2<2>FRKRSSY(V2WHNDrxmkk@Cip`JI9^$*Hn=(_DjF^lDg}kD3oqt*Dg`2*O$o@=Z;BQtC3*{I&zFx3h+_mW8}vN(DYW zc&Py$b&iq^io!phJF6k6FZx-1y-@eluUZ%wANY3{S?i-0q^)`5B60-p{K!*_#d+=5 zM_9Sg#u^)=h|g2r)+FXiUoetI*)F1rA!9xOBrv=E&!%v+@yQM48#>mh)%X>`Jyxs1 z&}TFXQXiEut*>mu@$;SAZnDY`w{js9@L$v2_Ni!A(GY@)Zk{#`spF>}N0r?b12l5g6(mxxKymNLLkhnc11p5rN)#U6lo*~TquJBRhSGzc%P+h zt7xmnAgVyjcZW{TkZF-l29mKK$&3(w4=?i(ubLHg_(UPhrJ~NUa*Z-ntJKZ|_Dh3P z^Rih)u~e5L0q6fXGZcxP=+vDaPV4t;R+k_)%cViYO^;`9g{B?q<%5GYb6FhbXBurV zD%-~OS4gyC;SsQW;;^+o!@o990ZFb}tdJx_0y&ROc=W83O{<&^k(W!jca0v!cvi5= zX!yxKEti0K2q`!>V7pg)4#ZhaU_`J=D9tl?NN=XKA>jdrsakfo_a=$da(2jjXinx# zw28VFVpp%Oq>D5y@xJ0W%QQ#L6idNd2Yy4vc9v3QXYbM1QKK#h&#C|8Cun1Xv0yz4 z6SS!n6(RbHOgZ-~9ADDzWKUpBqtf1jQK-TSu@~(GitwwsYF#fT?!!}Oi`JI0)fxB4 z4-e%Pnsa6*9l(==iKK62>azy2E65s-7Szt#$N7}62)XUu5rg;!wtLM5+uWb2A|7bM z%ZxD=MT6PF0=wu@t=?mJDaT4(In$(auw3b%TbEQu2Y6qD&T^n&;Sttt zL?IdA`3%q0dw%fWl1&}AZQ)P#faQ;Jy&Wo>fIlqCRCJ1i>FFR~>XxsS73^Z)Agl;E z-~PNC(v7%pEJ=st1Muf(mrO&bS$P?^cDXmSH zDhqZVhJ#^^#n3NT0uHRyf0t#Xig#nF12wbcL(az|oZnAdP?Q(yRQ;>Ev&=KUp!8Kb z&ag7TnnnT!96#SO)%*Ig(_4ZsQFuJpZm?u^B696E28F^^0Q3W3g#{QgJJXe+)Fnte zyac;e8mcQmbx zXfM6J2+`NKoc`+80IP{G4}^+2AsWC5HlfUA_;VR&96)>^btgR2c6~(HjCVgkvrO=s zSnEb;*V)RU4Q?0=c!zG|;E=&H@gTr&M|J57iKkg?N}ktBLwf!xBreh3KYsTRUDs1P z93)A6>kC?(+t5M%uHC%Ef{;OUf~_O00YE==4j+kvQ5m2_}pxJJ%9Y<0LN7Pv^KXo)qLAq z9icJw>IWi%KAlOfCq|fvE6xa?R&R2yjq6zx!F$K3x$o)Lv$rWsIP3hMgU><^6J0cG zI`9@&%a+n7he9V$?5G~mMX=Wk=87g+*^m6w5Jr2lm+S=yx$N-qWgNoN{OV7>^|M_t z$@k09oIF;6#Sz=YH$()$^?0tKtUbCm6YDd2mluiYC_a=#xVrPQY;X#f_PT!WqWyat4_5GS+oD57I|h z69B20X&2@3ztouXwHQ)n#Ep0@dX&sFR@t+)qkntt0@hMXbNf5({nHsS>Je)5;e98k z9kxtdnk{?auTuBR`f-$Q>0LX;=jm~y{N2Jq^hB!?(J%YG(ALY%aT__MVF$f#IMSGp zgs8Orv-Am|+;jjc5vT2KHR|-MYb>5zqk$FOk(9TrKu2;z|0KdFU1L&P#!sD#GVai; zr?h+|T4rcByxZ(q$B#T9Y<02-V28!3n$5fhVg?tk6lW`^YhXkW3)*LT`q96cZVuqW zq~Vk4TD)aZY#<(XQ0V@%Cb6EXP#m){QFcfthpf|j#g_toWju{YPZ_8x;SD{MYvCNs zUoHAvc;T6^{@5P_mJ#R;mZzR>YMq$n_5ohceAE-wMjg!|f}n1H<+-uSgAv|EP(pIq z00%5I69XoFk3BB!4UziCxVaUA;i33p)B;M9VF+8rS6EJu1KA*AF8{Ht*N2H@rB%Sz zI3e;8K;BMf?uL1B|3)4c<(gDnd1Fh(jkF?6awqO`ctUrB7G;a;d{@5_UG|V$<_9B* zzECJbh*>;xlqLhex;^UidymDK;Ev}MTaEW)=vSbprCOMfY!$FuqL-w2i=C80Ipw+l zE)O5nKLIm@Q4bM<1tY&&oUG=Q*$CMZCz2ddm?=d)iReST z)quKyTj~2SgJ07!D|B@VcPSxmwX8@9DvXXA!pyvkBeA`~PsihZ!2oG!_QiSnYK!YJ zx|c)?nHGKRS`X*vl$GC-x2m^9JFi3$ID>m04++L7MEKqZxjmWL93)rs=PS!8^by~T zc*9r_o+L&m$Wf>&P}Gy?IgOl5@gaI|#j~SanvJx|gVOHj(U{2Z8ASJ+OhOT#=v-ks zWeBMj8p_6Jr%Fo76w>V+)z=PJJ7es@(F;QQ>1>AgAX=#<)SL<9_P@P z<3+*8p93DMT_Rnb8jq@0@weGTuUsZ)gL}NxA7l-up}(cCl3#)*LigxVvrq9uR11E8 z2o}TMn}rX1ha~TcbqCUq=zIu!L-Bq?MDH=age02-{@JVyeEAm3#=q_xSC6os{|24E z+1i$Bd%r3FQnud_@f4lY1y&4U>|Yw{$$u*}AsM>=RvC@= zO)&eV*&BM<67K&S*74sQ`!U36yU{-4V{6#^{1`tfHVM&hot@@pG6?8Nqw-hhZZw9k z47A?{$*qpF1|o{^Yss1l7tZ4O5W2RD9T(f}w2Al|42W0DH`{CM;~F-oreXiIAgckd;zUE ztUZ(`H%-Kql+w_emv|gA%4W&g?LHRO-;_dE8^NP&KCcTa*i@!Dln}XlL&J{v<~Zz5 z!E?+t(}~^`I!(0|3;QK!q@nO@B?LN$`IGh*!wzrcIUM@yv9nQ&2=tHE!vmv?iX|k2 z>b+n%Egr?I>ZIzfn(EVJtR#PFv{1yMR5Zyoa=AD$lq4+lSD5&6C@oXTkCt#L!C^?%r^<;Er#MgQS6RaMuY&ph$_8*8)`nj)_P+?MsQ-gthx(V`e<0ol8Ce`h+$00-l<4C(j6I({TiZB8SfHnHiY^MIFnqo>m&*}A|36V~y0X!; z?C!5{lgJf;HG{y`M$5}y4EQ4H1JJvcG5js#=$j2ljGfwK3Fy-+H|}#lH@kV4Et-*lTm>TD`>&&x2B;WkpnT-q3#tFb z^YW6ckA$>QQ2n!67Z?c2n&Yl+NqpU4;#J%Cl5Q|o3`v~3f14laT)j(3`w8npAHQ^D zW6rouMGGmxHDK3#?du{zDD}6|{yG5aw+}!sly1mwa0?3(Vp@aobKO2RG`T^r=dLVi zG&vENLOXfzRzzHE#*#*IB)eCr_&;CpH(HO^ghz-T29nER>ku;9pU=%;v7lPI_B6_V z$87@83+zGXjmIfahLn9c7C1gjlF6ok2fxekb;cLK6`{@A-L8UB5Ku2mo|*d*CKKr;#h& zqoWqFl;a~rz=)uweSn8YFEgK>2GWrYIPNZyvs)p|o-}T|Y^TaO7zPMP5kN1Vfgk)5 zR3r!J+eq`KU#1cmy2Dv8{vAJqUH^ld+NgP?2kZ)`v=UiVfGt5XLv$Lhw z@Yjg$&5jMnogntUgkwbGERef>U{%M~`jI4-LH5L3@5i_J@4NJEd=K)Ek(TOP#E-zw zFCxgf{#*9ULJyuf=9{c(maNiA>p#;K$#Qx30MRMBoNGS5pWh=pR`xJyH|*TEu;1hJ zZRuV3qN>8a(pVyvvMhBbm95S$TlTB?mm+U0^8B0FKJm-Q7olACeauH7`>xb2+VhYX zkcS zQ^LLkYujhmeMIq%MBm1K17hg{8xnttxl3;Sx>;8}1ZWJj84{euGUAJ+%pg1OUxQ!dyy>>Xf1()epBBN*?-b zB?wEW=uxhtrCZp9R*ohy`A)KuhmgVvHL^;QsPeOeQuq%n2 zOn6?@JBo09z=~HK^?&B;EdA=gT2H~ayaSy|h;_+^N|&8N;x!6D#}Ma>66|DH#y?*l zzBN$uGZ}uuz^-mqh+f z`Wox*$BT4rrEY)|_lB%M0-;trnso}`W!e$Vkm(^ zsDg1NM)_pi>6;-iLgb6k8EP+qYH_1pwCO||mI`QMs@GZC6AUp8LdnRaGY%ds1hVs} zf35AB`PbTidV?XU)W2(j9gWPF8AeFZHAMt2CAEWhGo7uOZJVdEQ(0_!L=OO(gjLv1ImKS$7O3>gxCBN|Ba@*xrFgAE$QR|9-d~XC} z?q67|Q+&AIxAo}h8zaIX%N^T4A>7XihXKe?&buck5l9ibsho)4`agjGwPKLN_~+mF zZz1rTURx39om0!Wo~!}Z7qoJvP2m3kbPAgX$g64;~X`)&2J5v3RmMfsfdkyf{)4I#-CnYZJlF z`yZHhB%Cozn5}=faqG7Ka@Rf${T1ApMD@=QFZN`I8`s|&BF3BGNTs+d2!0w8qDaNE_PkFoG{ZNbRx)rj0<%@EUbQ^qP^^b3W z{!APlbTeG!8GOE>lXj^=<$3|s)l0_4sGUJEO^i-FXJ}P~M$iyLsyNHBtVI4jRde-; z4GLLu2tw+_SPhHqV$&x;4)LnS9z7ctSp5}8`Cg6j`BI`hO%(&R0{7>Tk~$)Py=^!? zUoD#%1san*B*E-u42!8yE(0&$Z%{|PM>4C_-T>36%i~MwB)1!hI?UzVUx)7z-1XCY zbt+2CJ+SInUzGhTBw_cly)INNOYmt1B}n5(l&hv>%qhve*1%o{I{B45wxlvOy7E7l`Ms~*&*Y@_<`IO8@4qnG%}Z}}9hjAQ zC9R|j3p~0rZx?+PLPkONWB=q>e=uQ(a6+?bhx^dSE2A*yc!F(N5NAF()E3$@j(_5* zaBOTWgvPrA8Xi;RWo~d-o}ORaD*=l8wLmqdlr~-ixcNxlIP|%s%4Kt0*W5-Y1<=pC z?T`KsiBwgMw^Dw*vXt&v>0fXEA7gI;6j#u+jpFX^uE8O=6N0-3cXxMp2=4B|A;F!+ z-2(()AUMHc@gI`+{lD+lty^_#tF~w7>=`*{_ssKj_jJ1uzj@CdA`5z=M+`Im{Rha5 z##HI$<%(;Jw$AOt>}w9@oL+q>Pjh)5=I}0V9?TOU?x3GR7z9L5Ut)ADc|eMeKj8HO|1$DuhPZ?tm#HHdNCF z-3b?XQpDwg&3!<&26l4q7JStz4(+hUAsZ-vTUNdN`JO z#Ar`u!)(@6Z;t$^oPKKtou>=C+)D6Zj1;EP;uyY|yz%=kb%bwcBxlUPT7`NQeehY! zqwa?+!|Px{UO57;SC_^ln9@71#O-H{6<$iBnU*61PMlyXJ0@S9`v~&rSZ9Qazza=n z;Gyj#(Hh+pqA+aHZ>-jV8D zu2z68JmH0ZhY~N^{}7J>3GahQr=pL=KV=B@+=$lcfMo92y^JVk-F)i5 z;XW~POKCKzF8E)=Y!6{3#tk;S7~9SDeGALzC9S?dbF^WK`l;bvee!IVV-!`SJcU{# zE&Sonbm?Q=P+YWB_ZRPYFGzKHt@H!_o4fx1g_(5FDGJkw730ko))`_y`j5pbF~sj} zDxBdJ?}%bxO(_)KiGNo-tpjNeK z#ZRw0{6kNI$N%Ji(3E;uFB21qM$>gZEdvC>-uLi#wP zbz?s{ogXQGU4*-O9&=KU0jtPp$abi~&S)9!to2Qvu@9x6(^ zb1a1AgrrdaJHUqV5A>We519_NA!#Kkq+=O^h;_D0v%(EPKp}iun@8~HppX08Cpi5M zC)C{Z4e(WDNMJ*;#GY38{Nn#`(Ga2L)X6W3w`5>$Wn8 z*3bU2Ce$%RkED`=jn9JITP=Nw1xRFZ8Fis1k<0Hugs|+wHgg-hQ-F5YzRHH|;3MNh z{zUPddO*`{0B0AXhk0~pF9dYxYrs~7!VGC}g?~Eb`)TNOpR0=HW&|bYdwWO5=tvs? zbq&HwA!v6y6Ob}xlxA!z1#Zy_*`c^0M2YP~hmVCC_*+UrR9Z8GBi)YUsox(Y`{-6^ zae+0S1HRpfacfFk4fl}LstyHjSs0IJL;(jCOfS~$QfZV0!(HlF66^Nyj1Mlz^! zx6`;8MrASDOvcMKk4v&CSC*ySD4li62A(*EilBPlnt1mZOjS}≤I!-q&_SSksMe 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 F{{!88K#~9e literal 0 HcmV?d00001 diff --git a/Tests/images/test-card-lossy-tiled.jp2 b/Tests/images/test-card-lossy-tiled.jp2 new file mode 100644 index 0000000000000000000000000000000000000000..4827731885fef924b4a49de496b13567711645a2 GIT binary patch literal 74557 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 z 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

)B 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

W=;AMlIrFz?-xj4KFuUhABKia^6RGJa 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 D^4Y=1 literal 0 HcmV?d00001 diff --git a/Tests/images/test-card.png b/Tests/images/test-card.png new file mode 100644 index 0000000000000000000000000000000000000000..4b0e1f8ded3626b6f3eb44e03c5515ddc74f5456 GIT binary patch literal 30776 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

IkT)I= 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 literal 0 HcmV?d00001 diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py new file mode 100644 index 000000000..1230f7977 --- /dev/null +++ b/Tests/test_file_jpeg2k.py @@ -0,0 +1,106 @@ +from tester import * + +from PIL import Image +from PIL import ImageFile + +codecs = dir(Image.core) + +if "jpeg2k_encoder" not in codecs or "jpeg2k_decoder" not in codecs: + skip('JPEG 2000 support not available') + +test_card = Image.open('Tests/images/test-card.png') +test_card.load() + +def roundtrip(im, **options): + out = BytesIO() + im.save(out, "JPEG2000", **options) + bytes = out.tell() + out.seek(0) + im = Image.open(out) + im.bytes = bytes # for testing only + im.load() + return im + +# ---------------------------------------------------------------------- + +def test_sanity(): + # Internal version number + assert_match(Image.core.jp2klib_version, '\d+\.\d+\.\d+$') + + im = Image.open('Tests/images/test-card-lossless.jp2') + im.load() + assert_equal(im.mode, 'RGB') + assert_equal(im.size, (640, 480)) + assert_equal(im.format, 'JPEG2000') + +# ---------------------------------------------------------------------- + +# These two test pre-written JPEG 2000 files that were not written with +# PIL (they were made using Adobe Photoshop) + +def test_lossless(): + im = Image.open('Tests/images/test-card-lossless.jp2') + im.load() + im.save('/tmp/test-card.png') + assert_image_similar(im, test_card, 1.0e-3) + +def test_lossy_tiled(): + im = Image.open('Tests/images/test-card-lossy-tiled.jp2') + im.load() + assert_image_similar(im, test_card, 2.0) + +# ---------------------------------------------------------------------- + +def test_lossless_rt(): + im = roundtrip(test_card) + assert_image_equal(im, test_card) + +def test_lossy_rt(): + im = roundtrip(test_card, quality_layers=[20]) + assert_image_similar(im, test_card, 2.0) + +def test_tiled_rt(): + im = roundtrip(test_card, tile_size=(128, 128)) + assert_image_equal(im, test_card) + +def test_tiled_offset_rt(): + im = roundtrip(test_card, tile_size=(128, 128), tile_offset=(0, 0), + offset=(32, 32)) + assert_image_equal(im, test_card) + +def test_irreversible_rt(): + im = roundtrip(test_card, irreversible=True, quality_layers=[20]) + assert_image_similar(im, test_card, 2.0) + +def test_prog_qual_rt(): + im = roundtrip(test_card, quality_layers=[60, 40, 20], progression='LRCP') + assert_image_similar(im, test_card, 2.0) + +def test_prog_res_rt(): + im = roundtrip(test_card, num_resolutions=8, progression='RLCP') + assert_image_equal(im, test_card) + +# ---------------------------------------------------------------------- + +def test_reduce(): + im = Image.open('Tests/images/test-card-lossless.jp2') + im.reduce = 2 + im.load() + assert_equal(im.size, (160, 120)) + +def test_layers(): + out = BytesIO() + test_card.save(out, 'JPEG2000', quality_layers=[100, 50, 10], + progression='LRCP') + out.seek(0) + + im = Image.open(out) + im.layers = 1 + im.load() + assert_image_similar(im, test_card, 13) + + out.seek(0) + im = Image.open(out) + im.layers = 3 + im.load() + assert_image_similar(im, test_card, 0.4) From 831216f609dfb4413dd51559f52b83f6a11f7d0f Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 25 Mar 2014 16:58:44 +0000 Subject: [PATCH 20/20] Fixed a Python 3 specific crash. --- libImaging/Jpeg2KEncode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libImaging/Jpeg2KEncode.c b/libImaging/Jpeg2KEncode.c index 2fcc8e586..c1e16e97f 100644 --- a/libImaging/Jpeg2KEncode.c +++ b/libImaging/Jpeg2KEncode.c @@ -327,7 +327,7 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, tile_height = im->ysize; } - if (PySequence_Check(context->quality_layers)) { + if (context->quality_layers && PySequence_Check(context->quality_layers)) { Py_ssize_t len = PySequence_Length(context->quality_layers); Py_ssize_t n; float *pq;