Merge pull request #1934 from wiredfool/incremental_removal

Rewrite of Jpeg2k data handling
This commit is contained in:
wiredfool 2016-06-30 12:28:10 +01:00 committed by GitHub
commit a5dde79068
13 changed files with 465 additions and 850 deletions

View File

@ -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:]
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)
l,s = 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()

View File

@ -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:

View File

@ -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,7 @@ PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args)
int layers = 0;
int fd = -1;
PY_LONG_LONG length = -1;
if (!PyArg_ParseTuple(args, "ss|iiiL", &mode, &format,
&reduce, &layers, &fd, &length))
return NULL;
@ -829,11 +869,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 +884,4 @@ PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args)
return (PyObject*) decoder;
}
#endif /* HAVE_OPENJPEG */

View File

@ -1,15 +1,27 @@
.. _file-decoders:
.. _image-plugins:
Writing Your Own File Decoder
Writing Your Own Image Plugin
=============================
The Python Imaging Library uses a plug-in model which allows you to
add your own decoders to the library, without any changes to the
library itself. Such plug-ins usually have names like
The Pillow uses a plug-in model which allows you to add your own
decoders to the library, without any changes to the library
itself. Such plug-ins usually have names like
:file:`XxxImagePlugin.py`, where ``Xxx`` is a unique format name
(usually an abbreviation).
.. warning:: Pillow >= 2.1.0 no longer automatically imports any file in the Python path with a name ending in :file:`ImagePlugin.py`. You will need to import your decoder manually.
.. warning:: Pillow >= 2.1.0 no longer automatically imports any file in the Python path with a name ending in :file:`ImagePlugin.py`. You will need to import your image plugin manually.
Pillow decodes files in 2 stages:
1. It loops over the available image plugins in the loaded order, and
calls the plugin's ``accept` function with the first 16 bytes of
the file. If the ``accept`` function returns true, the plugin's
``_open`` method is called to set up the image metadata and image
tiles. The ``_open`` method is not for decoding the actual image
data.
2. When the image data is requested, the ``ImageFile.load`` method is
called, which sets up a decoder for each tile and feeds the data to
it.
A decoder plug-in should contain a decoder class, based on the
:py:class:`PIL.ImageFile.ImageFile` base class. This class should provide an
@ -22,6 +34,11 @@ call to the :py:mod:`~PIL.Image` module.
For performance reasons, it is important that the :py:meth:`_open` method
quickly rejects files that do not have the appropriate contents.
The ``raw`` decoder is useful for uncompressed image formats, but many
formats require more control of the decoding context, either with a
decoder written in ``C`` or by linking in an external library to do
the decoding. (Examples of this include PNG, Tiff, and Jpeg support)
Example
-------
@ -74,11 +91,13 @@ true color.
Image.register_extension(SpamImageFile.format, ".spam")
Image.register_extension(SpamImageFile.format, ".spa") # dos version
The format handler must always set the :py:attr:`~PIL.Image.Image.size` and
:py:attr:`~PIL.Image.Image.mode` attributes. If these are not set, the file
cannot be opened. To simplify the decoder, the calling code considers
exceptions like :py:exc:`SyntaxError`, :py:exc:`KeyError`, and
:py:exc:`IndexError`, as a failure to identify the file.
The format handler must always set the
:py:attr:`~PIL.Image.Image.size` and :py:attr:`~PIL.Image.Image.mode`
attributes. If these are not set, the file cannot be opened. To
simplify the decoder, the calling code considers exceptions like
:py:exc:`SyntaxError`, :py:exc:`KeyError`, :py:exc:`IndexError`,
:py:exc:`EOFError` and :py:exc:`struct.error` as a failure to identify
the file.
Note that the decoder must be explicitly registered using
:py:func:`PIL.Image.register_open`. Although not required, it is also a good
@ -282,3 +301,93 @@ The fields are used as follows:
**orientation**
Whether the first line in the image is the top line on the screen (1), or
the bottom line (-1). If omitted, the orientation defaults to 1.
.. _file-decoders:
Writing Your Own File Decoder
=============================
There are 3 stages in a file decoder's lifetime:
1. Setup: Pillow looks for a function named ``[decodername]_decoder``
on the internal core image object. That function is called with the ``args`` tuple
from the ``tile`` setup in the ``_open`` method.
2. Decoding: The decoder's decode function is repeadedly called with
chunks of image data.
3. Cleanup: If the decoder has registered a cleanup function, it will
be called at the end of the decoding process, even if there was an
exception raised.
Setup
-----
The current conventions are that the decoder setup function is named
``PyImaging_[Decodername]DecoderNew`` and defined in ``decode.c``. The
python binding for it is named ``[decodername]_decoder`` and is setup
from within the ``_imaging.c`` file in the codecs section of the
function array.
The setup function needs to call ``PyImaging_DecoderNew`` and at the
very least, set the ``decode`` function pointer. The fields of
interest in this object are:
**decode**
Function pointer to the decode function, which has access to
``im``, ``state``, and the buffer of data to be added to the image.
**cleanup**
Function pointer to the cleanup function, has access to ``state``.
**im**
The target image, will be set by Pillow.
**state**
An ImagingCodecStateInstance, will be set by Pillow. The **context**
member is an opaque struct that can be used by the decoder to store
any format spefific state or options.
**handles_eof**
UNDONE, set if your code handles EOF errors.
**pulls_fd**
**EXPERIMENTAL** -- **WARNING**, interface may change. If set to 1,
``state->fd`` will be a pointer to the Python file like object. The
decoder may use the functions in ``codec_fd.c`` to read directly
from the file like object rather than have the data pushed through a
buffer. Note that this implementation may be refactored until this
warning is removed.
.. versionadded:: 3.3.0
Decoding
--------
The decode function is called with the target (core) image, the
decoder state structure, and a buffer of data to be decoded.
**Experimental** -- If ``pulls_fd`` is set, then the decode function
is called once, with an empty buffer. It is the decoder's
responibility to decode the entire tile in that one call. The rest of
this secton only applies if ``pulls_fd`` is not set.
It is the decoder's responsibility to pull as much data as possible
out of the buffer and return the number of bytes consumed. The next
call to the decoder will include the previous unconsumed tail. The
decoder function will be called multiple times as the data is read
from the file like object.
If an error occurs, set ``state->errcode`` and return -1.
Return -1 on success, without setting the errcode.
Cleanup
-------
The cleanup function is called after the decoder returns a negative
value, or if there is a read error from the file. This function should
free any allocated memory and release any resources from external
libraries.

View File

@ -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,

View File

@ -486,33 +486,18 @@ 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);
/* Codec read/write python fd */
enum {
INCREMENTAL_CODEC_READ = 1,
INCREMENTAL_CODEC_WRITE = 2
};
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_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

View File

@ -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);
}

View File

@ -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;
/*

View File

@ -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,22 @@ j2k_decode_entry(Imaging im, ImagingCodecState state,
size_t buffer_size = 0;
unsigned n;
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;
}
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 +750,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);
@ -763,28 +770,28 @@ j2k_decode_entry(Imaging im, ImagingCodecState state,
int
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;
}
/* -------------------------------------------------------------------- */
@ -795,17 +802,12 @@ int
ImagingJpeg2KDecodeCleanup(ImagingCodecState state) {
JPEG2KDECODESTATE *context = (JPEG2KDECODESTATE *)state->context;
if (context->error_msg)
if (context->error_msg) {
free ((void *)context->error_msg);
if (context->decoder)
ImagingIncrementalCodecDestroy(context->decoder);
}
context->error_msg = NULL;
/* Prevent multiple calls to ImagingIncrementalCodecDestroy */
context->decoder = NULL;
return -1;
}

View File

@ -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,43 @@ 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;
off_t pos = 0;
_imaging_seek_pyFd(state->fd, p_nb_bytes, SEEK_SET);
pos = _imaging_tell_pyFd(state->fd);
return pos == p_nb_bytes;
}
@ -244,8 +267,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 +288,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 +303,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 +484,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, &params, image);
/* Start encoding */
@ -483,10 +504,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 +569,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)
@ -564,32 +585,17 @@ j2k_encode_entry(Imaging im, ImagingCodecState state,
int
ImagingJpeg2KEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes)
{
JPEG2KENCODESTATE *context = (JPEG2KENCODESTATE *)state->context;
if (state->state == J2K_STATE_FAILED)
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;
}

79
libImaging/codec_fd.c Normal file
View File

@ -0,0 +1,79 @@
#include "Python.h"
#include "Imaging.h"
#include "../py3.h"
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;
}
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;
}
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;
}

1
py3.h
View File

@ -19,6 +19,7 @@
#define PyInt_FromLong PyLong_FromLong
#define PyInt_AS_LONG PyLong_AS_LONG
#define PyInt_FromSsize_t PyLong_FromSsize_t
#define PyInt_AsSsize_t PyLong_AsSsize_t
#else /* PY_VERSION_HEX < 0x03000000 */
#define PY_ARG_BYTES_LENGTH "s#"

View File

@ -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", "codec_fd")
DEBUG = False