mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-26 01:46:18 +03:00
Added a JPEG 2000 decoder based on OpenJPEG.
This commit is contained in:
parent
1a03ca9224
commit
d6b8f0f666
|
@ -205,7 +205,7 @@ class ImageFile(Image.Image):
|
||||||
else:
|
else:
|
||||||
raise IndexError(ie)
|
raise IndexError(ie)
|
||||||
|
|
||||||
if not s: # truncated jpeg
|
if not s and not d.handles_eof: # truncated jpeg
|
||||||
self.tile = []
|
self.tile = []
|
||||||
|
|
||||||
# JpegDecode needs to clean things up here either way
|
# JpegDecode needs to clean things up here either way
|
||||||
|
|
189
PIL/Jpeg2KImagePlugin.py
Normal file
189
PIL/Jpeg2KImagePlugin.py
Normal 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")
|
|
@ -33,6 +33,7 @@ _plugins = ['ArgImagePlugin',
|
||||||
'ImtImagePlugin',
|
'ImtImagePlugin',
|
||||||
'IptcImagePlugin',
|
'IptcImagePlugin',
|
||||||
'JpegImagePlugin',
|
'JpegImagePlugin',
|
||||||
|
'Jpeg2KImagePlugin',
|
||||||
'McIdasImagePlugin',
|
'McIdasImagePlugin',
|
||||||
'MicImagePlugin',
|
'MicImagePlugin',
|
||||||
'MpegImagePlugin',
|
'MpegImagePlugin',
|
||||||
|
|
|
@ -3283,6 +3283,7 @@ extern PyObject* PyImaging_FliDecoderNew(PyObject* self, PyObject* args);
|
||||||
extern PyObject* PyImaging_GifDecoderNew(PyObject* self, PyObject* args);
|
extern PyObject* PyImaging_GifDecoderNew(PyObject* self, PyObject* args);
|
||||||
extern PyObject* PyImaging_HexDecoderNew(PyObject* self, PyObject* args);
|
extern PyObject* PyImaging_HexDecoderNew(PyObject* self, PyObject* args);
|
||||||
extern PyObject* PyImaging_JpegDecoderNew(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_TiffLzwDecoderNew(PyObject* self, PyObject* args);
|
||||||
extern PyObject* PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args);
|
extern PyObject* PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args);
|
||||||
extern PyObject* PyImaging_MspDecoderNew(PyObject* self, PyObject* args);
|
extern PyObject* PyImaging_MspDecoderNew(PyObject* self, PyObject* args);
|
||||||
|
@ -3351,6 +3352,9 @@ static PyMethodDef functions[] = {
|
||||||
#ifdef HAVE_LIBJPEG
|
#ifdef HAVE_LIBJPEG
|
||||||
{"jpeg_decoder", (PyCFunction)PyImaging_JpegDecoderNew, 1},
|
{"jpeg_decoder", (PyCFunction)PyImaging_JpegDecoderNew, 1},
|
||||||
{"jpeg_encoder", (PyCFunction)PyImaging_JpegEncoderNew, 1},
|
{"jpeg_encoder", (PyCFunction)PyImaging_JpegEncoderNew, 1},
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_OPENJPEG
|
||||||
|
{"jpeg2k_decoder", (PyCFunction)PyImaging_Jpeg2KDecoderNew, 1},
|
||||||
#endif
|
#endif
|
||||||
{"tiff_lzw_decoder", (PyCFunction)PyImaging_TiffLzwDecoderNew, 1},
|
{"tiff_lzw_decoder", (PyCFunction)PyImaging_TiffLzwDecoderNew, 1},
|
||||||
#ifdef HAVE_LIBTIFF
|
#ifdef HAVE_LIBTIFF
|
||||||
|
|
76
decode.c
76
decode.c
|
@ -52,6 +52,7 @@ typedef struct {
|
||||||
struct ImagingCodecStateInstance state;
|
struct ImagingCodecStateInstance state;
|
||||||
Imaging im;
|
Imaging im;
|
||||||
PyObject* lock;
|
PyObject* lock;
|
||||||
|
int handles_eof;
|
||||||
} ImagingDecoderObject;
|
} ImagingDecoderObject;
|
||||||
|
|
||||||
static PyTypeObject ImagingDecoderType;
|
static PyTypeObject ImagingDecoderType;
|
||||||
|
@ -194,6 +195,12 @@ _setimage(ImagingDecoderObject* decoder, PyObject* args)
|
||||||
return Py_None;
|
return Py_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_get_handles_eof(ImagingDecoderObject *decoder)
|
||||||
|
{
|
||||||
|
return PyBool_FromLong(decoder->handles_eof);
|
||||||
|
}
|
||||||
|
|
||||||
static struct PyMethodDef methods[] = {
|
static struct PyMethodDef methods[] = {
|
||||||
{"decode", (PyCFunction)_decode, 1},
|
{"decode", (PyCFunction)_decode, 1},
|
||||||
{"cleanup", (PyCFunction)_decode_cleanup, 1},
|
{"cleanup", (PyCFunction)_decode_cleanup, 1},
|
||||||
|
@ -201,6 +208,13 @@ static struct PyMethodDef methods[] = {
|
||||||
{NULL, NULL} /* sentinel */
|
{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 = {
|
static PyTypeObject ImagingDecoderType = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0)
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
"ImagingDecoder", /*tp_name*/
|
"ImagingDecoder", /*tp_name*/
|
||||||
|
@ -232,7 +246,7 @@ static PyTypeObject ImagingDecoderType = {
|
||||||
0, /*tp_iternext*/
|
0, /*tp_iternext*/
|
||||||
methods, /*tp_methods*/
|
methods, /*tp_methods*/
|
||||||
0, /*tp_members*/
|
0, /*tp_members*/
|
||||||
0, /*tp_getset*/
|
getseters, /*tp_getset*/
|
||||||
};
|
};
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
|
@ -762,3 +776,63 @@ PyImaging_JpegDecoderNew(PyObject* self, PyObject* args)
|
||||||
return (PyObject*) decoder;
|
return (PyObject*) decoder;
|
||||||
}
|
}
|
||||||
#endif
|
#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 */
|
||||||
|
|
|
@ -424,6 +424,11 @@ extern int ImagingJpegDecodeCleanup(ImagingCodecState state);
|
||||||
extern int ImagingJpegEncode(Imaging im, ImagingCodecState state,
|
extern int ImagingJpegEncode(Imaging im, ImagingCodecState state,
|
||||||
UINT8* buffer, int bytes);
|
UINT8* buffer, int bytes);
|
||||||
#endif
|
#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,
|
extern int ImagingLzwDecode(Imaging im, ImagingCodecState state,
|
||||||
UINT8* buffer, int bytes);
|
UINT8* buffer, int bytes);
|
||||||
#ifdef HAVE_LIBTIFF
|
#ifdef HAVE_LIBTIFF
|
||||||
|
@ -497,6 +502,19 @@ struct ImagingCodecStateInstance {
|
||||||
void *context;
|
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 */
|
/* Errcodes */
|
||||||
#define IMAGING_CODEC_END 1
|
#define IMAGING_CODEC_END 1
|
||||||
#define IMAGING_CODEC_OVERRUN -1
|
#define IMAGING_CODEC_OVERRUN -1
|
||||||
|
|
366
libImaging/Incremental.c
Normal file
366
libImaging/Incremental.c
Normal 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
44
libImaging/Jpeg2K.h
Normal 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
475
libImaging/Jpeg2KDecode.c
Normal 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(¶ms);
|
||||||
|
params.cp_reduce = context->reduce;
|
||||||
|
params.cp_layer = context->layers;
|
||||||
|
|
||||||
|
codec = opj_create_decompress(context->format);
|
||||||
|
|
||||||
|
if (!codec) {
|
||||||
|
state->errcode = IMAGING_CODEC_BROKEN;
|
||||||
|
state->state = J2K_STATE_FAILED;
|
||||||
|
goto quick_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
opj_set_error_handler(codec, j2k_error, context);
|
||||||
|
opj_setup_decoder(codec, ¶ms);
|
||||||
|
|
||||||
|
if (!opj_read_header(stream, codec, &image)) {
|
||||||
|
state->errcode = IMAGING_CODEC_BROKEN;
|
||||||
|
state->state = J2K_STATE_FAILED;
|
||||||
|
goto quick_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that this image is something we can handle */
|
||||||
|
if (image->numcomps < 1 || image->numcomps > 4
|
||||||
|
|| image->color_space == OPJ_CLRSPC_UNKNOWN) {
|
||||||
|
state->errcode = IMAGING_CODEC_BROKEN;
|
||||||
|
state->state = J2K_STATE_FAILED;
|
||||||
|
goto quick_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (n = 1; n < image->numcomps; ++n) {
|
||||||
|
/* Check that the sample frequency is uniform */
|
||||||
|
if (image->comps[0].dx != image->comps[n].dx
|
||||||
|
|| image->comps[0].dy != image->comps[n].dy) {
|
||||||
|
state->errcode = IMAGING_CODEC_BROKEN;
|
||||||
|
state->state = J2K_STATE_FAILED;
|
||||||
|
goto quick_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that the bit depth is uniform */
|
||||||
|
if (image->comps[0].prec != image->comps[n].prec) {
|
||||||
|
state->errcode = IMAGING_CODEC_BROKEN;
|
||||||
|
state->state = J2K_STATE_FAILED;
|
||||||
|
goto quick_exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Colorspace Number of components PIL mode
|
||||||
|
------------------------------------------------------
|
||||||
|
sRGB 3 RGB
|
||||||
|
sRGB 4 RGBA
|
||||||
|
gray 1 L or I
|
||||||
|
gray 2 LA
|
||||||
|
YCC 3 YCbCr
|
||||||
|
|
||||||
|
|
||||||
|
If colorspace is unspecified, we assume:
|
||||||
|
|
||||||
|
Number of components Colorspace
|
||||||
|
-----------------------------------------
|
||||||
|
1 gray
|
||||||
|
2 gray (+ alpha)
|
||||||
|
3 sRGB
|
||||||
|
4 sRGB (+ alpha)
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Find the correct unpacker */
|
||||||
|
color_space = image->color_space;
|
||||||
|
|
||||||
|
if (color_space == OPJ_CLRSPC_UNSPECIFIED) {
|
||||||
|
switch (image->numcomps) {
|
||||||
|
case 1: case 2: color_space = OPJ_CLRSPC_GRAY; break;
|
||||||
|
case 3: case 4: color_space = OPJ_CLRSPC_SRGB; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (n = 0; n < sizeof(j2k_unpackers) / sizeof (j2k_unpackers[0]); ++n) {
|
||||||
|
if (color_space == j2k_unpackers[n].color_space
|
||||||
|
&& image->numcomps == j2k_unpackers[n].components
|
||||||
|
&& strcmp (context->mode, j2k_unpackers[n].mode) == 0) {
|
||||||
|
unpack = j2k_unpackers[n].unpacker;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!unpack) {
|
||||||
|
state->errcode = IMAGING_CODEC_BROKEN;
|
||||||
|
state->state = J2K_STATE_FAILED;
|
||||||
|
goto quick_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decode and unpack the image */
|
||||||
|
if (!opj_decode(codec, stream, image)
|
||||||
|
|| !opj_end_decompress(codec, stream)) {
|
||||||
|
state->errcode = IMAGING_CODEC_BROKEN;
|
||||||
|
state->state = J2K_STATE_FAILED;
|
||||||
|
goto quick_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
unpack(image, im);
|
||||||
|
|
||||||
|
quick_exit:
|
||||||
|
if (codec)
|
||||||
|
opj_destroy_codec(codec);
|
||||||
|
if (image)
|
||||||
|
opj_image_destroy(image);
|
||||||
|
if (stream)
|
||||||
|
opj_stream_destroy(stream);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ImagingJpeg2KDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
|
||||||
|
{
|
||||||
|
JPEG2KSTATE *context = (JPEG2KSTATE *) state->context;
|
||||||
|
|
||||||
|
if (state->state == J2K_STATE_DONE || state->state == J2K_STATE_FAILED)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (state->state == J2K_STATE_START) {
|
||||||
|
context->decoder = ImagingIncrementalDecoderCreate(j2k_decode_entry,
|
||||||
|
im, state);
|
||||||
|
|
||||||
|
if (!context->decoder) {
|
||||||
|
state->errcode = IMAGING_CODEC_BROKEN;
|
||||||
|
state->state = J2K_STATE_FAILED;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
state->state = J2K_STATE_DECODING;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ImagingIncrementalDecodeData(context->decoder, buf, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------- */
|
||||||
|
/* Cleanup */
|
||||||
|
/* -------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
int
|
||||||
|
ImagingJpeg2KDecodeCleanup(ImagingCodecState state) {
|
||||||
|
JPEG2KSTATE *context = (JPEG2KSTATE *)state->context;
|
||||||
|
|
||||||
|
if (context->decoder)
|
||||||
|
ImagingIncrementalDecoderDestroy(context->decoder);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
ImagingJpeg2KVersion(void)
|
||||||
|
{
|
||||||
|
return opj_version();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* HAVE_OPENJPEG */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local Variables:
|
||||||
|
* c-basic-offset: 4
|
||||||
|
* End:
|
||||||
|
*
|
||||||
|
*/
|
13
setup.py
13
setup.py
|
@ -33,7 +33,8 @@ _LIB_IMAGING = (
|
||||||
"QuantHeap", "PcdDecode", "PcxDecode", "PcxEncode", "Point",
|
"QuantHeap", "PcdDecode", "PcxDecode", "PcxEncode", "Point",
|
||||||
"RankFilter", "RawDecode", "RawEncode", "Storage", "SunRleDecode",
|
"RankFilter", "RawDecode", "RawEncode", "Storage", "SunRleDecode",
|
||||||
"TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask", "XbmDecode",
|
"TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask", "XbmDecode",
|
||||||
"XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode")
|
"XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode", "Incremental",
|
||||||
|
"Jpeg2KDecode")
|
||||||
|
|
||||||
|
|
||||||
def _add_directory(path, dir, where=None):
|
def _add_directory(path, dir, where=None):
|
||||||
|
@ -98,6 +99,7 @@ class pil_build_ext(build_ext):
|
||||||
|
|
||||||
class feature:
|
class feature:
|
||||||
zlib = jpeg = tiff = freetype = tcl = tk = lcms = webp = webpmux = None
|
zlib = jpeg = tiff = freetype = tcl = tk = lcms = webp = webpmux = None
|
||||||
|
jpeg2000 = None
|
||||||
required = []
|
required = []
|
||||||
|
|
||||||
def require(self, feat):
|
def require(self, feat):
|
||||||
|
@ -349,6 +351,11 @@ class pil_build_ext(build_ext):
|
||||||
_find_library_file(self, "libjpeg")):
|
_find_library_file(self, "libjpeg")):
|
||||||
feature.jpeg = "libjpeg" # alternative name
|
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 feature.want('tiff'):
|
||||||
if _find_library_file(self, "tiff"):
|
if _find_library_file(self, "tiff"):
|
||||||
feature.tiff = "tiff"
|
feature.tiff = "tiff"
|
||||||
|
@ -430,6 +437,9 @@ class pil_build_ext(build_ext):
|
||||||
if feature.jpeg:
|
if feature.jpeg:
|
||||||
libs.append(feature.jpeg)
|
libs.append(feature.jpeg)
|
||||||
defs.append(("HAVE_LIBJPEG", None))
|
defs.append(("HAVE_LIBJPEG", None))
|
||||||
|
if feature.jpeg2000:
|
||||||
|
libs.append(feature.jpeg2000)
|
||||||
|
defs.append(("HAVE_OPENJPEG", None))
|
||||||
if feature.zlib:
|
if feature.zlib:
|
||||||
libs.append(feature.zlib)
|
libs.append(feature.zlib)
|
||||||
defs.append(("HAVE_LIBZ", None))
|
defs.append(("HAVE_LIBZ", None))
|
||||||
|
@ -537,6 +547,7 @@ class pil_build_ext(build_ext):
|
||||||
options = [
|
options = [
|
||||||
(feature.tcl and feature.tk, "TKINTER"),
|
(feature.tcl and feature.tk, "TKINTER"),
|
||||||
(feature.jpeg, "JPEG"),
|
(feature.jpeg, "JPEG"),
|
||||||
|
(feature.jpeg2000, "OPENJPEG (JPEG2000)"),
|
||||||
(feature.zlib, "ZLIB (PNG/ZIP)"),
|
(feature.zlib, "ZLIB (PNG/ZIP)"),
|
||||||
(feature.tiff, "LIBTIFF"),
|
(feature.tiff, "LIBTIFF"),
|
||||||
(feature.freetype, "FREETYPE2"),
|
(feature.freetype, "FREETYPE2"),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user