Added a JPEG 2000 decoder based on OpenJPEG.

This commit is contained in:
Alastair Houghton 2014-03-12 16:25:59 +00:00
parent 1a03ca9224
commit d6b8f0f666
10 changed files with 1185 additions and 3 deletions

View File

@ -205,7 +205,7 @@ class ImageFile(Image.Image):
else:
raise IndexError(ie)
if not s: # truncated jpeg
if not s and not d.handles_eof: # truncated jpeg
self.tile = []
# JpegDecode needs to clean things up here either way

189
PIL/Jpeg2KImagePlugin.py Normal file
View File

@ -0,0 +1,189 @@
#
# The Python Imaging Library
# $Id$
#
# JPEG2000 file handling
#
# History:
# 2014-03-12 ajh Created
#
# Copyright (c) 2014 Coriolis Systems Limited
# Copyright (c) 2014 Alastair Houghton
#
# See the README file for information on usage and redistribution.
#
__version__ = "0.1"
from PIL import Image, ImageFile, _binary
import struct
import os
import io
def _parse_codestream(fp):
"""Parse the JPEG 2000 codestream to extract the size and component
count from the SIZ marker segment, returning a PIL (size, mode) tuple."""
hdr = fp.read(2)
lsiz = struct.unpack('>H', hdr)[0]
siz = hdr + fp.read(lsiz - 2)
lsiz, rsiz, xsiz, ysiz, xosiz, yosiz, xtsiz, ytsiz, \
xtosiz, ytosiz, csiz \
= struct.unpack('>HHIIIIIIIIH', siz[:38])
ssiz = [None]*csiz
xrsiz = [None]*csiz
yrsiz = [None]*csiz
for i in range(csiz):
ssiz[i], xrsiz[i], yrsiz[i] \
= struct.unpack('>BBB', siz[36 + 3 * i:39 + 3 * i])
size = (xsiz - xosiz, ysiz - yosiz)
if csiz == 1:
mode = 'L'
elif csiz == 2:
mode = 'LA'
elif csiz == 3:
mode = 'RGB'
elif csiz == 4:
mode == 'RGBA'
else:
mode = None
return (size, mode)
def _parse_jp2_header(fp):
"""Parse the JP2 header box to extract size, component count and
color space information, returning a PIL (size, mode) tuple."""
# Find the JP2 header box
header = None
while True:
lbox, tbox = struct.unpack('>I4s', fp.read(8))
if lbox == 1:
lbox = struct.unpack('>Q', fp.read(8))[0]
hlen = 16
else:
hlen = 8
if tbox == b'jp2h':
header = fp.read(lbox - hlen)
break
else:
fp.seek(lbox - hlen, os.SEEK_CUR)
if header is None:
raise SyntaxError('could not find JP2 header')
size = None
mode = None
hio = io.BytesIO(header)
while True:
lbox, tbox = struct.unpack('>I4s', hio.read(8))
if lbox == 1:
lbox = struct.unpack('>Q', hio.read(8))[0]
hlen = 16
else:
hlen = 8
content = hio.read(lbox - hlen)
if tbox == b'ihdr':
height, width, nc, bpc, c, unkc, ipr \
= struct.unpack('>IIHBBBB', content)
size = (width, height)
if unkc:
if nc == 1:
mode = 'L'
elif nc == 2:
mode = 'LA'
elif nc == 3:
mode = 'RGB'
elif nc == 4:
mode = 'RGBA'
break
elif tbox == b'colr':
meth, prec, approx = struct.unpack('>BBB', content[:3])
if meth == 1:
cs = struct.unpack('>I', content[3:7])[0]
if cs == 16: # sRGB
if nc == 3:
mode = 'RGB'
elif nc == 4:
mode = 'RGBA'
break
elif cs == 17: # grayscale
if nc == 1:
mode = 'L'
elif nc == 2:
mode = 'LA'
break
elif cs == 18: # sYCC
if nc == 3:
mode = 'RGB'
elif nc == 4:
mode == 'RGBA'
break
return (size, mode)
##
# Image plugin for JPEG2000 images.
class Jpeg2KImageFile(ImageFile.ImageFile):
format = "JPEG2000"
format_description = "JPEG 2000 (ISO 15444)"
def _open(self):
sig = self.fp.read(4)
if sig == b'\xff\x4f\xff\x51':
self.codec = "j2k"
print 'Reading size/mode'
try:
self.size, self.mode = _parse_codestream(self.fp)
except Exception as e:
print e
print '%r, %r' % (self.size, self.mode)
else:
sig = sig + self.fp.read(8)
if sig == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a':
self.codec = "jp2"
self.size, self.mode = _parse_jp2_header(self.fp)
else:
raise SyntaxError('not a JPEG 2000 file')
if self.size is None or self.mode is None:
raise SyntaxError('unable to determine size/mode')
self.reduce = 0
self.layers = 0
self.tile = [('jpeg2k', (0, 0) + self.size, 0,
(self.codec, self.reduce, self.layers))]
def load(self):
if self.reduce:
power = 1 << self.reduce
self.size = (self.size[0] / power, self.size[1] / power)
ImageFile.ImageFile.load(self)
def _accept(prefix):
return (prefix[:4] == b'\xff\x4f\xff\x51'
or prefix[:12] == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a')
# ------------------------------------------------------------
# Registry stuff
Image.register_open("JPEG2000", Jpeg2KImageFile, _accept)
Image.register_extension("JPEG2000", ".jp2")
Image.register_extension("JPEG2000", ".j2k")
Image.register_extension("JPEG2000", ".jpc")
Image.register_extension("JPEG2000", ".jpf")
Image.register_extension("JPEG2000", ".jpx")
Image.register_extension("JPEG2000", ".j2c")
Image.register_mime("JPEG2000", "image/jp2")
Image.register_mime("JPEG2000", "image/jpx")

View File

@ -33,6 +33,7 @@ _plugins = ['ArgImagePlugin',
'ImtImagePlugin',
'IptcImagePlugin',
'JpegImagePlugin',
'Jpeg2KImagePlugin',
'McIdasImagePlugin',
'MicImagePlugin',
'MpegImagePlugin',

View File

@ -3283,6 +3283,7 @@ extern PyObject* PyImaging_FliDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_GifDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_HexDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_JpegDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_TiffLzwDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_MspDecoderNew(PyObject* self, PyObject* args);
@ -3351,6 +3352,9 @@ static PyMethodDef functions[] = {
#ifdef HAVE_LIBJPEG
{"jpeg_decoder", (PyCFunction)PyImaging_JpegDecoderNew, 1},
{"jpeg_encoder", (PyCFunction)PyImaging_JpegEncoderNew, 1},
#endif
#ifdef HAVE_OPENJPEG
{"jpeg2k_decoder", (PyCFunction)PyImaging_Jpeg2KDecoderNew, 1},
#endif
{"tiff_lzw_decoder", (PyCFunction)PyImaging_TiffLzwDecoderNew, 1},
#ifdef HAVE_LIBTIFF

View File

@ -52,6 +52,7 @@ typedef struct {
struct ImagingCodecStateInstance state;
Imaging im;
PyObject* lock;
int handles_eof;
} ImagingDecoderObject;
static PyTypeObject ImagingDecoderType;
@ -194,6 +195,12 @@ _setimage(ImagingDecoderObject* decoder, PyObject* args)
return Py_None;
}
static PyObject *
_get_handles_eof(ImagingDecoderObject *decoder)
{
return PyBool_FromLong(decoder->handles_eof);
}
static struct PyMethodDef methods[] = {
{"decode", (PyCFunction)_decode, 1},
{"cleanup", (PyCFunction)_decode_cleanup, 1},
@ -201,6 +208,13 @@ static struct PyMethodDef methods[] = {
{NULL, NULL} /* sentinel */
};
static struct PyGetSetDef getseters[] = {
{"handles_eof", (getter)_get_handles_eof, NULL,
"True if this decoder expects to handle EOF itself.",
NULL},
{NULL, NULL, NULL, NULL, NULL} /* sentinel */
};
static PyTypeObject ImagingDecoderType = {
PyVarObject_HEAD_INIT(NULL, 0)
"ImagingDecoder", /*tp_name*/
@ -232,7 +246,7 @@ static PyTypeObject ImagingDecoderType = {
0, /*tp_iternext*/
methods, /*tp_methods*/
0, /*tp_members*/
0, /*tp_getset*/
getseters, /*tp_getset*/
};
/* -------------------------------------------------------------------- */
@ -762,3 +776,63 @@ PyImaging_JpegDecoderNew(PyObject* self, PyObject* args)
return (PyObject*) decoder;
}
#endif
/* -------------------------------------------------------------------- */
/* JPEG2000 */
/* -------------------------------------------------------------------- */
#ifdef HAVE_OPENJPEG
/* We better define this decoder last in this file, so the following
undef's won't mess things up for the Imaging library proper. */
#undef UINT8
#undef UINT16
#undef UINT32
#undef INT8
#undef INT16
#undef INT32
#include "Jpeg2K.h"
PyObject*
PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args)
{
ImagingDecoderObject* decoder;
JPEG2KSTATE *context;
char* mode;
char* format;
OPJ_CODEC_FORMAT codec_format;
int reduce = 0;
int layers = 0;
if (!PyArg_ParseTuple(args, "ss|ii", &mode, &format,
&reduce, &layers))
return NULL;
if (strcmp (format, "j2k") == 0)
codec_format = OPJ_CODEC_J2K;
else if (strcmp (format, "jpt") == 0)
codec_format = OPJ_CODEC_JPT;
else if (strcmp (format, "jp2") == 0)
codec_format = OPJ_CODEC_JP2;
else
return NULL;
decoder = PyImaging_DecoderNew(sizeof(JPEG2KSTATE));
if (decoder == NULL)
return NULL;
decoder->handles_eof = 1;
decoder->decode = ImagingJpeg2KDecode;
decoder->cleanup = ImagingJpeg2KDecodeCleanup;
context = (JPEG2KSTATE *)decoder->state.context;
strncpy(context->mode, mode, 8);
context->format = codec_format;
context->reduce = reduce;
context->layers = layers;
return (PyObject*) decoder;
}
#endif /* HAVE_OPENJPEG */

View File

@ -424,6 +424,11 @@ extern int ImagingJpegDecodeCleanup(ImagingCodecState state);
extern int ImagingJpegEncode(Imaging im, ImagingCodecState state,
UINT8* buffer, int bytes);
#endif
#ifdef HAVE_OPENJPEG
extern int ImagingJpeg2KDecode(Imaging im, ImagingCodecState state,
UINT8* buffer, int bytes);
extern int ImagingJpeg2KDecodeCleanup(ImagingCodecState state);
#endif
extern int ImagingLzwDecode(Imaging im, ImagingCodecState state,
UINT8* buffer, int bytes);
#ifdef HAVE_LIBTIFF
@ -497,6 +502,19 @@ struct ImagingCodecStateInstance {
void *context;
};
/* Incremental decoding support */
typedef struct ImagingIncrementalDecoderStruct *ImagingIncrementalDecoder;
typedef int (*ImagingIncrementalDecoderEntry)(Imaging im,
ImagingCodecState state,
ImagingIncrementalDecoder decoder);
extern ImagingIncrementalDecoder ImagingIncrementalDecoderCreate(ImagingIncrementalDecoderEntry decoder_entry, Imaging im, ImagingCodecState state);
extern void ImagingIncrementalDecoderDestroy(ImagingIncrementalDecoder decoder);
extern int ImagingIncrementalDecodeData(ImagingIncrementalDecoder decoder, UINT8 *buf, int bytes);
size_t ImagingIncrementalDecoderRead(ImagingIncrementalDecoder decoder, void *buffer, size_t bytes);
off_t ImagingIncrementalDecoderSkip(ImagingIncrementalDecoder decoder, off_t bytes);
/* Errcodes */
#define IMAGING_CODEC_END 1
#define IMAGING_CODEC_OVERRUN -1

366
libImaging/Incremental.c Normal file
View File

@ -0,0 +1,366 @@
/*
* The Python Imaging Library
* $Id$
*
* incremental decoding adaptor.
*
* Copyright (c) 2014 Coriolis Systems Limited
* Copyright (c) 2014 Alastair Houghton
*
*/
#include "Imaging.h"
/* The idea behind this interface is simple: the actual decoding proceeds in
a thread, which is run in lock step with the main thread. Whenever the
ImagingIncrementalDecoderRead() call runs short on data, it suspends the
decoding thread and wakes the main thread. Conversely, the
ImagingIncrementalDecodeData() call suspends the main thread and wakes
the decoding thread, providing a buffer of data.
The two threads are never running simultaneously, so there is no need for
any addition synchronisation measures outside of this file.
Note also that we start the thread suspended (on Windows), or make it
immediately wait (other platforms), so that it's possible to initialise
things before the thread starts running.
This interface is useful to allow PIL to interact efficiently with any
third-party imaging library that does not support suspendable reads;
one example is OpenJPEG (which is used for J2K support). The TIFF library
might also benefit from using this code.
Note that if using this module, you want to set handles_eof on your
decoder to true. Why? Because otherwise ImageFile.load() will abort,
thinking that the image is truncated, whereas generally you want it to
pass the EOF condition (0 bytes to read) through to your code. */
#ifdef _WIN32
#include <windows.h>
#include <process.h>
#else
#include <pthread.h>
#endif
struct ImagingIncrementalStreamStruct {
UINT8 *buffer;
UINT8 *ptr;
UINT8 *end;
};
struct ImagingIncrementalDecoderStruct {
#ifdef _WIN32
HANDLE hDecodeEvent;
HANDLE hDataEvent;
HANDLE hThread;
#else
pthread_mutex_t start_mutex;
pthread_cond_t start_cond;
pthread_mutex_t decode_mutex;
pthread_cond_t decode_cond;
pthread_mutex_t data_mutex;
pthread_cond_t data_cond;
pthread_t thread;
#endif
ImagingIncrementalDecoderEntry entry;
Imaging im;
ImagingCodecState state;
struct {
UINT8 *buffer;
UINT8 *ptr;
UINT8 *end;
} stream;
int started;
int result;
};
#if _WIN32
static void __stdcall
incremental_thread(void *ptr)
{
ImagingIncrementalDecoder decoder = (ImagingIncrementalDecoder)ptr;
decoder->result = decoder->entry(decoder->im, decoder->state, decoder);
SetEvent(decoder->hDecodeEvent);
}
#else
static void *
incremental_thread(void *ptr)
{
ImagingIncrementalDecoder decoder = (ImagingIncrementalDecoder)ptr;
decoder->result = decoder->entry(decoder->im, decoder->state, decoder);
pthread_cond_signal(&decoder->decode_cond);
return NULL;
}
#endif
/**
* Create a new incremental decoder */
ImagingIncrementalDecoder
ImagingIncrementalDecoderCreate(ImagingIncrementalDecoderEntry decoder_entry,
Imaging im,
ImagingCodecState state)
{
ImagingIncrementalDecoder decoder = (ImagingIncrementalDecoder)malloc(sizeof(struct ImagingIncrementalDecoderStruct));
decoder->entry = decoder_entry;
decoder->im = im;
decoder->state = state;
decoder->result = 0;
decoder->stream.buffer = decoder->stream.ptr = decoder->stream.end = NULL;
decoder->started = 0;
/* System specific set-up */
#if _WIN32
decoder->hDecodeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!decoder->hDecodeEvent) {
free(decoder);
return NULL;
}
decoder->hDataEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!decoder->hDataEvent) {
CloseHandle(decoder->hDecodeEvent);
free(decoder);
return NULL;
}
decoder->hThread = _beginthreadex(NULL, 0, incremental_thread, decoder,
CREATE_SUSPENDED, NULL);
if (!decoder->hThread) {
CloseHandle(decoder->hDecodeEvent);
CloseHandle(decoder->hDataEvent);
free(decoder);
return NULL;
}
#else
if (pthread_mutex_init(&decoder->start_mutex, NULL)) {
free (decoder);
return NULL;
}
if (pthread_mutex_init(&decoder->decode_mutex, NULL)) {
pthread_mutex_destroy(&decoder->start_mutex);
free(decoder);
return NULL;
}
if (pthread_mutex_init(&decoder->data_mutex, NULL)) {
pthread_mutex_destroy(&decoder->start_mutex);
pthread_mutex_destroy(&decoder->decode_mutex);
free(decoder);
return NULL;
}
if (pthread_cond_init(&decoder->start_cond, NULL)) {
pthread_mutex_destroy(&decoder->start_mutex);
pthread_mutex_destroy(&decoder->decode_mutex);
pthread_mutex_destroy(&decoder->data_mutex);
free(decoder);
return NULL;
}
if (pthread_cond_init(&decoder->decode_cond, NULL)) {
pthread_mutex_destroy(&decoder->start_mutex);
pthread_mutex_destroy(&decoder->decode_mutex);
pthread_mutex_destroy(&decoder->data_mutex);
pthread_cond_destroy(&decoder->start_cond);
free(decoder);
return NULL;
}
if (pthread_cond_init(&decoder->data_cond, NULL)) {
pthread_mutex_destroy(&decoder->start_mutex);
pthread_mutex_destroy(&decoder->decode_mutex);
pthread_mutex_destroy(&decoder->data_mutex);
pthread_cond_destroy(&decoder->start_cond);
pthread_cond_destroy(&decoder->decode_cond);
free(decoder);
return NULL;
}
if (pthread_create(&decoder->thread, NULL, incremental_thread, decoder)) {
pthread_mutex_destroy(&decoder->start_mutex);
pthread_mutex_destroy(&decoder->decode_mutex);
pthread_mutex_destroy(&decoder->data_mutex);
pthread_cond_destroy(&decoder->start_cond);
pthread_cond_destroy(&decoder->decode_cond);
pthread_cond_destroy(&decoder->data_cond);
free(decoder);
return NULL;
}
#endif
return decoder;
}
/**
* Destroy an incremental decoder */
void
ImagingIncrementalDecoderDestroy(ImagingIncrementalDecoder decoder)
{
if (!decoder->started) {
#ifdef _WIN32
ResumeThread(decoder->hThread);
#else
pthread_cond_signal(&decoder->start_cond);
#endif
decoder->started = 1;
}
#ifndef _WIN32
pthread_mutex_lock(&decoder->data_mutex);
#endif
decoder->stream.buffer = decoder->stream.ptr = decoder->stream.end = NULL;
#ifdef _WIN32
SetEvent(decoder->hDataEvent);
WaitForSingleObject(decoder->hThread, INFINITE);
CloseHandle(decoder->hThread);
CloseHandle(decoder->hDecodeEvent);
CloseHandle(decoder->hDataEvent);
#else
pthread_cond_signal(&decoder->data_cond);
pthread_mutex_unlock(&decoder->data_mutex);
pthread_join(decoder->thread, NULL);
pthread_mutex_destroy(&decoder->start_mutex);
pthread_mutex_destroy(&decoder->decode_mutex);
pthread_mutex_destroy(&decoder->data_mutex);
pthread_cond_destroy(&decoder->start_cond);
pthread_cond_destroy(&decoder->decode_cond);
pthread_cond_destroy(&decoder->data_cond);
#endif
free (decoder);
}
/**
* Decode data using an incremental decoder */
int
ImagingIncrementalDecodeData(ImagingIncrementalDecoder decoder,
UINT8 *buf, int bytes)
{
if (!decoder->started) {
#ifdef _WIN32
ResumeThread(decoder->hThread);
#else
pthread_cond_signal(&decoder->start_cond);
#endif
decoder->started = 1;
}
#ifndef _WIN32
pthread_mutex_lock(&decoder->data_mutex);
#endif
decoder->stream.buffer = decoder->stream.ptr = buf;
decoder->stream.end = buf + bytes;
#ifdef _WIN32
SetEvent(decoder->hDataEvent);
WaitForSingleObject(decoder->hDecodeEvent);
#else
pthread_cond_signal(&decoder->data_cond);
pthread_mutex_unlock(&decoder->data_mutex);
pthread_mutex_lock(&decoder->decode_mutex);
pthread_cond_wait(&decoder->decode_cond, &decoder->decode_mutex);
pthread_mutex_unlock(&decoder->decode_mutex);
#endif
return decoder->result;
}
size_t
ImagingIncrementalDecoderRead(ImagingIncrementalDecoder decoder,
void *buffer, size_t bytes)
{
UINT8 *ptr = (UINT8 *)buffer;
size_t done = 0;
pthread_mutex_lock(&decoder->data_mutex);
while (bytes) {
size_t todo = bytes;
size_t remaining = decoder->stream.end - decoder->stream.ptr;
if (!remaining) {
#ifndef _WIN32
pthread_mutex_lock(&decoder->decode_mutex);
#endif
decoder->result = (int)(decoder->stream.ptr - decoder->stream.buffer);
#if _WIN32
SetEvent(decoder->hDecodeEvent);
WaitForSingleObject(decoder->hDataEvent);
#else
pthread_cond_signal(&decoder->decode_cond);
pthread_mutex_unlock(&decoder->decode_mutex);
pthread_cond_wait(&decoder->data_cond, &decoder->data_mutex);
#endif
remaining = decoder->stream.end - decoder->stream.ptr;
}
if (todo > remaining)
todo = remaining;
if (!todo)
break;
memcpy (ptr, decoder->stream.ptr, todo);
decoder->stream.ptr += todo;
bytes -= todo;
done += todo;
ptr += todo;
}
pthread_mutex_unlock(&decoder->data_mutex);
return done;
}
off_t
ImagingIncrementalDecoderSkip(ImagingIncrementalDecoder decoder,
off_t bytes)
{
off_t done = 0;
while (bytes) {
off_t todo = bytes;
off_t remaining = decoder->stream.end - decoder->stream.ptr;
if (!remaining) {
decoder->result = (int)(decoder->stream.ptr - decoder->stream.buffer);
#if _WIN32
SetEvent(decoder->hDecodeEvent);
WaitForSingleObject(decoder->hDataEvent);
#else
pthread_cond_signal(&decoder->decode_cond);
pthread_mutex_lock(&decoder->data_mutex);
pthread_cond_wait(&decoder->data_cond, &decoder->data_mutex);
#endif
remaining = decoder->stream.end - decoder->stream.ptr;
}
if (todo > remaining)
todo = remaining;
if (!todo)
break;
decoder->stream.ptr += todo;
bytes -= todo;
done += todo;
}
return done;
}

44
libImaging/Jpeg2K.h Normal file
View File

@ -0,0 +1,44 @@
/*
* The Python Imaging Library
* $Id$
*
* declarations for the OpenJPEG codec interface.
*
* Copyright (c) 2014 by Coriolis Systems Limited
* Copyright (c) 2014 by Alastair Houghton
*/
#include <openjpeg-2.0/openjpeg.h>
/* -------------------------------------------------------------------- */
/* Decoder */
typedef struct {
/* CONFIGURATION */
/* Output mode */
char mode[8];
/* Specify the desired format */
OPJ_CODEC_FORMAT format;
/* Set to divide image resolution by 2**reduce. */
int reduce;
/* Set to limit the number of quality layers to decode (0 = all layers) */
int layers;
/* PRIVATE CONTEXT (set by decoder) */
const char *error_msg;
ImagingIncrementalDecoder decoder;
opj_stream_t *stream;
} JPEG2KSTATE;
/*
* Local Variables:
* c-basic-offset: 4
* End:
*
*/

475
libImaging/Jpeg2KDecode.c Normal file
View File

@ -0,0 +1,475 @@
/*
* The Python Imaging Library.
* $Id$
*
* decoder for JPEG2000 image data.
*
* history:
* 2014-03-12 ajh Created
*
* Copyright (c) 2014 Coriolis Systems Limited
* Copyright (c) 2014 Alastair Houghton
*
* See the README file for details on usage and redistribution.
*/
#include "Imaging.h"
#ifdef HAVE_OPENJPEG
#include <unistd.h>
#include "Jpeg2K.h"
/* -------------------------------------------------------------------- */
/* Error handler */
/* -------------------------------------------------------------------- */
static void
j2k_error(const char *msg, void *client_data)
{
JPEG2KSTATE *state = (JPEG2KSTATE *) client_data;
free((void *)state->error_msg);
state->error_msg = strdup(msg);
}
/* -------------------------------------------------------------------- */
/* Buffer input stream */
/* -------------------------------------------------------------------- */
static OPJ_SIZE_T
j2k_read(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data)
{
ImagingIncrementalDecoder decoder = (ImagingIncrementalDecoder)p_user_data;
return ImagingIncrementalDecoderRead(decoder, p_buffer, p_nb_bytes);
}
static OPJ_SIZE_T
j2k_write(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data)
{
return OPJ_FALSE;
}
static OPJ_OFF_T
j2k_skip(OPJ_OFF_T p_nb_bytes, void *p_user_data)
{
ImagingIncrementalDecoder decoder = (ImagingIncrementalDecoder)p_user_data;
return ImagingIncrementalDecoderSkip(decoder, p_nb_bytes);
}
static OPJ_BOOL
j2k_seek(OPJ_OFF_T p_nb_bytes, void *p_user_data)
{
// We *deliberately* don't implement this
return OPJ_FALSE;
}
/* -------------------------------------------------------------------- */
/* Unpackers */
/* -------------------------------------------------------------------- */
typedef void (*j2k_unpacker_t)(opj_image_t *in, Imaging im);
struct j2k_decode_unpacker {
const char *mode;
OPJ_COLOR_SPACE color_space;
unsigned components;
j2k_unpacker_t unpacker;
};
static inline
unsigned j2ku_shift(unsigned x, int n)
{
if (n < 0)
return x >> -n;
else
return x << n;
}
static void
j2ku_gray_l(opj_image_t *in, Imaging im)
{
unsigned x0 = in->comps[0].x0, y0 = in->comps[0].y0;
unsigned w = in->comps[0].w, h = in->comps[0].h;
int shift = 8 - in->comps[0].prec;
int offset = in->comps[0].sgnd ? 1 << (in->comps[0].prec - 1) : 0;
unsigned x, y;
if (shift < 0)
offset += 1 << (-shift - 1);
for (y = 0; y < h; ++y) {
OPJ_INT32 *data = &in->comps[0].data[y * w];
UINT8 *row = (UINT8 *)im->image[y0 + y] + x0;
for (x = 0; x < w; ++x)
*row++ = j2ku_shift(offset + *data++, shift);
}
}
static void
j2ku_gray_rgb(opj_image_t *in, Imaging im)
{
unsigned x0 = in->comps[0].x0, y0 = in->comps[0].y0;
unsigned w = in->comps[0].w, h = in->comps[0].h;
int shift = 8 - in->comps[0].prec;
int offset = in->comps[0].sgnd ? 1 << (in->comps[0].prec - 1) : 0;
unsigned x, y;
if (shift < 0)
offset += 1 << (-shift - 1);
for (y = 0; y < h; ++y) {
OPJ_INT32 *data = &in->comps[0].data[y * w];
UINT8 *row = (UINT8 *)im->image[y0 + y] + x0;
for (x = 0; x < w; ++x) {
UINT8 byte = j2ku_shift(offset + *data++, shift);
row[0] = row[1] = row[2] = byte;
row[3] = 0xff;
row += 4;
}
}
}
static void
j2ku_graya_la(opj_image_t *in, Imaging im)
{
unsigned x0 = in->comps[0].x0, y0 = in->comps[0].y0;
unsigned w = in->comps[0].w, h = in->comps[0].h;
int shift = 8 - in->comps[0].prec;
int offset = in->comps[0].sgnd ? 1 << (in->comps[0].prec - 1) : 0;
int ashift = 8 - in->comps[1].prec;
int aoffset = in->comps[1].sgnd ? 1 << (in->comps[1].prec - 1) : 0;
unsigned x, y;
if (shift < 0)
offset += 1 << (-shift - 1);
if (ashift < 0)
aoffset += 1 << (-ashift - 1);
for (y = 0; y < h; ++y) {
OPJ_INT32 *data = &in->comps[0].data[y * w];
OPJ_INT32 *adata = &in->comps[1].data[y * w];
UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4;
for (x = 0; x < w; ++x) {
UINT8 byte = j2ku_shift(offset + *data++, shift);
row[0] = row[1] = row[2] = byte;
row[3] = (unsigned)(offset + *adata++) >> shift;
row += 4;
}
}
}
static void
j2ku_srgb_rgb(opj_image_t *in, Imaging im)
{
unsigned x0 = in->comps[0].x0, y0 = in->comps[0].y0;
unsigned w = in->comps[0].w, h = in->comps[0].h;
int shifts[3], offsets[3];
unsigned n, x, y;
for (n = 0; n < 3; ++n) {
shifts[n] = 8 - in->comps[n].prec;
offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0;
if (shifts[n] < 0)
offsets[n] += 1 << (-shifts[n] - 1);
}
for (y = 0; y < h; ++y) {
OPJ_INT32 *data[3];
UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4;
for (n = 0; n < 3; ++n)
data[n] = &in->comps[n].data[y * w];
for (x = 0; x < w; ++x) {
for (n = 0; n < 3; ++n)
row[n] = j2ku_shift(offsets[n] + *data[n]++, shifts[n]);
row[3] = 0xff;
row += 4;
}
}
}
static void
j2ku_sycc_rgb(opj_image_t *in, Imaging im)
{
unsigned x0 = in->comps[0].x0, y0 = in->comps[0].y0;
unsigned w = in->comps[0].w, h = in->comps[0].h;
int shifts[3], offsets[3];
unsigned n, x, y;
for (n = 0; n < 3; ++n) {
shifts[n] = 8 - in->comps[n].prec;
offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0;
if (shifts[n] < 0)
offsets[n] += 1 << (-shifts[n] - 1);
}
for (y = 0; y < h; ++y) {
OPJ_INT32 *data[3];
UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4;
UINT8 *row_start = row;
for (n = 0; n < 3; ++n)
data[n] = &in->comps[n].data[y * w];
for (x = 0; x < w; ++x) {
for (n = 0; n < 3; ++n)
row[n] = j2ku_shift(offsets[n] + *data[n]++, shifts[n]);
row[3] = 0xff;
row += 4;
}
ImagingConvertYCbCr2RGB(row_start, row_start, w);
}
}
static void
j2ku_srgba_rgba(opj_image_t *in, Imaging im)
{
unsigned x0 = in->comps[0].x0, y0 = in->comps[0].y0;
unsigned w = in->comps[0].w, h = in->comps[0].h;
int shifts[4], offsets[4];
unsigned n, x, y;
for (n = 0; n < 4; ++n) {
shifts[n] = 8 - in->comps[n].prec;
offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0;
if (shifts[n] < 0)
offsets[n] += 1 << (-shifts[n] - 1);
}
for (y = 0; y < h; ++y) {
OPJ_INT32 *data[4];
UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4;
for (n = 0; n < 4; ++n)
data[n] = &in->comps[n].data[y * w];
for (x = 0; x < w; ++x) {
for (n = 0; n < 4; ++n)
row[n] = j2ku_shift(offsets[n] + *data[n]++, shifts[n]);
row += 4;
}
}
}
static const struct j2k_decode_unpacker j2k_unpackers[] = {
{ "L", OPJ_CLRSPC_GRAY, 1, j2ku_gray_l },
{ "LA", OPJ_CLRSPC_GRAY, 2, j2ku_graya_la },
{ "RGB", OPJ_CLRSPC_GRAY, 1, j2ku_gray_rgb },
{ "RGB", OPJ_CLRSPC_GRAY, 2, j2ku_gray_rgb },
{ "RGB", OPJ_CLRSPC_SRGB, 3, j2ku_srgb_rgb },
{ "RGB", OPJ_CLRSPC_SYCC, 3, j2ku_sycc_rgb },
{ "RGBA", OPJ_CLRSPC_GRAY, 1, j2ku_gray_rgb },
{ "RGBA", OPJ_CLRSPC_GRAY, 2, j2ku_graya_la },
{ "RGBA", OPJ_CLRSPC_SRGB, 3, j2ku_srgb_rgb },
{ "RGBA", OPJ_CLRSPC_SYCC, 3, j2ku_sycc_rgb },
{ "RGBA", OPJ_CLRSPC_SRGB, 4, j2ku_srgba_rgba },
};
/* -------------------------------------------------------------------- */
/* Decoder */
/* -------------------------------------------------------------------- */
enum {
J2K_STATE_START = 0,
J2K_STATE_DECODING = 1,
J2K_STATE_DONE = 2,
J2K_STATE_FAILED = 3,
};
static int
j2k_decode_entry(Imaging im, ImagingCodecState state,
ImagingIncrementalDecoder decoder)
{
JPEG2KSTATE *context = (JPEG2KSTATE *) state->context;
opj_stream_t *stream;
opj_image_t *image;
opj_codec_t *codec;
opj_dparameters_t params;
OPJ_COLOR_SPACE color_space;
j2k_unpacker_t unpack;
unsigned n;
stream = opj_stream_default_create(OPJ_TRUE);
if (!stream) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
goto quick_exit;
}
opj_stream_set_read_function(stream, j2k_read);
opj_stream_set_write_function(stream, j2k_write);
opj_stream_set_skip_function(stream, j2k_skip);
opj_stream_set_seek_function(stream, j2k_seek);
opj_stream_set_user_data(stream, context->decoder);
/* Setup decompression context */
context->error_msg = NULL;
opj_set_default_decoder_parameters(&params);
params.cp_reduce = context->reduce;
params.cp_layer = context->layers;
codec = opj_create_decompress(context->format);
if (!codec) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
goto quick_exit;
}
opj_set_error_handler(codec, j2k_error, context);
opj_setup_decoder(codec, &params);
if (!opj_read_header(stream, codec, &image)) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
goto quick_exit;
}
/* Check that this image is something we can handle */
if (image->numcomps < 1 || image->numcomps > 4
|| image->color_space == OPJ_CLRSPC_UNKNOWN) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
goto quick_exit;
}
for (n = 1; n < image->numcomps; ++n) {
/* Check that the sample frequency is uniform */
if (image->comps[0].dx != image->comps[n].dx
|| image->comps[0].dy != image->comps[n].dy) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
goto quick_exit;
}
/* Check that the bit depth is uniform */
if (image->comps[0].prec != image->comps[n].prec) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
goto quick_exit;
}
}
/*
Colorspace Number of components PIL mode
------------------------------------------------------
sRGB 3 RGB
sRGB 4 RGBA
gray 1 L or I
gray 2 LA
YCC 3 YCbCr
If colorspace is unspecified, we assume:
Number of components Colorspace
-----------------------------------------
1 gray
2 gray (+ alpha)
3 sRGB
4 sRGB (+ alpha)
*/
/* Find the correct unpacker */
color_space = image->color_space;
if (color_space == OPJ_CLRSPC_UNSPECIFIED) {
switch (image->numcomps) {
case 1: case 2: color_space = OPJ_CLRSPC_GRAY; break;
case 3: case 4: color_space = OPJ_CLRSPC_SRGB; break;
}
}
for (n = 0; n < sizeof(j2k_unpackers) / sizeof (j2k_unpackers[0]); ++n) {
if (color_space == j2k_unpackers[n].color_space
&& image->numcomps == j2k_unpackers[n].components
&& strcmp (context->mode, j2k_unpackers[n].mode) == 0) {
unpack = j2k_unpackers[n].unpacker;
break;
}
}
if (!unpack) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
goto quick_exit;
}
/* Decode and unpack the image */
if (!opj_decode(codec, stream, image)
|| !opj_end_decompress(codec, stream)) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
goto quick_exit;
}
unpack(image, im);
quick_exit:
if (codec)
opj_destroy_codec(codec);
if (image)
opj_image_destroy(image);
if (stream)
opj_stream_destroy(stream);
return -1;
}
int
ImagingJpeg2KDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
{
JPEG2KSTATE *context = (JPEG2KSTATE *) state->context;
if (state->state == J2K_STATE_DONE || state->state == J2K_STATE_FAILED)
return -1;
if (state->state == J2K_STATE_START) {
context->decoder = ImagingIncrementalDecoderCreate(j2k_decode_entry,
im, state);
if (!context->decoder) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
return -1;
}
state->state = J2K_STATE_DECODING;
}
return ImagingIncrementalDecodeData(context->decoder, buf, bytes);
}
/* -------------------------------------------------------------------- */
/* Cleanup */
/* -------------------------------------------------------------------- */
int
ImagingJpeg2KDecodeCleanup(ImagingCodecState state) {
JPEG2KSTATE *context = (JPEG2KSTATE *)state->context;
if (context->decoder)
ImagingIncrementalDecoderDestroy(context->decoder);
return -1;
}
const char *
ImagingJpeg2KVersion(void)
{
return opj_version();
}
#endif /* HAVE_OPENJPEG */
/*
* Local Variables:
* c-basic-offset: 4
* End:
*
*/

View File

@ -33,7 +33,8 @@ _LIB_IMAGING = (
"QuantHeap", "PcdDecode", "PcxDecode", "PcxEncode", "Point",
"RankFilter", "RawDecode", "RawEncode", "Storage", "SunRleDecode",
"TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask", "XbmDecode",
"XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode")
"XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode", "Incremental",
"Jpeg2KDecode")
def _add_directory(path, dir, where=None):
@ -98,6 +99,7 @@ class pil_build_ext(build_ext):
class feature:
zlib = jpeg = tiff = freetype = tcl = tk = lcms = webp = webpmux = None
jpeg2000 = None
required = []
def require(self, feat):
@ -349,6 +351,11 @@ class pil_build_ext(build_ext):
_find_library_file(self, "libjpeg")):
feature.jpeg = "libjpeg" # alternative name
if feature.want('jpeg2000'):
if _find_include_file(self, "openjpeg-2.0/openjpeg.h"):
if _find_library_file(self, "openjp2"):
feature.jpeg2000 = "openjp2"
if feature.want('tiff'):
if _find_library_file(self, "tiff"):
feature.tiff = "tiff"
@ -430,6 +437,9 @@ class pil_build_ext(build_ext):
if feature.jpeg:
libs.append(feature.jpeg)
defs.append(("HAVE_LIBJPEG", None))
if feature.jpeg2000:
libs.append(feature.jpeg2000)
defs.append(("HAVE_OPENJPEG", None))
if feature.zlib:
libs.append(feature.zlib)
defs.append(("HAVE_LIBZ", None))
@ -537,6 +547,7 @@ class pil_build_ext(build_ext):
options = [
(feature.tcl and feature.tk, "TKINTER"),
(feature.jpeg, "JPEG"),
(feature.jpeg2000, "OPENJPEG (JPEG2000)"),
(feature.zlib, "ZLIB (PNG/ZIP)"),
(feature.tiff, "LIBTIFF"),
(feature.freetype, "FREETYPE2"),