From 76c8dda68114440dc07ab7fc9ea329b4a68a3948 Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Wed, 25 Feb 2015 10:03:02 +0100 Subject: [PATCH] webp: add decoder - support incremental decoding - support Image.draft - support reading Exif/ICC profile data even if Mux API is not available --- PIL/WebPImagePlugin.py | 178 +++++++++++++++++++++++++++++----- Tests/test_file_webp_draft.py | 56 +++++++++++ _imaging.c | 4 + _webp.c | 113 +-------------------- decode.c | 48 +++++++++ libImaging/Imaging.h | 5 + libImaging/WebP.h | 31 ++++++ libImaging/WebPDecode.c | 145 +++++++++++++++++++++++++++ setup.py | 20 ++-- 9 files changed, 459 insertions(+), 141 deletions(-) create mode 100644 Tests/test_file_webp_draft.py create mode 100644 libImaging/WebP.h create mode 100644 libImaging/WebPDecode.c diff --git a/PIL/WebPImagePlugin.py b/PIL/WebPImagePlugin.py index 78a7a5319..cdf5fe48e 100644 --- a/PIL/WebPImagePlugin.py +++ b/PIL/WebPImagePlugin.py @@ -1,19 +1,11 @@ -from PIL import Image -from PIL import ImageFile -from io import BytesIO -from PIL import _webp +from PIL import Image, ImageFile, _webp +from PIL._binary import i8, i32le +import os -_VALID_WEBP_MODES = { - "RGB": True, - "RGBA": True, - } +_VALID_WEBP_MODES = ("RGB", "RGBA") -_VP8_MODES_BY_IDENTIFIER = { - b"VP8 ": "RGB", - b"VP8X": "RGBA", - b"VP8L": "RGBA", # lossless - } +_VP8_MODES_BY_IDENTIFIER = (b"VP8 ", b"VP8X", b"VP8L") def _accept(prefix): @@ -23,6 +15,63 @@ def _accept(prefix): return is_riff_file_format and is_webp_file and is_valid_vp8_mode +# VP8X/VP8L/VP8 chunk parsing routines. +# Arguments: +# - chunk_size: RIFF chunk size +# - fp: file +# Return: +# - mode: RGB/RGBA +# - size: width/height +# - skip: how many bytes to skip to next chunk + +def _parse_vp8x(chunk_size, fp): + if chunk_size < 10: + raise SyntaxError("bad VP8X chunk") + vp8x = fp.read(10) + if 10 != len(vp8x): + raise SyntaxError("bad VP8X chunk") + flags = i8(vp8x[0]) + if (flags & 0b10000): + mode = 'RGBA' + else: + mode = 'RGB' + width = (i8(vp8x[4]) | (i8(vp8x[5]) << 8) | (i8(vp8x[6]) << 16)) + 1 + height = (i8(vp8x[7]) | (i8(vp8x[8]) << 8) | (i8(vp8x[9]) << 16)) + 1 + return mode, (width, height), chunk_size - 10 + +def _parse_vp8l(chunk_size, fp): + if chunk_size < 5: + raise SyntaxError("bad VP8L chunk") + vp8l = fp.read(5) + if 5 != len(vp8l): + raise SyntaxError("bad VP8L chunk") + vp8l = [i8(b) for b in vp8l] + # Check signature. + if 0x2f != vp8l[0] or 0 != (vp8l[4] >> 5): + raise SyntaxError("bad VP8L chunk") + if (vp8l[4] & 0b10000): + mode = 'RGBA' + else: + mode = 'RGB' + # 14 bits for each... + width = (vp8l[1] | ((vp8l[2] & 0b111111) << 8)) + 1 + height = ((vp8l[2] >> 6) | (vp8l[3] << 2) | ((vp8l[4] & 0b1111) << 10)) + 1 + return mode, (width, height), chunk_size - 5 + +def _parse_vp8(chunk_size, fp): + if chunk_size < 10: + raise SyntaxError("bad VP8 chunk") + vp8 = fp.read(10) + if 10 != len(vp8): + raise SyntaxError("bad VP8 chunk") + # Check signature. + if 0x9d != i8(vp8[3]) or 0x01 != i8(vp8[4]) or 0x2a != i8(vp8[5]): + raise SyntaxError("bad VP8 chunk") + mode = 'RGB' + width = (i8(vp8[6]) | (i8(vp8[7]) << 8)) & 0x3fff + height = (i8(vp8[8]) | (i8(vp8[9]) << 8)) & 0x3fff + return mode, (width, height), chunk_size - 10 + class WebPImageFile(ImageFile.ImageFile): @@ -30,17 +79,102 @@ class WebPImageFile(ImageFile.ImageFile): format_description = "WebP image" def _open(self): - data, width, height, self.mode, icc_profile, exif = \ - _webp.WebPDecode(self.fp.read()) - if icc_profile: - self.info["icc_profile"] = icc_profile - if exif: - self.info["exif"] = exif + header = self.fp.read(12) + if 12 != len(header): + raise SyntaxError("not a WebP file") - self.size = width, height - self.fp = BytesIO(data) - self.tile = [("raw", (0, 0) + self.size, 0, self.mode)] + if b'RIFF' != header[0:4] or b'WEBP' != header[8:12]: + raise SyntaxError("not a WebP file") + + mode = None + size = None + lossy = None + + first_chunk = True + + while True: + + chunk_header = self.fp.read(8) + if 8 != len(chunk_header): + if first_chunk: + raise SyntaxError("not a WebP file") + break + + chunk_fourcc = chunk_header[0:4] + chunk_size = i32le(chunk_header[4:8]) + + if first_chunk: + if chunk_fourcc not in _VP8_MODES_BY_IDENTIFIER: + raise SyntaxError("not a WebP file") + + if b'VP8X' == chunk_fourcc: + if first_chunk: + mode, size, chunk_size = _parse_vp8x(chunk_size, self.fp) + + elif b'VP8L' == chunk_fourcc: + if first_chunk: + mode, size, chunk_size = _parse_vp8l(chunk_size, self.fp) + lossy = False + + elif b'VP8 ' == chunk_fourcc: + if first_chunk: + mode, size, chunk_size = _parse_vp8(chunk_size, self.fp) + lossy = True + + elif b'ALPH' == chunk_fourcc: + mode = 'RGBA' + + elif b'EXIF' == chunk_fourcc: + exif = self.fp.read(chunk_size) + if chunk_size != len(exif): + raise SyntaxError("bad EXIF chunk") + self.info["exif"] = exif + chunk_size = 0 + + elif b'ICCP' == chunk_fourcc: + icc_profile = self.fp.read(chunk_size) + if chunk_size != len(icc_profile): + raise SyntaxError("bad ICCP chunk") + self.info["icc_profile"] = icc_profile + chunk_size = 0 + + if chunk_size > 0: + # Skip to next chunk. + pos = self.fp.tell() + self.fp.seek(chunk_size, os.SEEK_CUR) + if self.fp.tell() != (pos + chunk_size): + raise SyntaxError("not a WebP file") + + first_chunk = False + + if None in (mode, size, lossy): + raise SyntaxError("not a WebP file") + + self.mode = mode + self.size = size + self.tile = [('webp', (0, 0) + size, 0, + # Decoder params: rawmode, has_alpha, width, height. + (mode, 1 if 'RGBA' == mode else 0, size[0], size[1]))] + + def draft(self, mode, size): + + if 1 != len(self.tile): + return + + d, e, o, a = self.tile[0] + + if mode in _VALID_WEBP_MODES: + a = mode, a[1], a[2], a[3] + self.mode = mode + + if size: + e = e[0], e[1], size[0], size[1] + self.size = size + + self.tile = [(d, e, o, a)] + + return self def _getexif(self): from PIL.JpegImagePlugin import _getexif diff --git a/Tests/test_file_webp_draft.py b/Tests/test_file_webp_draft.py new file mode 100644 index 000000000..0e1d2cf32 --- /dev/null +++ b/Tests/test_file_webp_draft.py @@ -0,0 +1,56 @@ +from helper import unittest, PillowTestCase, fromstring, tostring + +from PIL import Image + +CODECS = dir(Image.core) +FILENAME = "Tests/images/hopper.webp" +DATA = tostring(Image.open(FILENAME).resize((512, 512)), "WEBP") +ALPHA_FILENAME = "Tests/images/transparent.webp" +ALPHA_DATA = tostring(Image.open(ALPHA_FILENAME).resize((512, 512)), "WEBP") + + +def draft(mode, size): + im = fromstring(DATA) + im.draft(mode, size) + return im + +def alpha_draft(mode, size): + im = fromstring(ALPHA_DATA) + im.draft(mode, size) + return im + + + +class TestImageDraft(PillowTestCase): + + def setUp(self): + if "webp_decoder" not in CODECS: + self.skipTest("WebP support not available") + + def test_size(self): + # Upscaling/downscaling to any size is supported. + self.assertEqual(draft("RGB", (1024, 1024)).size, (1024, 1024)) + self.assertEqual(draft("RGB", (512, 512)).size, (512, 512)) + self.assertEqual(draft("RGB", (256, 256)).size, (256, 256)) + self.assertEqual(draft("RGB", (128, 128)).size, (128, 128)) + self.assertEqual(draft("RGB", (64, 64)).size, (64, 64)) + self.assertEqual(draft("RGB", (32, 32)).size, (32, 32)) + + def test_mode(self): + # Decoder only support RGB/RGBA output. + self.assertEqual(draft("1", (512, 512)).mode, "RGB") + self.assertEqual(draft("L", (512, 512)).mode, "RGB") + self.assertEqual(draft("RGB", (512, 512)).mode, "RGB") + self.assertEqual(draft("RGBA", (512, 512)).mode, "RGBA") + self.assertEqual(draft("YCbCr", (512, 512)).mode, "RGB") + self.assertEqual(alpha_draft("1", (512, 512)).mode, "RGBA") + self.assertEqual(alpha_draft("L", (512, 512)).mode, "RGBA") + self.assertEqual(alpha_draft("RGB", (512, 512)).mode, "RGB") + self.assertEqual(alpha_draft("RGBA", (512, 512)).mode, "RGBA") + self.assertEqual(alpha_draft("YCbCr", (512, 512)).mode, "RGBA") + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/_imaging.c b/_imaging.c index c3be9beab..f98805a96 100644 --- a/_imaging.c +++ b/_imaging.c @@ -3343,6 +3343,7 @@ extern PyObject* PyImaging_BitDecoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_FliDecoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_GifDecoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_HexDecoderNew(PyObject* self, PyObject* args); +extern PyObject* PyImaging_WebPDecoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_JpegDecoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_TiffLzwDecoderNew(PyObject* self, PyObject* args); @@ -3411,6 +3412,9 @@ static PyMethodDef functions[] = { {"gif_encoder", (PyCFunction)PyImaging_GifEncoderNew, 1}, {"hex_decoder", (PyCFunction)PyImaging_HexDecoderNew, 1}, {"hex_encoder", (PyCFunction)PyImaging_EpsEncoderNew, 1}, /* EPS=HEX! */ +#ifdef HAVE_LIBWEBP + {"webp_decoder", (PyCFunction)PyImaging_WebPDecoderNew, 1}, +#endif #ifdef HAVE_LIBJPEG {"jpeg_decoder", (PyCFunction)PyImaging_JpegDecoderNew, 1}, {"jpeg_encoder", (PyCFunction)PyImaging_JpegEncoderNew, 1}, diff --git a/_webp.c b/_webp.c index a8c6d40af..6a69a96d0 100644 --- a/_webp.c +++ b/_webp.c @@ -9,7 +9,7 @@ #include #endif -PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args) +static PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args) { int width; int height; @@ -132,113 +132,9 @@ 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, *icc_profile = NULL, *exif = NULL; - WebPDecoderConfig config; - VP8StatusCode vp8_status_code = VP8_STATUS_OK; - char* mode = "RGB"; - - if (!PyArg_ParseTuple(args, "S", &webp_string)) { - Py_RETURN_NONE; - } - - if (!WebPInitDecoderConfig(&config)) { - Py_RETURN_NONE; - } - - PyBytes_AsStringAndSize((PyObject *) webp_string, (char**)&webp, &size); - - vp8_status_code = WebPGetFeatures(webp, size, &config.input); - if (vp8_status_code == VP8_STATUS_OK) { - // If we don't set it, we don't get alpha. - // Initialized to MODE_RGB - if (config.input.has_alpha) { - config.output.colorspace = MODE_RGBA; - 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) - goto end; - - if (config.output.colorspace < MODE_YUV) { - bytes = PyBytes_FromStringAndSize((char *)config.output.u.RGBA.rgba, - config.output.u.RGBA.size); - } else { - // Skipping YUV for now. Need Test Images. - // UNDONE -- unclear if we'll ever get here if we set mode_rgb* - bytes = PyBytes_FromStringAndSize((char *)config.output.u.YUVA.y, - config.output.u.YUVA.y_size); - } - -#if PY_VERSION_HEX >= 0x03000000 - pymode = PyUnicode_FromString(mode); -#else - pymode = PyString_FromString(mode); -#endif - 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; - - return ret; -} - // Return the decoder's version number, packed in hexadecimal using 8bits for // each of major/minor/revision. E.g: v2.5.7 is 0x020507. -PyObject* WebPDecoderVersion_wrapper(PyObject* self, PyObject* args){ +static PyObject* WebPDecoderVersion_wrapper(PyObject* self, PyObject* args){ return Py_BuildValue("i", WebPGetDecoderVersion()); } @@ -246,20 +142,19 @@ PyObject* WebPDecoderVersion_wrapper(PyObject* self, PyObject* args){ * The version of webp that ships with (0.1.3) Ubuntu 12.04 doesn't handle alpha well. * Files that are valid with 0.3 are reported as being invalid. */ -PyObject* WebPDecoderBuggyAlpha_wrapper(PyObject* self, PyObject* args){ +static PyObject* WebPDecoderBuggyAlpha_wrapper(PyObject* self, PyObject* args){ return Py_BuildValue("i", WebPGetDecoderVersion()==0x0103); } static PyMethodDef webpMethods[] = { {"WebPEncode", WebPEncode_wrapper, METH_VARARGS, "WebPEncode"}, - {"WebPDecode", WebPDecode_wrapper, METH_VARARGS, "WebPDecode"}, {"WebPDecoderVersion", WebPDecoderVersion_wrapper, METH_VARARGS, "WebPVersion"}, {"WebPDecoderBuggyAlpha", WebPDecoderBuggyAlpha_wrapper, METH_VARARGS, "WebPDecoderBuggyAlpha"}, {NULL, NULL} }; -void addMuxFlagToModule(PyObject* m) { +static void addMuxFlagToModule(PyObject* m) { #ifdef HAVE_WEBPMUX PyModule_AddObject(m, "HAVE_WEBPMUX", Py_True); #else diff --git a/decode.c b/decode.c index c56f42592..751580482 100644 --- a/decode.c +++ b/decode.c @@ -722,6 +722,54 @@ PyImaging_ZipDecoderNew(PyObject* self, PyObject* args) #endif +/* -------------------------------------------------------------------- */ +/* WebP */ +/* -------------------------------------------------------------------- */ + +#ifdef HAVE_LIBWEBP + +#include "WebP.h" + +PyObject* +PyImaging_WebPDecoderNew(PyObject* self, PyObject* args) +{ + ImagingDecoderObject* decoder; + WEBPSTATE* context; + + char* mode; + char* rawmode; /* what we wan't from the decoder */ + int has_alpha; + int width, height; + + if (!PyArg_ParseTuple(args, "ssiii", + &mode, &rawmode, + &has_alpha, &width, &height)) + return NULL; + + decoder = PyImaging_DecoderNew(sizeof(WEBPSTATE)); + if (decoder == NULL) + return NULL; + + if (get_unpacker(decoder, mode, rawmode) < 0) + return NULL; + + decoder->decode = ImagingWebPDecode; + decoder->cleanup = ImagingWebPDecodeCleanup; + + context = (WEBPSTATE*)decoder->state.context; + + strncpy(context->rawmode, rawmode, IMAGING_MODE_LENGTH - 1); + context->rawmode[IMAGING_MODE_LENGTH - 1] = '\0'; + context->has_alpha = has_alpha; + context->width = width; + context->height = height; + context->decoder = NULL; + + return (PyObject*) decoder; +} + +#endif + /* -------------------------------------------------------------------- */ /* JPEG */ /* -------------------------------------------------------------------- */ diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h index c341ac84e..f6a58641d 100644 --- a/libImaging/Imaging.h +++ b/libImaging/Imaging.h @@ -419,6 +419,11 @@ extern int ImagingGifEncode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); extern int ImagingHexDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); +#ifdef HAVE_LIBWEBP +extern int ImagingWebPDecode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +extern int ImagingWebPDecodeCleanup(ImagingCodecState state); +#endif #ifdef HAVE_LIBJPEG extern int ImagingJpegDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); diff --git a/libImaging/WebP.h b/libImaging/WebP.h new file mode 100644 index 000000000..4c79f6623 --- /dev/null +++ b/libImaging/WebP.h @@ -0,0 +1,31 @@ +/* + * The Python Imaging Library. + * + * declarations for the WebP codec interface. + */ + +#include +#include +#include + +/* -------------------------------------------------------------------- */ +/* Decoder */ + +typedef struct { + + /* CONFIGURATION */ + + /* Decoder output mode (input to the shuffler). */ + char rawmode[IMAGING_MODE_LENGTH]; + + /* Original image information. */ + int has_alpha; + int width, height; + + /* PRIVATE CONTEXT (set by decoder) */ + + WebPDecoderConfig config; + WebPIDecoder *decoder; + +} WEBPSTATE; + diff --git a/libImaging/WebPDecode.c b/libImaging/WebPDecode.c new file mode 100644 index 000000000..280bf40b5 --- /dev/null +++ b/libImaging/WebPDecode.c @@ -0,0 +1,145 @@ +/* + * The Python Imaging Library. + * + * decoder for WebP image data. + */ + + +#ifdef HAVE_LIBWEBP + +#include "Imaging.h" +#include "WebP.h" + +#include + +static int _vp8_status_to_codec_status(VP8StatusCode code) +{ + switch (code) + { + case VP8_STATUS_OK: + return 0; + case VP8_STATUS_OUT_OF_MEMORY: + return IMAGING_CODEC_MEMORY; + case VP8_STATUS_BITSTREAM_ERROR: + case VP8_STATUS_NOT_ENOUGH_DATA: + case VP8_STATUS_SUSPENDED: + return IMAGING_CODEC_BROKEN; + case VP8_STATUS_INVALID_PARAM: + case VP8_STATUS_UNSUPPORTED_FEATURE: + return IMAGING_CODEC_CONFIG; + default: + case VP8_STATUS_USER_ABORT: + return IMAGING_CODEC_UNKNOWN; + } +} + +/* -------------------------------------------------------------------- */ +/* Decoder */ +/* -------------------------------------------------------------------- */ + +int ImagingWebPDecode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) +{ + WEBPSTATE *context = (WEBPSTATE *)state->context; + VP8StatusCode vp8_status_code; + + if (!state->state) + { + WebPDecoderConfig *config = &context->config; + + if (!WebPInitDecoderConfig(config)) + { + /* Mismatched version. */ + state->errcode = IMAGING_CODEC_CONFIG; + return -1; + } + + if (0 == strcmp("RGBA", context->rawmode)) + config->output.colorspace = MODE_RGBA; + else + config->output.colorspace = MODE_RGB; + + if (state->xsize != context->width || state->ysize != context->height) + { + config->options.scaled_width = state->xsize; + config->options.scaled_height = state->ysize; + config->options.use_scaling = 1; + } + + context->decoder = WebPIDecode(NULL, 0, config); + if (NULL == context->decoder) + { + state->errcode = _vp8_status_to_codec_status(vp8_status_code); + return -1; + } + + state->state = 1; + } + + /* Consume the buffer, decoding as much as possible. */ + vp8_status_code = WebPIAppend(context->decoder, buf, bytes); + if (VP8_STATUS_NOT_ENOUGH_DATA != vp8_status_code && + VP8_STATUS_SUSPENDED != vp8_status_code && + VP8_STATUS_OK != vp8_status_code) + { + state->errcode = _vp8_status_to_codec_status(vp8_status_code); + return -1; + } + + if (VP8_STATUS_NOT_ENOUGH_DATA != vp8_status_code) + { + /* Check progress, and unpack available data. */ + + const uint8_t *rgba; + int last_y; + int width; + int height; + int stride; + + rgba = WebPIDecGetRGB(context->decoder, &last_y, &width, &height, &stride); + if (NULL != rgba) + { + assert(width == state->xsize); + assert(height == state->ysize); + assert(last_y <= state->ysize); + + for (; state->y < last_y; ++state->y) + { + assert(state->y < state->ysize); + state->shuffle((UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, + rgba + state->y * stride, state->xsize); + } + } + } + + if (VP8_STATUS_OK == vp8_status_code) + { + /* We're finished! */ + state->errcode = IMAGING_CODEC_END; + return -1; + } + + /* Return number of bytes consumed. */ + return bytes; +} + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + +int ImagingWebPDecodeCleanup(ImagingCodecState state) +{ + WEBPSTATE* context = (WEBPSTATE*) state->context; + + if (NULL != context->decoder) + { + WebPFreeDecBuffer(&context->config.output); + WebPIDelete(context->decoder); + context->decoder = NULL; + } + + return -1; +} + +#endif + diff --git a/setup.py b/setup.py index a5f114d88..4f4197e67 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ _LIB_IMAGING = ( "RankFilter", "RawDecode", "RawEncode", "Storage", "SunRleDecode", "TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask", "XbmDecode", "XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode", "Incremental", - "Jpeg2KDecode", "Jpeg2KEncode", "BoxBlur") + "Jpeg2KDecode", "Jpeg2KEncode", "BoxBlur", "WebPDecode") def _add_directory(path, dir, where=None): @@ -535,6 +535,14 @@ class pil_build_ext(build_ext): libs.extend(["kernel32", "user32", "gdi32"]) if struct.unpack("h", "\0\1".encode('ascii'))[0] == 1: defs.append(("WORDS_BIGENDIAN", None)) + if os.path.isfile("_webp.c") and feature.webp: + webp_defs = [("HAVE_LIBWEBP", None)] + webp_libs = [feature.webp] + if feature.webpmux: + webp_defs.append(("HAVE_WEBPMUX", None)) + webp_libs.append(feature.webpmux) + libs.extend(webp_libs) + defs.extend(webp_defs) exts = [(Extension( "PIL._imaging", files, libraries=libs, define_macros=defs))] @@ -564,16 +572,8 @@ class pil_build_ext(build_ext): libraries=["lcms2"] + extra)) if os.path.isfile("_webp.c") and feature.webp: - libs = [feature.webp] - defs = [] - - if feature.webpmux: - defs.append(("HAVE_WEBPMUX", None)) - libs.append(feature.webpmux) - libs.append(feature.webpmux.replace('pmux','pdemux')) - exts.append(Extension( - "PIL._webp", ["_webp.c"], libraries=libs, define_macros=defs)) + "PIL._webp", ["_webp.c"], libraries=webp_libs, define_macros=webp_defs)) if sys.platform == "darwin": # locate Tcl/Tk frameworks