From a4a856b58a7b55cfce359ec97c6fed8a16bcbad2 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 5 Apr 2013 15:36:48 -0700 Subject: [PATCH 01/27] large exif segments --- PIL/JpegImagePlugin.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 1c260dd6b..a659b4c47 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -561,6 +561,10 @@ def _save(im, fp, filename): bufsize=0 if "optimize" in info: bufsize = im.size[0]*im.size[1] + + # The exif info needs to be written as one block. Ensure that our buffer is big enough + if len(info.get("exif",b"")) > max(ImageFile.MAXBLOCK, bufsize): + bufsize = 2*len(info.get("exif",b"")) ImageFile._save(im, fp, [("jpeg", (0,0)+im.size, 0, rawmode)], bufsize) From 7200c40ada83853798169e03b026e9136744d8a1 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 5 Apr 2013 15:57:07 -0700 Subject: [PATCH 02/27] s/tabs/spaces/ --- PIL/JpegImagePlugin.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index a659b4c47..aeff66879 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -561,10 +561,10 @@ def _save(im, fp, filename): bufsize=0 if "optimize" in info: bufsize = im.size[0]*im.size[1] - - # The exif info needs to be written as one block. Ensure that our buffer is big enough - if len(info.get("exif",b"")) > max(ImageFile.MAXBLOCK, bufsize): - bufsize = 2*len(info.get("exif",b"")) + + # The exif info needs to be written as one block. Ensure that our buffer is big enough + if len(info.get("exif",b"")) > max(ImageFile.MAXBLOCK, bufsize): + bufsize = 2*len(info.get("exif",b"")) ImageFile._save(im, fp, [("jpeg", (0,0)+im.size, 0, rawmode)], bufsize) From 1344610a5243b86c3867a121ed7748a5e6401fd3 Mon Sep 17 00:00:00 2001 From: Euan Goddard Date: Mon, 13 May 2013 17:01:42 +0100 Subject: [PATCH 03/27] Added support for alpha transparent webp images #204 --- Images/transparent.png | Bin 0 -> 26538 bytes Images/transparent.webp | Bin 0 -> 8094 bytes PIL/WebPImagePlugin.py | 68 ++++++++++++++++++++++--- Tests/test_file_webp.py | 107 +++++++++++++++++++++++++++------------- _webp.c | 68 ++++++++++++++++++++++++- 5 files changed, 202 insertions(+), 41 deletions(-) create mode 100644 Images/transparent.png create mode 100644 Images/transparent.webp diff --git a/Images/transparent.png b/Images/transparent.png new file mode 100644 index 0000000000000000000000000000000000000000..902ea82723b84c0ed9cd77f159a701a7c5c7ddb6 GIT binary patch literal 26538 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%0AGjeBh|OI@@)vH+(LoFdF+>u+43`b@6y5h+ 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 OfP#!Nv`k9h=l=lo#qM+f literal 0 HcmV?d00001 diff --git a/Images/transparent.webp b/Images/transparent.webp new file mode 100644 index 0000000000000000000000000000000000000000..c1e38022efc05d5bd3f5ba54f883bcc0dbf1e1da GIT binary patch literal 8094 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 s{?#1sU size) { + Py_INCREF(Py_None); + return Py_None; + } + + ret_size = WebPEncodeRGBA(rgba, width, height, stride, quality_factor, &output); + if (ret_size > 0) { + PyObject *ret = PyBytes_FromStringAndSize((char*)output, ret_size); + free(output); + return ret; + } + Py_INCREF(Py_None); + return Py_None; + +} + + PyObject* WebPDecodeRGB_wrapper(PyObject* self, PyObject* args) { PyBytesObject *webp_string; @@ -62,13 +99,42 @@ PyObject* WebPDecodeRGB_wrapper(PyObject* self, PyObject* args) return Py_BuildValue("Sii", ret, width, height); } + +PyObject* WebPDecodeRGBA_wrapper(PyObject* self, PyObject* args) +{ + PyBytesObject *webp_string; + int width; + int height; + uint8_t *webp; + uint8_t *output; + Py_ssize_t size; + PyObject *ret; + + if (!PyArg_ParseTuple(args, "S", &webp_string)) { + Py_INCREF(Py_None); + return Py_None; + } + + PyBytes_AsStringAndSize((PyObject *) webp_string, (char**)&webp, &size); + + output = WebPDecodeRGBA(webp, size, &width, &height); + + ret = PyBytes_FromStringAndSize((char*)output, width * height * 4); + free(output); + return Py_BuildValue("Sii", ret, width, height); +} + + static PyMethodDef webpMethods[] = { {"WebPEncodeRGB", WebPEncodeRGB_wrapper, METH_VARARGS, "WebPEncodeRGB"}, - {"WebPDecodeRGB", WebPDecodeRGB_wrapper, METH_VARARGS, "WebPEncodeRGB"}, + {"WebPEncodeRGBA", WebPEncodeRGBA_wrapper, METH_VARARGS, "WebPEncodeRGBA"}, + {"WebPDecodeRGB", WebPDecodeRGB_wrapper, METH_VARARGS, "WebPDecodeRGB"}, + {"WebPDecodeRGBA", WebPDecodeRGBA_wrapper, METH_VARARGS, "WebPDecodeRGBA"}, {NULL, NULL} }; + #if PY_VERSION_HEX >= 0x03000000 PyMODINIT_FUNC PyInit__webp(void) { From 5a0308cc1965ccde1af37cef4f56807422b7d64d Mon Sep 17 00:00:00 2001 From: Euan Goddard Date: Mon, 13 May 2013 17:02:36 +0100 Subject: [PATCH 04/27] Partial work to add a wrapper for WebPGetFeatures to correctly support #204 --- _webp.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/_webp.c b/_webp.c index 54cbd845b..cf72846eb 100644 --- a/_webp.c +++ b/_webp.c @@ -2,6 +2,41 @@ #include "py3.h" #include #include +#include + + +PyObject* WebPGetFeatures_wrapper(PyObject* self, PyObject* args) +{ + PyBytesObject *webp_string; + const uint8_t* webp = NULL; + VP8StatusCode vp8_status_code = VP8_STATUS_OK; + Py_ssize_t size; + WebPBitstreamFeatures* const features; + + + if (!PyArg_ParseTuple(args, "S", &webp_string)) { + Py_INCREF(Py_None); + return Py_None; + } + + PyBytes_AsStringAndSize((PyObject *) webp_string, (char**)&webp, &size); + + vp8_status_code = WebPGetFeatures(webp, size, features); + + if (vp8_status_code == VP8_STATUS_OK) { + printf("%i", features->has_alpha); + + } else { + // TODO: raise some sort of error + printf("Error occured checking webp file with code: %d\n", vp8_status_code); + Py_INCREF(Py_None); + return Py_None; + } + + free((void*)webp); + return Py_BuildValue("b", features->has_alpha); +} + PyObject* WebPEncodeRGB_wrapper(PyObject* self, PyObject* args) { @@ -127,6 +162,7 @@ PyObject* WebPDecodeRGBA_wrapper(PyObject* self, PyObject* args) static PyMethodDef webpMethods[] = { + {"WebPGetFeatures", WebPGetFeatures_wrapper, METH_VARARGS, "WebPGetFeatures"}, {"WebPEncodeRGB", WebPEncodeRGB_wrapper, METH_VARARGS, "WebPEncodeRGB"}, {"WebPEncodeRGBA", WebPEncodeRGBA_wrapper, METH_VARARGS, "WebPEncodeRGBA"}, {"WebPDecodeRGB", WebPDecodeRGB_wrapper, METH_VARARGS, "WebPDecodeRGB"}, From 4852aa5b653bf016aade63a49140f36a27d5dbfd Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 13 May 2013 11:30:19 -0700 Subject: [PATCH 05/27] working WebPGetFeatures --- _webp.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/_webp.c b/_webp.c index cf72846eb..410982e11 100644 --- a/_webp.c +++ b/_webp.c @@ -8,10 +8,10 @@ PyObject* WebPGetFeatures_wrapper(PyObject* self, PyObject* args) { PyBytesObject *webp_string; - const uint8_t* webp = NULL; + uint8_t* webp = NULL; VP8StatusCode vp8_status_code = VP8_STATUS_OK; Py_ssize_t size; - WebPBitstreamFeatures* const features; + WebPBitstreamFeatures features; if (!PyArg_ParseTuple(args, "S", &webp_string)) { @@ -21,10 +21,10 @@ PyObject* WebPGetFeatures_wrapper(PyObject* self, PyObject* args) PyBytes_AsStringAndSize((PyObject *) webp_string, (char**)&webp, &size); - vp8_status_code = WebPGetFeatures(webp, size, features); + vp8_status_code = WebPGetFeatures(webp, size, (WebPBitstreamFeatures *)&features); if (vp8_status_code == VP8_STATUS_OK) { - printf("%i", features->has_alpha); + printf("%i", features.has_alpha); } else { // TODO: raise some sort of error @@ -33,8 +33,7 @@ PyObject* WebPGetFeatures_wrapper(PyObject* self, PyObject* args) return Py_None; } - free((void*)webp); - return Py_BuildValue("b", features->has_alpha); + return Py_BuildValue("b", features.has_alpha); } From b52c22316f5a88643a87281d5f07458330e3ecb2 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 13 May 2013 20:50:10 -0700 Subject: [PATCH 06/27] inprogress, sorta working --- PIL/WebPImagePlugin.py | 22 +++--------- _webp.c | 78 +++++++++++++++++++++++++----------------- 2 files changed, 51 insertions(+), 49 deletions(-) diff --git a/PIL/WebPImagePlugin.py b/PIL/WebPImagePlugin.py index 46953d2ca..f1478a965 100644 --- a/PIL/WebPImagePlugin.py +++ b/PIL/WebPImagePlugin.py @@ -11,8 +11,8 @@ _VALID_WEBP_ENCODERS_BY_MODE = { _VALID_WEBP_DECODERS_BY_MODE = { - "RGB": _webp.WebPDecodeRGB, - "RGBA": _webp.WebPDecodeRGBA, + "RGB": _webp.WebPDecode, + "RGBA": _webp.WebPDecode, } @@ -41,23 +41,11 @@ class WebPImageFile(ImageFile.ImageFile): format = "WEBP" format_description = "WebP image" - def _open(self): - file_header = self.fp.read(16) - vp8_header = file_header[12:16] - try: - webp_file_mode = _VP8_MODES_BY_IDENTIFIER[vp8_header] - except KeyError: - raise IOError("Unknown webp file mode") - finally: - self.fp.seek(0) - - self.mode = webp_file_mode - webp_decoder = _VALID_WEBP_DECODERS_BY_MODE[webp_file_mode] - - data, width, height = webp_decoder(self.fp.read()) + def _open(self): + 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, webp_file_mode)] + self.tile = [("raw", (0, 0) + self.size, 0, self.mode)] def _save(im, fp, filename): diff --git a/_webp.c b/_webp.c index 410982e11..d960c6e0b 100644 --- a/_webp.c +++ b/_webp.c @@ -21,7 +21,7 @@ PyObject* WebPGetFeatures_wrapper(PyObject* self, PyObject* args) PyBytes_AsStringAndSize((PyObject *) webp_string, (char**)&webp, &size); - vp8_status_code = WebPGetFeatures(webp, size, (WebPBitstreamFeatures *)&features); + vp8_status_code = WebPGetFeatures(webp, size, &features); if (vp8_status_code == VP8_STATUS_OK) { printf("%i", features.has_alpha); @@ -109,63 +109,77 @@ PyObject* WebPEncodeRGBA_wrapper(PyObject* self, PyObject* args) } -PyObject* WebPDecodeRGB_wrapper(PyObject* self, PyObject* args) +PyObject* WebPDecode_wrapper(PyObject* self, PyObject* args) { PyBytesObject *webp_string; - int width; + int width; int height; uint8_t *webp; uint8_t *output; Py_ssize_t size; - PyObject *ret; + PyObject *ret, *bytes; + WebPDecoderConfig config; + VP8StatusCode vp8_status_code = VP8_STATUS_OK; + char* mode = NULL; if (!PyArg_ParseTuple(args, "S", &webp_string)) { Py_INCREF(Py_None); return Py_None; } - PyBytes_AsStringAndSize((PyObject *) webp_string, (char**)&webp, &size); - - output = WebPDecodeRGB(webp, size, &width, &height); - - ret = PyBytes_FromStringAndSize((char*)output, width * height * 3); - free(output); - return Py_BuildValue("Sii", ret, width, height); -} - - -PyObject* WebPDecodeRGBA_wrapper(PyObject* self, PyObject* args) -{ - PyBytesObject *webp_string; - int width; - int height; - uint8_t *webp; - uint8_t *output; - Py_ssize_t size; - PyObject *ret; - - if (!PyArg_ParseTuple(args, "S", &webp_string)) { + if (!WebPInitDecoderConfig(&config)) { Py_INCREF(Py_None); return Py_None; - } + } PyBytes_AsStringAndSize((PyObject *) webp_string, (char**)&webp, &size); - output = WebPDecodeRGBA(webp, size, &width, &height); + vp8_status_code = WebPGetFeatures(webp, size, &config.input); + if (vp8_status_code == VP8_STATUS_OK) { + vp8_status_code = WebPDecode(webp, size, &config); + } + + if (vp8_status_code != VP8_STATUS_OK) { + Py_INCREF(Py_None); + return Py_None; + } + + 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. + bytes = PyBytes_FromStringAndSize((char *)config.output.u.YUVA.y, config.output.u.YUVA.y_size); + } + switch(config.output.colorspace) { + // UNDONE, alternate orderings + case MODE_RGB: + mode = "RGB"; + break; + case MODE_RGBA: + mode = "RGBA"; + break; + default: + mode = "ERR"; + } - ret = PyBytes_FromStringAndSize((char*)output, width * height * 4); - free(output); - return Py_BuildValue("Sii", ret, width, height); + height = config.output.height; + width = config.output.width; + + + ret = Py_BuildValue("SiiS", bytes, width, height, PyBytes_FromString(mode)); + WebPFreeDecBuffer(&config.output); + return ret; } + + static PyMethodDef webpMethods[] = { {"WebPGetFeatures", WebPGetFeatures_wrapper, METH_VARARGS, "WebPGetFeatures"}, {"WebPEncodeRGB", WebPEncodeRGB_wrapper, METH_VARARGS, "WebPEncodeRGB"}, {"WebPEncodeRGBA", WebPEncodeRGBA_wrapper, METH_VARARGS, "WebPEncodeRGBA"}, - {"WebPDecodeRGB", WebPDecodeRGB_wrapper, METH_VARARGS, "WebPDecodeRGB"}, - {"WebPDecodeRGBA", WebPDecodeRGBA_wrapper, METH_VARARGS, "WebPDecodeRGBA"}, + {"WebPDecode", WebPDecode_wrapper, METH_VARARGS, "WebPDecode"}, {NULL, NULL} }; From 0472b91d165e465a91c521aeba9799d970eafc89 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 13 May 2013 21:27:41 -0700 Subject: [PATCH 07/27] really working webp alpha decoding --- _webp.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/_webp.c b/_webp.c index d960c6e0b..b3fb73a00 100644 --- a/_webp.c +++ b/_webp.c @@ -112,15 +112,12 @@ PyObject* WebPEncodeRGBA_wrapper(PyObject* self, PyObject* args) PyObject* WebPDecode_wrapper(PyObject* self, PyObject* args) { PyBytesObject *webp_string; - int width; - int height; uint8_t *webp; - uint8_t *output; Py_ssize_t size; PyObject *ret, *bytes; WebPDecoderConfig config; VP8StatusCode vp8_status_code = VP8_STATUS_OK; - char* mode = NULL; + char* mode = "RGB"; if (!PyArg_ParseTuple(args, "S", &webp_string)) { Py_INCREF(Py_None); @@ -136,6 +133,12 @@ PyObject* WebPDecode_wrapper(PyObject* self, PyObject* args) 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"; + } vp8_status_code = WebPDecode(webp, size, &config); } From 014ca1497d1321f96b3722b4e8eb2e5362662985 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 13 May 2013 21:28:18 -0700 Subject: [PATCH 08/27] versions, feature checking --- Tests/test_file_webp.py | 44 +++++++++++++++++++++++++---------------- _webp.c | 42 ++++++++++++++++++++------------------- 2 files changed, 49 insertions(+), 37 deletions(-) diff --git a/Tests/test_file_webp.py b/Tests/test_file_webp.py index 8e91fdd1f..d81abb85c 100644 --- a/Tests/test_file_webp.py +++ b/Tests/test_file_webp.py @@ -8,6 +8,13 @@ except: skip('webp support not installed') +def test_version(): + assert_no_exception(lambda: _webp.WebPDecoderVersion()) + +def test_good_alpha(): + assert_equal(_webp.WebPDecoderBuggyAlpha(), 0) + + def test_read_rgb(): file_path = "Images/lena.webp" @@ -24,23 +31,6 @@ def test_read_rgb(): assert_image_equal(image, target) -def test_read_rgba(): - # Generated with `cwebp transparent.png -o transparent.webp` - file_path = "Images/transparent.webp" - image = Image.open(file_path) - - assert_equal(image.mode, "RGBA") - assert_equal(image.size, (200, 150)) - assert_equal(image.format, "WEBP") - assert_no_exception(lambda: image.load()) - assert_no_exception(lambda: image.getdata()) - - orig_bytes = image.tobytes() - - target = Image.open('Images/transparent.png') - assert_image_similar(image, target, 20.0) - - def test_write_rgb(): """ Can we write a RGB mode file to webp without error. Does it have the bits we @@ -98,3 +88,23 @@ def test_write_rgba(): assert_no_exception(image.getdata) assert_image_similar(image, pil_image, 1.0) + +if _webp.WebPDecoderBuggyAlpha(): + skip("Buggy early version of webp installed, not testing transparency") + +def test_read_rgba(): + # Generated with `cwebp transparent.png -o transparent.webp` + file_path = "Images/transparent.webp" + image = Image.open(file_path) + + assert_equal(image.mode, "RGBA") + assert_equal(image.size, (200, 150)) + assert_equal(image.format, "WEBP") + assert_no_exception(lambda: image.load()) + assert_no_exception(lambda: image.getdata()) + + orig_bytes = image.tobytes() + + target = Image.open('Images/transparent.png') + assert_image_similar(image, target, 20.0) + diff --git a/_webp.c b/_webp.c index b3fb73a00..a1048f718 100644 --- a/_webp.c +++ b/_webp.c @@ -148,34 +148,34 @@ PyObject* WebPDecode_wrapper(PyObject* self, PyObject* args) } if (config.output.colorspace < MODE_YUV) { - bytes = PyBytes_FromStringAndSize((char *)config.output.u.RGBA.rgba, config.output.u.RGBA.size); + bytes = PyBytes_FromStringAndSize((char *)config.output.u.RGBA.rgba, + config.output.u.RGBA.size); } else { - // Skipping YUV for now. - bytes = PyBytes_FromStringAndSize((char *)config.output.u.YUVA.y, config.output.u.YUVA.y_size); - } - switch(config.output.colorspace) { - // UNDONE, alternate orderings - case MODE_RGB: - mode = "RGB"; - break; - case MODE_RGBA: - mode = "RGBA"; - break; - default: - mode = "ERR"; + // 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); } - height = config.output.height; - width = config.output.width; - - - ret = Py_BuildValue("SiiS", bytes, width, height, PyBytes_FromString(mode)); + ret = Py_BuildValue("SiiS", bytes, config.output.width, + config.output.height, PyBytes_FromString(mode)); WebPFreeDecBuffer(&config.output); 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){ + return Py_BuildValue("i", WebPGetDecoderVersion()); +} - +/* + * The version of webp that ships with (0.1.2) 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){ + return Py_BuildValue("i", WebPGetDecoderVersion()==0x0102); +} static PyMethodDef webpMethods[] = { @@ -183,6 +183,8 @@ static PyMethodDef webpMethods[] = {"WebPEncodeRGB", WebPEncodeRGB_wrapper, METH_VARARGS, "WebPEncodeRGB"}, {"WebPEncodeRGBA", WebPEncodeRGBA_wrapper, METH_VARARGS, "WebPEncodeRGBA"}, {"WebPDecode", WebPDecode_wrapper, METH_VARARGS, "WebPDecode"}, + {"WebPDecoderVersion", WebPDecoderVersion_wrapper, METH_VARARGS, "WebPVersion"}, + {"WebPDecoderBuggyAlpha", WebPDecoderBuggyAlpha_wrapper, METH_VARARGS, "WebPDecoderBuggyAlpha"}, {NULL, NULL} }; From a7488d287e3dd7bcef1af41ba8d28a84b4eeac59 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 13 May 2013 21:43:13 -0700 Subject: [PATCH 09/27] py3k -- PyUnicode_FromString instead of PyString_fromString --- _webp.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/_webp.c b/_webp.c index a1048f718..75f86a422 100644 --- a/_webp.c +++ b/_webp.c @@ -114,7 +114,7 @@ PyObject* WebPDecode_wrapper(PyObject* self, PyObject* args) PyBytesObject *webp_string; uint8_t *webp; Py_ssize_t size; - PyObject *ret, *bytes; + PyObject *ret, *bytes, *pymode; WebPDecoderConfig config; VP8StatusCode vp8_status_code = VP8_STATUS_OK; char* mode = "RGB"; @@ -157,8 +157,13 @@ PyObject* WebPDecode_wrapper(PyObject* self, PyObject* args) config.output.u.YUVA.y_size); } +#if PY_VERSION_HEX >= 0x03000000 + pymode = PyUnicode_FromString(mode); +#else + pymode = PyString_FromString(mode); +#endif ret = Py_BuildValue("SiiS", bytes, config.output.width, - config.output.height, PyBytes_FromString(mode)); + config.output.height, pymode); WebPFreeDecBuffer(&config.output); return ret; } From 9ac38c1a3a9633a2e6c334bd87b1c677aaf063bd Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 13 May 2013 21:46:50 -0700 Subject: [PATCH 10/27] using the Py_RETURN_NONE macro --- _webp.c | 39 +++++++++++++-------------------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/_webp.c b/_webp.c index 75f86a422..d9b982237 100644 --- a/_webp.c +++ b/_webp.c @@ -15,8 +15,7 @@ PyObject* WebPGetFeatures_wrapper(PyObject* self, PyObject* args) if (!PyArg_ParseTuple(args, "S", &webp_string)) { - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } PyBytes_AsStringAndSize((PyObject *) webp_string, (char**)&webp, &size); @@ -29,9 +28,8 @@ PyObject* WebPGetFeatures_wrapper(PyObject* self, PyObject* args) } else { // TODO: raise some sort of error printf("Error occured checking webp file with code: %d\n", vp8_status_code); - Py_INCREF(Py_None); - return Py_None; - } + Py_RETURN_NONE; + } return Py_BuildValue("b", features.has_alpha); } @@ -50,15 +48,13 @@ PyObject* WebPEncodeRGB_wrapper(PyObject* self, PyObject* args) size_t ret_size; if (!PyArg_ParseTuple(args, "Siiif", &rgb_string, &width, &height, &stride, &quality_factor)) { - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } PyBytes_AsStringAndSize((PyObject *) rgb_string, (char**)&rgb, &size); if (stride * height > size) { - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } ret_size = WebPEncodeRGB(rgb, width, height, stride, quality_factor, &output); @@ -67,9 +63,7 @@ PyObject* WebPEncodeRGB_wrapper(PyObject* self, PyObject* args) free(output); return ret; } - Py_INCREF(Py_None); - return Py_None; - + Py_RETURN_NONE; } @@ -86,15 +80,13 @@ PyObject* WebPEncodeRGBA_wrapper(PyObject* self, PyObject* args) size_t ret_size; if (!PyArg_ParseTuple(args, "Siiif", &rgba_string, &width, &height, &stride, &quality_factor)) { - Py_INCREF(Py_None); - return Py_None; - } + Py_RETURN_NONE; + } PyBytes_AsStringAndSize((PyObject *) rgba_string, (char**)&rgba, &size); if (stride * height > size) { - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } ret_size = WebPEncodeRGBA(rgba, width, height, stride, quality_factor, &output); @@ -103,9 +95,7 @@ PyObject* WebPEncodeRGBA_wrapper(PyObject* self, PyObject* args) free(output); return ret; } - Py_INCREF(Py_None); - return Py_None; - + Py_RETURN_NONE; } @@ -120,13 +110,11 @@ PyObject* WebPDecode_wrapper(PyObject* self, PyObject* args) char* mode = "RGB"; if (!PyArg_ParseTuple(args, "S", &webp_string)) { - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } if (!WebPInitDecoderConfig(&config)) { - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } PyBytes_AsStringAndSize((PyObject *) webp_string, (char**)&webp, &size); @@ -143,8 +131,7 @@ PyObject* WebPDecode_wrapper(PyObject* self, PyObject* args) } if (vp8_status_code != VP8_STATUS_OK) { - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } if (config.output.colorspace < MODE_YUV) { From 21e3fd2eb77ea1f86ef22865906f7239d12588d2 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 13 May 2013 21:47:35 -0700 Subject: [PATCH 11/27] untabified whitespace --- _webp.c | 86 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/_webp.c b/_webp.c index d9b982237..34da4d5ee 100644 --- a/_webp.c +++ b/_webp.c @@ -15,7 +15,7 @@ PyObject* WebPGetFeatures_wrapper(PyObject* self, PyObject* args) if (!PyArg_ParseTuple(args, "S", &webp_string)) { - Py_RETURN_NONE; + Py_RETURN_NONE; } PyBytes_AsStringAndSize((PyObject *) webp_string, (char**)&webp, &size); @@ -28,7 +28,7 @@ PyObject* WebPGetFeatures_wrapper(PyObject* self, PyObject* args) } else { // TODO: raise some sort of error printf("Error occured checking webp file with code: %d\n", vp8_status_code); - Py_RETURN_NONE; + Py_RETURN_NONE; } return Py_BuildValue("b", features.has_alpha); @@ -48,13 +48,13 @@ PyObject* WebPEncodeRGB_wrapper(PyObject* self, PyObject* args) size_t ret_size; if (!PyArg_ParseTuple(args, "Siiif", &rgb_string, &width, &height, &stride, &quality_factor)) { - Py_RETURN_NONE; + Py_RETURN_NONE; } PyBytes_AsStringAndSize((PyObject *) rgb_string, (char**)&rgb, &size); if (stride * height > size) { - Py_RETURN_NONE; + Py_RETURN_NONE; } ret_size = WebPEncodeRGB(rgb, width, height, stride, quality_factor, &output); @@ -63,7 +63,7 @@ PyObject* WebPEncodeRGB_wrapper(PyObject* self, PyObject* args) free(output); return ret; } - Py_RETURN_NONE; + Py_RETURN_NONE; } @@ -80,13 +80,13 @@ PyObject* WebPEncodeRGBA_wrapper(PyObject* self, PyObject* args) size_t ret_size; if (!PyArg_ParseTuple(args, "Siiif", &rgba_string, &width, &height, &stride, &quality_factor)) { - Py_RETURN_NONE; + Py_RETURN_NONE; } PyBytes_AsStringAndSize((PyObject *) rgba_string, (char**)&rgba, &size); if (stride * height > size) { - Py_RETURN_NONE; + Py_RETURN_NONE; } ret_size = WebPEncodeRGBA(rgba, width, height, stride, quality_factor, &output); @@ -95,7 +95,7 @@ PyObject* WebPEncodeRGBA_wrapper(PyObject* self, PyObject* args) free(output); return ret; } - Py_RETURN_NONE; + Py_RETURN_NONE; } @@ -105,60 +105,60 @@ PyObject* WebPDecode_wrapper(PyObject* self, PyObject* args) uint8_t *webp; Py_ssize_t size; PyObject *ret, *bytes, *pymode; - WebPDecoderConfig config; + WebPDecoderConfig config; VP8StatusCode vp8_status_code = VP8_STATUS_OK; - char* mode = "RGB"; + char* mode = "RGB"; if (!PyArg_ParseTuple(args, "S", &webp_string)) { - Py_RETURN_NONE; + Py_RETURN_NONE; } - if (!WebPInitDecoderConfig(&config)) { - 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"; - } - vp8_status_code = WebPDecode(webp, size, &config); - } - - if (vp8_status_code != VP8_STATUS_OK) { - Py_RETURN_NONE; - } - - 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 (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"; + } + vp8_status_code = WebPDecode(webp, size, &config); + } + + if (vp8_status_code != VP8_STATUS_OK) { + Py_RETURN_NONE; + } + + 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("SiiS", bytes, config.output.width, - config.output.height, pymode); - WebPFreeDecBuffer(&config.output); - return ret; + ret = Py_BuildValue("SiiS", bytes, config.output.width, + config.output.height, pymode); + WebPFreeDecBuffer(&config.output); + 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){ - return Py_BuildValue("i", WebPGetDecoderVersion()); + return Py_BuildValue("i", WebPGetDecoderVersion()); } /* @@ -166,7 +166,7 @@ PyObject* WebPDecoderVersion_wrapper(PyObject* self, PyObject* args){ * Files that are valid with 0.3 are reported as being invalid. */ PyObject* WebPDecoderBuggyAlpha_wrapper(PyObject* self, PyObject* args){ - return Py_BuildValue("i", WebPGetDecoderVersion()==0x0102); + return Py_BuildValue("i", WebPGetDecoderVersion()==0x0102); } static PyMethodDef webpMethods[] = From 94239540db0921c46257a17e65433555eb509551 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 13 May 2013 22:21:52 -0700 Subject: [PATCH 12/27] fixing deferring the rgba tests, whitespace --- Tests/test_file_webp.py | 41 ++++++++++++++++++++++------------------- _webp.c | 4 ++-- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/Tests/test_file_webp.py b/Tests/test_file_webp.py index d81abb85c..30fd94d58 100644 --- a/Tests/test_file_webp.py +++ b/Tests/test_file_webp.py @@ -9,11 +9,11 @@ except: def test_version(): - assert_no_exception(lambda: _webp.WebPDecoderVersion()) + assert_no_exception(lambda: _webp.WebPDecoderVersion()) def test_good_alpha(): - assert_equal(_webp.WebPDecoderBuggyAlpha(), 0) - + assert_equal(_webp.WebPDecoderBuggyAlpha(), 0) + def test_read_rgb(): @@ -77,7 +77,10 @@ def test_write_rgba(): pil_image = Image.new("RGBA", (10, 10), (255, 0, 0, 20)) pil_image.save(temp_file) - + + if _webp.WebPDecoderBuggyAlpha(): + return + image = Image.open(temp_file) image.load() @@ -90,21 +93,21 @@ def test_write_rgba(): assert_image_similar(image, pil_image, 1.0) if _webp.WebPDecoderBuggyAlpha(): - skip("Buggy early version of webp installed, not testing transparency") + skip("Buggy early version of webp installed, not testing transparency") def test_read_rgba(): - # Generated with `cwebp transparent.png -o transparent.webp` - file_path = "Images/transparent.webp" - image = Image.open(file_path) - - assert_equal(image.mode, "RGBA") - assert_equal(image.size, (200, 150)) - assert_equal(image.format, "WEBP") - assert_no_exception(lambda: image.load()) - assert_no_exception(lambda: image.getdata()) - - orig_bytes = image.tobytes() - - target = Image.open('Images/transparent.png') - assert_image_similar(image, target, 20.0) + # Generated with `cwebp transparent.png -o transparent.webp` + file_path = "Images/transparent.webp" + image = Image.open(file_path) + + assert_equal(image.mode, "RGBA") + assert_equal(image.size, (200, 150)) + assert_equal(image.format, "WEBP") + assert_no_exception(lambda: image.load()) + assert_no_exception(lambda: image.getdata()) + + orig_bytes = image.tobytes() + + target = Image.open('Images/transparent.png') + assert_image_similar(image, target, 20.0) diff --git a/_webp.c b/_webp.c index 34da4d5ee..ec46fe317 100644 --- a/_webp.c +++ b/_webp.c @@ -162,11 +162,11 @@ PyObject* WebPDecoderVersion_wrapper(PyObject* self, PyObject* args){ } /* - * The version of webp that ships with (0.1.2) Ubuntu 12.04 doesn't handle alpha well. + * 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){ - return Py_BuildValue("i", WebPGetDecoderVersion()==0x0102); + return Py_BuildValue("i", WebPGetDecoderVersion()==0x0103); } static PyMethodDef webpMethods[] = From 11ce826121df510d7c1bedab3b01ab0f2798b94a Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 14 May 2013 20:45:07 -0700 Subject: [PATCH 13/27] mixed 8 char tabs & spaces -> spaces --- libImaging/JpegEncode.c | 108 ++++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/libImaging/JpegEncode.c b/libImaging/JpegEncode.c index 221f8ccfa..87c3ba434 100644 --- a/libImaging/JpegEncode.c +++ b/libImaging/JpegEncode.c @@ -22,7 +22,7 @@ #include "Imaging.h" -#ifdef HAVE_LIBJPEG +#ifdef HAVE_LIBJPEG #undef HAVE_PROTOTYPES #undef HAVE_STDLIB_H @@ -36,7 +36,7 @@ #include "Jpeg.h" /* -------------------------------------------------------------------- */ -/* Suspending output handler */ +/* Suspending output handler */ /* -------------------------------------------------------------------- */ METHODDEF(void) @@ -64,16 +64,16 @@ jpeg_buffer_dest(j_compress_ptr cinfo, JPEGDESTINATION* destination) /* -------------------------------------------------------------------- */ -/* Error handler */ +/* Error handler */ /* -------------------------------------------------------------------- */ METHODDEF(void) error(j_common_ptr cinfo) { - JPEGERROR* error; - error = (JPEGERROR*) cinfo->err; - (*cinfo->err->output_message) (cinfo); - longjmp(error->setjmp_buffer, 1); + JPEGERROR* error; + error = (JPEGERROR*) cinfo->err; + (*cinfo->err->output_message) (cinfo); + longjmp(error->setjmp_buffer, 1); } @@ -146,59 +146,59 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) /* Use custom quantization tables */ if (context->qtables) { - int i; - int quality = 100; - if (context->quality > 0) { - quality = context->quality; - } - for (i = 0; i < sizeof(context->qtables)/sizeof(unsigned int); i++) { - // TODO: Should add support for none baseline - jpeg_add_quant_table(&context->cinfo, i, context->qtables[i], - quality, TRUE); - } + int i; + int quality = 100; + if (context->quality > 0) { + quality = context->quality; + } + for (i = 0; i < sizeof(context->qtables)/sizeof(unsigned int); i++) { + // TODO: Should add support for none baseline + jpeg_add_quant_table(&context->cinfo, i, context->qtables[i], + quality, TRUE); + } } else if (context->quality > 0) { jpeg_set_quality(&context->cinfo, context->quality, 1); } /* Set subsampling options */ switch (context->subsampling) - { - case 0: /* 1x1 1x1 1x1 (4:4:4) : None */ + { + case 0: /* 1x1 1x1 1x1 (4:4:4) : None */ { - context->cinfo.comp_info[0].h_samp_factor = 1; - context->cinfo.comp_info[0].v_samp_factor = 1; - context->cinfo.comp_info[1].h_samp_factor = 1; - context->cinfo.comp_info[1].v_samp_factor = 1; - context->cinfo.comp_info[2].h_samp_factor = 1; - context->cinfo.comp_info[2].v_samp_factor = 1; - break; + context->cinfo.comp_info[0].h_samp_factor = 1; + context->cinfo.comp_info[0].v_samp_factor = 1; + context->cinfo.comp_info[1].h_samp_factor = 1; + context->cinfo.comp_info[1].v_samp_factor = 1; + context->cinfo.comp_info[2].h_samp_factor = 1; + context->cinfo.comp_info[2].v_samp_factor = 1; + break; } - case 1: /* 2x1, 1x1, 1x1 (4:2:2) : Medium */ + case 1: /* 2x1, 1x1, 1x1 (4:2:2) : Medium */ { - context->cinfo.comp_info[0].h_samp_factor = 2; - context->cinfo.comp_info[0].v_samp_factor = 1; - context->cinfo.comp_info[1].h_samp_factor = 1; - context->cinfo.comp_info[1].v_samp_factor = 1; - context->cinfo.comp_info[2].h_samp_factor = 1; - context->cinfo.comp_info[2].v_samp_factor = 1; - break; + context->cinfo.comp_info[0].h_samp_factor = 2; + context->cinfo.comp_info[0].v_samp_factor = 1; + context->cinfo.comp_info[1].h_samp_factor = 1; + context->cinfo.comp_info[1].v_samp_factor = 1; + context->cinfo.comp_info[2].h_samp_factor = 1; + context->cinfo.comp_info[2].v_samp_factor = 1; + break; } - case 2: /* 2x2, 1x1, 1x1 (4:1:1) : High */ + case 2: /* 2x2, 1x1, 1x1 (4:1:1) : High */ { - context->cinfo.comp_info[0].h_samp_factor = 2; - context->cinfo.comp_info[0].v_samp_factor = 2; - context->cinfo.comp_info[1].h_samp_factor = 1; - context->cinfo.comp_info[1].v_samp_factor = 1; - context->cinfo.comp_info[2].h_samp_factor = 1; - context->cinfo.comp_info[2].v_samp_factor = 1; - break; + context->cinfo.comp_info[0].h_samp_factor = 2; + context->cinfo.comp_info[0].v_samp_factor = 2; + context->cinfo.comp_info[1].h_samp_factor = 1; + context->cinfo.comp_info[1].v_samp_factor = 1; + context->cinfo.comp_info[2].h_samp_factor = 1; + context->cinfo.comp_info[2].v_samp_factor = 1; + break; } - default: + default: { - /* Use the lib's default */ - break; + /* Use the lib's default */ + break; } - } + } if (context->progressive) jpeg_simple_progression(&context->cinfo); context->cinfo.smoothing_factor = context->smooth; @@ -219,19 +219,19 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) jpeg_start_compress(&context->cinfo, FALSE); /* suppress extra section */ context->extra_offset = context->extra_size; - //add exif header - if (context->rawExifLen > 0) - jpeg_write_marker(&context->cinfo, JPEG_APP0+1, (unsigned char*)context->rawExif, context->rawExifLen); + //add exif header + if (context->rawExifLen > 0) + jpeg_write_marker(&context->cinfo, JPEG_APP0+1, (unsigned char*)context->rawExif, context->rawExifLen); break; default: /* interchange stream */ jpeg_start_compress(&context->cinfo, TRUE); - //add exif header - if (context->rawExifLen > 0) - jpeg_write_marker(&context->cinfo, JPEG_APP0+1, (unsigned char*)context->rawExif, context->rawExifLen); + //add exif header + if (context->rawExifLen > 0) + jpeg_write_marker(&context->cinfo, JPEG_APP0+1, (unsigned char*)context->rawExif, context->rawExifLen); - break; + break; } state->state++; /* fall through */ @@ -253,7 +253,7 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) else break; } else - state->state++; + state->state++; case 3: From 37359369ceaea5e888762f0e4e77fcf91fda4c8c Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 14 May 2013 22:29:31 -0700 Subject: [PATCH 14/27] Code to ensure buffer space for exif write and jpeg_write_scanline calls, tests, corrected buffer length calculation --- PIL/JpegImagePlugin.py | 7 ++++--- Tests/test_file_jpeg.py | 6 ++++++ libImaging/JpegEncode.c | 28 ++++++++++++++++++---------- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index aeff66879..4f36f5a1f 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -554,6 +554,7 @@ def _save(im, fp, filename): info.get("exif", b"") ) + # if we optimize, libjpeg needs a buffer big enough to hold the whole image in a shot. # Guessing on the size, at im.size bytes. (raw pizel size is channels*size, this # is a value that's been used in a django patch. @@ -562,9 +563,9 @@ def _save(im, fp, filename): if "optimize" in info: bufsize = im.size[0]*im.size[1] - # The exif info needs to be written as one block. Ensure that our buffer is big enough - if len(info.get("exif",b"")) > max(ImageFile.MAXBLOCK, bufsize): - bufsize = 2*len(info.get("exif",b"")) + # The exif info needs to be written as one block, + APP1, + one spare byte. + # Ensure that our buffer is big enough + bufsize = max(ImageFile.MAXBLOCK, bufsize, len(info.get("exif",b"")) + 5 ) ImageFile._save(im, fp, [("jpeg", (0,0)+im.size, 0, rawmode)], bufsize) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 556786aa7..1027d0174 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -120,6 +120,12 @@ def test_optimize_large_buffer(): im = Image.new("RGB", (4096,4096), 0xff3333) im.save(f, format="JPEG", optimize=True) +def test_large_exif(): + #https://github.com/python-imaging/Pillow/issues/148 + f = tempfile('temp.jpg') + im = lena() + im.save(f,'JPEG', quality=90, exif=b"1"*65532) + def test_progressive(): im1 = roundtrip(lena()) im2 = roundtrip(lena(), progressive=1) diff --git a/libImaging/JpegEncode.c b/libImaging/JpegEncode.c index 87c3ba434..5d0b85d6a 100644 --- a/libImaging/JpegEncode.c +++ b/libImaging/JpegEncode.c @@ -219,24 +219,29 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) jpeg_start_compress(&context->cinfo, FALSE); /* suppress extra section */ context->extra_offset = context->extra_size; - //add exif header - if (context->rawExifLen > 0) - jpeg_write_marker(&context->cinfo, JPEG_APP0+1, (unsigned char*)context->rawExif, context->rawExifLen); - break; default: /* interchange stream */ jpeg_start_compress(&context->cinfo, TRUE); - //add exif header - if (context->rawExifLen > 0) - jpeg_write_marker(&context->cinfo, JPEG_APP0+1, (unsigned char*)context->rawExif, context->rawExifLen); - break; } state->state++; /* fall through */ case 2: + // check for exif len + 'APP1' header bytes + if (context->rawExifLen + 5 > context->destination.pub.free_in_buffer){ + break; + } + //add exif header + if (context->rawExifLen > 0){ + jpeg_write_marker(&context->cinfo, JPEG_APP0+1, + (unsigned char*)context->rawExif, context->rawExifLen); + } + + state->state++; + /* fall through */ + case 3: if (context->extra) { /* copy extra buffer to output buffer */ @@ -255,7 +260,10 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) } else state->state++; - case 3: + case 4: + if (1024 > context->destination.pub.free_in_buffer){ + break; + } ok = 1; while (state->y < state->ysize) { @@ -273,7 +281,7 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) state->state++; /* fall through */ - case 4: + case 5: /* Finish compression */ if (context->destination.pub.free_in_buffer < 100) From 596a4ef1a3e8816f1e5991ef783f5226121381c4 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Wed, 15 May 2013 16:54:49 -0400 Subject: [PATCH 15/27] Move note --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index c3e40bb02..91d78ed86 100644 --- a/README.rst +++ b/README.rst @@ -21,8 +21,6 @@ The fork author's goal is to foster active development of PIL through: Porting your PIL code to Pillow ------------------------------- -.. Note:: PIL and Pillow currently cannot co-exist in the same environment. If you want to use Pillow, please remove PIL first. - Pillow is a functional drop-in replacement for the Python Imaging Library. To run your existing PIL-compatible code with Pillow, it needs to be modified to import the ``Imaging`` module from the ``PIL`` namespace *instead* of the global namespace. I.e. change:: import Image @@ -76,6 +74,8 @@ PIL needs you! Please help us maintain the Python Imaging Library here: Installation ------------ +.. Note:: PIL and Pillow currently cannot co-exist in the same environment. If you want to use Pillow, please remove PIL first. + Platform support ~~~~~~~~~~~~~~~~ From a235a69d0c7482979e0d470b81143d8fbddbf3b4 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 15 May 2013 16:39:01 -0700 Subject: [PATCH 16/27] removed WebPGetFeatures_wrapper, as it's not used from python anymore --- _webp.c | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/_webp.c b/_webp.c index ec46fe317..83be7828b 100644 --- a/_webp.c +++ b/_webp.c @@ -5,36 +5,6 @@ #include -PyObject* WebPGetFeatures_wrapper(PyObject* self, PyObject* args) -{ - PyBytesObject *webp_string; - uint8_t* webp = NULL; - VP8StatusCode vp8_status_code = VP8_STATUS_OK; - Py_ssize_t size; - WebPBitstreamFeatures features; - - - if (!PyArg_ParseTuple(args, "S", &webp_string)) { - Py_RETURN_NONE; - } - - PyBytes_AsStringAndSize((PyObject *) webp_string, (char**)&webp, &size); - - vp8_status_code = WebPGetFeatures(webp, size, &features); - - if (vp8_status_code == VP8_STATUS_OK) { - printf("%i", features.has_alpha); - - } else { - // TODO: raise some sort of error - printf("Error occured checking webp file with code: %d\n", vp8_status_code); - Py_RETURN_NONE; - } - - return Py_BuildValue("b", features.has_alpha); -} - - PyObject* WebPEncodeRGB_wrapper(PyObject* self, PyObject* args) { PyBytesObject *rgb_string; @@ -171,7 +141,6 @@ PyObject* WebPDecoderBuggyAlpha_wrapper(PyObject* self, PyObject* args){ static PyMethodDef webpMethods[] = { - {"WebPGetFeatures", WebPGetFeatures_wrapper, METH_VARARGS, "WebPGetFeatures"}, {"WebPEncodeRGB", WebPEncodeRGB_wrapper, METH_VARARGS, "WebPEncodeRGB"}, {"WebPEncodeRGBA", WebPEncodeRGBA_wrapper, METH_VARARGS, "WebPEncodeRGBA"}, {"WebPDecode", WebPDecode_wrapper, METH_VARARGS, "WebPDecode"}, From 11a0fb5f76abf94dc91cf5f2cb8eb1bf2c60828a Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 15 May 2013 16:56:59 -0700 Subject: [PATCH 17/27] consolidated to WebPEncode_wrapper --- PIL/WebPImagePlugin.py | 13 ++++------- _webp.c | 52 +++++++++--------------------------------- 2 files changed, 16 insertions(+), 49 deletions(-) diff --git a/PIL/WebPImagePlugin.py b/PIL/WebPImagePlugin.py index f1478a965..6c1fd3a25 100644 --- a/PIL/WebPImagePlugin.py +++ b/PIL/WebPImagePlugin.py @@ -5,8 +5,8 @@ import _webp _VALID_WEBP_ENCODERS_BY_MODE = { - "RGB": _webp.WebPEncodeRGB, - "RGBA": _webp.WebPEncodeRGBA, + "RGB": _webp.WebPEncode, + "RGBA": _webp.WebPEncode, } @@ -50,20 +50,17 @@ class WebPImageFile(ImageFile.ImageFile): def _save(im, fp, filename): image_mode = im.mode - if image_mode not in _VALID_WEBP_ENCODERS_BY_MODE: + if im.mode not in _VALID_WEBP_ENCODERS_BY_MODE: raise IOError("cannot write mode %s as WEBP" % image_mode) - - webp_encoder = _VALID_WEBP_ENCODERS_BY_MODE[image_mode] - stride = im.size[0] * _STRIDE_MULTIPLIERS_BY_MODE[image_mode] quality = im.encoderinfo.get("quality", 80) - data = webp_encoder( + data = _webp.WebPEncode( im.tobytes(), im.size[0], im.size[1], - stride, float(quality), + im.mode ) fp.write(data) diff --git a/_webp.c b/_webp.c index 83be7828b..882242c31 100644 --- a/_webp.c +++ b/_webp.c @@ -5,61 +5,32 @@ #include -PyObject* WebPEncodeRGB_wrapper(PyObject* self, PyObject* args) +PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args) { PyBytesObject *rgb_string; int width; int height; - int stride; float quality_factor; uint8_t *rgb; uint8_t *output; + char *mode; Py_ssize_t size; size_t ret_size; - if (!PyArg_ParseTuple(args, "Siiif", &rgb_string, &width, &height, &stride, &quality_factor)) { + if (!PyArg_ParseTuple(args, "Siifs", &rgb_string, &width, &height, &quality_factor, &mode)) { Py_RETURN_NONE; } PyBytes_AsStringAndSize((PyObject *) rgb_string, (char**)&rgb, &size); - if (stride * height > size) { - Py_RETURN_NONE; - } + if (strcmp(mode, "RGBA")==0){ + ret_size = WebPEncodeRGBA(rgb, width, height, 4* width, quality_factor, &output); + } else if (strcmp(mode, "RGB")==0){ + ret_size = WebPEncodeRGB(rgb, width, height, 3* width, quality_factor, &output); + } else { + Py_RETURN_NONE; + } - ret_size = WebPEncodeRGB(rgb, width, height, stride, quality_factor, &output); - if (ret_size > 0) { - PyObject *ret = PyBytes_FromStringAndSize((char*)output, ret_size); - free(output); - return ret; - } - Py_RETURN_NONE; -} - - -PyObject* WebPEncodeRGBA_wrapper(PyObject* self, PyObject* args) -{ - PyBytesObject *rgba_string; - int width; - int height; - int stride; - float quality_factor; - uint8_t *rgba; - uint8_t *output; - Py_ssize_t size; - size_t ret_size; - - if (!PyArg_ParseTuple(args, "Siiif", &rgba_string, &width, &height, &stride, &quality_factor)) { - Py_RETURN_NONE; - } - - PyBytes_AsStringAndSize((PyObject *) rgba_string, (char**)&rgba, &size); - - if (stride * height > size) { - Py_RETURN_NONE; - } - - ret_size = WebPEncodeRGBA(rgba, width, height, stride, quality_factor, &output); if (ret_size > 0) { PyObject *ret = PyBytes_FromStringAndSize((char*)output, ret_size); free(output); @@ -141,8 +112,7 @@ PyObject* WebPDecoderBuggyAlpha_wrapper(PyObject* self, PyObject* args){ static PyMethodDef webpMethods[] = { - {"WebPEncodeRGB", WebPEncodeRGB_wrapper, METH_VARARGS, "WebPEncodeRGB"}, - {"WebPEncodeRGBA", WebPEncodeRGBA_wrapper, METH_VARARGS, "WebPEncodeRGBA"}, + {"WebPEncode", WebPEncode_wrapper, METH_VARARGS, "WebPEncode"}, {"WebPDecode", WebPDecode_wrapper, METH_VARARGS, "WebPDecode"}, {"WebPDecoderVersion", WebPDecoderVersion_wrapper, METH_VARARGS, "WebPVersion"}, {"WebPDecoderBuggyAlpha", WebPDecoderBuggyAlpha_wrapper, METH_VARARGS, "WebPDecoderBuggyAlpha"}, From 89b6820530b15b93e53f65ae363415465c7cb27c Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 15 May 2013 17:04:17 -0700 Subject: [PATCH 18/27] checking raw image length, cleanup and DRY --- PIL/WebPImagePlugin.py | 21 ++++----------------- _webp.c | 11 +++++++---- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/PIL/WebPImagePlugin.py b/PIL/WebPImagePlugin.py index 6c1fd3a25..260b43e3d 100644 --- a/PIL/WebPImagePlugin.py +++ b/PIL/WebPImagePlugin.py @@ -4,24 +4,11 @@ from io import BytesIO import _webp -_VALID_WEBP_ENCODERS_BY_MODE = { - "RGB": _webp.WebPEncode, - "RGBA": _webp.WebPEncode, +_VALID_WEBP_MODES = { + "RGB": True, + "RGBA": True, } - -_VALID_WEBP_DECODERS_BY_MODE = { - "RGB": _webp.WebPDecode, - "RGBA": _webp.WebPDecode, - } - - -_STRIDE_MULTIPLIERS_BY_MODE = { - "RGB": 3, - "RGBA": 4, - } - - _VP8_MODES_BY_IDENTIFIER = { b"VP8 ": "RGB", b"VP8X": "RGBA", @@ -50,7 +37,7 @@ class WebPImageFile(ImageFile.ImageFile): def _save(im, fp, filename): image_mode = im.mode - if im.mode not in _VALID_WEBP_ENCODERS_BY_MODE: + if im.mode not in _VALID_WEBP_MODES: raise IOError("cannot write mode %s as WEBP" % image_mode) quality = im.encoderinfo.get("quality", 80) diff --git a/_webp.c b/_webp.c index 882242c31..806472ab4 100644 --- a/_webp.c +++ b/_webp.c @@ -7,7 +7,6 @@ PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args) { - PyBytesObject *rgb_string; int width; int height; float quality_factor; @@ -17,15 +16,19 @@ PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args) Py_ssize_t size; size_t ret_size; - if (!PyArg_ParseTuple(args, "Siifs", &rgb_string, &width, &height, &quality_factor, &mode)) { + if (!PyArg_ParseTuple(args, "s#iifs",(char**)&rgb, &size, &width, &height, &quality_factor, &mode)) { Py_RETURN_NONE; } - PyBytes_AsStringAndSize((PyObject *) rgb_string, (char**)&rgb, &size); - if (strcmp(mode, "RGBA")==0){ + if (size < width * height * 4){ + Py_RETURN_NONE; + } ret_size = WebPEncodeRGBA(rgb, width, height, 4* width, quality_factor, &output); } else if (strcmp(mode, "RGB")==0){ + if (size < width * height * 3){ + Py_RETURN_NONE; + } ret_size = WebPEncodeRGB(rgb, width, height, 3* width, quality_factor, &output); } else { Py_RETURN_NONE; From a5499170f9d030c07bea667dc66f3ae86484e9fb Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 15 May 2013 17:22:41 -0700 Subject: [PATCH 19/27] Feature for transparent webp --- selftest.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/selftest.py b/selftest.py index 91a14bb54..252231fbf 100644 --- a/selftest.py +++ b/selftest.py @@ -190,6 +190,14 @@ if __name__ == "__main__": check_module("FREETYPE2", "_imagingft") check_module("LITTLECMS", "_imagingcms") check_module("WEBP", "_webp") + try: + import _webp + if _webp.WebPDecoderBuggyAlpha(): + print("***", "Transparent WEBP", "support not installed") + else: + print("---", "Transparent WEBP", "support ok") + except Exception: + pass print("-"*68) # use doctest to make sure the test program behaves as documented! From 5712126d3c3eb4860ea4254b910c7bf50f353f12 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 15 May 2013 18:42:45 -0700 Subject: [PATCH 20/27] Noted tested versions of libwebp --- README.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index c3e40bb02..caf924e2e 100644 --- a/README.rst +++ b/README.rst @@ -134,7 +134,9 @@ Some (most?) of Pillow's features require external libraries. * **littlecms** provides color management -* **libwebp** provides the Webp format. +* **libwebp** provides the Webp format. + + * Pillow has been tested with version **0.1.3**, which does not read transparent webp files. Version **0.3.0** supports transparency. If the prerequisites are installed in the standard library locations for your machine (e.g. /usr or /usr/local), no additional configuration should be required. If they are installed in a non-standard location, you may need to configure setuptools to use those locations (i.e. by editing setup.py and/or setup.cfg) From 454a914b565cb81cfebaef63dbb118aa902c7f79 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 15 May 2013 19:57:45 -0700 Subject: [PATCH 21/27] Whitespace, 8 space tabs -> 4 spaces --- libImaging/JpegDecode.c | 202 ++++++++++++++++++++-------------------- 1 file changed, 101 insertions(+), 101 deletions(-) diff --git a/libImaging/JpegDecode.c b/libImaging/JpegDecode.c index 11b2767ba..bc8cfa3c2 100644 --- a/libImaging/JpegDecode.c +++ b/libImaging/JpegDecode.c @@ -24,7 +24,7 @@ #include "Imaging.h" -#ifdef HAVE_LIBJPEG +#ifdef HAVE_LIBJPEG #undef HAVE_PROTOTYPES #undef HAVE_STDLIB_H @@ -39,7 +39,7 @@ /* -------------------------------------------------------------------- */ -/* Suspending input handler */ +/* Suspending input handler */ /* -------------------------------------------------------------------- */ METHODDEF(void) @@ -61,16 +61,16 @@ skip_input_data(j_decompress_ptr cinfo, long num_bytes) JPEGSOURCE* source = (JPEGSOURCE*) cinfo->src; if (num_bytes > (long) source->pub.bytes_in_buffer) { - /* We need to skip more data than we have in the buffer. - This will force the JPEG library to suspend decoding. */ - source->skip = num_bytes - source->pub.bytes_in_buffer; - source->pub.next_input_byte += source->pub.bytes_in_buffer; - source->pub.bytes_in_buffer = 0; + /* We need to skip more data than we have in the buffer. + This will force the JPEG library to suspend decoding. */ + source->skip = num_bytes - source->pub.bytes_in_buffer; + source->pub.next_input_byte += source->pub.bytes_in_buffer; + source->pub.bytes_in_buffer = 0; } else { - /* Skip portion of the buffer */ - source->pub.bytes_in_buffer -= num_bytes; - source->pub.next_input_byte += num_bytes; - source->skip = 0; + /* Skip portion of the buffer */ + source->pub.bytes_in_buffer -= num_bytes; + source->pub.next_input_byte += num_bytes; + source->skip = 0; } } @@ -93,7 +93,7 @@ jpeg_buffer_src(j_decompress_ptr cinfo, JPEGSOURCE* source) /* -------------------------------------------------------------------- */ -/* Error handler */ +/* Error handler */ /* -------------------------------------------------------------------- */ METHODDEF(void) @@ -111,7 +111,7 @@ output(j_common_ptr cinfo) } /* -------------------------------------------------------------------- */ -/* Decoder */ +/* Decoder */ /* -------------------------------------------------------------------- */ int @@ -121,23 +121,23 @@ ImagingJpegDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) int ok; if (setjmp(context->error.setjmp_buffer)) { - /* JPEG error handler */ - jpeg_destroy_decompress(&context->cinfo); - state->errcode = IMAGING_CODEC_BROKEN; - return -1; + /* JPEG error handler */ + jpeg_destroy_decompress(&context->cinfo); + state->errcode = IMAGING_CODEC_BROKEN; + return -1; } if (!state->state) { - /* Setup decompression context */ - context->cinfo.err = jpeg_std_error(&context->error.pub); - context->error.pub.error_exit = error; - context->error.pub.output_message = output; - jpeg_create_decompress(&context->cinfo); - jpeg_buffer_src(&context->cinfo, &context->source); + /* Setup decompression context */ + context->cinfo.err = jpeg_std_error(&context->error.pub); + context->error.pub.error_exit = error; + context->error.pub.output_message = output; + jpeg_create_decompress(&context->cinfo); + jpeg_buffer_src(&context->cinfo, &context->source); - /* Ready to decode */ - state->state = 1; + /* Ready to decode */ + state->state = 1; } @@ -146,115 +146,115 @@ ImagingJpegDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) context->source.pub.bytes_in_buffer = bytes; if (context->source.skip > 0) { - skip_input_data(&context->cinfo, context->source.skip); - if (context->source.skip > 0) - return context->source.pub.next_input_byte - buf; + skip_input_data(&context->cinfo, context->source.skip); + if (context->source.skip > 0) + return context->source.pub.next_input_byte - buf; } switch (state->state) { case 1: - /* Read JPEG header, until we find an image body. */ - do { - - /* Note that we cannot return unless we have decoded - as much data as possible. */ - ok = jpeg_read_header(&context->cinfo, FALSE); + /* Read JPEG header, until we find an image body. */ + do { + + /* Note that we cannot return unless we have decoded + as much data as possible. */ + ok = jpeg_read_header(&context->cinfo, FALSE); - } while (ok == JPEG_HEADER_TABLES_ONLY); + } while (ok == JPEG_HEADER_TABLES_ONLY); - if (ok == JPEG_SUSPENDED) - break; + if (ok == JPEG_SUSPENDED) + break; - /* Decoder settings */ + /* Decoder settings */ - /* jpegmode indicates whats in the file; if not set, we'll - trust the decoder */ - if (strcmp(context->jpegmode, "L") == 0) - context->cinfo.jpeg_color_space = JCS_GRAYSCALE; - else if (strcmp(context->jpegmode, "RGB") == 0) - context->cinfo.jpeg_color_space = JCS_RGB; - else if (strcmp(context->jpegmode, "CMYK") == 0) - context->cinfo.jpeg_color_space = JCS_CMYK; - else if (strcmp(context->jpegmode, "YCbCr") == 0) - context->cinfo.jpeg_color_space = JCS_YCbCr; - else if (strcmp(context->jpegmode, "YCbCrK") == 0) { - context->cinfo.jpeg_color_space = JCS_YCCK; - } + /* jpegmode indicates whats in the file; if not set, we'll + trust the decoder */ + if (strcmp(context->jpegmode, "L") == 0) + context->cinfo.jpeg_color_space = JCS_GRAYSCALE; + else if (strcmp(context->jpegmode, "RGB") == 0) + context->cinfo.jpeg_color_space = JCS_RGB; + else if (strcmp(context->jpegmode, "CMYK") == 0) + context->cinfo.jpeg_color_space = JCS_CMYK; + else if (strcmp(context->jpegmode, "YCbCr") == 0) + context->cinfo.jpeg_color_space = JCS_YCbCr; + else if (strcmp(context->jpegmode, "YCbCrK") == 0) { + context->cinfo.jpeg_color_space = JCS_YCCK; + } - /* rawmode indicates what we want from the decoder. if not - set, conversions are disabled */ - if (strcmp(context->rawmode, "L") == 0) - context->cinfo.out_color_space = JCS_GRAYSCALE; - else if (strcmp(context->rawmode, "RGB") == 0) - context->cinfo.out_color_space = JCS_RGB; - else if (strcmp(context->rawmode, "CMYK") == 0 || + /* rawmode indicates what we want from the decoder. if not + set, conversions are disabled */ + if (strcmp(context->rawmode, "L") == 0) + context->cinfo.out_color_space = JCS_GRAYSCALE; + else if (strcmp(context->rawmode, "RGB") == 0) + context->cinfo.out_color_space = JCS_RGB; + else if (strcmp(context->rawmode, "CMYK") == 0 || strcmp(context->rawmode, "CMYK;I") == 0) - context->cinfo.out_color_space = JCS_CMYK; - else if (strcmp(context->rawmode, "YCbCr") == 0) - context->cinfo.out_color_space = JCS_YCbCr; - else if (strcmp(context->rawmode, "YCbCrK") == 0) - context->cinfo.out_color_space = JCS_YCCK; - else { - /* Disable decoder conversions */ - context->cinfo.jpeg_color_space = JCS_UNKNOWN; - context->cinfo.out_color_space = JCS_UNKNOWN; - } + context->cinfo.out_color_space = JCS_CMYK; + else if (strcmp(context->rawmode, "YCbCr") == 0) + context->cinfo.out_color_space = JCS_YCbCr; + else if (strcmp(context->rawmode, "YCbCrK") == 0) + context->cinfo.out_color_space = JCS_YCCK; + else { + /* Disable decoder conversions */ + context->cinfo.jpeg_color_space = JCS_UNKNOWN; + context->cinfo.out_color_space = JCS_UNKNOWN; + } - if (context->scale > 1) { - context->cinfo.scale_num = 1; - context->cinfo.scale_denom = context->scale; - } - if (context->draft) { - context->cinfo.do_fancy_upsampling = FALSE; - context->cinfo.dct_method = JDCT_FASTEST; - } + if (context->scale > 1) { + context->cinfo.scale_num = 1; + context->cinfo.scale_denom = context->scale; + } + if (context->draft) { + context->cinfo.do_fancy_upsampling = FALSE; + context->cinfo.dct_method = JDCT_FASTEST; + } state->state++; - /* fall through */ + /* fall through */ case 2: /* Set things up for decompression (this processes the entire file if necessary to return data line by line) */ - if (!jpeg_start_decompress(&context->cinfo)) + if (!jpeg_start_decompress(&context->cinfo)) break; - state->state++; - /* fall through */ + state->state++; + /* fall through */ case 3: - /* Decompress a single line of data */ - ok = 1; - while (state->y < state->ysize) { - ok = jpeg_read_scanlines(&context->cinfo, &state->buffer, 1); - if (ok != 1) - break; - state->shuffle((UINT8*) im->image[state->y + state->yoff] + - state->xoff * im->pixelsize, state->buffer, - state->xsize); - state->y++; - } - if (ok != 1) - break; - state->state++; - /* fall through */ + /* Decompress a single line of data */ + ok = 1; + while (state->y < state->ysize) { + ok = jpeg_read_scanlines(&context->cinfo, &state->buffer, 1); + if (ok != 1) + break; + state->shuffle((UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, state->buffer, + state->xsize); + state->y++; + } + if (ok != 1) + break; + state->state++; + /* fall through */ case 4: - /* Finish decompression */ - if (!jpeg_finish_decompress(&context->cinfo)) { + /* Finish decompression */ + if (!jpeg_finish_decompress(&context->cinfo)) { /* FIXME: add strictness mode test */ if (state->y < state->ysize) break; } - /* Clean up */ - jpeg_destroy_decompress(&context->cinfo); - /* if (jerr.pub.num_warnings) return BROKEN; */ - return -1; + /* Clean up */ + jpeg_destroy_decompress(&context->cinfo); + /* if (jerr.pub.num_warnings) return BROKEN; */ + return -1; } From 601ff3bd2f0e07b526cbf24ace502d3c61d3d568 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 15 May 2013 21:14:03 -0700 Subject: [PATCH 22/27] Whitespace, 8 space tabs -> 4 spaces --- decode.c | 215 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 108 insertions(+), 107 deletions(-) diff --git a/decode.c b/decode.c index 41557d687..ce8e391f7 100644 --- a/decode.c +++ b/decode.c @@ -66,21 +66,21 @@ PyImaging_DecoderNew(int contextsize) decoder = PyObject_New(ImagingDecoderObject, &ImagingDecoderType); if (decoder == NULL) - return NULL; + return NULL; /* Clear the decoder state */ memset(&decoder->state, 0, sizeof(decoder->state)); /* Allocate decoder context */ if (contextsize > 0) { - context = (void*) calloc(1, contextsize); - if (!context) { - Py_DECREF(decoder); - (void) PyErr_NoMemory(); - return NULL; - } + context = (void*) calloc(1, contextsize); + if (!context) { + Py_DECREF(decoder); + (void) PyErr_NoMemory(); + return NULL; + } } else - context = 0; + context = 0; /* Initialize decoder context */ decoder->state.context = context; @@ -129,10 +129,10 @@ _setimage(ImagingDecoderObject* decoder, PyObject* args) /* FIXME: should publish the ImagingType descriptor */ if (!PyArg_ParseTuple(args, "O|(iiii)", &op, &x0, &y0, &x1, &y1)) - return NULL; + return NULL; im = PyImaging_AsImaging(op); if (!im) - return NULL; + return NULL; decoder->im = im; @@ -140,30 +140,30 @@ _setimage(ImagingDecoderObject* decoder, PyObject* args) /* Setup decoding tile extent */ if (x0 == 0 && x1 == 0) { - state->xsize = im->xsize; - state->ysize = im->ysize; + state->xsize = im->xsize; + state->ysize = im->ysize; } else { - state->xoff = x0; - state->yoff = y0; - state->xsize = x1 - x0; - state->ysize = y1 - y0; + state->xoff = x0; + state->yoff = y0; + state->xsize = x1 - x0; + state->ysize = y1 - y0; } if (state->xsize <= 0 || - state->xsize + state->xoff > (int) im->xsize || - state->ysize <= 0 || - state->ysize + state->yoff > (int) im->ysize) { - PyErr_SetString(PyExc_ValueError, "tile cannot extend outside image"); - return NULL; + state->xsize + state->xoff > (int) im->xsize || + state->ysize <= 0 || + state->ysize + state->yoff > (int) im->ysize) { + PyErr_SetString(PyExc_ValueError, "tile cannot extend outside image"); + return NULL; } /* Allocate memory buffer (if bits field is set) */ if (state->bits > 0) { if (!state->bytes) state->bytes = (state->bits * state->xsize+7)/8; - state->buffer = (UINT8*) malloc(state->bytes); - if (!state->buffer) - return PyErr_NoMemory(); + state->buffer = (UINT8*) malloc(state->bytes); + if (!state->buffer) + return PyErr_NoMemory(); } /* Keep a reference to the image object, to make sure it doesn't @@ -178,18 +178,19 @@ _setimage(ImagingDecoderObject* decoder, PyObject* args) static struct PyMethodDef methods[] = { {"decode", (PyCFunction)_decode, 1}, + {"cleanup", (PyCFunction)_decode_cleanup, 1}, {"setimage", (PyCFunction)_setimage, 1}, {NULL, NULL} /* sentinel */ }; static PyTypeObject ImagingDecoderType = { - PyVarObject_HEAD_INIT(NULL, 0) - "ImagingDecoder", /*tp_name*/ - sizeof(ImagingDecoderObject), /*tp_size*/ - 0, /*tp_itemsize*/ - /* methods */ - (destructor)_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ + PyVarObject_HEAD_INIT(NULL, 0) + "ImagingDecoder", /*tp_name*/ + sizeof(ImagingDecoderObject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ @@ -227,9 +228,9 @@ get_unpacker(ImagingDecoderObject* decoder, const char* mode, unpack = ImagingFindUnpacker(mode, rawmode, &bits); if (!unpack) { - Py_DECREF(decoder); - PyErr_SetString(PyExc_ValueError, "unknown raw mode"); - return -1; + Py_DECREF(decoder); + PyErr_SetString(PyExc_ValueError, "unknown raw mode"); + return -1; } decoder->state.shuffle = unpack; @@ -240,7 +241,7 @@ get_unpacker(ImagingDecoderObject* decoder, const char* mode, /* -------------------------------------------------------------------- */ -/* BIT (packed fields) */ +/* BIT (packed fields) */ /* -------------------------------------------------------------------- */ PyObject* @@ -256,16 +257,16 @@ PyImaging_BitDecoderNew(PyObject* self, PyObject* args) int ystep = 1; if (!PyArg_ParseTuple(args, "s|iiiii", &mode, &bits, &pad, &fill, &sign, &ystep)) - return NULL; + return NULL; if (strcmp(mode, "F") != 0) { - PyErr_SetString(PyExc_ValueError, "bad image mode"); + PyErr_SetString(PyExc_ValueError, "bad image mode"); return NULL; } decoder = PyImaging_DecoderNew(sizeof(BITSTATE)); if (decoder == NULL) - return NULL; + return NULL; decoder->decode = ImagingBitDecode; @@ -281,7 +282,7 @@ PyImaging_BitDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* FLI */ +/* FLI */ /* -------------------------------------------------------------------- */ PyObject* @@ -291,7 +292,7 @@ PyImaging_FliDecoderNew(PyObject* self, PyObject* args) decoder = PyImaging_DecoderNew(0); if (decoder == NULL) - return NULL; + return NULL; decoder->decode = ImagingFliDecode; @@ -300,7 +301,7 @@ PyImaging_FliDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* GIF */ +/* GIF */ /* -------------------------------------------------------------------- */ PyObject* @@ -312,16 +313,16 @@ PyImaging_GifDecoderNew(PyObject* self, PyObject* args) int bits = 8; int interlace = 0; if (!PyArg_ParseTuple(args, "s|ii", &mode, &bits, &interlace)) - return NULL; + return NULL; if (strcmp(mode, "L") != 0 && strcmp(mode, "P") != 0) { - PyErr_SetString(PyExc_ValueError, "bad image mode"); + PyErr_SetString(PyExc_ValueError, "bad image mode"); return NULL; } decoder = PyImaging_DecoderNew(sizeof(GIFDECODERSTATE)); if (decoder == NULL) - return NULL; + return NULL; decoder->decode = ImagingGifDecode; @@ -333,7 +334,7 @@ PyImaging_GifDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* HEX */ +/* HEX */ /* -------------------------------------------------------------------- */ PyObject* @@ -344,14 +345,14 @@ PyImaging_HexDecoderNew(PyObject* self, PyObject* args) char* mode; char* rawmode; if (!PyArg_ParseTuple(args, "ss", &mode, &rawmode)) - return NULL; + return NULL; decoder = PyImaging_DecoderNew(0); if (decoder == NULL) - return NULL; + return NULL; if (get_unpacker(decoder, mode, rawmode) < 0) - return NULL; + return NULL; decoder->decode = ImagingHexDecode; @@ -360,7 +361,7 @@ PyImaging_HexDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* LZW */ +/* LZW */ /* -------------------------------------------------------------------- */ PyObject* @@ -372,14 +373,14 @@ PyImaging_TiffLzwDecoderNew(PyObject* self, PyObject* args) char* rawmode; int filter = 0; if (!PyArg_ParseTuple(args, "ss|i", &mode, &rawmode, &filter)) - return NULL; + return NULL; decoder = PyImaging_DecoderNew(sizeof(LZWSTATE)); if (decoder == NULL) - return NULL; + return NULL; if (get_unpacker(decoder, mode, rawmode) < 0) - return NULL; + return NULL; decoder->decode = ImagingLzwDecode; @@ -389,7 +390,7 @@ PyImaging_TiffLzwDecoderNew(PyObject* self, PyObject* args) } /* -------------------------------------------------------------------- */ -/* LibTiff */ +/* LibTiff */ /* -------------------------------------------------------------------- */ #ifdef HAVE_LIBTIFF @@ -409,17 +410,17 @@ PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args) char* rawmode; char* compname; int compression; - int fp; + int fp; if (! PyArg_ParseTuple(args, "sssi", &mode, &rawmode, &compname, &fp)) - return NULL; + return NULL; TRACE(("new tiff decoder %s\n", compname)); - - /* UNDONE -- we can probably do almost any arbitrary compression here, - * since we're effective passing in the whole file in one shot and - * getting back the data row by row. V2 maybe - */ + + /* UNDONE -- we can probably do almost any arbitrary compression here, + * since we're effective passing in the whole file in one shot and + * getting back the data row by row. V2 maybe + */ if (strcasecmp(compname, "tiff_ccitt") == 0) { compression = COMPRESSION_CCITTRLE; @@ -459,7 +460,7 @@ PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args) #endif /* -------------------------------------------------------------------- */ -/* MSP */ +/* MSP */ /* -------------------------------------------------------------------- */ PyObject* @@ -469,10 +470,10 @@ PyImaging_MspDecoderNew(PyObject* self, PyObject* args) decoder = PyImaging_DecoderNew(0); if (decoder == NULL) - return NULL; + return NULL; if (get_unpacker(decoder, "1", "1") < 0) - return NULL; + return NULL; decoder->decode = ImagingMspDecode; @@ -481,7 +482,7 @@ PyImaging_MspDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* PackBits */ +/* PackBits */ /* -------------------------------------------------------------------- */ PyObject* @@ -492,14 +493,14 @@ PyImaging_PackbitsDecoderNew(PyObject* self, PyObject* args) char* mode; char* rawmode; if (!PyArg_ParseTuple(args, "ss", &mode, &rawmode)) - return NULL; + return NULL; decoder = PyImaging_DecoderNew(0); if (decoder == NULL) - return NULL; + return NULL; if (get_unpacker(decoder, mode, rawmode) < 0) - return NULL; + return NULL; decoder->decode = ImagingPackbitsDecode; @@ -508,7 +509,7 @@ PyImaging_PackbitsDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* PCD */ +/* PCD */ /* -------------------------------------------------------------------- */ PyObject* @@ -518,11 +519,11 @@ PyImaging_PcdDecoderNew(PyObject* self, PyObject* args) decoder = PyImaging_DecoderNew(0); if (decoder == NULL) - return NULL; + return NULL; /* Unpack from PhotoYCC to RGB */ if (get_unpacker(decoder, "RGB", "YCC;P") < 0) - return NULL; + return NULL; decoder->decode = ImagingPcdDecode; @@ -531,7 +532,7 @@ PyImaging_PcdDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* PCX */ +/* PCX */ /* -------------------------------------------------------------------- */ PyObject* @@ -543,14 +544,14 @@ PyImaging_PcxDecoderNew(PyObject* self, PyObject* args) char* rawmode; int stride; if (!PyArg_ParseTuple(args, "ssi", &mode, &rawmode, &stride)) - return NULL; + return NULL; decoder = PyImaging_DecoderNew(0); if (decoder == NULL) - return NULL; + return NULL; if (get_unpacker(decoder, mode, rawmode) < 0) - return NULL; + return NULL; decoder->state.bytes = stride; @@ -561,7 +562,7 @@ PyImaging_PcxDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* RAW */ +/* RAW */ /* -------------------------------------------------------------------- */ PyObject* @@ -574,14 +575,14 @@ PyImaging_RawDecoderNew(PyObject* self, PyObject* args) int stride = 0; int ystep = 1; if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &stride, &ystep)) - return NULL; + return NULL; decoder = PyImaging_DecoderNew(sizeof(RAWSTATE)); if (decoder == NULL) - return NULL; + return NULL; if (get_unpacker(decoder, mode, rawmode) < 0) - return NULL; + return NULL; decoder->decode = ImagingRawDecode; @@ -594,7 +595,7 @@ PyImaging_RawDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* SUN RLE */ +/* SUN RLE */ /* -------------------------------------------------------------------- */ PyObject* @@ -605,14 +606,14 @@ PyImaging_SunRleDecoderNew(PyObject* self, PyObject* args) char* mode; char* rawmode; if (!PyArg_ParseTuple(args, "ss", &mode, &rawmode)) - return NULL; + return NULL; decoder = PyImaging_DecoderNew(0); if (decoder == NULL) - return NULL; + return NULL; if (get_unpacker(decoder, mode, rawmode) < 0) - return NULL; + return NULL; decoder->decode = ImagingSunRleDecode; @@ -621,7 +622,7 @@ PyImaging_SunRleDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* TGA RLE */ +/* TGA RLE */ /* -------------------------------------------------------------------- */ PyObject* @@ -634,14 +635,14 @@ PyImaging_TgaRleDecoderNew(PyObject* self, PyObject* args) int ystep = 1; int depth = 8; if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &ystep, &depth)) - return NULL; + return NULL; decoder = PyImaging_DecoderNew(0); if (decoder == NULL) - return NULL; + return NULL; if (get_unpacker(decoder, mode, rawmode) < 0) - return NULL; + return NULL; decoder->decode = ImagingTgaRleDecode; @@ -653,7 +654,7 @@ PyImaging_TgaRleDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* XBM */ +/* XBM */ /* -------------------------------------------------------------------- */ PyObject* @@ -663,10 +664,10 @@ PyImaging_XbmDecoderNew(PyObject* self, PyObject* args) decoder = PyImaging_DecoderNew(0); if (decoder == NULL) - return NULL; + return NULL; if (get_unpacker(decoder, "1", "1;R") < 0) - return NULL; + return NULL; decoder->decode = ImagingXbmDecode; @@ -675,7 +676,7 @@ PyImaging_XbmDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* ZIP */ +/* ZIP */ /* -------------------------------------------------------------------- */ #ifdef HAVE_LIBZ @@ -691,14 +692,14 @@ PyImaging_ZipDecoderNew(PyObject* self, PyObject* args) char* rawmode; int interlaced = 0; if (!PyArg_ParseTuple(args, "ss|i", &mode, &rawmode, &interlaced)) - return NULL; + return NULL; decoder = PyImaging_DecoderNew(sizeof(ZIPSTATE)); if (decoder == NULL) - return NULL; + return NULL; if (get_unpacker(decoder, mode, rawmode) < 0) - return NULL; + return NULL; decoder->decode = ImagingZipDecode; @@ -710,7 +711,7 @@ PyImaging_ZipDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* JPEG */ +/* JPEG */ /* -------------------------------------------------------------------- */ #ifdef HAVE_LIBJPEG @@ -718,15 +719,15 @@ PyImaging_ZipDecoderNew(PyObject* self, PyObject* args) /* We better define this decoder last in this file, so the following undef's won't mess things up for the Imaging library proper. */ -#undef HAVE_PROTOTYPES -#undef HAVE_STDDEF_H -#undef HAVE_STDLIB_H -#undef UINT8 -#undef UINT16 -#undef UINT32 -#undef INT8 -#undef INT16 -#undef INT32 +#undef HAVE_PROTOTYPES +#undef HAVE_STDDEF_H +#undef HAVE_STDLIB_H +#undef UINT8 +#undef UINT16 +#undef UINT32 +#undef INT8 +#undef INT16 +#undef INT32 #include "Jpeg.h" @@ -742,17 +743,17 @@ PyImaging_JpegDecoderNew(PyObject* self, PyObject* args) int draft = 0; if (!PyArg_ParseTuple(args, "ssz|ii", &mode, &rawmode, &jpegmode, &scale, &draft)) - return NULL; + return NULL; if (!jpegmode) - jpegmode = ""; + jpegmode = ""; decoder = PyImaging_DecoderNew(sizeof(JPEGSTATE)); if (decoder == NULL) - return NULL; + return NULL; if (get_unpacker(decoder, mode, rawmode) < 0) - return NULL; + return NULL; decoder->decode = ImagingJpegDecode; From c4a040d069c11b6ef71bbb3262d8a7ec2df1bcf1 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 15 May 2013 21:15:24 -0700 Subject: [PATCH 23/27] Whitespace, 8 space tabs -> 4 spaces --- decode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/decode.c b/decode.c index ce8e391f7..da759ae8f 100644 --- a/decode.c +++ b/decode.c @@ -41,7 +41,7 @@ /* -------------------------------------------------------------------- */ -/* Common */ +/* Common */ /* -------------------------------------------------------------------- */ typedef struct { @@ -108,7 +108,7 @@ _decode(ImagingDecoderObject* decoder, PyObject* args) int bufsize, status; if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH, &buffer, &bufsize)) - return NULL; + return NULL; status = decoder->decode(decoder->im, &decoder->state, buffer, bufsize); From f6381ba00c06cf7714bc583a04fccf7e18d55115 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 15 May 2013 21:16:37 -0700 Subject: [PATCH 24/27] Added cleanup method to prevent memory leaks from truncated jpeg decodes --- PIL/ImageFile.py | 4 ++++ decode.c | 21 ++++++++++++++++++++- libImaging/Imaging.h | 2 ++ libImaging/JpegDecode.c | 15 +++++++++++++++ 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index b08f14b90..0cc1d0f17 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -209,6 +209,10 @@ class ImageFile(Image.Image): if not s: # 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: diff --git a/decode.c b/decode.c index da759ae8f..29d417ed0 100644 --- a/decode.c +++ b/decode.c @@ -47,7 +47,8 @@ typedef struct { PyObject_HEAD int (*decode)(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); + UINT8* buffer, int bytes); + int (*cleanup)(ImagingCodecState state); struct ImagingCodecStateInstance state; Imaging im; PyObject* lock; @@ -88,6 +89,9 @@ PyImaging_DecoderNew(int contextsize) /* Target image */ decoder->lock = NULL; decoder->im = NULL; + + /* Initialize the cleanup function pointer */ + decoder->cleanup = NULL; return decoder; } @@ -115,6 +119,20 @@ _decode(ImagingDecoderObject* decoder, PyObject* args) return Py_BuildValue("ii", status, decoder->state.errcode); } +static PyObject* +_decode_cleanup(ImagingDecoderObject* decoder, PyObject* args) +{ + int status = 0; + + if (decoder->cleanup){ + status = decoder->cleanup(&decoder->state); + } + + return Py_BuildValue("i", status); +} + + + extern Imaging PyImaging_AsImaging(PyObject *op); static PyObject* @@ -756,6 +774,7 @@ PyImaging_JpegDecoderNew(PyObject* self, PyObject* args) return NULL; decoder->decode = ImagingJpegDecode; + decoder->cleanup = ImagingJpegDecodeCleanup; strncpy(((JPEGSTATE*)decoder->state.context)->rawmode, rawmode, 8); strncpy(((JPEGSTATE*)decoder->state.context)->jpegmode, jpegmode, 8); diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h index cf94dae0b..ddcec14c9 100644 --- a/libImaging/Imaging.h +++ b/libImaging/Imaging.h @@ -417,6 +417,8 @@ extern int ImagingHexDecode(Imaging im, ImagingCodecState state, #ifdef HAVE_LIBJPEG extern int ImagingJpegDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); +extern int ImagingJpegDecodeCleanup(ImagingCodecState state); + extern int ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); #endif diff --git a/libImaging/JpegDecode.c b/libImaging/JpegDecode.c index bc8cfa3c2..57544f7ad 100644 --- a/libImaging/JpegDecode.c +++ b/libImaging/JpegDecode.c @@ -263,5 +263,20 @@ ImagingJpegDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) } +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + +int ImagingJpegDecodeCleanup(ImagingCodecState state){ + /* called to fee the decompression engine when the decode terminates + due to a corrupt or truncated image + */ + JPEGSTATE* context = (JPEGSTATE*) state->context; + + /* Clean up */ + jpeg_destroy_decompress(&context->cinfo); + return -1; +} + #endif From 158cdbe8db4e375e503b12283bc10c4b8c2eddcb Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 16 May 2013 08:32:24 -0600 Subject: [PATCH 25/27] Enabling cleanup Enabling the cleanup call in ImageFile.load --- PIL/ImageFile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index 0cc1d0f17..28c60c8b3 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -211,7 +211,7 @@ class ImageFile(Image.Image): # JpegDecode needs to clean things up here either way # If we don't destroy the decompressor, we have a memory leak. - #d.cleanup() + d.cleanup() if LOAD_TRUNCATED_IMAGES: break From a7d778eba4c20c3bea95b7e2216a359f3e461a2a Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 16 May 2013 10:45:47 -0700 Subject: [PATCH 26/27] added free buffer if there's a problem decoding --- _webp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/_webp.c b/_webp.c index 806472ab4..cb872bc28 100644 --- a/_webp.c +++ b/_webp.c @@ -75,6 +75,7 @@ PyObject* WebPDecode_wrapper(PyObject* self, PyObject* args) } if (vp8_status_code != VP8_STATUS_OK) { + WebPFreeDecBuffer(&config.output); Py_RETURN_NONE; } From cb28745049315cccafd5aa47cad693d3802ec48a Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 21 May 2013 17:44:21 -0400 Subject: [PATCH 27/27] Wording --- selftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selftest.py b/selftest.py index 252231fbf..b0ad4ff67 100644 --- a/selftest.py +++ b/selftest.py @@ -177,7 +177,7 @@ if __name__ == "__main__": print("-"*68) #print("PIL", Image.VERSION, "TEST SUMMARY ") - print("PIL (Pillow) TEST SUMMARY ") + print("PIL TEST SUMMARY ") print("-"*68) print("Python modules loaded from", os.path.dirname(Image.__file__)) print("Binary modules loaded from", os.path.dirname(Image.core.__file__))