From 61fb89ec54818f4e2a4d35e9c4bc97ec7716e6b3 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Thu, 13 Mar 2014 18:27:16 +0000 Subject: [PATCH] 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 62aced03e49..94d49f70538 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 84b25219b36..fe623e78014 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 359b996957e..0bc64ec77a2 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 8be44d8ec50..979742a8480 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 7a7eb9c662e..1602622a2c2 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 bb51d91c7f3..45f06457091 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 codec->result; +} - return decoder->result; +size_t +ImagingIncrementalCodecBytesInBuffer(ImagingIncrementalCodec codec) +{ + return codec->stream.ptr - codec->stream.buffer; } size_t -ImagingIncrementalDecoderRead(ImagingIncrementalDecoder decoder, +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 26935729600..128302f1947 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; + +/* -------------------------------------------------------------------- */ +/* 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; - opj_stream_t *stream; -} JPEG2KSTATE; + ImagingIncrementalCodec encoder; +} JPEG2KENCODESTATE; /* * Local Variables: diff --git a/libImaging/Jpeg2KDecode.c b/libImaging/Jpeg2KDecode.c index 77615cce577..c3254d889d9 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 00000000000..75f40d58f7a --- /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 e948e1050f2..8d3511e0c9a 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ "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):