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