mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-24 00:46:16 +03:00
- Conditonally compile animation support, only if the mux.h and demux.h headers meet the ABI version requirements
- Add WEBPMUX support back to WebPDecode_wrapper (to support older versions of libwebp that have mux support, but not animation) - Add HAVE_WEBPANIM flag, and use it appropriately - Update documentation / tests
This commit is contained in:
parent
e534991409
commit
c18d26b04b
|
@ -33,7 +33,7 @@ class WebPImageFile(ImageFile.ImageFile):
|
|||
format_description = "WebP image"
|
||||
|
||||
def _open(self):
|
||||
if not _webp.HAVE_WEBPMUX:
|
||||
if not _webp.HAVE_WEBPANIM:
|
||||
# Legacy mode
|
||||
data, width, height, self.mode = _webp.WebPDecode(self.fp.read())
|
||||
self.size = width, height
|
||||
|
@ -80,18 +80,18 @@ class WebPImageFile(ImageFile.ImageFile):
|
|||
|
||||
@property
|
||||
def n_frames(self):
|
||||
if not _webp.HAVE_WEBPMUX:
|
||||
if not _webp.HAVE_WEBPANIM:
|
||||
return 1
|
||||
return self._n_frames
|
||||
|
||||
@property
|
||||
def is_animated(self):
|
||||
if not _webp.HAVE_WEBPMUX:
|
||||
if not _webp.HAVE_WEBPANIM:
|
||||
return False
|
||||
return self._n_frames > 1
|
||||
|
||||
def seek(self, frame):
|
||||
if not _webp.HAVE_WEBPMUX:
|
||||
if not _webp.HAVE_WEBPANIM:
|
||||
return super(WebPImageFile, self).seek(frame)
|
||||
|
||||
# Perform some simple checks first
|
||||
|
@ -141,7 +141,7 @@ class WebPImageFile(ImageFile.ImageFile):
|
|||
self._get_next()
|
||||
|
||||
def load(self):
|
||||
if _webp.HAVE_WEBPMUX:
|
||||
if _webp.HAVE_WEBPANIM:
|
||||
if self.__loaded != self.__logical_frame:
|
||||
self._seek(self.__logical_frame)
|
||||
|
||||
|
@ -158,7 +158,7 @@ class WebPImageFile(ImageFile.ImageFile):
|
|||
return super(WebPImageFile, self).load()
|
||||
|
||||
def tell(self):
|
||||
if not _webp.HAVE_WEBPMUX:
|
||||
if not _webp.HAVE_WEBPANIM:
|
||||
return super(WebPImageFile, self).tell()
|
||||
|
||||
return self.__logical_frame
|
||||
|
@ -301,7 +301,7 @@ def _save(im, fp, filename):
|
|||
|
||||
Image.register_open(WebPImageFile.format, WebPImageFile, _accept)
|
||||
Image.register_save(WebPImageFile.format, _save)
|
||||
if _webp.HAVE_WEBPMUX:
|
||||
if _webp.HAVE_WEBPANIM:
|
||||
Image.register_save_all(WebPImageFile.format, _save_all)
|
||||
Image.register_extension(WebPImageFile.format, ".webp")
|
||||
Image.register_mime(WebPImageFile.format, "image/webp")
|
||||
|
|
|
@ -43,6 +43,7 @@ def get_supported_codecs():
|
|||
return [f for f in codecs if check_codec(f)]
|
||||
|
||||
features = {
|
||||
"webp_anim": ("PIL._webp", 'HAVE_WEBPANIM'),
|
||||
"webp_mux": ("PIL._webp", 'HAVE_WEBPMUX'),
|
||||
"transp_webp": ("PIL._webp", "HAVE_TRANSPARENCY"),
|
||||
"raqm": ("PIL._imagingft", "HAVE_RAQM")
|
||||
|
@ -53,7 +54,7 @@ def check_feature(feature):
|
|||
raise ValueError("Unknown feature %s" % feature)
|
||||
|
||||
module, flag = features[feature]
|
||||
|
||||
|
||||
try:
|
||||
imported_module = __import__(module, fromlist=['PIL'])
|
||||
return getattr(imported_module, flag)
|
||||
|
@ -75,4 +76,4 @@ def get_supported():
|
|||
ret.extend(get_supported_features())
|
||||
ret.extend(get_supported_codecs())
|
||||
return ret
|
||||
|
||||
|
||||
|
|
|
@ -35,6 +35,11 @@ class TestFeatures(PillowTestCase):
|
|||
self.assertEqual(features.check('webp_mux'),
|
||||
_webp.HAVE_WEBPMUX)
|
||||
|
||||
@unittest.skipUnless(HAVE_WEBP, True)
|
||||
def check_webp_anim(self):
|
||||
self.assertEqual(features.check('webp_anim'),
|
||||
_webp.HAVE_WEBPANIM)
|
||||
|
||||
def test_check_modules(self):
|
||||
for feature in features.modules:
|
||||
self.assertIn(features.check_module(feature), [True, False])
|
||||
|
|
|
@ -17,8 +17,8 @@ class TestFileWebpAnimation(PillowTestCase):
|
|||
except ImportError:
|
||||
self.skipTest('WebP support not installed')
|
||||
|
||||
if not _webp.HAVE_WEBPMUX:
|
||||
self.skipTest("WebP not compiled with mux support, "
|
||||
if not _webp.HAVE_WEBPANIM:
|
||||
self.skipTest("WebP library does not contain animation support, "
|
||||
"not testing animation")
|
||||
|
||||
def test_n_frames(self):
|
||||
|
|
|
@ -2,17 +2,22 @@ from helper import unittest, PillowTestCase
|
|||
|
||||
from PIL import Image
|
||||
|
||||
try:
|
||||
from PIL import _webp
|
||||
HAVE_WEBP = True
|
||||
HAVE_WEBPMUX = _webp.HAVE_WEBPMUX
|
||||
HAVE_WEBPANIM = _webp.HAVE_WEBPANIM
|
||||
except ImportError:
|
||||
HAVE_WEBP = False
|
||||
|
||||
class TestFileWebpMetadata(PillowTestCase):
|
||||
|
||||
def setUp(self):
|
||||
try:
|
||||
from PIL import _webp
|
||||
except ImportError:
|
||||
if not HAVE_WEBP:
|
||||
self.skipTest('WebP support not installed')
|
||||
return
|
||||
|
||||
if not _webp.HAVE_WEBPMUX:
|
||||
if not HAVE_WEBPMUX:
|
||||
self.skipTest('WebPMux support not installed')
|
||||
|
||||
def test_read_exif_metadata(self):
|
||||
|
@ -107,6 +112,7 @@ class TestFileWebpMetadata(PillowTestCase):
|
|||
|
||||
self.assertFalse(webp_image._getexif())
|
||||
|
||||
@unittest.skipUnless(HAVE_WEBPANIM, 'WebP animation support not available')
|
||||
def test_write_animated_metadata(self):
|
||||
iccp_data = '<iccp_data>'.encode('utf-8')
|
||||
exif_data = '<exif_data>'.encode('utf-8')
|
||||
|
|
71
_webp.c
71
_webp.c
|
@ -10,6 +10,19 @@
|
|||
#include <webp/mux.h>
|
||||
#include <webp/demux.h>
|
||||
|
||||
/*
|
||||
* Check the versions from mux.h and demux.h, to ensure the WebPAnimEncoder and
|
||||
* WebPAnimDecoder API's are present (initial support was added in 0.5.0). The
|
||||
* very early versions added had some significant differences, so we require
|
||||
* later versions, before enabling animation support.
|
||||
*/
|
||||
#if WEBP_MUX_ABI_VERSION >= 0x0104 && WEBP_DEMUX_ABI_VERSION >= 0x0105
|
||||
#define HAVE_WEBPANIM
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_WEBPANIM
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* WebP Muxer Error Codes */
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -624,13 +637,12 @@ PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args)
|
|||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
PyObject* WebPDecode_wrapper(PyObject* self, PyObject* args)
|
||||
{
|
||||
PyBytesObject* webp_string;
|
||||
const uint8_t* webp;
|
||||
Py_ssize_t size;
|
||||
PyObject *ret = Py_None, *bytes = NULL, *pymode = NULL;
|
||||
PyObject *ret = Py_None, *bytes = NULL, *pymode = NULL, *icc_profile = NULL, *exif = NULL;
|
||||
WebPDecoderConfig config;
|
||||
VP8StatusCode vp8_status_code = VP8_STATUS_OK;
|
||||
char* mode = "RGB";
|
||||
|
@ -654,7 +666,41 @@ PyObject* WebPDecode_wrapper(PyObject* self, PyObject* args)
|
|||
mode = "RGBA";
|
||||
}
|
||||
|
||||
#ifndef HAVE_WEBPMUX
|
||||
vp8_status_code = WebPDecode(webp, size, &config);
|
||||
#else
|
||||
{
|
||||
int copy_data = 0;
|
||||
WebPData data = { webp, size };
|
||||
WebPMuxFrameInfo image;
|
||||
WebPData icc_profile_data = {0};
|
||||
WebPData exif_data = {0};
|
||||
|
||||
WebPMux* mux = WebPMuxCreate(&data, copy_data);
|
||||
if (NULL == mux)
|
||||
goto end;
|
||||
|
||||
if (WEBP_MUX_OK != WebPMuxGetFrame(mux, 1, &image))
|
||||
{
|
||||
WebPMuxDelete(mux);
|
||||
goto end;
|
||||
}
|
||||
|
||||
webp = image.bitstream.bytes;
|
||||
size = image.bitstream.size;
|
||||
|
||||
vp8_status_code = WebPDecode(webp, size, &config);
|
||||
|
||||
if (WEBP_MUX_OK == WebPMuxGetChunk(mux, "ICCP", &icc_profile_data))
|
||||
icc_profile = PyBytes_FromStringAndSize((const char*)icc_profile_data.bytes, icc_profile_data.size);
|
||||
|
||||
if (WEBP_MUX_OK == WebPMuxGetChunk(mux, "EXIF", &exif_data))
|
||||
exif = PyBytes_FromStringAndSize((const char*)exif_data.bytes, exif_data.size);
|
||||
|
||||
WebPDataClear(&image.bitstream);
|
||||
WebPMuxDelete(mux);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (vp8_status_code != VP8_STATUS_OK)
|
||||
|
@ -675,14 +721,18 @@ PyObject* WebPDecode_wrapper(PyObject* self, PyObject* args)
|
|||
#else
|
||||
pymode = PyString_FromString(mode);
|
||||
#endif
|
||||
ret = Py_BuildValue("SiiS", bytes, config.output.width,
|
||||
config.output.height, pymode);
|
||||
ret = Py_BuildValue("SiiSSS", bytes, config.output.width,
|
||||
config.output.height, pymode,
|
||||
NULL == icc_profile ? Py_None : icc_profile,
|
||||
NULL == exif ? Py_None : exif);
|
||||
|
||||
end:
|
||||
WebPFreeDecBuffer(&config.output);
|
||||
|
||||
Py_XDECREF(bytes);
|
||||
Py_XDECREF(pymode);
|
||||
Py_XDECREF(icc_profile);
|
||||
Py_XDECREF(exif);
|
||||
|
||||
if (Py_None == ret)
|
||||
Py_RETURN_NONE;
|
||||
|
@ -714,7 +764,7 @@ PyObject* WebPDecoderBuggyAlpha_wrapper(PyObject* self, PyObject* args){
|
|||
|
||||
static PyMethodDef webpMethods[] =
|
||||
{
|
||||
#ifdef HAVE_WEBPMUX
|
||||
#ifdef HAVE_WEBPANIM
|
||||
{"WebPAnimDecoder", _anim_decoder_new, METH_VARARGS, "WebPAnimDecoder"},
|
||||
{"WebPAnimEncoder", _anim_encoder_new, METH_VARARGS, "WebPAnimEncoder"},
|
||||
#endif
|
||||
|
@ -733,6 +783,14 @@ void addMuxFlagToModule(PyObject* m) {
|
|||
#endif
|
||||
}
|
||||
|
||||
void addAnimFlagToModule(PyObject* m) {
|
||||
#ifdef HAVE_WEBPANIM
|
||||
PyModule_AddObject(m, "HAVE_WEBPANIM", Py_True);
|
||||
#else
|
||||
PyModule_AddObject(m, "HAVE_WEBPANIM", Py_False);
|
||||
#endif
|
||||
}
|
||||
|
||||
void addTransparencyFlagToModule(PyObject* m) {
|
||||
PyModule_AddObject(m, "HAVE_TRANSPARENCY",
|
||||
PyBool_FromLong(!WebPDecoderBuggyAlpha()));
|
||||
|
@ -740,9 +798,10 @@ void addTransparencyFlagToModule(PyObject* m) {
|
|||
|
||||
static int setup_module(PyObject* m) {
|
||||
addMuxFlagToModule(m);
|
||||
addAnimFlagToModule(m);
|
||||
addTransparencyFlagToModule(m);
|
||||
|
||||
#ifdef HAVE_WEBPMUX
|
||||
#ifdef HAVE_WEBPANIM
|
||||
/* Ready object types */
|
||||
if (PyType_Ready(&WebPAnimDecoder_Type) < 0 ||
|
||||
PyType_Ready(&WebPAnimEncoder_Type) < 0)
|
||||
|
|
|
@ -683,8 +683,8 @@ Saving sequences
|
|||
.. note::
|
||||
|
||||
Support for animated WebP files will only be enabled if the system webp
|
||||
library was built with webpmux support. You can check webpmux support
|
||||
at runtime by inspecting the `_webp.HAVE_WEBPMUX` module flag.
|
||||
library is v0.5.0 or later. You can check webp animation support at
|
||||
runtime by inspecting the `_webp.HAVE_WEBPANIM` module flag.
|
||||
|
||||
When calling :py:meth:`~PIL.Image.Image.save`, the following options
|
||||
are available when the save_all argument is present and true.
|
||||
|
|
|
@ -178,8 +178,9 @@ if __name__ == "__main__":
|
|||
("freetype2", "FREETYPE2"),
|
||||
("littlecms2", "LITTLECMS2"),
|
||||
("webp", "WEBP"),
|
||||
("transp_webp", "Transparent WEBP"),
|
||||
("transp_webp", "WEBP Transparency"),
|
||||
("webp_mux", "WEBPMUX"),
|
||||
("webp_anim", "WEBP Animation"),
|
||||
("jpg", "JPEG"),
|
||||
("jpg_2000", "OPENJPEG (JPEG2000)"),
|
||||
("zlib", "ZLIB (PNG/ZIP)"),
|
||||
|
|
Loading…
Reference in New Issue
Block a user