Added a JPEG 2000 encoder.

This commit is contained in:
Alastair Houghton 2014-03-13 18:27:16 +00:00
parent aea0ec56b2
commit 61fb89ec54
10 changed files with 1004 additions and 208 deletions

View File

@ -173,17 +173,29 @@ def _accept(prefix):
return (prefix[:4] == b'\xff\x4f\xff\x51' return (prefix[:4] == b'\xff\x4f\xff\x51'
or prefix[:12] == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a') 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 # 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', '.jp2')
Image.register_extension("JPEG2000", ".j2k") Image.register_extension('JPEG2000', '.j2k')
Image.register_extension("JPEG2000", ".jpc") Image.register_extension('JPEG2000', '.jpc')
Image.register_extension("JPEG2000", ".jpf") Image.register_extension('JPEG2000', '.jpf')
Image.register_extension("JPEG2000", ".jpx") Image.register_extension('JPEG2000', '.jpx')
Image.register_extension("JPEG2000", ".j2c") Image.register_extension('JPEG2000', '.j2c')
Image.register_mime("JPEG2000", "image/jp2") Image.register_mime('JPEG2000', 'image/jp2')
Image.register_mime("JPEG2000", "image/jpx") Image.register_mime('JPEG2000', 'image/jpx')

View File

@ -3300,6 +3300,7 @@ extern PyObject* PyImaging_ZipDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_EpsEncoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_EpsEncoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_GifEncoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_GifEncoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_JpegEncoderNew(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_PcxEncoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_RawEncoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_RawEncoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_XbmEncoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_XbmEncoderNew(PyObject* self, PyObject* args);
@ -3355,6 +3356,7 @@ static PyMethodDef functions[] = {
#endif #endif
#ifdef HAVE_OPENJPEG #ifdef HAVE_OPENJPEG
{"jpeg2k_decoder", (PyCFunction)PyImaging_Jpeg2KDecoderNew, 1}, {"jpeg2k_decoder", (PyCFunction)PyImaging_Jpeg2KDecoderNew, 1},
{"jpeg2k_encoder", (PyCFunction)PyImaging_Jpeg2KEncoderNew, 1},
#endif #endif
{"tiff_lzw_decoder", (PyCFunction)PyImaging_TiffLzwDecoderNew, 1}, {"tiff_lzw_decoder", (PyCFunction)PyImaging_TiffLzwDecoderNew, 1},
#ifdef HAVE_LIBTIFF #ifdef HAVE_LIBTIFF

View File

@ -778,27 +778,18 @@ PyImaging_JpegDecoderNew(PyObject* self, PyObject* args)
#endif #endif
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
/* JPEG2000 */ /* JPEG 2000 */
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
#ifdef HAVE_OPENJPEG #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" #include "Jpeg2K.h"
PyObject* PyObject*
PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args) PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args)
{ {
ImagingDecoderObject* decoder; ImagingDecoderObject* decoder;
JPEG2KSTATE *context; JPEG2KDECODESTATE *context;
char* mode; char* mode;
char* format; char* format;
@ -809,16 +800,16 @@ PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args)
&reduce, &layers)) &reduce, &layers))
return NULL; return NULL;
if (strcmp (format, "j2k") == 0) if (strcmp(format, "j2k") == 0)
codec_format = OPJ_CODEC_J2K; codec_format = OPJ_CODEC_J2K;
else if (strcmp (format, "jpt") == 0) else if (strcmp(format, "jpt") == 0)
codec_format = OPJ_CODEC_JPT; codec_format = OPJ_CODEC_JPT;
else if (strcmp (format, "jp2") == 0) else if (strcmp(format, "jp2") == 0)
codec_format = OPJ_CODEC_JP2; codec_format = OPJ_CODEC_JP2;
else else
return NULL; return NULL;
decoder = PyImaging_DecoderNew(sizeof(JPEG2KSTATE)); decoder = PyImaging_DecoderNew(sizeof(JPEG2KDECODESTATE));
if (decoder == NULL) if (decoder == NULL)
return NULL; return NULL;
@ -826,9 +817,8 @@ PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args)
decoder->decode = ImagingJpeg2KDecode; decoder->decode = ImagingJpeg2KDecode;
decoder->cleanup = ImagingJpeg2KDecodeCleanup; decoder->cleanup = ImagingJpeg2KDecodeCleanup;
context = (JPEG2KSTATE *)decoder->state.context; context = (JPEG2KDECODESTATE *)decoder->state.context;
strncpy(context->mode, mode, 8);
context->format = codec_format; context->format = codec_format;
context->reduce = reduce; context->reduce = reduce;
context->layers = layers; context->layers = layers;

135
encode.c
View File

@ -40,6 +40,7 @@ typedef struct {
PyObject_HEAD PyObject_HEAD
int (*encode)(Imaging im, ImagingCodecState state, int (*encode)(Imaging im, ImagingCodecState state,
UINT8* buffer, int bytes); UINT8* buffer, int bytes);
int (*cleanup)(ImagingCodecState state);
struct ImagingCodecStateInstance state; struct ImagingCodecStateInstance state;
Imaging im; Imaging im;
PyObject* lock; PyObject* lock;
@ -77,6 +78,9 @@ PyImaging_EncoderNew(int contextsize)
/* Initialize encoder context */ /* Initialize encoder context */
encoder->state.context = context; encoder->state.context = context;
/* Most encoders don't need this */
encoder->cleanup = NULL;
/* Target image */ /* Target image */
encoder->lock = NULL; encoder->lock = NULL;
encoder->im = NULL; encoder->im = NULL;
@ -87,6 +91,8 @@ PyImaging_EncoderNew(int contextsize)
static void static void
_dealloc(ImagingEncoderObject* encoder) _dealloc(ImagingEncoderObject* encoder)
{ {
if (encoder->cleanup)
encoder->cleanup(&encoder->state);
free(encoder->state.buffer); free(encoder->state.buffer);
free(encoder->state.context); free(encoder->state.context);
Py_XDECREF(encoder->lock); Py_XDECREF(encoder->lock);
@ -793,3 +799,132 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
#endif #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:
*
*/

View File

@ -428,6 +428,9 @@ extern int ImagingJpegEncode(Imaging im, ImagingCodecState state,
extern int ImagingJpeg2KDecode(Imaging im, ImagingCodecState state, extern int ImagingJpeg2KDecode(Imaging im, ImagingCodecState state,
UINT8* buffer, int bytes); UINT8* buffer, int bytes);
extern int ImagingJpeg2KDecodeCleanup(ImagingCodecState state); extern int ImagingJpeg2KDecodeCleanup(ImagingCodecState state);
extern int ImagingJpeg2KEncode(Imaging im, ImagingCodecState state,
UINT8* buffer, int bytes);
extern int ImagingJpeg2KEncodeCleanup(ImagingCodecState state);
#endif #endif
extern int ImagingLzwDecode(Imaging im, ImagingCodecState state, extern int ImagingLzwDecode(Imaging im, ImagingCodecState state,
UINT8* buffer, int bytes); UINT8* buffer, int bytes);
@ -502,18 +505,20 @@ struct ImagingCodecStateInstance {
void *context; void *context;
}; };
/* Incremental decoding support */ /* Incremental encoding/decoding support */
typedef struct ImagingIncrementalDecoderStruct *ImagingIncrementalDecoder; typedef struct ImagingIncrementalCodecStruct *ImagingIncrementalCodec;
typedef int (*ImagingIncrementalDecoderEntry)(Imaging im, typedef int (*ImagingIncrementalCodecEntry)(Imaging im,
ImagingCodecState state, ImagingCodecState state,
ImagingIncrementalDecoder decoder); ImagingIncrementalCodec codec);
extern ImagingIncrementalDecoder ImagingIncrementalDecoderCreate(ImagingIncrementalDecoderEntry decoder_entry, Imaging im, ImagingCodecState state); extern ImagingIncrementalCodec ImagingIncrementalCodecCreate(ImagingIncrementalCodecEntry codec_entry, Imaging im, ImagingCodecState state);
extern void ImagingIncrementalDecoderDestroy(ImagingIncrementalDecoder decoder); extern void ImagingIncrementalCodecDestroy(ImagingIncrementalCodec codec);
extern int ImagingIncrementalDecodeData(ImagingIncrementalDecoder decoder, UINT8 *buf, int bytes); extern int ImagingIncrementalCodecPushBuffer(ImagingIncrementalCodec codec, UINT8 *buf, int bytes);
size_t ImagingIncrementalDecoderRead(ImagingIncrementalDecoder decoder, void *buffer, size_t bytes); extern size_t ImagingIncrementalCodecRead(ImagingIncrementalCodec codec, void *buffer, size_t bytes);
off_t ImagingIncrementalDecoderSkip(ImagingIncrementalDecoder decoder, off_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 */ /* Errcodes */
#define IMAGING_CODEC_END 1 #define IMAGING_CODEC_END 1

View File

@ -13,9 +13,9 @@
/* The idea behind this interface is simple: the actual decoding proceeds in /* 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 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 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 decoding thread, providing a buffer of data.
The two threads are never running simultaneously, so there is no need for The two threads are never running simultaneously, so there is no need for
@ -50,27 +50,21 @@
#define DEBUG(...) #define DEBUG(...)
#endif #endif
struct ImagingIncrementalStreamStruct { struct ImagingIncrementalCodecStruct {
UINT8 *buffer;
UINT8 *ptr;
UINT8 *end;
};
struct ImagingIncrementalDecoderStruct {
#ifdef _WIN32 #ifdef _WIN32
HANDLE hDecodeEvent; HANDLE hCodecEvent;
HANDLE hDataEvent; HANDLE hDataEvent;
HANDLE hThread; HANDLE hThread;
#else #else
pthread_mutex_t start_mutex; pthread_mutex_t start_mutex;
pthread_cond_t start_cond; pthread_cond_t start_cond;
pthread_mutex_t decode_mutex; pthread_mutex_t codec_mutex;
pthread_cond_t decode_cond; pthread_cond_t codec_cond;
pthread_mutex_t data_mutex; pthread_mutex_t data_mutex;
pthread_cond_t data_cond; pthread_cond_t data_cond;
pthread_t thread; pthread_t thread;
#endif #endif
ImagingIncrementalDecoderEntry entry; ImagingIncrementalCodecEntry entry;
Imaging im; Imaging im;
ImagingCodecState state; ImagingCodecState state;
struct { struct {
@ -84,222 +78,228 @@ struct ImagingIncrementalDecoderStruct {
#if _WIN32 #if _WIN32
static void __stdcall 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 #else
static void * 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; return NULL;
} }
#endif #endif
/** /**
* Create a new incremental decoder */ * Create a new incremental codec */
ImagingIncrementalDecoder ImagingIncrementalCodec
ImagingIncrementalDecoderCreate(ImagingIncrementalDecoderEntry decoder_entry, ImagingIncrementalCodecCreate(ImagingIncrementalCodecEntry codec_entry,
Imaging im, Imaging im,
ImagingCodecState state) ImagingCodecState state)
{ {
ImagingIncrementalDecoder decoder = (ImagingIncrementalDecoder)malloc(sizeof(struct ImagingIncrementalDecoderStruct)); ImagingIncrementalCodec codec = (ImagingIncrementalCodec)malloc(sizeof(struct ImagingIncrementalCodecStruct));
decoder->entry = decoder_entry; codec->entry = codec_entry;
decoder->im = im; codec->im = im;
decoder->state = state; codec->state = state;
decoder->result = 0; codec->result = 0;
decoder->stream.buffer = decoder->stream.ptr = decoder->stream.end = NULL; codec->stream.buffer = codec->stream.ptr = codec->stream.end = NULL;
decoder->started = 0; codec->started = 0;
/* System specific set-up */ /* System specific set-up */
#if _WIN32 #if _WIN32
decoder->hDecodeEvent = CreateEvent(NULL, FALSE, FALSE, NULL); codec->hCodecEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!decoder->hDecodeEvent) { if (!codec->hCodecEvent) {
free(decoder); free(codec);
return NULL; return NULL;
} }
decoder->hDataEvent = CreateEvent(NULL, FALSE, FALSE, NULL); codec->hDataEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!decoder->hDataEvent) { if (!codec->hDataEvent) {
CloseHandle(decoder->hDecodeEvent); CloseHandle(codec->hCodecEvent);
free(decoder); free(codec);
return NULL; return NULL;
} }
decoder->hThread = _beginthreadex(NULL, 0, incremental_thread, decoder, codec->hThread = _beginthreadex(NULL, 0, codec_thread, codec,
CREATE_SUSPENDED, NULL); CREATE_SUSPENDED, NULL);
if (!decoder->hThread) { if (!codec->hThread) {
CloseHandle(decoder->hDecodeEvent); CloseHandle(codec->hCodecEvent);
CloseHandle(decoder->hDataEvent); CloseHandle(codec->hDataEvent);
free(decoder); free(codec);
return NULL; return NULL;
} }
#else #else
if (pthread_mutex_init(&decoder->start_mutex, NULL)) { if (pthread_mutex_init(&codec->start_mutex, NULL)) {
free (decoder); free (codec);
return NULL; return NULL;
} }
if (pthread_mutex_init(&decoder->decode_mutex, NULL)) { if (pthread_mutex_init(&codec->codec_mutex, NULL)) {
pthread_mutex_destroy(&decoder->start_mutex); pthread_mutex_destroy(&codec->start_mutex);
free(decoder); free(codec);
return NULL; return NULL;
} }
if (pthread_mutex_init(&decoder->data_mutex, NULL)) { if (pthread_mutex_init(&codec->data_mutex, NULL)) {
pthread_mutex_destroy(&decoder->start_mutex); pthread_mutex_destroy(&codec->start_mutex);
pthread_mutex_destroy(&decoder->decode_mutex); pthread_mutex_destroy(&codec->codec_mutex);
free(decoder); free(codec);
return NULL; return NULL;
} }
if (pthread_cond_init(&decoder->start_cond, NULL)) { if (pthread_cond_init(&codec->start_cond, NULL)) {
pthread_mutex_destroy(&decoder->start_mutex); pthread_mutex_destroy(&codec->start_mutex);
pthread_mutex_destroy(&decoder->decode_mutex); pthread_mutex_destroy(&codec->codec_mutex);
pthread_mutex_destroy(&decoder->data_mutex); pthread_mutex_destroy(&codec->data_mutex);
free(decoder); free(codec);
return NULL; return NULL;
} }
if (pthread_cond_init(&decoder->decode_cond, NULL)) { if (pthread_cond_init(&codec->codec_cond, NULL)) {
pthread_mutex_destroy(&decoder->start_mutex); pthread_mutex_destroy(&codec->start_mutex);
pthread_mutex_destroy(&decoder->decode_mutex); pthread_mutex_destroy(&codec->codec_mutex);
pthread_mutex_destroy(&decoder->data_mutex); pthread_mutex_destroy(&codec->data_mutex);
pthread_cond_destroy(&decoder->start_cond); pthread_cond_destroy(&codec->start_cond);
free(decoder); free(codec);
return NULL; return NULL;
} }
if (pthread_cond_init(&decoder->data_cond, NULL)) { if (pthread_cond_init(&codec->data_cond, NULL)) {
pthread_mutex_destroy(&decoder->start_mutex); pthread_mutex_destroy(&codec->start_mutex);
pthread_mutex_destroy(&decoder->decode_mutex); pthread_mutex_destroy(&codec->codec_mutex);
pthread_mutex_destroy(&decoder->data_mutex); pthread_mutex_destroy(&codec->data_mutex);
pthread_cond_destroy(&decoder->start_cond); pthread_cond_destroy(&codec->start_cond);
pthread_cond_destroy(&decoder->decode_cond); pthread_cond_destroy(&codec->codec_cond);
free(decoder); free(codec);
return NULL; return NULL;
} }
if (pthread_create(&decoder->thread, NULL, incremental_thread, decoder)) { if (pthread_create(&codec->thread, NULL, codec_thread, codec)) {
pthread_mutex_destroy(&decoder->start_mutex); pthread_mutex_destroy(&codec->start_mutex);
pthread_mutex_destroy(&decoder->decode_mutex); pthread_mutex_destroy(&codec->codec_mutex);
pthread_mutex_destroy(&decoder->data_mutex); pthread_mutex_destroy(&codec->data_mutex);
pthread_cond_destroy(&decoder->start_cond); pthread_cond_destroy(&codec->start_cond);
pthread_cond_destroy(&decoder->decode_cond); pthread_cond_destroy(&codec->codec_cond);
pthread_cond_destroy(&decoder->data_cond); pthread_cond_destroy(&codec->data_cond);
free(decoder); free(codec);
return NULL; return NULL;
} }
#endif #endif
return decoder; return codec;
} }
/** /**
* Destroy an incremental decoder */ * Destroy an incremental codec */
void void
ImagingIncrementalDecoderDestroy(ImagingIncrementalDecoder decoder) ImagingIncrementalCodecDestroy(ImagingIncrementalCodec codec)
{ {
DEBUG("destroying\n"); DEBUG("destroying\n");
if (!decoder->started) { if (!codec->started) {
#ifdef _WIN32 #ifdef _WIN32
ResumeThread(decoder->hThread); ResumeThread(codec->hThread);
#else #else
pthread_cond_signal(&decoder->start_cond); pthread_cond_signal(&codec->start_cond);
#endif #endif
decoder->started = 1; codec->started = 1;
} }
#ifndef _WIN32 #ifndef _WIN32
pthread_mutex_lock(&decoder->data_mutex); pthread_mutex_lock(&codec->data_mutex);
#endif #endif
decoder->stream.buffer = decoder->stream.ptr = decoder->stream.end = NULL; codec->stream.buffer = codec->stream.ptr = codec->stream.end = NULL;
#ifdef _WIN32 #ifdef _WIN32
SetEvent(decoder->hDataEvent); SetEvent(codec->hDataEvent);
WaitForSingleObject(decoder->hThread, INFINITE); WaitForSingleObject(codec->hThread, INFINITE);
CloseHandle(decoder->hThread); CloseHandle(codec->hThread);
CloseHandle(decoder->hDecodeEvent); CloseHandle(codec->hCodecEvent);
CloseHandle(decoder->hDataEvent); CloseHandle(codec->hDataEvent);
#else #else
pthread_cond_signal(&decoder->data_cond); pthread_cond_signal(&codec->data_cond);
pthread_mutex_unlock(&decoder->data_mutex); 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(&codec->start_mutex);
pthread_mutex_destroy(&decoder->decode_mutex); pthread_mutex_destroy(&codec->codec_mutex);
pthread_mutex_destroy(&decoder->data_mutex); pthread_mutex_destroy(&codec->data_mutex);
pthread_cond_destroy(&decoder->start_cond); pthread_cond_destroy(&codec->start_cond);
pthread_cond_destroy(&decoder->decode_cond); pthread_cond_destroy(&codec->codec_cond);
pthread_cond_destroy(&decoder->data_cond); pthread_cond_destroy(&codec->data_cond);
#endif #endif
free (decoder); free (codec);
} }
/** /**
* Decode data using an incremental decoder */ * Push a data buffer for an incremental codec */
int int
ImagingIncrementalDecodeData(ImagingIncrementalDecoder decoder, ImagingIncrementalCodecPushBuffer(ImagingIncrementalCodec codec,
UINT8 *buf, int bytes) UINT8 *buf, int bytes)
{ {
if (!decoder->started) { if (!codec->started) {
DEBUG("starting\n"); DEBUG("starting\n");
#ifdef _WIN32 #ifdef _WIN32
ResumeThread(decoder->hThread); ResumeThread(codec->hThread);
#else #else
pthread_cond_signal(&decoder->start_cond); pthread_cond_signal(&codec->start_cond);
#endif #endif
decoder->started = 1; codec->started = 1;
} }
DEBUG("providing %p, %d\n", buf, bytes); DEBUG("providing %p, %d\n", buf, bytes);
#ifndef _WIN32 #ifndef _WIN32
pthread_mutex_lock(&decoder->data_mutex); pthread_mutex_lock(&codec->data_mutex);
#endif #endif
decoder->stream.buffer = decoder->stream.ptr = buf; codec->stream.buffer = codec->stream.ptr = buf;
decoder->stream.end = buf + bytes; codec->stream.end = buf + bytes;
#ifdef _WIN32 #ifdef _WIN32
SetEvent(decoder->hDataEvent); SetEvent(codec->hDataEvent);
WaitForSingleObject(decoder->hDecodeEvent); WaitForSingleObject(codec->hCodecEvent);
#else #else
pthread_cond_signal(&decoder->data_cond); pthread_cond_signal(&codec->data_cond);
pthread_mutex_unlock(&decoder->data_mutex); pthread_mutex_unlock(&codec->data_mutex);
pthread_mutex_lock(&decoder->decode_mutex); pthread_mutex_lock(&codec->codec_mutex);
pthread_cond_wait(&decoder->decode_cond, &decoder->decode_mutex); pthread_cond_wait(&codec->codec_cond, &codec->codec_mutex);
pthread_mutex_unlock(&decoder->decode_mutex); pthread_mutex_unlock(&codec->codec_mutex);
#endif #endif
DEBUG("got result %d\n", decoder->result); DEBUG("got result %d\n", codec->result);
return decoder->result; return codec->result;
} }
size_t 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) void *buffer, size_t bytes)
{ {
UINT8 *ptr = (UINT8 *)buffer; UINT8 *ptr = (UINT8 *)buffer;
@ -308,29 +308,29 @@ ImagingIncrementalDecoderRead(ImagingIncrementalDecoder decoder,
DEBUG("reading (want %llu bytes)\n", (unsigned long long)bytes); DEBUG("reading (want %llu bytes)\n", (unsigned long long)bytes);
#ifndef _WIN32 #ifndef _WIN32
pthread_mutex_lock(&decoder->data_mutex); pthread_mutex_lock(&codec->data_mutex);
#endif #endif
while (bytes) { while (bytes) {
size_t todo = 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) { if (!remaining) {
DEBUG("waiting for data\n"); DEBUG("waiting for data\n");
#ifndef _WIN32 #ifndef _WIN32
pthread_mutex_lock(&decoder->decode_mutex); pthread_mutex_lock(&codec->codec_mutex);
#endif #endif
decoder->result = (int)(decoder->stream.ptr - decoder->stream.buffer); codec->result = (int)(codec->stream.ptr - codec->stream.buffer);
#if _WIN32 #if _WIN32
SetEvent(decoder->hDecodeEvent); SetEvent(codec->hCodecEvent);
WaitForSingleObject(decoder->hDataEvent); WaitForSingleObject(codec->hDataEvent);
#else #else
pthread_cond_signal(&decoder->decode_cond); pthread_cond_signal(&codec->codec_cond);
pthread_mutex_unlock(&decoder->decode_mutex); pthread_mutex_unlock(&codec->codec_mutex);
pthread_cond_wait(&decoder->data_cond, &decoder->data_mutex); pthread_cond_wait(&codec->data_cond, &codec->data_mutex);
#endif #endif
remaining = decoder->stream.end - decoder->stream.ptr; remaining = codec->stream.end - codec->stream.ptr;
DEBUG("got %llu bytes\n", (unsigned long long)remaining); DEBUG("got %llu bytes\n", (unsigned long long)remaining);
} }
@ -340,14 +340,14 @@ ImagingIncrementalDecoderRead(ImagingIncrementalDecoder decoder,
if (!todo) if (!todo)
break; break;
memcpy (ptr, decoder->stream.ptr, todo); memcpy (ptr, codec->stream.ptr, todo);
decoder->stream.ptr += todo; codec->stream.ptr += todo;
bytes -= todo; bytes -= todo;
done += todo; done += todo;
ptr += todo; ptr += todo;
} }
#ifndef _WIN32 #ifndef _WIN32
pthread_mutex_unlock(&decoder->data_mutex); pthread_mutex_unlock(&codec->data_mutex);
#endif #endif
DEBUG("read total %llu bytes\n", (unsigned long long)done); DEBUG("read total %llu bytes\n", (unsigned long long)done);
@ -356,7 +356,7 @@ ImagingIncrementalDecoderRead(ImagingIncrementalDecoder decoder,
} }
off_t off_t
ImagingIncrementalDecoderSkip(ImagingIncrementalDecoder decoder, ImagingIncrementalCodecSkip(ImagingIncrementalCodec codec,
off_t bytes) off_t bytes)
{ {
off_t done = 0; off_t done = 0;
@ -364,29 +364,29 @@ ImagingIncrementalDecoderSkip(ImagingIncrementalDecoder decoder,
DEBUG("skipping (want %llu bytes)\n", (unsigned long long)bytes); DEBUG("skipping (want %llu bytes)\n", (unsigned long long)bytes);
#ifndef _WIN32 #ifndef _WIN32
pthread_mutex_lock(&decoder->data_mutex); pthread_mutex_lock(&codec->data_mutex);
#endif #endif
while (bytes) { while (bytes) {
off_t todo = 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) { if (!remaining) {
DEBUG("waiting for data\n"); DEBUG("waiting for data\n");
#ifndef _WIN32 #ifndef _WIN32
pthread_mutex_lock(&decoder->decode_mutex); pthread_mutex_lock(&codec->codec_mutex);
#endif #endif
decoder->result = (int)(decoder->stream.ptr - decoder->stream.buffer); codec->result = (int)(codec->stream.ptr - codec->stream.buffer);
#if _WIN32 #if _WIN32
SetEvent(decoder->hDecodeEvent); SetEvent(codec->hCodecEvent);
WaitForSingleObject(decoder->hDataEvent); WaitForSingleObject(codec->hDataEvent);
#else #else
pthread_cond_signal(&decoder->decode_cond); pthread_cond_signal(&codec->codec_cond);
pthread_mutex_unlock(&decoder->decode_mutex); pthread_mutex_unlock(&codec->codec_mutex);
pthread_cond_wait(&decoder->data_cond, &decoder->data_mutex); pthread_cond_wait(&codec->data_cond, &codec->data_mutex);
#endif #endif
remaining = decoder->stream.end - decoder->stream.ptr; remaining = codec->stream.end - codec->stream.ptr;
} }
if (todo > remaining) if (todo > remaining)
todo = remaining; todo = remaining;
@ -394,12 +394,12 @@ ImagingIncrementalDecoderSkip(ImagingIncrementalDecoder decoder,
if (!todo) if (!todo)
break; break;
decoder->stream.ptr += todo; codec->stream.ptr += todo;
bytes -= todo; bytes -= todo;
done += todo; done += todo;
} }
#ifndef _WIN32 #ifndef _WIN32
pthread_mutex_unlock(&decoder->data_mutex); pthread_mutex_unlock(&codec->data_mutex);
#endif #endif
DEBUG("skipped total %llu bytes\n", (unsigned long long)done); DEBUG("skipped total %llu bytes\n", (unsigned long long)done);
@ -407,3 +407,59 @@ ImagingIncrementalDecoderSkip(ImagingIncrementalDecoder decoder,
return done; 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;
}

View File

@ -12,13 +12,11 @@
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
/* Decoder */ /* Decoder */
/* -------------------------------------------------------------------- */
typedef struct { typedef struct {
/* CONFIGURATION */ /* CONFIGURATION */
/* Output mode */
char mode[8];
/* Specify the desired format */ /* Specify the desired format */
OPJ_CODEC_FORMAT format; OPJ_CODEC_FORMAT format;
@ -31,10 +29,50 @@ typedef struct {
/* PRIVATE CONTEXT (set by decoder) */ /* PRIVATE CONTEXT (set by decoder) */
const char *error_msg; 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: * Local Variables:

View File

@ -34,7 +34,7 @@ typedef struct {
static void static void
j2k_error(const char *msg, void *client_data) j2k_error(const char *msg, void *client_data)
{ {
JPEG2KSTATE *state = (JPEG2KSTATE *) client_data; JPEG2KDECODESTATE *state = (JPEG2KDECODESTATE *) client_data;
free((void *)state->error_msg); free((void *)state->error_msg);
state->error_msg = strdup(msg); state->error_msg = strdup(msg);
} }
@ -46,9 +46,9 @@ j2k_error(const char *msg, void *client_data)
static OPJ_SIZE_T static OPJ_SIZE_T
j2k_read(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) 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; 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 static OPJ_OFF_T
j2k_skip(OPJ_OFF_T p_nb_bytes, void *p_user_data) j2k_skip(OPJ_OFF_T p_nb_bytes, void *p_user_data)
{ {
ImagingIncrementalDecoder decoder = (ImagingIncrementalDecoder)p_user_data; ImagingIncrementalCodec decoder = (ImagingIncrementalCodec)p_user_data;
off_t pos = ImagingIncrementalDecoderSkip(decoder, p_nb_bytes); off_t pos = ImagingIncrementalCodecSkip(decoder, p_nb_bytes);
return pos ? pos : (OPJ_OFF_T)-1; return pos ? pos : (OPJ_OFF_T)-1;
} }
@ -455,9 +455,9 @@ enum {
static int static int
j2k_decode_entry(Imaging im, ImagingCodecState state, 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_stream_t *stream = NULL;
opj_image_t *image = NULL; opj_image_t *image = NULL;
opj_codec_t *codec = 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); opj_stream_set_user_data(stream, context->decoder);
/* Setup decompression context */ /* Setup decompression context */
context->error_msg = NULL; 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) { for (n = 0; n < sizeof(j2k_unpackers) / sizeof (j2k_unpackers[0]); ++n) {
if (color_space == j2k_unpackers[n].color_space if (color_space == j2k_unpackers[n].color_space
&& image->numcomps == j2k_unpackers[n].components && 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; unpack = j2k_unpackers[n].unpacker;
break; break;
} }
@ -617,6 +616,14 @@ j2k_decode_entry(Imaging im, ImagingCodecState state,
unpack(image, &tile_info, state->buffer, im); 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: quick_exit:
if (codec) if (codec)
opj_destroy_codec(codec); opj_destroy_codec(codec);
@ -631,14 +638,14 @@ j2k_decode_entry(Imaging im, ImagingCodecState state,
int int
ImagingJpeg2KDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) 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) if (state->state == J2K_STATE_DONE || state->state == J2K_STATE_FAILED)
return -1; return -1;
if (state->state == J2K_STATE_START) { if (state->state == J2K_STATE_START) {
context->decoder = ImagingIncrementalDecoderCreate(j2k_decode_entry, context->decoder = ImagingIncrementalCodecCreate(j2k_decode_entry,
im, state); im, state);
if (!context->decoder) { if (!context->decoder) {
state->errcode = IMAGING_CODEC_BROKEN; state->errcode = IMAGING_CODEC_BROKEN;
@ -649,7 +656,7 @@ ImagingJpeg2KDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
state->state = J2K_STATE_DECODING; 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 int
ImagingJpeg2KDecodeCleanup(ImagingCodecState state) { 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) if (context->decoder)
ImagingIncrementalDecoderDestroy(context->decoder); ImagingIncrementalCodecDestroy(context->decoder);
return -1; return -1;
} }

548
libImaging/Jpeg2KEncode.c Normal file
View File

@ -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(&params);
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, &params);
/* 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, &params, 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:
*
*/

View File

@ -34,7 +34,7 @@ _LIB_IMAGING = (
"RankFilter", "RawDecode", "RawEncode", "Storage", "SunRleDecode", "RankFilter", "RawDecode", "RawEncode", "Storage", "SunRleDecode",
"TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask", "XbmDecode", "TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask", "XbmDecode",
"XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode", "Incremental", "XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode", "Incremental",
"Jpeg2KDecode") "Jpeg2KDecode", "Jpeg2KEncode")
def _add_directory(path, dir, where=None): def _add_directory(path, dir, where=None):