diff --git a/Images/transparent.png b/Images/transparent.png new file mode 100644 index 000000000..902ea8272 Binary files /dev/null and b/Images/transparent.png differ diff --git a/Images/transparent.webp b/Images/transparent.webp new file mode 100644 index 000000000..c1e38022e Binary files /dev/null and b/Images/transparent.webp differ diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index b08f14b90..28c60c8b3 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/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 1c260dd6b..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. @@ -561,6 +562,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, + 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/PIL/WebPImagePlugin.py b/PIL/WebPImagePlugin.py index bc2a31315..a8be5307c 100644 --- a/PIL/WebPImagePlugin.py +++ b/PIL/WebPImagePlugin.py @@ -3,29 +3,55 @@ from PIL import ImageFile from io import BytesIO from PIL import _webp + +_VALID_WEBP_MODES = { + "RGB": True, + "RGBA": True, + } + +_VP8_MODES_BY_IDENTIFIER = { + b"VP8 ": "RGB", + b"VP8X": "RGBA", + } + + def _accept(prefix): - return prefix[:4] == b"RIFF" and prefix[8:16] == b"WEBPVP8 " + is_riff_file_format = prefix[:4] == b"RIFF" + is_webp_file = prefix[8:12] == b"WEBP" + is_valid_vp8_mode = prefix[12:16] in _VP8_MODES_BY_IDENTIFIER + + return is_riff_file_format and is_webp_file and is_valid_vp8_mode + class WebPImageFile(ImageFile.ImageFile): format = "WEBP" format_description = "WebP image" - def _open(self): - self.mode = "RGB" - data, width, height = _webp.WebPDecodeRGB(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, 'RGB')] + self.tile = [("raw", (0, 0) + self.size, 0, self.mode)] + def _save(im, fp, filename): - if im.mode != "RGB": - raise IOError("cannot write mode %s as WEBP" % im.mode) + image_mode = im.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) - data = _webp.WebPEncodeRGB(im.tobytes(), im.size[0], im.size[1], im.size[0] * 3, float(quality)) + data = _webp.WebPEncode( + im.tobytes(), + im.size[0], + im.size[1], + float(quality), + im.mode + ) fp.write(data) + Image.register_open("WEBP", WebPImageFile, _accept) Image.register_save("WEBP", _save) diff --git a/README.rst b/README.rst index c3e40bb02..12e5716b2 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 ~~~~~~~~~~~~~~~~ @@ -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) 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/Tests/test_file_webp.py b/Tests/test_file_webp.py index 1d5337af1..4f8157f69 100644 --- a/Tests/test_file_webp.py +++ b/Tests/test_file_webp.py @@ -6,54 +6,108 @@ try: from PIL import _webp except: skip('webp support not installed') - -def test_read(): - """ Can we write a webp without error. Does it have the bits we expect?""" + + +def test_version(): + assert_no_exception(lambda: _webp.WebPDecoderVersion()) + +def test_good_alpha(): + assert_equal(_webp.WebPDecoderBuggyAlpha(), 0) - file = "Images/lena.webp" - im = Image.open(file) - - assert_equal(im.mode, "RGB") - assert_equal(im.size, (128, 128)) - assert_equal(im.format, "WEBP") - assert_no_exception(lambda: im.load()) - assert_no_exception(lambda: im.getdata()) - - orig_bytes = im.tobytes() +def test_read_rgb(): + + file_path = "Images/lena.webp" + image = Image.open(file_path) + + assert_equal(image.mode, "RGB") + assert_equal(image.size, (128, 128)) + assert_equal(image.format, "WEBP") + assert_no_exception(lambda: image.load()) + assert_no_exception(lambda: image.getdata()) + # generated with: dwebp -ppm ../../Images/lena.webp -o lena_webp_bits.ppm target = Image.open('Tests/images/lena_webp_bits.ppm') - assert_image_equal(im, target) + assert_image_equal(image, target) -def test_write(): - """ Can we write a webp without error. Does it have the bits we expect?""" - - file = tempfile("temp.webp") - - lena("RGB").save(file) - - im= Image.open(file) - im.load() - - assert_equal(im.mode, "RGB") - assert_equal(im.size, (128, 128)) - assert_equal(im.format, "WEBP") - assert_no_exception(lambda: im.load()) - assert_no_exception(lambda: im.getdata()) - +def test_write_rgb(): + """ + Can we write a RGB mode file to webp without error. Does it have the bits we + expect? + + """ + + temp_file = tempfile("temp.webp") + + lena("RGB").save(temp_file) + + image = Image.open(temp_file) + image.load() + + assert_equal(image.mode, "RGB") + assert_equal(image.size, (128, 128)) + assert_equal(image.format, "WEBP") + assert_no_exception(lambda: image.load()) + assert_no_exception(lambda: image.getdata()) + # If we're using the exact same version of webp, this test should pass. # but it doesn't if the webp is generated on Ubuntu and tested on Fedora. - + # generated with: dwebp -ppm temp.webp -o lena_webp_write.ppm #target = Image.open('Tests/images/lena_webp_write.ppm') - #assert_image_equal(im, target) - + #assert_image_equal(image, target) + # This test asserts that the images are similar. If the average pixel difference # between the two images is less than the epsilon value, then we're going to # accept that it's a reasonable lossy version of the image. The included lena images # for webp are showing ~16 on Ubuntu, the jpegs are showing ~18. - target = lena('RGB') - assert_image_similar(im, target, 20.0) + target = lena("RGB") + assert_image_similar(image, target, 20.0) + + +def test_write_rgba(): + """ + Can we write a RGBA mode file to webp without error. Does it have the bits we + expect? + + """ + + temp_file = tempfile("temp.webp") + + 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() + + assert_equal(image.mode, "RGBA") + assert_equal(image.size, (10, 10)) + assert_equal(image.format, "WEBP") + assert_no_exception(image.load) + 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 0fd5be2f4..cb872bc28 100644 --- a/_webp.c +++ b/_webp.c @@ -2,73 +2,128 @@ #include "py3.h" #include #include +#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)) { - Py_INCREF(Py_None); - return Py_None; + 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; + } - if (stride * height > size) { - Py_INCREF(Py_None); - return Py_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_INCREF(Py_None); - return Py_None; - + Py_RETURN_NONE; } -PyObject* WebPDecodeRGB_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; + PyObject *ret, *bytes, *pymode; + WebPDecoderConfig config; + VP8StatusCode vp8_status_code = VP8_STATUS_OK; + char* mode = "RGB"; if (!PyArg_ParseTuple(args, "S", &webp_string)) { - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } + if (!WebPInitDecoderConfig(&config)) { + Py_RETURN_NONE; + } + PyBytes_AsStringAndSize((PyObject *) webp_string, (char**)&webp, &size); - output = WebPDecodeRGB(webp, size, &width, &height); + 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) { + WebPFreeDecBuffer(&config.output); + 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); + } - ret = PyBytes_FromStringAndSize((char*)output, width * height * 3); - free(output); - return Py_BuildValue("Sii", ret, width, height); +#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; +} + +// 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.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()==0x0103); } static PyMethodDef webpMethods[] = { - {"WebPEncodeRGB", WebPEncodeRGB_wrapper, METH_VARARGS, "WebPEncodeRGB"}, - {"WebPDecodeRGB", WebPDecodeRGB_wrapper, METH_VARARGS, "WebPEncodeRGB"}, + {"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} }; + #if PY_VERSION_HEX >= 0x03000000 PyMODINIT_FUNC PyInit__webp(void) { diff --git a/decode.c b/decode.c index 41557d687..29d417ed0 100644 --- a/decode.c +++ b/decode.c @@ -41,13 +41,14 @@ /* -------------------------------------------------------------------- */ -/* Common */ +/* Common */ /* -------------------------------------------------------------------- */ 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; @@ -66,21 +67,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; @@ -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; } @@ -108,13 +112,27 @@ _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); 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* @@ -129,10 +147,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 +158,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 +196,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 +246,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 +259,7 @@ get_unpacker(ImagingDecoderObject* decoder, const char* mode, /* -------------------------------------------------------------------- */ -/* BIT (packed fields) */ +/* BIT (packed fields) */ /* -------------------------------------------------------------------- */ PyObject* @@ -256,16 +275,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 +300,7 @@ PyImaging_BitDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* FLI */ +/* FLI */ /* -------------------------------------------------------------------- */ PyObject* @@ -291,7 +310,7 @@ PyImaging_FliDecoderNew(PyObject* self, PyObject* args) decoder = PyImaging_DecoderNew(0); if (decoder == NULL) - return NULL; + return NULL; decoder->decode = ImagingFliDecode; @@ -300,7 +319,7 @@ PyImaging_FliDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* GIF */ +/* GIF */ /* -------------------------------------------------------------------- */ PyObject* @@ -312,16 +331,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 +352,7 @@ PyImaging_GifDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* HEX */ +/* HEX */ /* -------------------------------------------------------------------- */ PyObject* @@ -344,14 +363,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 +379,7 @@ PyImaging_HexDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* LZW */ +/* LZW */ /* -------------------------------------------------------------------- */ PyObject* @@ -372,14 +391,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 +408,7 @@ PyImaging_TiffLzwDecoderNew(PyObject* self, PyObject* args) } /* -------------------------------------------------------------------- */ -/* LibTiff */ +/* LibTiff */ /* -------------------------------------------------------------------- */ #ifdef HAVE_LIBTIFF @@ -409,17 +428,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 +478,7 @@ PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args) #endif /* -------------------------------------------------------------------- */ -/* MSP */ +/* MSP */ /* -------------------------------------------------------------------- */ PyObject* @@ -469,10 +488,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 +500,7 @@ PyImaging_MspDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* PackBits */ +/* PackBits */ /* -------------------------------------------------------------------- */ PyObject* @@ -492,14 +511,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 +527,7 @@ PyImaging_PackbitsDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* PCD */ +/* PCD */ /* -------------------------------------------------------------------- */ PyObject* @@ -518,11 +537,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 +550,7 @@ PyImaging_PcdDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* PCX */ +/* PCX */ /* -------------------------------------------------------------------- */ PyObject* @@ -543,14 +562,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 +580,7 @@ PyImaging_PcxDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* RAW */ +/* RAW */ /* -------------------------------------------------------------------- */ PyObject* @@ -574,14 +593,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 +613,7 @@ PyImaging_RawDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* SUN RLE */ +/* SUN RLE */ /* -------------------------------------------------------------------- */ PyObject* @@ -605,14 +624,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 +640,7 @@ PyImaging_SunRleDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* TGA RLE */ +/* TGA RLE */ /* -------------------------------------------------------------------- */ PyObject* @@ -634,14 +653,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 +672,7 @@ PyImaging_TgaRleDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* XBM */ +/* XBM */ /* -------------------------------------------------------------------- */ PyObject* @@ -663,10 +682,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 +694,7 @@ PyImaging_XbmDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* ZIP */ +/* ZIP */ /* -------------------------------------------------------------------- */ #ifdef HAVE_LIBZ @@ -691,14 +710,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 +729,7 @@ PyImaging_ZipDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* JPEG */ +/* JPEG */ /* -------------------------------------------------------------------- */ #ifdef HAVE_LIBJPEG @@ -718,15 +737,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,19 +761,20 @@ 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; + 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 11b2767ba..57544f7ad 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; } @@ -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 diff --git a/libImaging/JpegEncode.c b/libImaging/JpegEncode.c index 221f8ccfa..5d0b85d6a 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,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; + 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 */ @@ -253,9 +258,12 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) else break; } else - state->state++; + 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) diff --git a/selftest.py b/selftest.py index 3c1846d59..26fdc2215 100644 --- a/selftest.py +++ b/selftest.py @@ -188,7 +188,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__)) @@ -201,6 +201,14 @@ if __name__ == "__main__": check_module("FREETYPE2", "PIL._imagingft") check_module("LITTLECMS", "PIL._imagingcms") check_module("WEBP", "PIL._webp") + try: + from PIL 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!