mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-12 10:16:17 +03:00
Add support for reading animated WebP files
This commit is contained in:
parent
6e4766155d
commit
482d803717
|
@ -28,24 +28,134 @@ class WebPImageFile(ImageFile.ImageFile):
|
||||||
format_description = "WebP image"
|
format_description = "WebP image"
|
||||||
|
|
||||||
def _open(self):
|
def _open(self):
|
||||||
data, width, height, self.mode, icc_profile, exif = \
|
if not _webp.HAVE_WEBPMUX:
|
||||||
_webp.WebPDecode(self.fp.read())
|
# Legacy mode
|
||||||
|
data, width, height, self.mode = _webp.WebPDecode(self.fp.read())
|
||||||
|
self.size = width, height
|
||||||
|
self.fp = BytesIO(data)
|
||||||
|
self.tile = [("raw", (0, 0) + self.size, 0, self.mode)]
|
||||||
|
return
|
||||||
|
|
||||||
|
# Use the newer AnimDecoder API to parse the (possibly) animated file,
|
||||||
|
# and access muxed chunks like ICC/EXIF/XMP.
|
||||||
|
self._decoder = _webp.WebPAnimDecoder(self.fp.read())
|
||||||
|
|
||||||
|
# Get info from decoder
|
||||||
|
width, height, loop_count, bgcolor, frame_count, mode = self._decoder.get_info()
|
||||||
|
self.size = width, height
|
||||||
|
self.info["loop"] = loop_count
|
||||||
|
bg_a, bg_r, bg_g, bg_b = \
|
||||||
|
(bgcolor >> 24) & 0xFF, \
|
||||||
|
(bgcolor >> 16) & 0xFF, \
|
||||||
|
(bgcolor >> 8) & 0xFF, \
|
||||||
|
bgcolor & 0xFF
|
||||||
|
self.info["background"] = (bg_r, bg_g, bg_b, bg_a)
|
||||||
|
self._n_frames = frame_count
|
||||||
|
self.mode = mode
|
||||||
|
self.tile = []
|
||||||
|
|
||||||
|
# Attempt to read ICC / EXIF / XMP chunks from file
|
||||||
|
icc_profile = self._decoder.get_chunk("ICCP")
|
||||||
|
exif = self._decoder.get_chunk("EXIF")
|
||||||
|
xmp = self._decoder.get_chunk("XMP ")
|
||||||
if icc_profile:
|
if icc_profile:
|
||||||
self.info["icc_profile"] = icc_profile
|
self.info["icc_profile"] = icc_profile
|
||||||
if exif:
|
if exif:
|
||||||
self.info["exif"] = exif
|
self.info["exif"] = exif
|
||||||
|
if xmp:
|
||||||
|
self.info["xmp"] = xmp
|
||||||
|
|
||||||
self.size = width, height
|
# Initialize seek state
|
||||||
self.fp = BytesIO(data)
|
self._reset(reset=False)
|
||||||
self.tile = [("raw", (0, 0) + self.size, 0, self.mode)]
|
self.seek(0)
|
||||||
|
|
||||||
def _getexif(self):
|
def _getexif(self):
|
||||||
from .JpegImagePlugin import _getexif
|
from .JpegImagePlugin import _getexif
|
||||||
return _getexif(self)
|
return _getexif(self)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def n_frames(self):
|
||||||
|
if not _webp.HAVE_WEBPMUX:
|
||||||
|
return 1
|
||||||
|
return self._n_frames
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_animated(self):
|
||||||
|
if not _webp.HAVE_WEBPMUX:
|
||||||
|
return False
|
||||||
|
return self._n_frames > 1
|
||||||
|
|
||||||
|
def seek(self, frame):
|
||||||
|
# Perform some simple checks first
|
||||||
|
if frame >= self._n_frames:
|
||||||
|
raise EOFError("attempted to seek beyond end of sequence")
|
||||||
|
if frame < 0:
|
||||||
|
raise EOFError("negative frame index is not valid")
|
||||||
|
|
||||||
|
# Set logical frame to requested position
|
||||||
|
self.__logical_frame = frame
|
||||||
|
|
||||||
|
def _reset(self, reset=True):
|
||||||
|
if reset:
|
||||||
|
self._decoder.reset()
|
||||||
|
self.__physical_frame = 0
|
||||||
|
self.__loaded = -1
|
||||||
|
self.__timestamp = 0
|
||||||
|
|
||||||
|
def _get_next(self):
|
||||||
|
# Get next frame
|
||||||
|
ret = self._decoder.get_next()
|
||||||
|
self.__physical_frame += 1
|
||||||
|
|
||||||
|
# Check if an error occurred
|
||||||
|
if ret is None:
|
||||||
|
self._reset() # Reset just to be safe
|
||||||
|
self.seek(0)
|
||||||
|
raise EOFError("failed to decode next frame in WebP file")
|
||||||
|
|
||||||
|
# Compute duration
|
||||||
|
data, timestamp = ret
|
||||||
|
duration = timestamp - self.__timestamp
|
||||||
|
self.__timestamp = timestamp
|
||||||
|
timestamp -= duration # libwebp gives frame end, adjust to start of frame
|
||||||
|
return data, timestamp, duration
|
||||||
|
|
||||||
|
def _seek(self, frame):
|
||||||
|
if self.__physical_frame == frame:
|
||||||
|
return # Nothing to do
|
||||||
|
|
||||||
|
if frame < self.__physical_frame:
|
||||||
|
# Rewind to beginning
|
||||||
|
self._reset()
|
||||||
|
|
||||||
|
# Advance to the requested frame
|
||||||
|
while self.__physical_frame < frame:
|
||||||
|
self._get_next()
|
||||||
|
|
||||||
|
def load(self):
|
||||||
|
if self.__loaded != self.__logical_frame:
|
||||||
|
self._seek(self.__logical_frame)
|
||||||
|
|
||||||
|
# We need to load the image data for this frame
|
||||||
|
data, timestamp, duration = self._get_next()
|
||||||
|
self.info["timestamp"] = timestamp
|
||||||
|
self.info["duration"] = duration
|
||||||
|
self.__loaded = self.__logical_frame
|
||||||
|
|
||||||
|
# Set tile
|
||||||
|
self.fp = BytesIO(data)
|
||||||
|
self.tile = [("raw", (0, 0) + self.size, 0, self.mode)]
|
||||||
|
|
||||||
|
return super(WebPImageFile, self).load()
|
||||||
|
|
||||||
|
def tell(self):
|
||||||
|
return self.__logical_frame
|
||||||
|
|
||||||
def _save_all(im, fp, filename):
|
def _save_all(im, fp, filename):
|
||||||
|
if not _webp.HAVE_WEBPMUX:
|
||||||
|
_save(im, fp, filename)
|
||||||
|
return
|
||||||
|
|
||||||
encoderinfo = im.encoderinfo.copy()
|
encoderinfo = im.encoderinfo.copy()
|
||||||
append_images = encoderinfo.get("append_images", [])
|
append_images = encoderinfo.get("append_images", [])
|
||||||
background = encoderinfo.get("background", (0, 0, 0, 0))
|
background = encoderinfo.get("background", (0, 0, 0, 0))
|
||||||
|
@ -105,7 +215,10 @@ def _save_all(im, fp, filename):
|
||||||
ims.load()
|
ims.load()
|
||||||
|
|
||||||
# Make sure image mode is supported
|
# Make sure image mode is supported
|
||||||
frame = ims if ims.mode in _VALID_WEBP_MODES else ims.convert("RGBA")
|
frame = ims
|
||||||
|
if not ims.mode in _VALID_WEBP_MODES:
|
||||||
|
alpha = 'A' in ims.im.getpalettemode()
|
||||||
|
frame = image.convert('RGBA' if alpha else 'RGB')
|
||||||
|
|
||||||
# Append the frame to the animation encoder
|
# Append the frame to the animation encoder
|
||||||
enc.add(
|
enc.add(
|
||||||
|
@ -139,11 +252,15 @@ def _save_all(im, fp, filename):
|
||||||
|
|
||||||
fp.write(data)
|
fp.write(data)
|
||||||
|
|
||||||
|
|
||||||
def _save(im, fp, filename):
|
def _save(im, fp, filename):
|
||||||
|
if _webp.HAVE_WEBPMUX:
|
||||||
|
_save_all(im, fp, filename)
|
||||||
|
return
|
||||||
|
|
||||||
image_mode = im.mode
|
image_mode = im.mode
|
||||||
if im.mode not in _VALID_WEBP_MODES:
|
if im.mode not in _VALID_WEBP_MODES:
|
||||||
im = im.convert("RGBA")
|
alpha = 'A' in im.im.getpalettemode()
|
||||||
|
im = im.convert('RGBA' if alpha else 'RGB')
|
||||||
|
|
||||||
lossless = im.encoderinfo.get("lossless", False)
|
lossless = im.encoderinfo.get("lossless", False)
|
||||||
quality = im.encoderinfo.get("quality", 80)
|
quality = im.encoderinfo.get("quality", 80)
|
||||||
|
|
273
_webp.c
273
_webp.c
|
@ -8,13 +8,13 @@
|
||||||
|
|
||||||
#ifdef HAVE_WEBPMUX
|
#ifdef HAVE_WEBPMUX
|
||||||
#include <webp/mux.h>
|
#include <webp/mux.h>
|
||||||
#endif
|
#include <webp/demux.h>
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
/* WebP Animation Support */
|
/* WebP Animation Support */
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
#ifdef HAVE_WEBPMUX
|
|
||||||
|
|
||||||
|
// Encoder type
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
WebPAnimEncoder* enc;
|
WebPAnimEncoder* enc;
|
||||||
|
@ -23,6 +23,17 @@ typedef struct {
|
||||||
|
|
||||||
static PyTypeObject WebPAnimEncoder_Type;
|
static PyTypeObject WebPAnimEncoder_Type;
|
||||||
|
|
||||||
|
// Decoder type
|
||||||
|
typedef struct {
|
||||||
|
PyObject_HEAD
|
||||||
|
WebPAnimDecoder* dec;
|
||||||
|
WebPAnimInfo info;
|
||||||
|
WebPData data;
|
||||||
|
} WebPAnimDecoderObject;
|
||||||
|
|
||||||
|
static PyTypeObject WebPAnimDecoder_Type;
|
||||||
|
|
||||||
|
// Encoder functions
|
||||||
PyObject* _anim_encoder_new(PyObject* self, PyObject* args)
|
PyObject* _anim_encoder_new(PyObject* self, PyObject* args)
|
||||||
{
|
{
|
||||||
int width, height;
|
int width, height;
|
||||||
|
@ -177,10 +188,207 @@ PyObject* _anim_encoder_assemble(PyObject* self, PyObject* args)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
// Decoder functions
|
||||||
|
PyObject* _anim_decoder_new(PyObject* self, PyObject* args)
|
||||||
|
{
|
||||||
|
PyBytesObject *webp_string;
|
||||||
|
const uint8_t *webp;
|
||||||
|
Py_ssize_t size;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "S", &webp_string)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyBytes_AsStringAndSize((PyObject *) webp_string, (char**)&webp, &size);
|
||||||
|
WebPData webp_src = {webp, size};
|
||||||
|
|
||||||
|
// Create the decoder (default mode is RGBA, if no options passed)
|
||||||
|
WebPAnimDecoderObject* decp;
|
||||||
|
decp = PyObject_New(WebPAnimDecoderObject, &WebPAnimDecoder_Type);
|
||||||
|
if (decp) {
|
||||||
|
if (WebPDataCopy(&webp_src, &(decp->data))) {
|
||||||
|
WebPAnimDecoder* dec = WebPAnimDecoderNew(&(decp->data), NULL);
|
||||||
|
if (dec) {
|
||||||
|
if (WebPAnimDecoderGetInfo(dec, &(decp->info))) {
|
||||||
|
decp->dec = dec;
|
||||||
|
return (PyObject*) decp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PyObject_Del(decp);
|
||||||
|
}
|
||||||
|
fprintf(stderr, "Error! Could not create decoder object.\n");
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* _anim_decoder_dealloc(PyObject* self)
|
||||||
|
{
|
||||||
|
WebPAnimDecoderObject* decp = (WebPAnimDecoderObject *)self;
|
||||||
|
WebPDataClear(&(decp->data));
|
||||||
|
WebPAnimDecoderDelete(decp->dec);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* _anim_decoder_get_info(PyObject* self, PyObject* args)
|
||||||
|
{
|
||||||
|
WebPAnimDecoderObject* decp = (WebPAnimDecoderObject *)self;
|
||||||
|
WebPAnimInfo* info = &(decp->info);
|
||||||
|
return Py_BuildValue("IIIIIs",
|
||||||
|
info->canvas_width, info->canvas_height,
|
||||||
|
info->loop_count,
|
||||||
|
info->bgcolor,
|
||||||
|
info->frame_count,
|
||||||
|
"RGBA" // WebPAnimDecoder defaults to RGBA if no mode is specified
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* _anim_decoder_get_chunk(PyObject* self, PyObject* args)
|
||||||
|
{
|
||||||
|
char *mode;
|
||||||
|
PyObject *ret;
|
||||||
|
WebPChunkIterator iter;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "s", &mode)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
WebPAnimDecoderObject* decp = (WebPAnimDecoderObject *)self;
|
||||||
|
const WebPDemuxer* demux = WebPAnimDecoderGetDemuxer(decp->dec);
|
||||||
|
if (!WebPDemuxGetChunk(demux, mode, 1, &iter)) {
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = PyBytes_FromStringAndSize((const char*)iter.chunk.bytes, iter.chunk.size);
|
||||||
|
WebPDemuxReleaseChunkIterator(&iter);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* _anim_decoder_get_next(PyObject* self, PyObject* args)
|
||||||
|
{
|
||||||
|
uint8_t* buf;
|
||||||
|
int timestamp;
|
||||||
|
PyObject *bytes;
|
||||||
|
|
||||||
|
WebPAnimDecoderObject* decp = (WebPAnimDecoderObject *)self;
|
||||||
|
if (!WebPAnimDecoderGetNext(decp->dec, &buf, ×tamp)) {
|
||||||
|
fprintf(stderr, "Error! Failed to read next frame.\n");
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = PyBytes_FromStringAndSize((char *)buf,
|
||||||
|
decp->info.canvas_width * 4 * decp->info.canvas_height);
|
||||||
|
return Py_BuildValue("Si", bytes, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* _anim_decoder_has_more_frames(PyObject* self, PyObject* args)
|
||||||
|
{
|
||||||
|
WebPAnimDecoderObject* decp = (WebPAnimDecoderObject *)self;
|
||||||
|
return Py_BuildValue("i", WebPAnimDecoderHasMoreFrames(decp->dec));
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* _anim_decoder_reset(PyObject* self, PyObject* args)
|
||||||
|
{
|
||||||
|
WebPAnimDecoderObject* decp = (WebPAnimDecoderObject *)self;
|
||||||
|
WebPAnimDecoderReset(decp->dec);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
/* WebP Single-Frame Support */
|
/* Type Definitions */
|
||||||
|
/* -------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
// WebPAnimEncoder methods
|
||||||
|
static struct PyMethodDef _anim_encoder_methods[] = {
|
||||||
|
{"add", (PyCFunction)_anim_encoder_add, METH_VARARGS, "add"},
|
||||||
|
{"assemble", (PyCFunction)_anim_encoder_assemble, METH_VARARGS, "assemble"},
|
||||||
|
{NULL, NULL} /* sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
|
// WebPAnimDecoder type definition
|
||||||
|
static PyTypeObject WebPAnimEncoder_Type = {
|
||||||
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
|
"WebPAnimEncoder", /*tp_name */
|
||||||
|
sizeof(WebPAnimEncoderObject), /*tp_size */
|
||||||
|
0, /*tp_itemsize */
|
||||||
|
/* methods */
|
||||||
|
(destructor)_anim_encoder_dealloc, /*tp_dealloc*/
|
||||||
|
0, /*tp_print*/
|
||||||
|
0, /*tp_getattr*/
|
||||||
|
0, /*tp_setattr*/
|
||||||
|
0, /*tp_compare*/
|
||||||
|
0, /*tp_repr*/
|
||||||
|
0, /*tp_as_number */
|
||||||
|
0, /*tp_as_sequence */
|
||||||
|
0, /*tp_as_mapping */
|
||||||
|
0, /*tp_hash*/
|
||||||
|
0, /*tp_call*/
|
||||||
|
0, /*tp_str*/
|
||||||
|
0, /*tp_getattro*/
|
||||||
|
0, /*tp_setattro*/
|
||||||
|
0, /*tp_as_buffer*/
|
||||||
|
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
||||||
|
0, /*tp_doc*/
|
||||||
|
0, /*tp_traverse*/
|
||||||
|
0, /*tp_clear*/
|
||||||
|
0, /*tp_richcompare*/
|
||||||
|
0, /*tp_weaklistoffset*/
|
||||||
|
0, /*tp_iter*/
|
||||||
|
0, /*tp_iternext*/
|
||||||
|
_anim_encoder_methods, /*tp_methods*/
|
||||||
|
0, /*tp_members*/
|
||||||
|
0, /*tp_getset*/
|
||||||
|
};
|
||||||
|
|
||||||
|
// WebPAnimDecoder methods
|
||||||
|
static struct PyMethodDef _anim_decoder_methods[] = {
|
||||||
|
{"get_info", (PyCFunction)_anim_decoder_get_info, METH_VARARGS, "get_info"},
|
||||||
|
{"get_chunk", (PyCFunction)_anim_decoder_get_chunk, METH_VARARGS, "get_chunk"},
|
||||||
|
{"get_next", (PyCFunction)_anim_decoder_get_next, METH_VARARGS, "get_next"},
|
||||||
|
{"has_more_frames", (PyCFunction)_anim_decoder_has_more_frames, METH_VARARGS, "has_more_frames"},
|
||||||
|
{"reset", (PyCFunction)_anim_decoder_reset, METH_VARARGS, "reset"},
|
||||||
|
{NULL, NULL} /* sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
|
// WebPAnimDecoder type definition
|
||||||
|
static PyTypeObject WebPAnimDecoder_Type = {
|
||||||
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
|
"WebPAnimDecoder", /*tp_name */
|
||||||
|
sizeof(WebPAnimDecoderObject), /*tp_size */
|
||||||
|
0, /*tp_itemsize */
|
||||||
|
/* methods */
|
||||||
|
(destructor)_anim_decoder_dealloc, /*tp_dealloc*/
|
||||||
|
0, /*tp_print*/
|
||||||
|
0, /*tp_getattr*/
|
||||||
|
0, /*tp_setattr*/
|
||||||
|
0, /*tp_compare*/
|
||||||
|
0, /*tp_repr*/
|
||||||
|
0, /*tp_as_number */
|
||||||
|
0, /*tp_as_sequence */
|
||||||
|
0, /*tp_as_mapping */
|
||||||
|
0, /*tp_hash*/
|
||||||
|
0, /*tp_call*/
|
||||||
|
0, /*tp_str*/
|
||||||
|
0, /*tp_getattro*/
|
||||||
|
0, /*tp_setattro*/
|
||||||
|
0, /*tp_as_buffer*/
|
||||||
|
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
||||||
|
0, /*tp_doc*/
|
||||||
|
0, /*tp_traverse*/
|
||||||
|
0, /*tp_clear*/
|
||||||
|
0, /*tp_richcompare*/
|
||||||
|
0, /*tp_weaklistoffset*/
|
||||||
|
0, /*tp_iter*/
|
||||||
|
0, /*tp_iternext*/
|
||||||
|
_anim_decoder_methods, /*tp_methods*/
|
||||||
|
0, /*tp_members*/
|
||||||
|
0, /*tp_getset*/
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_WEBPMUX
|
||||||
|
/* -------------------------------------------------------------------- */
|
||||||
|
/* Legacy WebP Support */
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
|
|
||||||
PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args)
|
PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args)
|
||||||
|
@ -410,6 +618,8 @@ end:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
// Return the decoder's version number, packed in hexadecimal using 8bits for
|
// Return the decoder's version number, packed in hexadecimal using 8bits for
|
||||||
// each of major/minor/revision. E.g: v2.5.7 is 0x020507.
|
// each of major/minor/revision. E.g: v2.5.7 is 0x020507.
|
||||||
PyObject* WebPDecoderVersion_wrapper(PyObject* self, PyObject* args){
|
PyObject* WebPDecoderVersion_wrapper(PyObject* self, PyObject* args){
|
||||||
|
@ -428,54 +638,6 @@ PyObject* WebPDecoderBuggyAlpha_wrapper(PyObject* self, PyObject* args){
|
||||||
return Py_BuildValue("i", WebPDecoderBuggyAlpha());
|
return Py_BuildValue("i", WebPDecoderBuggyAlpha());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
|
||||||
/* WebPAnimEncoder Type */
|
|
||||||
/* -------------------------------------------------------------------- */
|
|
||||||
#ifdef HAVE_WEBPMUX
|
|
||||||
|
|
||||||
static struct PyMethodDef _anim_encoder_methods[] = {
|
|
||||||
{"add", (PyCFunction)_anim_encoder_add, 1},
|
|
||||||
{"assemble", (PyCFunction)_anim_encoder_assemble, 1},
|
|
||||||
{NULL, NULL} /* sentinel */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* type description */
|
|
||||||
static PyTypeObject WebPAnimEncoder_Type = {
|
|
||||||
PyVarObject_HEAD_INIT(NULL, 0)
|
|
||||||
"WebPAnimEncoder", /*tp_name */
|
|
||||||
sizeof(WebPAnimEncoderObject), /*tp_size */
|
|
||||||
0, /*tp_itemsize */
|
|
||||||
/* methods */
|
|
||||||
(destructor)_anim_encoder_dealloc, /*tp_dealloc*/
|
|
||||||
0, /*tp_print*/
|
|
||||||
0, /*tp_getattr*/
|
|
||||||
0, /*tp_setattr*/
|
|
||||||
0, /*tp_compare*/
|
|
||||||
0, /*tp_repr*/
|
|
||||||
0, /*tp_as_number */
|
|
||||||
0, /*tp_as_sequence */
|
|
||||||
0, /*tp_as_mapping */
|
|
||||||
0, /*tp_hash*/
|
|
||||||
0, /*tp_call*/
|
|
||||||
0, /*tp_str*/
|
|
||||||
0, /*tp_getattro*/
|
|
||||||
0, /*tp_setattro*/
|
|
||||||
0, /*tp_as_buffer*/
|
|
||||||
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
|
||||||
0, /*tp_doc*/
|
|
||||||
0, /*tp_traverse*/
|
|
||||||
0, /*tp_clear*/
|
|
||||||
0, /*tp_richcompare*/
|
|
||||||
0, /*tp_weaklistoffset*/
|
|
||||||
0, /*tp_iter*/
|
|
||||||
0, /*tp_iternext*/
|
|
||||||
_anim_encoder_methods, /*tp_methods*/
|
|
||||||
0, /*tp_members*/
|
|
||||||
0, /*tp_getset*/
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
/* Module Setup */
|
/* Module Setup */
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
|
@ -483,10 +645,12 @@ static PyTypeObject WebPAnimEncoder_Type = {
|
||||||
static PyMethodDef webpMethods[] =
|
static PyMethodDef webpMethods[] =
|
||||||
{
|
{
|
||||||
#ifdef HAVE_WEBPMUX
|
#ifdef HAVE_WEBPMUX
|
||||||
|
{"WebPAnimDecoder", _anim_decoder_new, METH_VARARGS, "WebPAnimDecoder"},
|
||||||
{"WebPAnimEncoder", _anim_encoder_new, METH_VARARGS, "WebPAnimEncoder"},
|
{"WebPAnimEncoder", _anim_encoder_new, METH_VARARGS, "WebPAnimEncoder"},
|
||||||
#endif
|
#else
|
||||||
{"WebPEncode", WebPEncode_wrapper, METH_VARARGS, "WebPEncode"},
|
{"WebPEncode", WebPEncode_wrapper, METH_VARARGS, "WebPEncode"},
|
||||||
{"WebPDecode", WebPDecode_wrapper, METH_VARARGS, "WebPDecode"},
|
{"WebPDecode", WebPDecode_wrapper, METH_VARARGS, "WebPDecode"},
|
||||||
|
#endif
|
||||||
{"WebPDecoderVersion", WebPDecoderVersion_wrapper, METH_VARARGS, "WebPVersion"},
|
{"WebPDecoderVersion", WebPDecoderVersion_wrapper, METH_VARARGS, "WebPVersion"},
|
||||||
{"WebPDecoderBuggyAlpha", WebPDecoderBuggyAlpha_wrapper, METH_VARARGS, "WebPDecoderBuggyAlpha"},
|
{"WebPDecoderBuggyAlpha", WebPDecoderBuggyAlpha_wrapper, METH_VARARGS, "WebPDecoderBuggyAlpha"},
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
|
@ -511,7 +675,8 @@ static int setup_module(PyObject* m) {
|
||||||
|
|
||||||
#ifdef HAVE_WEBPMUX
|
#ifdef HAVE_WEBPMUX
|
||||||
/* Ready object types */
|
/* Ready object types */
|
||||||
if (PyType_Ready(&WebPAnimEncoder_Type) < 0)
|
if (PyType_Ready(&WebPAnimDecoder_Type) < 0 ||
|
||||||
|
PyType_Ready(&WebPAnimEncoder_Type) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user