mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-25 17:36:18 +03:00
Initial take for removing the Incremental decoder due to threading issues. J2k tests pass, other tests fail
This commit is contained in:
parent
9f48b1e3d6
commit
d3b0413780
|
@ -185,53 +185,59 @@ class ImageFile(Image.Image):
|
|||
except AttributeError:
|
||||
prefix = b""
|
||||
|
||||
for d, e, o, a in self.tile:
|
||||
d = Image._getdecoder(self.mode, d, a, self.decoderconfig)
|
||||
seek(o)
|
||||
for decoder_name, extents, offset, args in self.tile:
|
||||
decoder = Image._getdecoder(self.mode, decoder_name,
|
||||
args, self.decoderconfig)
|
||||
seek(offset)
|
||||
try:
|
||||
d.setimage(self.im, e)
|
||||
decoder.setimage(self.im, extents)
|
||||
except ValueError:
|
||||
continue
|
||||
b = prefix
|
||||
while True:
|
||||
try:
|
||||
s = read(self.decodermaxblock)
|
||||
except (IndexError, struct.error): # truncated png/gif
|
||||
if LOAD_TRUNCATED_IMAGES:
|
||||
if decoder.pulls_fd:
|
||||
decoder.setfd(self.fp)
|
||||
status, err_code = decoder.decode(b"")
|
||||
else:
|
||||
b = prefix
|
||||
while True:
|
||||
try:
|
||||
s = read(self.decodermaxblock)
|
||||
except (IndexError, struct.error): # truncated png/gif
|
||||
if LOAD_TRUNCATED_IMAGES:
|
||||
break
|
||||
else:
|
||||
raise IOError("image file is truncated")
|
||||
|
||||
if not s and not decoder.handles_eof: # truncated jpeg
|
||||
self.tile = []
|
||||
|
||||
# JpegDecode needs to clean things up here either way
|
||||
# If we don't destroy the decompressor,
|
||||
# we have a memory leak.
|
||||
decoder.cleanup()
|
||||
|
||||
if LOAD_TRUNCATED_IMAGES:
|
||||
break
|
||||
else:
|
||||
raise IOError("image file is truncated "
|
||||
"(%d bytes not processed)" % len(b))
|
||||
|
||||
b = b + s
|
||||
n, err_code = decoder.decode(b)
|
||||
if n < 0:
|
||||
break
|
||||
else:
|
||||
raise IOError("image file is truncated")
|
||||
|
||||
if not s and not d.handles_eof: # truncated jpeg
|
||||
self.tile = []
|
||||
|
||||
# JpegDecode needs to clean things up here either way
|
||||
# If we don't destroy the decompressor,
|
||||
# we have a memory leak.
|
||||
d.cleanup()
|
||||
|
||||
if LOAD_TRUNCATED_IMAGES:
|
||||
break
|
||||
else:
|
||||
raise IOError("image file is truncated "
|
||||
"(%d bytes not processed)" % len(b))
|
||||
|
||||
b = b + s
|
||||
n, e = d.decode(b)
|
||||
if n < 0:
|
||||
break
|
||||
b = b[n:]
|
||||
|
||||
# Need to cleanup here to prevent leaks in PyPy
|
||||
d.cleanup()
|
||||
decoder.cleanup()
|
||||
|
||||
self.tile = []
|
||||
self.readonly = readonly
|
||||
|
||||
self.fp = None # might be shared
|
||||
|
||||
if not self.map and not LOAD_TRUNCATED_IMAGES and e < 0:
|
||||
if not self.map and not LOAD_TRUNCATED_IMAGES and err_code < 0:
|
||||
# still raised if decoder fails to return anything
|
||||
raise_ioerror(e)
|
||||
raise_ioerror(err_code)
|
||||
|
||||
# post processing
|
||||
if hasattr(self, "tile_post_rotate"):
|
||||
|
@ -465,11 +471,15 @@ def _save(im, fp, tile, bufsize=0):
|
|||
if o > 0:
|
||||
fp.seek(o, 0)
|
||||
e.setimage(im.im, b)
|
||||
while True:
|
||||
l, s, d = e.encode(bufsize)
|
||||
fp.write(d)
|
||||
if s:
|
||||
break
|
||||
if e.pushes_fd:
|
||||
e.setfd(fp)
|
||||
l,s = e.encode_to_pyfd()
|
||||
else:
|
||||
while True:
|
||||
l, s, d = e.encode(bufsize)
|
||||
fp.write(d)
|
||||
if s:
|
||||
break
|
||||
if s < 0:
|
||||
raise IOError("encoder error %d when writing image file" % s)
|
||||
e.cleanup()
|
||||
|
@ -480,7 +490,11 @@ def _save(im, fp, tile, bufsize=0):
|
|||
if o > 0:
|
||||
fp.seek(o, 0)
|
||||
e.setimage(im.im, b)
|
||||
s = e.encode_to_file(fh, bufsize)
|
||||
if e.pushes_fd:
|
||||
e.setfd(fp)
|
||||
e.encode_to_pyfd()
|
||||
else:
|
||||
s = e.encode_to_file(fh, bufsize)
|
||||
if s < 0:
|
||||
raise IOError("encoder error %d when writing image file" % s)
|
||||
e.cleanup()
|
||||
|
|
|
@ -192,7 +192,7 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
|
|||
length = -1
|
||||
|
||||
self.tile = [('jpeg2k', (0, 0) + self.size, 0,
|
||||
(self.codec, self.reduce, self.layers, fd, length))]
|
||||
(self.codec, self.reduce, self.layers, fd, length, self.fp))]
|
||||
|
||||
def load(self):
|
||||
if self.reduce:
|
||||
|
|
108
decode.c
108
decode.c
|
@ -52,7 +52,8 @@ typedef struct {
|
|||
struct ImagingCodecStateInstance state;
|
||||
Imaging im;
|
||||
PyObject* lock;
|
||||
int handles_eof;
|
||||
int handles_eof;
|
||||
int pulls_fd;
|
||||
} ImagingDecoderObject;
|
||||
|
||||
static PyTypeObject ImagingDecoderType;
|
||||
|
@ -97,6 +98,10 @@ PyImaging_DecoderNew(int contextsize)
|
|||
/* Most decoders don't want to handle EOF themselves */
|
||||
decoder->handles_eof = 0;
|
||||
|
||||
/* set if the decoder needs to pull data from the fd, instead of
|
||||
having it pushed */
|
||||
decoder->pulls_fd = 0;
|
||||
|
||||
return decoder;
|
||||
}
|
||||
|
||||
|
@ -108,6 +113,7 @@ _dealloc(ImagingDecoderObject* decoder)
|
|||
free(decoder->state.buffer);
|
||||
free(decoder->state.context);
|
||||
Py_XDECREF(decoder->lock);
|
||||
Py_XDECREF(decoder->state.fd);
|
||||
PyObject_Del(decoder);
|
||||
}
|
||||
|
||||
|
@ -121,11 +127,15 @@ _decode(ImagingDecoderObject* decoder, PyObject* args)
|
|||
if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH, &buffer, &bufsize))
|
||||
return NULL;
|
||||
|
||||
ImagingSectionEnter(&cookie);
|
||||
if (!decoder->pulls_fd) {
|
||||
ImagingSectionEnter(&cookie);
|
||||
}
|
||||
|
||||
status = decoder->decode(decoder->im, &decoder->state, buffer, bufsize);
|
||||
|
||||
ImagingSectionLeave(&cookie);
|
||||
if (!decoder->pulls_fd) {
|
||||
ImagingSectionLeave(&cookie);
|
||||
}
|
||||
|
||||
return Py_BuildValue("ii", status, decoder->state.errcode);
|
||||
}
|
||||
|
@ -210,16 +220,42 @@ _setimage(ImagingDecoderObject* decoder, PyObject* args)
|
|||
return Py_None;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
_setfd(ImagingDecoderObject* decoder, PyObject* args)
|
||||
{
|
||||
PyObject* fd;
|
||||
ImagingCodecState state;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "O", &fd))
|
||||
return NULL;
|
||||
|
||||
state = &decoder->state;
|
||||
|
||||
Py_XINCREF(fd);
|
||||
state->fd = fd;
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
|
||||
static PyObject *
|
||||
_get_handles_eof(ImagingDecoderObject *decoder)
|
||||
{
|
||||
return PyBool_FromLong(decoder->handles_eof);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
_get_pulls_fd(ImagingDecoderObject *decoder)
|
||||
{
|
||||
return PyBool_FromLong(decoder->pulls_fd);
|
||||
}
|
||||
|
||||
static struct PyMethodDef methods[] = {
|
||||
{"decode", (PyCFunction)_decode, 1},
|
||||
{"cleanup", (PyCFunction)_decode_cleanup, 1},
|
||||
{"setimage", (PyCFunction)_setimage, 1},
|
||||
{"setfd", (PyCFunction)_setfd, 1},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
|
@ -227,6 +263,9 @@ static struct PyGetSetDef getseters[] = {
|
|||
{"handles_eof", (getter)_get_handles_eof, NULL,
|
||||
"True if this decoder expects to handle EOF itself.",
|
||||
NULL},
|
||||
{"pulls_fd", (getter)_get_pulls_fd, NULL,
|
||||
"True if this decoder expects to pull from self.fd itself.",
|
||||
NULL},
|
||||
{NULL, NULL, NULL, NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
|
@ -811,6 +850,8 @@ PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args)
|
|||
int layers = 0;
|
||||
int fd = -1;
|
||||
PY_LONG_LONG length = -1;
|
||||
PyObject * py_fd;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "ss|iiiL", &mode, &format,
|
||||
&reduce, &layers, &fd, &length))
|
||||
return NULL;
|
||||
|
@ -829,11 +870,12 @@ PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args)
|
|||
return NULL;
|
||||
|
||||
decoder->handles_eof = 1;
|
||||
decoder->pulls_fd = 1;
|
||||
decoder->decode = ImagingJpeg2KDecode;
|
||||
decoder->cleanup = ImagingJpeg2KDecodeCleanup;
|
||||
|
||||
context = (JPEG2KDECODESTATE *)decoder->state.context;
|
||||
|
||||
|
||||
context->fd = fd;
|
||||
context->length = (off_t)length;
|
||||
context->format = codec_format;
|
||||
|
@ -843,3 +885,61 @@ PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args)
|
|||
return (PyObject*) decoder;
|
||||
}
|
||||
#endif /* HAVE_OPENJPEG */
|
||||
|
||||
Py_ssize_t
|
||||
_imaging_read_pyFd(PyObject *fd, char* dest, Py_ssize_t bytes)
|
||||
{
|
||||
/* dest should be a buffer bytes long, returns length of read
|
||||
-1 on error */
|
||||
|
||||
PyObject *result;
|
||||
char *buffer;
|
||||
Py_ssize_t length;
|
||||
int bytes_result;
|
||||
|
||||
result = PyObject_CallMethod(fd, "read", "n", bytes);
|
||||
|
||||
bytes_result = PyBytes_AsStringAndSize(result, &buffer, &length);
|
||||
if (bytes_result == -1) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (length > bytes) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
memcpy(dest, buffer, length);
|
||||
|
||||
Py_DECREF(result);
|
||||
return length;
|
||||
|
||||
err:
|
||||
Py_DECREF(result);
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
_imaging_seek_pyFd(PyObject *fd, Py_ssize_t offset, int whence)
|
||||
{
|
||||
PyObject *result;
|
||||
|
||||
result = PyObject_CallMethod(fd, "seek", "ni", offset, whence);
|
||||
|
||||
Py_DECREF(result);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
Py_ssize_t
|
||||
_imaging_tell_pyFd(PyObject *fd)
|
||||
{
|
||||
PyObject *result;
|
||||
Py_ssize_t location;
|
||||
|
||||
result = PyObject_CallMethod(fd, "tell", NULL);
|
||||
location = PyInt_AsSsize_t(result);
|
||||
|
||||
Py_DECREF(result);
|
||||
return location;
|
||||
}
|
||||
|
|
80
encode.c
80
encode.c
|
@ -44,6 +44,7 @@ typedef struct {
|
|||
struct ImagingCodecStateInstance state;
|
||||
Imaging im;
|
||||
PyObject* lock;
|
||||
int pushes_fd;
|
||||
} ImagingEncoderObject;
|
||||
|
||||
static PyTypeObject ImagingEncoderType;
|
||||
|
@ -84,6 +85,7 @@ PyImaging_EncoderNew(int contextsize)
|
|||
/* Target image */
|
||||
encoder->lock = NULL;
|
||||
encoder->im = NULL;
|
||||
encoder->pushes_fd = 0;
|
||||
|
||||
return encoder;
|
||||
}
|
||||
|
@ -96,6 +98,7 @@ _dealloc(ImagingEncoderObject* encoder)
|
|||
free(encoder->state.buffer);
|
||||
free(encoder->state.context);
|
||||
Py_XDECREF(encoder->lock);
|
||||
Py_XDECREF(encoder->state.fd);
|
||||
PyObject_Del(encoder);
|
||||
}
|
||||
|
||||
|
@ -143,6 +146,27 @@ _encode(ImagingEncoderObject* encoder, PyObject* args)
|
|||
return result;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
_encode_to_pyfd(ImagingEncoderObject* encoder, PyObject* args)
|
||||
{
|
||||
|
||||
PyObject *result;
|
||||
int status;
|
||||
|
||||
if (!encoder->pushes_fd) {
|
||||
// UNDONE, appropriate errcode???
|
||||
result = Py_BuildValue("ii", 0, IMAGING_CODEC_CONFIG);;
|
||||
return result;
|
||||
}
|
||||
|
||||
status = encoder->encode(encoder->im, &encoder->state,
|
||||
(UINT8*) NULL, 0);
|
||||
|
||||
result = Py_BuildValue("ii", status, encoder->state.errcode);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
_encode_to_file(ImagingEncoderObject* encoder, PyObject* args)
|
||||
{
|
||||
|
@ -254,14 +278,47 @@ _setimage(ImagingEncoderObject* encoder, PyObject* args)
|
|||
return Py_None;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
_setfd(ImagingEncoderObject* encoder, PyObject* args)
|
||||
{
|
||||
PyObject* fd;
|
||||
ImagingCodecState state;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "O", &fd))
|
||||
return NULL;
|
||||
|
||||
state = &encoder->state;
|
||||
|
||||
Py_XINCREF(fd);
|
||||
state->fd = fd;
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
_get_pushes_fd(ImagingEncoderObject *encoder)
|
||||
{
|
||||
return PyBool_FromLong(encoder->pushes_fd);
|
||||
}
|
||||
|
||||
static struct PyMethodDef methods[] = {
|
||||
{"encode", (PyCFunction)_encode, 1},
|
||||
{"cleanup", (PyCFunction)_encode_cleanup, 1},
|
||||
{"encode_to_file", (PyCFunction)_encode_to_file, 1},
|
||||
{"encode_to_pyfd", (PyCFunction)_encode_to_pyfd, 1},
|
||||
{"setimage", (PyCFunction)_setimage, 1},
|
||||
{"setfd", (PyCFunction)_setfd, 1},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
static struct PyGetSetDef getseters[] = {
|
||||
{"pushes_fd", (getter)_get_pushes_fd, NULL,
|
||||
"True if this decoder expects to push directly to self.fd",
|
||||
NULL},
|
||||
{NULL, NULL, NULL, NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
static PyTypeObject ImagingEncoderType = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"ImagingEncoder", /*tp_name*/
|
||||
|
@ -293,7 +350,7 @@ static PyTypeObject ImagingEncoderType = {
|
|||
0, /*tp_iternext*/
|
||||
methods, /*tp_methods*/
|
||||
0, /*tp_members*/
|
||||
0, /*tp_getset*/
|
||||
getseters, /*tp_getset*/
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -916,6 +973,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args)
|
|||
|
||||
encoder->encode = ImagingJpeg2KEncode;
|
||||
encoder->cleanup = ImagingJpeg2KEncodeCleanup;
|
||||
encoder->pushes_fd = 1;
|
||||
|
||||
context = (JPEG2KENCODESTATE *)encoder->state.context;
|
||||
|
||||
|
@ -923,6 +981,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args)
|
|||
context->format = codec_format;
|
||||
context->offset_x = context->offset_y = 0;
|
||||
|
||||
|
||||
j2k_decode_coord_tuple(offset, &context->offset_x, &context->offset_y);
|
||||
j2k_decode_coord_tuple(tile_offset,
|
||||
&context->tile_offset_x,
|
||||
|
@ -973,6 +1032,25 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args)
|
|||
|
||||
#endif
|
||||
|
||||
Py_ssize_t
|
||||
_imaging_write_pyFd(PyObject *fd, char* src, Py_ssize_t bytes)
|
||||
{
|
||||
|
||||
PyObject *result;
|
||||
PyObject *byteObj;
|
||||
|
||||
byteObj = PyBytes_FromStringAndSize(src, bytes);
|
||||
result = PyObject_CallMethod(fd, "write", "O", byteObj);
|
||||
|
||||
Py_DECREF(byteObj);
|
||||
Py_DECREF(result);
|
||||
|
||||
return bytes;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* c-basic-offset: 4
|
||||
|
|
|
@ -486,33 +486,16 @@ struct ImagingCodecStateInstance {
|
|||
int bits, bytes;
|
||||
UINT8 *buffer;
|
||||
void *context;
|
||||
PyObject *fd;
|
||||
};
|
||||
|
||||
/* Incremental encoding/decoding support */
|
||||
typedef struct ImagingIncrementalCodecStruct *ImagingIncrementalCodec;
|
||||
|
||||
typedef int (*ImagingIncrementalCodecEntry)(Imaging im,
|
||||
ImagingCodecState state,
|
||||
ImagingIncrementalCodec codec);
|
||||
extern Py_ssize_t _imaging_read_pyFd(PyObject *fd, char* dest, Py_ssize_t bytes);
|
||||
extern Py_ssize_t _imaging_write_pyFd(PyObject *fd, char* src, Py_ssize_t bytes);
|
||||
extern int _imaging_seek_pyFd(PyObject *fd, Py_ssize_t offset, int whence);
|
||||
extern Py_ssize_t _imaging_tell_pyFd(PyObject *fd);
|
||||
|
||||
enum {
|
||||
INCREMENTAL_CODEC_READ = 1,
|
||||
INCREMENTAL_CODEC_WRITE = 2
|
||||
};
|
||||
|
||||
enum {
|
||||
INCREMENTAL_CODEC_NOT_SEEKABLE = 0,
|
||||
INCREMENTAL_CODEC_SEEKABLE = 1
|
||||
};
|
||||
|
||||
extern ImagingIncrementalCodec ImagingIncrementalCodecCreate(ImagingIncrementalCodecEntry codec_entry, Imaging im, ImagingCodecState state, int read_or_write, int seekable, int fd);
|
||||
extern void ImagingIncrementalCodecDestroy(ImagingIncrementalCodec codec);
|
||||
extern int ImagingIncrementalCodecPushBuffer(ImagingIncrementalCodec codec, UINT8 *buf, int bytes);
|
||||
extern Py_ssize_t ImagingIncrementalCodecRead(ImagingIncrementalCodec codec, void *buffer, size_t bytes);
|
||||
extern off_t ImagingIncrementalCodecSkip(ImagingIncrementalCodec codec, off_t bytes);
|
||||
extern Py_ssize_t ImagingIncrementalCodecWrite(ImagingIncrementalCodec codec, const void *buffer, size_t bytes);
|
||||
extern off_t ImagingIncrementalCodecSeek(ImagingIncrementalCodec codec, off_t bytes);
|
||||
extern size_t ImagingIncrementalCodecBytesInBuffer(ImagingIncrementalCodec codec);
|
||||
|
||||
/* Errcodes */
|
||||
#define IMAGING_CODEC_END 1
|
||||
|
|
|
@ -1,687 +0,0 @@
|
|||
/*
|
||||
* 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
|
||||
ImagingIncrementalCodecRead() call runs short on data, it suspends the
|
||||
decoding thread and wakes the main thread. Conversely, the
|
||||
ImagingIncrementalCodecPushBuffer() call suspends the main thread and wakes
|
||||
the decoding thread, providing a buffer of data.
|
||||
|
||||
The two threads are never running simultaneously, so there is no need for
|
||||
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. */
|
||||
|
||||
/* Additional complication: *Some* codecs need to seek; this is fine if
|
||||
there is a file descriptor, but if we're buffering data it becomes
|
||||
awkward. The incremental adaptor now contains code to handle these
|
||||
two cases. */
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <process.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
#define DEBUG_INCREMENTAL 0
|
||||
|
||||
#if DEBUG_INCREMENTAL
|
||||
#define DEBUG(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG(...)
|
||||
#endif
|
||||
|
||||
struct ImagingIncrementalCodecStruct {
|
||||
#ifdef _WIN32
|
||||
HANDLE hCodecEvent;
|
||||
HANDLE hDataEvent;
|
||||
HANDLE hThread;
|
||||
#else
|
||||
pthread_mutex_t start_mutex;
|
||||
pthread_cond_t start_cond;
|
||||
pthread_mutex_t codec_mutex;
|
||||
pthread_cond_t codec_cond;
|
||||
pthread_mutex_t data_mutex;
|
||||
pthread_cond_t data_cond;
|
||||
pthread_t thread;
|
||||
#endif
|
||||
ImagingIncrementalCodecEntry entry;
|
||||
Imaging im;
|
||||
ImagingCodecState state;
|
||||
struct {
|
||||
int fd;
|
||||
UINT8 *buffer; /* Base of buffer */
|
||||
UINT8 *ptr; /* Current pointer in buffer */
|
||||
UINT8 *top; /* Highest point in buffer we've used */
|
||||
UINT8 *end; /* End of buffer */
|
||||
} stream;
|
||||
int read_or_write;
|
||||
int seekable;
|
||||
int started;
|
||||
int result;
|
||||
};
|
||||
|
||||
static void flush_stream(ImagingIncrementalCodec codec);
|
||||
|
||||
#if _WIN32
|
||||
static unsigned int __stdcall
|
||||
codec_thread(void *ptr)
|
||||
{
|
||||
ImagingIncrementalCodec codec = (ImagingIncrementalCodec)ptr;
|
||||
|
||||
DEBUG("Entering thread\n");
|
||||
|
||||
codec->result = codec->entry(codec->im, codec->state, codec);
|
||||
|
||||
DEBUG("Leaving thread (%d)\n", codec->result);
|
||||
|
||||
flush_stream(codec);
|
||||
|
||||
SetEvent(codec->hCodecEvent);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static void *
|
||||
codec_thread(void *ptr)
|
||||
{
|
||||
ImagingIncrementalCodec codec = (ImagingIncrementalCodec)ptr;
|
||||
|
||||
DEBUG("Entering thread\n");
|
||||
|
||||
codec->result = codec->entry(codec->im, codec->state, codec);
|
||||
|
||||
DEBUG("Leaving thread (%d)\n", codec->result);
|
||||
|
||||
flush_stream(codec);
|
||||
|
||||
pthread_mutex_lock(&codec->codec_mutex);
|
||||
pthread_cond_signal(&codec->codec_cond);
|
||||
pthread_mutex_unlock(&codec->codec_mutex);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
flush_stream(ImagingIncrementalCodec codec)
|
||||
{
|
||||
UINT8 *buffer;
|
||||
size_t bytes;
|
||||
|
||||
/* This is to flush data from the write buffer for a seekable write
|
||||
codec. */
|
||||
if (codec->read_or_write != INCREMENTAL_CODEC_WRITE
|
||||
|| codec->state->errcode != IMAGING_CODEC_END
|
||||
|| !codec->seekable
|
||||
|| codec->stream.fd >= 0)
|
||||
return;
|
||||
|
||||
DEBUG("flushing data\n");
|
||||
|
||||
buffer = codec->stream.buffer;
|
||||
bytes = codec->stream.ptr - codec->stream.buffer;
|
||||
|
||||
codec->state->errcode = 0;
|
||||
codec->seekable = INCREMENTAL_CODEC_NOT_SEEKABLE;
|
||||
codec->stream.buffer = codec->stream.ptr = codec->stream.end
|
||||
= codec->stream.top = NULL;
|
||||
|
||||
ImagingIncrementalCodecWrite(codec, buffer, bytes);
|
||||
|
||||
codec->state->errcode = IMAGING_CODEC_END;
|
||||
codec->result = (int)ImagingIncrementalCodecBytesInBuffer(codec);
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new incremental codec */
|
||||
ImagingIncrementalCodec
|
||||
ImagingIncrementalCodecCreate(ImagingIncrementalCodecEntry codec_entry,
|
||||
Imaging im,
|
||||
ImagingCodecState state,
|
||||
int read_or_write,
|
||||
int seekable,
|
||||
int fd)
|
||||
{
|
||||
/* malloc check ok, small constant allocation */
|
||||
ImagingIncrementalCodec codec = (ImagingIncrementalCodec)malloc(sizeof(struct ImagingIncrementalCodecStruct));
|
||||
|
||||
codec->entry = codec_entry;
|
||||
codec->im = im;
|
||||
codec->state = state;
|
||||
codec->result = 0;
|
||||
codec->stream.fd = fd;
|
||||
codec->stream.buffer = codec->stream.ptr = codec->stream.end
|
||||
= codec->stream.top = NULL;
|
||||
codec->started = 0;
|
||||
codec->seekable = seekable;
|
||||
codec->read_or_write = read_or_write;
|
||||
|
||||
if (fd >= 0)
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
|
||||
/* System specific set-up */
|
||||
#if _WIN32
|
||||
codec->hCodecEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
|
||||
if (!codec->hCodecEvent) {
|
||||
free(codec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
codec->hDataEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
|
||||
if (!codec->hDataEvent) {
|
||||
CloseHandle(codec->hCodecEvent);
|
||||
free(codec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
codec->hThread = _beginthreadex(NULL, 0, codec_thread, codec,
|
||||
CREATE_SUSPENDED, NULL);
|
||||
|
||||
if (!codec->hThread) {
|
||||
CloseHandle(codec->hCodecEvent);
|
||||
CloseHandle(codec->hDataEvent);
|
||||
free(codec);
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
if (pthread_mutex_init(&codec->start_mutex, NULL)) {
|
||||
free (codec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pthread_mutex_init(&codec->codec_mutex, NULL)) {
|
||||
pthread_mutex_destroy(&codec->start_mutex);
|
||||
free(codec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pthread_mutex_init(&codec->data_mutex, NULL)) {
|
||||
pthread_mutex_destroy(&codec->start_mutex);
|
||||
pthread_mutex_destroy(&codec->codec_mutex);
|
||||
free(codec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pthread_cond_init(&codec->start_cond, NULL)) {
|
||||
pthread_mutex_destroy(&codec->start_mutex);
|
||||
pthread_mutex_destroy(&codec->codec_mutex);
|
||||
pthread_mutex_destroy(&codec->data_mutex);
|
||||
free(codec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pthread_cond_init(&codec->codec_cond, NULL)) {
|
||||
pthread_mutex_destroy(&codec->start_mutex);
|
||||
pthread_mutex_destroy(&codec->codec_mutex);
|
||||
pthread_mutex_destroy(&codec->data_mutex);
|
||||
pthread_cond_destroy(&codec->start_cond);
|
||||
free(codec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pthread_cond_init(&codec->data_cond, NULL)) {
|
||||
pthread_mutex_destroy(&codec->start_mutex);
|
||||
pthread_mutex_destroy(&codec->codec_mutex);
|
||||
pthread_mutex_destroy(&codec->data_mutex);
|
||||
pthread_cond_destroy(&codec->start_cond);
|
||||
pthread_cond_destroy(&codec->codec_cond);
|
||||
free(codec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pthread_create(&codec->thread, NULL, codec_thread, codec)) {
|
||||
pthread_mutex_destroy(&codec->start_mutex);
|
||||
pthread_mutex_destroy(&codec->codec_mutex);
|
||||
pthread_mutex_destroy(&codec->data_mutex);
|
||||
pthread_cond_destroy(&codec->start_cond);
|
||||
pthread_cond_destroy(&codec->codec_cond);
|
||||
pthread_cond_destroy(&codec->data_cond);
|
||||
free(codec);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
return codec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy an incremental codec */
|
||||
void
|
||||
ImagingIncrementalCodecDestroy(ImagingIncrementalCodec codec)
|
||||
{
|
||||
DEBUG("destroying\n");
|
||||
|
||||
if (!codec->started) {
|
||||
#ifdef _WIN32
|
||||
ResumeThread(codec->hThread);
|
||||
#else
|
||||
pthread_cond_signal(&codec->start_cond);
|
||||
#endif
|
||||
codec->started = 1;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
pthread_mutex_lock(&codec->data_mutex);
|
||||
#endif
|
||||
|
||||
if (codec->seekable && codec->stream.fd < 0)
|
||||
free (codec->stream.buffer);
|
||||
|
||||
codec->stream.buffer = codec->stream.ptr = codec->stream.end
|
||||
= codec->stream.top = NULL;
|
||||
|
||||
#ifdef _WIN32
|
||||
SetEvent(codec->hDataEvent);
|
||||
|
||||
WaitForSingleObject(codec->hThread, INFINITE);
|
||||
|
||||
CloseHandle(codec->hThread);
|
||||
CloseHandle(codec->hCodecEvent);
|
||||
CloseHandle(codec->hDataEvent);
|
||||
#else
|
||||
pthread_cond_signal(&codec->data_cond);
|
||||
pthread_mutex_unlock(&codec->data_mutex);
|
||||
|
||||
pthread_join(codec->thread, NULL);
|
||||
|
||||
pthread_mutex_destroy(&codec->start_mutex);
|
||||
pthread_mutex_destroy(&codec->codec_mutex);
|
||||
pthread_mutex_destroy(&codec->data_mutex);
|
||||
pthread_cond_destroy(&codec->start_cond);
|
||||
pthread_cond_destroy(&codec->codec_cond);
|
||||
pthread_cond_destroy(&codec->data_cond);
|
||||
#endif
|
||||
free (codec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a data buffer for an incremental codec */
|
||||
int
|
||||
ImagingIncrementalCodecPushBuffer(ImagingIncrementalCodec codec,
|
||||
UINT8 *buf, int bytes)
|
||||
{
|
||||
if (!codec->started) {
|
||||
DEBUG("starting\n");
|
||||
|
||||
#ifdef _WIN32
|
||||
ResumeThread(codec->hThread);
|
||||
#else
|
||||
pthread_cond_signal(&codec->start_cond);
|
||||
#endif
|
||||
codec->started = 1;
|
||||
|
||||
/* Wait for the thread to ask for data */
|
||||
#ifdef _WIN32
|
||||
WaitForSingleObject(codec->hCodecEvent, INFINITE);
|
||||
#else
|
||||
pthread_mutex_lock(&codec->codec_mutex);
|
||||
pthread_cond_wait(&codec->codec_cond, &codec->codec_mutex);
|
||||
pthread_mutex_unlock(&codec->codec_mutex);
|
||||
#endif
|
||||
if (codec->result < 0) {
|
||||
DEBUG("got result %d\n", codec->result);
|
||||
|
||||
return codec->result;
|
||||
}
|
||||
}
|
||||
|
||||
/* Codecs using an fd don't need data, so when we get here, we're done */
|
||||
if (codec->stream.fd >= 0) {
|
||||
DEBUG("got result %d\n", codec->result);
|
||||
|
||||
return codec->result;
|
||||
}
|
||||
|
||||
DEBUG("providing %p, %d\n", buf, bytes);
|
||||
|
||||
#ifndef _WIN32
|
||||
pthread_mutex_lock(&codec->data_mutex);
|
||||
#endif
|
||||
|
||||
if (codec->read_or_write == INCREMENTAL_CODEC_READ
|
||||
&& codec->seekable && codec->stream.fd < 0) {
|
||||
/* In this specific case, we append to a buffer we allocate ourselves */
|
||||
size_t old_size = codec->stream.end - codec->stream.buffer;
|
||||
size_t new_size = codec->stream.end - codec->stream.buffer + bytes;
|
||||
UINT8 *new;
|
||||
|
||||
if (old_size > SIZE_MAX - bytes) {
|
||||
codec->state->errcode = IMAGING_CODEC_MEMORY;
|
||||
#ifndef _WIN32
|
||||
pthread_mutex_unlock(&codec->data_mutex);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
/* malloc check ok, overflow checked */
|
||||
new = (UINT8 *)realloc (codec->stream.buffer, new_size);
|
||||
|
||||
if (!new) {
|
||||
codec->state->errcode = IMAGING_CODEC_MEMORY;
|
||||
#ifndef _WIN32
|
||||
pthread_mutex_unlock(&codec->data_mutex);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
codec->stream.ptr = codec->stream.ptr - codec->stream.buffer + new;
|
||||
codec->stream.end = new + new_size;
|
||||
codec->stream.buffer = new;
|
||||
|
||||
memcpy(new + old_size, buf, bytes);
|
||||
} else {
|
||||
codec->stream.buffer = codec->stream.ptr = buf;
|
||||
codec->stream.end = buf + bytes;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
SetEvent(codec->hDataEvent);
|
||||
WaitForSingleObject(codec->hCodecEvent, INFINITE);
|
||||
#else
|
||||
pthread_cond_signal(&codec->data_cond);
|
||||
pthread_mutex_unlock(&codec->data_mutex);
|
||||
|
||||
pthread_mutex_lock(&codec->codec_mutex);
|
||||
pthread_cond_wait(&codec->codec_cond, &codec->codec_mutex);
|
||||
pthread_mutex_unlock(&codec->codec_mutex);
|
||||
#endif
|
||||
|
||||
DEBUG("got result %d\n", codec->result);
|
||||
|
||||
return codec->result;
|
||||
}
|
||||
|
||||
size_t
|
||||
ImagingIncrementalCodecBytesInBuffer(ImagingIncrementalCodec codec)
|
||||
{
|
||||
return codec->stream.ptr - codec->stream.buffer;
|
||||
}
|
||||
|
||||
Py_ssize_t
|
||||
ImagingIncrementalCodecRead(ImagingIncrementalCodec codec,
|
||||
void *buffer, size_t bytes)
|
||||
{
|
||||
UINT8 *ptr = (UINT8 *)buffer;
|
||||
size_t done = 0;
|
||||
|
||||
if (codec->read_or_write == INCREMENTAL_CODEC_WRITE) {
|
||||
DEBUG("attempt to read from write codec\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUG("reading (want %llu bytes)\n", (unsigned long long)bytes);
|
||||
|
||||
if (codec->stream.fd >= 0) {
|
||||
Py_ssize_t ret = read(codec->stream.fd, buffer, bytes);
|
||||
DEBUG("read %lld bytes from fd\n", (long long)ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
pthread_mutex_lock(&codec->data_mutex);
|
||||
#endif
|
||||
while (bytes) {
|
||||
size_t todo = bytes;
|
||||
size_t remaining = codec->stream.end - codec->stream.ptr;
|
||||
|
||||
if (!remaining) {
|
||||
DEBUG("waiting for data\n");
|
||||
|
||||
#ifndef _WIN32
|
||||
pthread_mutex_lock(&codec->codec_mutex);
|
||||
#endif
|
||||
codec->result = (int)(codec->stream.ptr - codec->stream.buffer);
|
||||
#if _WIN32
|
||||
SetEvent(codec->hCodecEvent);
|
||||
WaitForSingleObject(codec->hDataEvent, INFINITE);
|
||||
#else
|
||||
pthread_cond_signal(&codec->codec_cond);
|
||||
pthread_mutex_unlock(&codec->codec_mutex);
|
||||
pthread_cond_wait(&codec->data_cond, &codec->data_mutex);
|
||||
#endif
|
||||
|
||||
remaining = codec->stream.end - codec->stream.ptr;
|
||||
codec->stream.top = codec->stream.end;
|
||||
|
||||
DEBUG("got %llu bytes\n", (unsigned long long)remaining);
|
||||
}
|
||||
|
||||
if (todo > remaining)
|
||||
todo = remaining;
|
||||
|
||||
if (!todo)
|
||||
break;
|
||||
|
||||
memcpy (ptr, codec->stream.ptr, todo);
|
||||
codec->stream.ptr += todo;
|
||||
bytes -= todo;
|
||||
done += todo;
|
||||
ptr += todo;
|
||||
}
|
||||
#ifndef _WIN32
|
||||
pthread_mutex_unlock(&codec->data_mutex);
|
||||
#endif
|
||||
|
||||
DEBUG("read total %llu bytes\n", (unsigned long long)done);
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
off_t
|
||||
ImagingIncrementalCodecSkip(ImagingIncrementalCodec codec,
|
||||
off_t bytes)
|
||||
{
|
||||
off_t done = 0;
|
||||
|
||||
DEBUG("skipping (want %llu bytes)\n", (unsigned long long)bytes);
|
||||
|
||||
/* In write mode, explicitly fill with zeroes */
|
||||
if (codec->read_or_write == INCREMENTAL_CODEC_WRITE) {
|
||||
static const UINT8 zeroes[256] = { 0 };
|
||||
off_t done = 0;
|
||||
while (bytes) {
|
||||
size_t todo = (size_t)(bytes > 256 ? 256 : bytes);
|
||||
Py_ssize_t written = ImagingIncrementalCodecWrite(codec, zeroes, todo);
|
||||
if (written <= 0)
|
||||
break;
|
||||
done += written;
|
||||
bytes -= written;
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
if (codec->stream.fd >= 0)
|
||||
return lseek(codec->stream.fd, bytes, SEEK_CUR);
|
||||
|
||||
#ifndef _WIN32
|
||||
pthread_mutex_lock(&codec->data_mutex);
|
||||
#endif
|
||||
while (bytes) {
|
||||
off_t todo = bytes;
|
||||
off_t remaining = codec->stream.end - codec->stream.ptr;
|
||||
|
||||
if (!remaining) {
|
||||
DEBUG("waiting for data\n");
|
||||
|
||||
#ifndef _WIN32
|
||||
pthread_mutex_lock(&codec->codec_mutex);
|
||||
#endif
|
||||
codec->result = (int)(codec->stream.ptr - codec->stream.buffer);
|
||||
#if _WIN32
|
||||
SetEvent(codec->hCodecEvent);
|
||||
WaitForSingleObject(codec->hDataEvent, INFINITE);
|
||||
#else
|
||||
pthread_cond_signal(&codec->codec_cond);
|
||||
pthread_mutex_unlock(&codec->codec_mutex);
|
||||
pthread_cond_wait(&codec->data_cond, &codec->data_mutex);
|
||||
#endif
|
||||
|
||||
remaining = codec->stream.end - codec->stream.ptr;
|
||||
}
|
||||
|
||||
if (todo > remaining)
|
||||
todo = remaining;
|
||||
|
||||
if (!todo)
|
||||
break;
|
||||
|
||||
codec->stream.ptr += todo;
|
||||
bytes -= todo;
|
||||
done += todo;
|
||||
}
|
||||
#ifndef _WIN32
|
||||
pthread_mutex_unlock(&codec->data_mutex);
|
||||
#endif
|
||||
|
||||
DEBUG("skipped total %llu bytes\n", (unsigned long long)done);
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
Py_ssize_t
|
||||
ImagingIncrementalCodecWrite(ImagingIncrementalCodec codec,
|
||||
const void *buffer, size_t bytes)
|
||||
{
|
||||
const UINT8 *ptr = (const UINT8 *)buffer;
|
||||
size_t done = 0;
|
||||
|
||||
if (codec->read_or_write == INCREMENTAL_CODEC_READ) {
|
||||
DEBUG("attempt to write from read codec\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUG("write (have %llu bytes)\n", (unsigned long long)bytes);
|
||||
|
||||
if (codec->stream.fd >= 0)
|
||||
return write(codec->stream.fd, buffer, bytes);
|
||||
|
||||
#ifndef _WIN32
|
||||
pthread_mutex_lock(&codec->data_mutex);
|
||||
#endif
|
||||
while (bytes) {
|
||||
size_t todo = bytes;
|
||||
size_t remaining = codec->stream.end - codec->stream.ptr;
|
||||
|
||||
if (!remaining) {
|
||||
if (codec->seekable && codec->stream.fd < 0) {
|
||||
/* In this case, we maintain the stream buffer ourselves */
|
||||
size_t old_size = codec->stream.top - codec->stream.buffer;
|
||||
size_t new_size = (old_size + bytes + 65535) & ~65535;
|
||||
UINT8 *new = (UINT8 *)realloc(codec->stream.buffer, new_size);
|
||||
|
||||
if (!new) {
|
||||
codec->state->errcode = IMAGING_CODEC_MEMORY;
|
||||
#ifndef _WIN32
|
||||
pthread_mutex_unlock(&codec->data_mutex);
|
||||
#endif
|
||||
return done == 0 ? -1 : done;
|
||||
}
|
||||
|
||||
codec->stream.ptr = codec->stream.ptr - codec->stream.buffer + new;
|
||||
codec->stream.buffer = new;
|
||||
codec->stream.end = new + new_size;
|
||||
codec->stream.top = new + old_size;
|
||||
} else {
|
||||
DEBUG("waiting for space\n");
|
||||
|
||||
#ifndef _WIN32
|
||||
pthread_mutex_lock(&codec->codec_mutex);
|
||||
#endif
|
||||
codec->result = (int)(codec->stream.ptr - codec->stream.buffer);
|
||||
#if _WIN32
|
||||
SetEvent(codec->hCodecEvent);
|
||||
WaitForSingleObject(codec->hDataEvent, INFINITE);
|
||||
#else
|
||||
pthread_cond_signal(&codec->codec_cond);
|
||||
pthread_mutex_unlock(&codec->codec_mutex);
|
||||
pthread_cond_wait(&codec->data_cond, &codec->data_mutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
remaining = codec->stream.end - codec->stream.ptr;
|
||||
|
||||
DEBUG("got %llu bytes\n", (unsigned long long)remaining);
|
||||
}
|
||||
if (todo > remaining)
|
||||
todo = remaining;
|
||||
|
||||
if (!todo)
|
||||
break;
|
||||
|
||||
memcpy (codec->stream.ptr, ptr, todo);
|
||||
codec->stream.ptr += todo;
|
||||
bytes -= todo;
|
||||
done += todo;
|
||||
ptr += todo;
|
||||
}
|
||||
|
||||
if (codec->stream.ptr > codec->stream.top)
|
||||
codec->stream.top = codec->stream.ptr;
|
||||
|
||||
#ifndef _WIN32
|
||||
pthread_mutex_unlock(&codec->data_mutex);
|
||||
#endif
|
||||
|
||||
DEBUG("wrote total %llu bytes\n", (unsigned long long)done);
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
off_t
|
||||
ImagingIncrementalCodecSeek(ImagingIncrementalCodec codec,
|
||||
off_t bytes)
|
||||
{
|
||||
off_t buffered;
|
||||
|
||||
DEBUG("seeking (going to %llu bytes)\n", (unsigned long long)bytes);
|
||||
|
||||
if (codec->stream.fd >= 0)
|
||||
return lseek(codec->stream.fd, bytes, SEEK_SET);
|
||||
|
||||
if (!codec->seekable) {
|
||||
DEBUG("attempt to seek non-seekable stream\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bytes < 0) {
|
||||
DEBUG("attempt to seek before stream start\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
buffered = codec->stream.top - codec->stream.buffer;
|
||||
|
||||
if (bytes <= buffered) {
|
||||
DEBUG("seek within buffer\n");
|
||||
codec->stream.ptr = codec->stream.buffer + bytes;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
return buffered + ImagingIncrementalCodecSkip(codec, bytes - buffered);
|
||||
}
|
|
@ -10,6 +10,9 @@
|
|||
|
||||
#include <openjpeg.h>
|
||||
|
||||
/* 1MB for now */
|
||||
#define BUFFER_SIZE OPJ_J2K_STREAM_CHUNK_SIZE
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* Decoder */
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -20,6 +23,9 @@ typedef struct {
|
|||
/* File descriptor, if available; otherwise, -1 */
|
||||
int fd;
|
||||
|
||||
/* File pointer, when opened */
|
||||
FILE * pfile;
|
||||
|
||||
/* Length of data, if available; otherwise, -1 */
|
||||
off_t length;
|
||||
|
||||
|
@ -34,8 +40,7 @@ typedef struct {
|
|||
|
||||
/* PRIVATE CONTEXT (set by decoder) */
|
||||
const char *error_msg;
|
||||
|
||||
ImagingIncrementalCodec decoder;
|
||||
|
||||
} JPEG2KDECODESTATE;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -48,6 +53,9 @@ typedef struct {
|
|||
/* File descriptor, if available; otherwise, -1 */
|
||||
int fd;
|
||||
|
||||
/* File pointer, when opened */
|
||||
FILE * pfile;
|
||||
|
||||
/* Specify the desired format */
|
||||
OPJ_CODEC_FORMAT format;
|
||||
|
||||
|
@ -83,7 +91,7 @@ typedef struct {
|
|||
/* PRIVATE CONTEXT (set by decoder) */
|
||||
const char *error_msg;
|
||||
|
||||
ImagingIncrementalCodec encoder;
|
||||
|
||||
} JPEG2KENCODESTATE;
|
||||
|
||||
/*
|
||||
|
|
|
@ -46,9 +46,9 @@ j2k_error(const char *msg, void *client_data)
|
|||
static OPJ_SIZE_T
|
||||
j2k_read(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data)
|
||||
{
|
||||
ImagingIncrementalCodec decoder = (ImagingIncrementalCodec)p_user_data;
|
||||
ImagingCodecState state = (ImagingCodecState)p_user_data;
|
||||
|
||||
size_t len = ImagingIncrementalCodecRead(decoder, p_buffer, p_nb_bytes);
|
||||
size_t len = _imaging_read_pyFd(state->fd, p_buffer, p_nb_bytes);
|
||||
|
||||
return len ? len : (OPJ_SIZE_T)-1;
|
||||
}
|
||||
|
@ -56,8 +56,10 @@ j2k_read(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data)
|
|||
static OPJ_OFF_T
|
||||
j2k_skip(OPJ_OFF_T p_nb_bytes, void *p_user_data)
|
||||
{
|
||||
ImagingIncrementalCodec decoder = (ImagingIncrementalCodec)p_user_data;
|
||||
off_t pos = ImagingIncrementalCodecSkip(decoder, p_nb_bytes);
|
||||
ImagingCodecState state = (ImagingCodecState)p_user_data;
|
||||
|
||||
_imaging_seek_pyFd(state->fd, p_nb_bytes, SEEK_CUR);
|
||||
off_t pos = _imaging_tell_pyFd(state->fd);
|
||||
|
||||
return pos ? pos : (OPJ_OFF_T)-1;
|
||||
}
|
||||
|
@ -545,8 +547,7 @@ enum {
|
|||
};
|
||||
|
||||
static int
|
||||
j2k_decode_entry(Imaging im, ImagingCodecState state,
|
||||
ImagingIncrementalCodec decoder)
|
||||
j2k_decode_entry(Imaging im, ImagingCodecState state)
|
||||
{
|
||||
JPEG2KDECODESTATE *context = (JPEG2KDECODESTATE *) state->context;
|
||||
opj_stream_t *stream = NULL;
|
||||
|
@ -558,22 +559,35 @@ j2k_decode_entry(Imaging im, ImagingCodecState state,
|
|||
size_t buffer_size = 0;
|
||||
unsigned n;
|
||||
|
||||
stream = opj_stream_default_create(OPJ_TRUE);
|
||||
// fastpath of using a filepointer, and not our own shufflers.
|
||||
/* if (context->fp) {
|
||||
context->pfile = fdopen(context->fp, 'rb');
|
||||
stream = opj_stream_create_default_file_stream(pfile, OPJ_TRUE);
|
||||
if (!stream) {
|
||||
state->errcode = IMAGING_CODEC_BROKEN;
|
||||
state->state = J2K_STATE_FAILED;
|
||||
goto quick_exit;
|
||||
}
|
||||
} else {
|
||||
*/ // Using our own shufflers.
|
||||
//stream = opj_stream_default_create(OPJ_TRUE);
|
||||
stream = opj_stream_create(BUFFER_SIZE, OPJ_TRUE);
|
||||
|
||||
if (!stream) {
|
||||
state->errcode = IMAGING_CODEC_BROKEN;
|
||||
state->state = J2K_STATE_FAILED;
|
||||
goto quick_exit;
|
||||
}
|
||||
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_skip_function(stream, j2k_skip);
|
||||
opj_stream_set_read_function(stream, j2k_read);
|
||||
opj_stream_set_skip_function(stream, j2k_skip);
|
||||
/* }*/
|
||||
|
||||
/* OpenJPEG 2.0 doesn't have OPJ_VERSION_MAJOR */
|
||||
#ifndef OPJ_VERSION_MAJOR
|
||||
opj_stream_set_user_data(stream, decoder);
|
||||
opj_stream_set_user_data(stream, state);
|
||||
#else
|
||||
opj_stream_set_user_data(stream, decoder, NULL);
|
||||
opj_stream_set_user_data(stream, state, NULL);
|
||||
|
||||
/* Hack: if we don't know the length, the largest file we can
|
||||
possibly support is 4GB. We can't go larger than this, because
|
||||
|
@ -749,6 +763,12 @@ j2k_decode_entry(Imaging im, ImagingCodecState state,
|
|||
state->state = J2K_STATE_DONE;
|
||||
state->errcode = IMAGING_CODEC_END;
|
||||
|
||||
if (context->pfile) {
|
||||
if(fclose(context->pfile)){
|
||||
context->pfile = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
quick_exit:
|
||||
if (codec)
|
||||
opj_destroy_codec(codec);
|
||||
|
@ -765,26 +785,27 @@ ImagingJpeg2KDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
|
|||
{
|
||||
JPEG2KDECODESTATE *context = (JPEG2KDECODESTATE *) state->context;
|
||||
|
||||
if (bytes){
|
||||
state->errcode = IMAGING_CODEC_BROKEN;
|
||||
state->state = J2K_STATE_FAILED;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (state->state == J2K_STATE_DONE || state->state == J2K_STATE_FAILED)
|
||||
return -1;
|
||||
|
||||
if (state->state == J2K_STATE_START) {
|
||||
context->decoder = ImagingIncrementalCodecCreate(j2k_decode_entry,
|
||||
im, state,
|
||||
INCREMENTAL_CODEC_READ,
|
||||
INCREMENTAL_CODEC_NOT_SEEKABLE,
|
||||
context->fd);
|
||||
|
||||
if (!context->decoder) {
|
||||
state->errcode = IMAGING_CODEC_BROKEN;
|
||||
state->state = J2K_STATE_FAILED;
|
||||
return -1;
|
||||
}
|
||||
|
||||
state->state = J2K_STATE_DECODING;
|
||||
|
||||
return j2k_decode_entry(im, state);
|
||||
}
|
||||
|
||||
return ImagingIncrementalCodecPushBuffer(context->decoder, buf, bytes);
|
||||
if (state->state == J2K_STATE_DECODING) {
|
||||
state->errcode = IMAGING_CODEC_BROKEN;
|
||||
state->state = J2K_STATE_FAILED;
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -798,13 +819,13 @@ ImagingJpeg2KDecodeCleanup(ImagingCodecState state) {
|
|||
if (context->error_msg)
|
||||
free ((void *)context->error_msg);
|
||||
|
||||
if (context->decoder)
|
||||
ImagingIncrementalCodecDestroy(context->decoder);
|
||||
/* if (context->decoder)*/
|
||||
|
||||
|
||||
context->error_msg = NULL;
|
||||
|
||||
/* Prevent multiple calls to ImagingIncrementalCodecDestroy */
|
||||
context->decoder = NULL;
|
||||
//context->decoder = NULL;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -36,6 +36,12 @@ j2k_error(const char *msg, void *client_data)
|
|||
state->error_msg = strdup(msg);
|
||||
}
|
||||
|
||||
static void
|
||||
j2k_warn(const char *msg, void *client_data)
|
||||
{
|
||||
// Null handler
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* Buffer output stream */
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -43,26 +49,41 @@ j2k_error(const char *msg, void *client_data)
|
|||
static OPJ_SIZE_T
|
||||
j2k_write(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data)
|
||||
{
|
||||
ImagingIncrementalCodec encoder = (ImagingIncrementalCodec)p_user_data;
|
||||
size_t len = ImagingIncrementalCodecWrite(encoder, p_buffer, p_nb_bytes);
|
||||
ImagingCodecState state = (ImagingCodecState)p_user_data;
|
||||
int result;
|
||||
|
||||
return len ? len : (OPJ_SIZE_T)-1;
|
||||
result = _imaging_write_pyFd(state->fd, p_buffer, p_nb_bytes);
|
||||
|
||||
return result ? result : (OPJ_SIZE_T)-1;
|
||||
}
|
||||
|
||||
|
||||
static OPJ_OFF_T
|
||||
j2k_skip(OPJ_OFF_T p_nb_bytes, void *p_user_data)
|
||||
{
|
||||
ImagingIncrementalCodec encoder = (ImagingIncrementalCodec)p_user_data;
|
||||
off_t pos = ImagingIncrementalCodecSkip(encoder, p_nb_bytes);
|
||||
ImagingCodecState state = (ImagingCodecState)p_user_data;
|
||||
char *buffer;
|
||||
int result;
|
||||
|
||||
return pos ? pos : (OPJ_OFF_T)-1;
|
||||
/* Explicitly write zeros */
|
||||
buffer = calloc(p_nb_bytes,1);
|
||||
if (!buffer) {
|
||||
return (OPJ_OFF_T)-1;
|
||||
}
|
||||
|
||||
result = _imaging_write_pyFd(state->fd, buffer, p_nb_bytes);
|
||||
|
||||
free(buffer);
|
||||
|
||||
return result ? result : p_nb_bytes;
|
||||
}
|
||||
|
||||
static OPJ_BOOL
|
||||
j2k_seek(OPJ_OFF_T p_nb_bytes, void *p_user_data)
|
||||
{
|
||||
ImagingIncrementalCodec encoder = (ImagingIncrementalCodec)p_user_data;
|
||||
off_t pos = ImagingIncrementalCodecSeek(encoder, p_nb_bytes);
|
||||
ImagingCodecState state = (ImagingCodecState)p_user_data;
|
||||
int ignored = _imaging_seek_pyFd(state->fd, p_nb_bytes, SEEK_SET);
|
||||
off_t pos = _imaging_tell_pyFd(state->fd);
|
||||
|
||||
return pos == p_nb_bytes;
|
||||
}
|
||||
|
@ -244,8 +265,7 @@ j2k_set_cinema_params(Imaging im, int components, opj_cparameters_t *params)
|
|||
}
|
||||
|
||||
static int
|
||||
j2k_encode_entry(Imaging im, ImagingCodecState state,
|
||||
ImagingIncrementalCodec encoder)
|
||||
j2k_encode_entry(Imaging im, ImagingCodecState state)
|
||||
{
|
||||
JPEG2KENCODESTATE *context = (JPEG2KENCODESTATE *)state->context;
|
||||
opj_stream_t *stream = NULL;
|
||||
|
@ -266,11 +286,8 @@ j2k_encode_entry(Imaging im, ImagingCodecState state,
|
|||
unsigned prec = 8;
|
||||
unsigned bpp = 8;
|
||||
unsigned _overflow_scale_factor;
|
||||
/* SIZE_MAX is not working in the conditionals unless it's a typed
|
||||
variable */
|
||||
unsigned _SIZE__MAX = SIZE_MAX;
|
||||
|
||||
stream = opj_stream_default_create(OPJ_FALSE);
|
||||
stream = opj_stream_create(BUFFER_SIZE, OPJ_FALSE);
|
||||
|
||||
if (!stream) {
|
||||
state->errcode = IMAGING_CODEC_BROKEN;
|
||||
|
@ -284,9 +301,9 @@ j2k_encode_entry(Imaging im, ImagingCodecState state,
|
|||
|
||||
/* OpenJPEG 2.0 doesn't have OPJ_VERSION_MAJOR */
|
||||
#ifndef OPJ_VERSION_MAJOR
|
||||
opj_stream_set_user_data(stream, encoder);
|
||||
opj_stream_set_user_data(stream, state);
|
||||
#else
|
||||
opj_stream_set_user_data(stream, encoder, NULL);
|
||||
opj_stream_set_user_data(stream, state, NULL);
|
||||
#endif
|
||||
|
||||
/* Setup an opj_image */
|
||||
|
@ -465,6 +482,8 @@ j2k_encode_entry(Imaging im, ImagingCodecState state,
|
|||
}
|
||||
|
||||
opj_set_error_handler(codec, j2k_error, context);
|
||||
opj_set_info_handler(codec, j2k_warn, context);
|
||||
opj_set_warning_handler(codec, j2k_warn, context);
|
||||
opj_setup_encoder(codec, ¶ms, image);
|
||||
|
||||
/* Start encoding */
|
||||
|
@ -483,10 +502,10 @@ j2k_encode_entry(Imaging im, ImagingCodecState state,
|
|||
/* check for integer overflow for the malloc line, checking any expression
|
||||
that may multiply either tile_width or tile_height */
|
||||
_overflow_scale_factor = components * prec;
|
||||
if (( tile_width > _SIZE__MAX / _overflow_scale_factor ) ||
|
||||
( tile_height > _SIZE__MAX / _overflow_scale_factor ) ||
|
||||
( tile_width > _SIZE__MAX / (tile_height * _overflow_scale_factor )) ||
|
||||
( tile_height > _SIZE__MAX / (tile_width * _overflow_scale_factor ))) {
|
||||
if (( tile_width > UINT_MAX / _overflow_scale_factor ) ||
|
||||
( tile_height > UINT_MAX / _overflow_scale_factor ) ||
|
||||
( tile_width > UINT_MAX / (tile_height * _overflow_scale_factor )) ||
|
||||
( tile_height > UINT_MAX / (tile_width * _overflow_scale_factor ))) {
|
||||
state->errcode = IMAGING_CODEC_BROKEN;
|
||||
state->state = J2K_STATE_FAILED;
|
||||
goto quick_exit;
|
||||
|
@ -548,7 +567,7 @@ j2k_encode_entry(Imaging im, ImagingCodecState state,
|
|||
|
||||
state->errcode = IMAGING_CODEC_END;
|
||||
state->state = J2K_STATE_DONE;
|
||||
ret = (int)ImagingIncrementalCodecBytesInBuffer(encoder);
|
||||
ret = -1;
|
||||
|
||||
quick_exit:
|
||||
if (codec)
|
||||
|
@ -570,26 +589,13 @@ ImagingJpeg2KEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes)
|
|||
return -1;
|
||||
|
||||
if (state->state == J2K_STATE_START) {
|
||||
int seekable = (context->format != OPJ_CODEC_J2K
|
||||
? INCREMENTAL_CODEC_SEEKABLE
|
||||
: INCREMENTAL_CODEC_NOT_SEEKABLE);
|
||||
|
||||
context->encoder = ImagingIncrementalCodecCreate(j2k_encode_entry,
|
||||
im, state,
|
||||
INCREMENTAL_CODEC_WRITE,
|
||||
seekable,
|
||||
context->fd);
|
||||
|
||||
if (!context->encoder) {
|
||||
state->errcode = IMAGING_CODEC_BROKEN;
|
||||
state->state = J2K_STATE_FAILED;
|
||||
return -1;
|
||||
}
|
||||
|
||||
state->state = J2K_STATE_ENCODING;
|
||||
|
||||
return j2k_encode_entry(im, state);
|
||||
}
|
||||
|
||||
return ImagingIncrementalCodecPushBuffer(context->encoder, buf, bytes);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -600,19 +606,16 @@ int
|
|||
ImagingJpeg2KEncodeCleanup(ImagingCodecState state) {
|
||||
JPEG2KENCODESTATE *context = (JPEG2KENCODESTATE *)state->context;
|
||||
|
||||
if (context->quality_layers && context->encoder)
|
||||
Py_DECREF(context->quality_layers);
|
||||
if (context->quality_layers) {
|
||||
Py_XDECREF(context->quality_layers);
|
||||
context->quality_layers = NULL;
|
||||
}
|
||||
|
||||
if (context->error_msg)
|
||||
free ((void *)context->error_msg);
|
||||
|
||||
context->error_msg = NULL;
|
||||
|
||||
if (context->encoder)
|
||||
ImagingIncrementalCodecDestroy(context->encoder);
|
||||
|
||||
/* Prevent multiple calls to ImagingIncrementalCodecDestroy */
|
||||
context->encoder = NULL;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
|
4
setup.py
4
setup.py
|
@ -35,8 +35,8 @@ _LIB_IMAGING = (
|
|||
"QuantHash", "QuantHeap", "PcdDecode", "PcxDecode", "PcxEncode", "Point",
|
||||
"RankFilter", "RawDecode", "RawEncode", "Storage", "SunRleDecode",
|
||||
"TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask", "XbmDecode",
|
||||
"XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode", "Incremental",
|
||||
"Jpeg2KDecode", "Jpeg2KEncode", "BoxBlur", "QuantPngQuant")
|
||||
"XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode", "Jpeg2KDecode",
|
||||
"Jpeg2KEncode", "BoxBlur", "QuantPngQuant")
|
||||
|
||||
DEBUG = False
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user