From eb354be7c43a673931cc3e7b55ffb1655ef8880f Mon Sep 17 00:00:00 2001 From: rr- Date: Thu, 5 May 2016 21:36:45 +0200 Subject: [PATCH 01/12] Add method=Image.LIBIMAGEQUANT for quantize() --- PIL/Image.py | 9 ++- Tests/test_image_quantize.py | 8 ++ libImaging/Quant.c | 22 +++++- libImaging/QuantPngQuant.c | 141 +++++++++++++++++++++++++++++++++++ libImaging/QuantPngQuant.h | 15 ++++ setup.py | 19 ++++- 6 files changed, 206 insertions(+), 8 deletions(-) create mode 100644 libImaging/QuantPngQuant.c create mode 100644 libImaging/QuantPngQuant.h diff --git a/PIL/Image.py b/PIL/Image.py index 87868f8dc..96e871bd7 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -185,6 +185,7 @@ ADAPTIVE = 1 MEDIANCUT = 0 MAXCOVERAGE = 1 FASTOCTREE = 2 +LIBIMAGEQUANT = 3 # categories NORMAL = 0 @@ -967,6 +968,7 @@ class Image(object): :param method: 0 = median cut 1 = maximum coverage 2 = fast octree + 3 = libimagequant :param kmeans: Integer :param palette: Quantize to the :py:class:`PIL.ImagingPalette` palette. :returns: A new image @@ -981,10 +983,11 @@ class Image(object): if self.mode == 'RGBA': method = 2 - if self.mode == 'RGBA' and method != 2: + if self.mode == 'RGBA' and method not in (2, 3): # Caller specified an invalid mode. - raise ValueError('Fast Octree (method == 2) is the ' + - ' only valid method for quantizing RGBA images') + raise ValueError( + 'Fast Octree (method == 2) and libimagequant (method == 3) ' + + 'are the only valid methods for quantizing RGBA images') if palette: # use palette from reference image diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py index 986a0a6cc..5e04ea9b0 100644 --- a/Tests/test_image_quantize.py +++ b/Tests/test_image_quantize.py @@ -15,6 +15,14 @@ class TestImageQuantize(PillowTestCase): im = im.quantize(palette=hopper("P")) self.assert_image(im, "P", im.size) + def test_libimagequant_quantize(self): + im = hopper() + + im = im.quantize(100, Image.LIBIMAGEQUANT) + self.assert_image(im, "P", im.size) + + assert len(im.getcolors()) == 100 + def test_octree_quantize(self): im = hopper() diff --git a/libImaging/Quant.c b/libImaging/Quant.c index 5b8a8d994..b66ecd9fa 100644 --- a/libImaging/Quant.c +++ b/libImaging/Quant.c @@ -1484,7 +1484,7 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) return ImagingError_ModeError(); /* only octree supports RGBA */ - if (!strcmp(im->mode, "RGBA") && mode != 2) + if (!strcmp(im->mode, "RGBA") && mode != 2 && mode != 3) return ImagingError_ModeError(); p = malloc(sizeof(Pixel) * im->xsize * im->ysize); @@ -1503,8 +1503,10 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) should be done by a simple copy... */ for (i = y = 0; y < im->ysize; y++) - for (x = 0; x < im->xsize; x++, i++) + for (x = 0; x < im->xsize; x++, i++) { p[i].c.r = p[i].c.g = p[i].c.b = im->image8[y][x]; + p[i].c.a = 255; + } } else if (!strcmp(im->mode, "P")) { /* palette */ @@ -1517,6 +1519,7 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) p[i].c.r = pp[v*4+0]; p[i].c.g = pp[v*4+1]; p[i].c.b = pp[v*4+2]; + p[i].c.a = pp[v*4+3]; } } else if (!strcmp(im->mode, "RGB") || !strcmp(im->mode, "RGBA")) { @@ -1572,6 +1575,21 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) withAlpha ); break; + case 3: + if (!strcmp(im->mode, "RGBA")) { + withAlpha = 1; + } + result = quantize_pngquant( + p, + im->xsize, + im->ysize, + colors, + &palette, + &paletteLength, + &newData, + withAlpha + ); + break; default: result = 0; break; diff --git a/libImaging/QuantPngQuant.c b/libImaging/QuantPngQuant.c new file mode 100644 index 000000000..55b697b93 --- /dev/null +++ b/libImaging/QuantPngQuant.c @@ -0,0 +1,141 @@ +/* Copyright (c) 2010 Oliver Tonnhofer , Omniscale +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +*/ + +/* +// This file implements a quantization using libimagequant, a part of pngquant. +*/ + +#include +#include +#include + +#include "QuantPngQuant.h" + +#ifdef HAVE_LIBIMAGEQUANT +#include "libimagequant.h" + +int +quantize_pngquant( + Pixel *pixelData, + uint32_t width, + uint32_t height, + uint32_t quantPixels, + Pixel **palette, + uint32_t *paletteLength, + uint32_t **quantizedPixels, + int withAlpha) +{ + int result = 0; + liq_image *image = NULL; + liq_attr *attr = NULL; + liq_result *remap = NULL; + char *charMatrix = NULL; + char **charMatrixRows = NULL; + *palette = NULL; + *paletteLength = 0; + *quantizedPixels = NULL; + + /* configure pngquant */ + attr = liq_attr_create(); + if (!attr) { goto err; } + if (quantPixels) { + liq_set_max_colors(attr, quantPixels); + } + + /* prepare input image */ + image = liq_image_create_rgba( + attr, + pixelData, + width, + height, + 0.45455 /* gamma */); + if (!image) { goto err; } + + /* quantize the image */ + remap = liq_quantize_image(attr, image); + if (!remap) { goto err; } + liq_set_output_gamma(remap, 0.45455); + liq_set_dithering_level(remap, 1); + + /* write output palette */ + const liq_palette *l_palette = liq_get_palette(remap); + *paletteLength = l_palette->count; + *palette = malloc(sizeof(Pixel) * l_palette->count); + if (!*palette) { goto err; } + for (unsigned int i = 0; i < l_palette->count; i++) { + (*palette)[i].c.b = l_palette->entries[i].b; + (*palette)[i].c.g = l_palette->entries[i].g; + (*palette)[i].c.r = l_palette->entries[i].r; + (*palette)[i].c.a = l_palette->entries[i].a; + } + + /* write output pixels (pngquant uses char array) */ + charMatrix = malloc(width * height); + if (!charMatrix) { goto err; } + charMatrixRows = malloc(height * sizeof(char*)); + if (!charMatrixRows) { goto err; } + for (unsigned int y = 0; y < height; y++) { + charMatrixRows[y] = &charMatrix[y * width]; + } + if (LIQ_OK != liq_write_remapped_image_rows(remap, image, charMatrixRows)) { + goto err; + } + + /* transcribe output pixels (pillow uses uint32_t array) */ + *quantizedPixels = malloc(sizeof(uint32_t) * width * height); + if (!*quantizedPixels) { goto err; } + for (int i = 0; i < width * height; i++) { + (*quantizedPixels)[i] = charMatrix[i]; + } + + result = 1; + +err: + if (attr) liq_attr_destroy(attr); + if (image) liq_image_destroy(image); + if (remap) liq_result_destroy(remap); + free(charMatrix); + free(charMatrixRows); + if (!result) { + free(*quantizedPixels); + free(*palette); + } + return result; +} + +#else + +/* Offer dummy implementation */ +int +quantize_pngquant( + Pixel *pixelData, + uint32_t width, + uint32_t height, + uint32_t quantPixels, + Pixel **palette, + uint32_t *paletteLength, + uint32_t **quantizedPixels, + int withAlpha) +{ + return 0; +} + +#endif diff --git a/libImaging/QuantPngQuant.h b/libImaging/QuantPngQuant.h new file mode 100644 index 000000000..dc8dd3cfa --- /dev/null +++ b/libImaging/QuantPngQuant.h @@ -0,0 +1,15 @@ +#ifndef __QUANT_PNGQUANT_H__ +#define __QUANT_PNGQUANT_H__ + +#include "QuantTypes.h" + +int quantize_pngquant(Pixel *, + uint32_t, + uint32_t, + uint32_t, + Pixel **, + uint32_t *, + uint32_t **, + int); + +#endif diff --git a/setup.py b/setup.py index 144c273ee..59ba76df7 100644 --- a/setup.py +++ b/setup.py @@ -35,7 +35,7 @@ _LIB_IMAGING = ( "RankFilter", "RawDecode", "RawEncode", "Storage", "SunRleDecode", "TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask", "XbmDecode", "XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode", "Incremental", - "Jpeg2KDecode", "Jpeg2KEncode", "BoxBlur") + "Jpeg2KDecode", "Jpeg2KEncode", "BoxBlur", "QuantPngQuant") DEBUG = False @@ -114,6 +114,7 @@ TCL_ROOT = None JPEG_ROOT = None JPEG2K_ROOT = None ZLIB_ROOT = None +IMAGEQUANT_ROOT = None TIFF_ROOT = None FREETYPE_ROOT = None LCMS_ROOT = None @@ -122,7 +123,7 @@ LCMS_ROOT = None class pil_build_ext(build_ext): class feature: features = ['zlib', 'jpeg', 'tiff', 'freetype', 'tcl', 'tk', 'lcms', - 'webp', 'webpmux', 'jpeg2000'] + 'webp', 'webpmux', 'jpeg2000', 'imagequant'] required = set(['jpeg', 'zlib']) @@ -185,7 +186,7 @@ class pil_build_ext(build_ext): # add configured kits for root in (TCL_ROOT, JPEG_ROOT, JPEG2K_ROOT, TIFF_ROOT, ZLIB_ROOT, - FREETYPE_ROOT, LCMS_ROOT): + FREETYPE_ROOT, LCMS_ROOT, IMAGEQUANT_ROOT): if isinstance(root, type(())): lib_root, include_root = root else: @@ -478,6 +479,14 @@ class pil_build_ext(build_ext): feature.openjpeg_version = '.'.join([str(x) for x in best_version]) + if feature.want('imagequant'): + _dbg('Looking for imagequant') + if _find_include_file(self, 'libimagequant.h'): + if _find_library_file(self, "imagequant"): + feature.imagequant = "imagequant" + elif _find_library_file(self, "libimagequant"): + feature.imagequant = "libimagequant" + if feature.want('tiff'): _dbg('Looking for tiff') if _find_include_file(self, 'tiff.h'): @@ -591,6 +600,9 @@ class pil_build_ext(build_ext): if feature.zlib: libs.append(feature.zlib) defs.append(("HAVE_LIBZ", None)) + if feature.imagequant: + libs.append(feature.imagequant) + defs.append(("HAVE_LIBIMAGEQUANT", None)) if feature.tiff: libs.append(feature.tiff) defs.append(("HAVE_LIBTIFF", None)) @@ -698,6 +710,7 @@ class pil_build_ext(build_ext): (feature.jpeg2000, "OPENJPEG (JPEG2000)", feature.openjpeg_version), (feature.zlib, "ZLIB (PNG/ZIP)"), + (feature.imagequant, "LIBIMAGEQUANT"), (feature.tiff, "LIBTIFF"), (feature.freetype, "FREETYPE2"), (feature.lcms, "LITTLECMS2"), From 4b260ac369cec01fc23e56746b1c6d6d3fe65daa Mon Sep 17 00:00:00 2001 From: rr- Date: Fri, 6 May 2016 17:33:07 +0200 Subject: [PATCH 02/12] Install libimagequant for Travis build --- .travis.yml | 3 +++ depends/README.rst | 4 ++-- depends/debian_8.2.sh | 1 + depends/install_imagequant.sh | 12 ++++++++++++ depends/ubuntu_12.04.sh | 1 + depends/ubuntu_14.04.sh | 1 + 6 files changed, 20 insertions(+), 2 deletions(-) create mode 100755 depends/install_imagequant.sh diff --git a/.travis.yml b/.travis.yml index 8b6c9e55d..3c95c001c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,6 +45,9 @@ install: # openjpeg - pushd depends && ./install_openjpeg.sh && popd + # libimagequant + - pushd depends && ./install_imagequant.sh && popd + script: - if [ "$TRAVIS_PYTHON_VERSION" != "nightly" ]; then coverage erase; fi - python setup.py clean diff --git a/depends/README.rst b/depends/README.rst index dd05b86ca..050173679 100644 --- a/depends/README.rst +++ b/depends/README.rst @@ -1,8 +1,8 @@ Depends ======= -``install_openjpeg.sh`` and ``install_webp.sh`` can be used to -download, build & install non-packaged dependencies; useful for +``install_openjpeg.sh``, ``install_webp.sh`` and ``install_imagequant.sh`` can +be used to download, build & install non-packaged dependencies; useful for testing with Travis CI. The other scripts can be used to install all of the dependencies for diff --git a/depends/debian_8.2.sh b/depends/debian_8.2.sh index dd0679a9f..96e6a8e2b 100755 --- a/depends/debian_8.2.sh +++ b/depends/debian_8.2.sh @@ -14,3 +14,4 @@ sudo apt-get -y install libtiff5-dev libjpeg62-turbo-dev zlib1g-dev \ python-tk python3-tk ./install_openjpeg.sh +./install_imagequant.sh diff --git a/depends/install_imagequant.sh b/depends/install_imagequant.sh new file mode 100755 index 000000000..3799621e8 --- /dev/null +++ b/depends/install_imagequant.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# install libimagequant + +git clone https://github.com/pornel/pngquant + +pushd pngquant + +make -C lib shared +sudo cp lib/libimagequant.so* /usr/lib/ +sudo cp lib/libimagequant.h /usr/include/ + +popd diff --git a/depends/ubuntu_12.04.sh b/depends/ubuntu_12.04.sh index e9b16d2b4..9bfae43b0 100755 --- a/depends/ubuntu_12.04.sh +++ b/depends/ubuntu_12.04.sh @@ -14,3 +14,4 @@ sudo apt-get install libtiff4-dev libjpeg8-dev zlib1g-dev \ ./install_openjpeg.sh ./install_webp.sh +./install_imagequant.sh diff --git a/depends/ubuntu_14.04.sh b/depends/ubuntu_14.04.sh index 14b9e7066..a548f74fa 100755 --- a/depends/ubuntu_14.04.sh +++ b/depends/ubuntu_14.04.sh @@ -12,3 +12,4 @@ sudo apt-get -y install libtiff5-dev libjpeg8-dev zlib1g-dev \ python-tk python3-tk ./install_openjpeg.sh +./install_imagequant.sh From cb4ded4929954bfa1e6af7075e4174a933b348e7 Mon Sep 17 00:00:00 2001 From: rr- Date: Fri, 6 May 2016 19:26:29 +0200 Subject: [PATCH 03/12] Minor fixes --- libImaging/Quant.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libImaging/Quant.c b/libImaging/Quant.c index b66ecd9fa..3dc9ab399 100644 --- a/libImaging/Quant.c +++ b/libImaging/Quant.c @@ -27,6 +27,7 @@ #include "QuantTypes.h" #include "QuantOctree.h" +#include "QuantPngQuant.h" #include "QuantHash.h" #include "QuantHeap.h" @@ -1483,7 +1484,7 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) strcmp(im->mode, "RGB") != 0 && strcmp(im->mode, "RGBA") !=0) return ImagingError_ModeError(); - /* only octree supports RGBA */ + /* only octree and imagequant supports RGBA */ if (!strcmp(im->mode, "RGBA") && mode != 2 && mode != 3) return ImagingError_ModeError(); From 749ecd634eb960af3734052cc7893ae72fbbb70f Mon Sep 17 00:00:00 2001 From: rr- Date: Fri, 6 May 2016 19:26:41 +0200 Subject: [PATCH 04/12] Document libimagequant dependency --- docs/installation.rst | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 4b4d6d07e..a174b6082 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -140,6 +140,8 @@ Many of Pillow's features require external libraries: * **libfreetype** provides type related services +* **libimagequant** provides improved color quantization + * **littlecms** provides color management * Pillow version 2.2.1 and below uses liblcms1, Pillow 2.3.0 and @@ -190,17 +192,17 @@ Build Options * Build flags: ``--disable-zlib``, ``--disable-jpeg``, ``--disable-tiff``, ``--disable-freetype``, ``--disable-tcl``, ``--disable-tk``, ``--disable-lcms``, ``--disable-webp``, - ``--disable-webpmux``, ``--disable-jpeg2000``. Disable building the - corresponding feature even if the development libraries are present - on the building machine. + ``--disable-webpmux``, ``--disable-jpeg2000``, ``--disable-imagequant``. + Disable building the corresponding feature even if the development + libraries are present on the building machine. * Build flags: ``--enable-zlib``, ``--enable-jpeg``, ``--enable-tiff``, ``--enable-freetype``, ``--enable-tcl``, ``--enable-tk``, ``--enable-lcms``, ``--enable-webp``, - ``--enable-webpmux``, ``--enable-jpeg2000``. Require that the - corresponding feature is built. The build will raise an exception if - the libraries are not found. Webpmux (WebP metadata) relies on WebP - support. Tcl and Tk also must be used together. + ``--enable-webpmux``, ``--enable-jpeg2000``, ``--enable-imagequant``. + Require that the corresponding feature is built. The build will raise + an exception if the libraries are not found. Webpmux (WebP metadata) + relies on WebP support. Tcl and Tk also must be used together. * Build flags: ``--debug``. Adds a debugging flag to the include and library search process to dump all paths searched for and found to From 0904eb3e2777f149328c3c896cfd58d293f659ec Mon Sep 17 00:00:00 2001 From: rr- Date: Fri, 6 May 2016 19:27:39 +0200 Subject: [PATCH 05/12] Use specific version of libimagequant --- depends/install_imagequant.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depends/install_imagequant.sh b/depends/install_imagequant.sh index 3799621e8..dd497dc3c 100755 --- a/depends/install_imagequant.sh +++ b/depends/install_imagequant.sh @@ -1,7 +1,7 @@ #!/bin/bash # install libimagequant -git clone https://github.com/pornel/pngquant +git clone -b 2.6.0 https://github.com/pornel/pngquant pushd pngquant From 0b4a0893215a9b6f87adc44a1ed310f0929aa867 Mon Sep 17 00:00:00 2001 From: rr- Date: Fri, 6 May 2016 20:07:55 +0200 Subject: [PATCH 06/12] Fix C99 errors inside libimagequant quantizer --- libImaging/QuantPngQuant.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/libImaging/QuantPngQuant.c b/libImaging/QuantPngQuant.c index 55b697b93..2df0e5340 100644 --- a/libImaging/QuantPngQuant.c +++ b/libImaging/QuantPngQuant.c @@ -47,8 +47,9 @@ quantize_pngquant( liq_image *image = NULL; liq_attr *attr = NULL; liq_result *remap = NULL; - char *charMatrix = NULL; - char **charMatrixRows = NULL; + unsigned char *charMatrix = NULL; + unsigned char **charMatrixRows = NULL; + unsigned int i, y; *palette = NULL; *paletteLength = 0; *quantizedPixels = NULL; @@ -80,7 +81,7 @@ quantize_pngquant( *paletteLength = l_palette->count; *palette = malloc(sizeof(Pixel) * l_palette->count); if (!*palette) { goto err; } - for (unsigned int i = 0; i < l_palette->count; i++) { + for (i = 0; i < l_palette->count; i++) { (*palette)[i].c.b = l_palette->entries[i].b; (*palette)[i].c.g = l_palette->entries[i].g; (*palette)[i].c.r = l_palette->entries[i].r; @@ -90,9 +91,9 @@ quantize_pngquant( /* write output pixels (pngquant uses char array) */ charMatrix = malloc(width * height); if (!charMatrix) { goto err; } - charMatrixRows = malloc(height * sizeof(char*)); + charMatrixRows = malloc(height * sizeof(unsigned char*)); if (!charMatrixRows) { goto err; } - for (unsigned int y = 0; y < height; y++) { + for (y = 0; y < height; y++) { charMatrixRows[y] = &charMatrix[y * width]; } if (LIQ_OK != liq_write_remapped_image_rows(remap, image, charMatrixRows)) { @@ -102,7 +103,7 @@ quantize_pngquant( /* transcribe output pixels (pillow uses uint32_t array) */ *quantizedPixels = malloc(sizeof(uint32_t) * width * height); if (!*quantizedPixels) { goto err; } - for (int i = 0; i < width * height; i++) { + for (i = 0; i < width * height; i++) { (*quantizedPixels)[i] = charMatrix[i]; } From 8b7fb44c6b2c2ee77b572ee09cbbbf95b5ea6343 Mon Sep 17 00:00:00 2001 From: rr- Date: Fri, 6 May 2016 21:05:52 +0200 Subject: [PATCH 07/12] Make libimagequant visibility detectable --- libImaging/Quant.c | 12 +++++++++++- libImaging/QuantPngQuant.c | 17 ----------------- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/libImaging/Quant.c b/libImaging/Quant.c index 3dc9ab399..6c122eee2 100644 --- a/libImaging/Quant.c +++ b/libImaging/Quant.c @@ -1577,6 +1577,7 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) ); break; case 3: +#ifdef HAVE_LIBIMAGEQUANT if (!strcmp(im->mode, "RGBA")) { withAlpha = 1; } @@ -1590,6 +1591,9 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) &newData, withAlpha ); +#else + result = -1; +#endif break; default: result = 0; @@ -1599,7 +1603,7 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) free(p); ImagingSectionLeave(&cookie); - if (result) { + if (result > 0) { imOut = ImagingNew("P", im->xsize, im->ysize); ImagingSectionEnter(&cookie); @@ -1639,6 +1643,12 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) } else { + if (result == -1) { + return (Imaging) ImagingError_ValueError( + "dependency required by this method was not " + "enabled at compile time"); + } + return (Imaging) ImagingError_ValueError("quantization error"); } diff --git a/libImaging/QuantPngQuant.c b/libImaging/QuantPngQuant.c index 2df0e5340..8ae1fa901 100644 --- a/libImaging/QuantPngQuant.c +++ b/libImaging/QuantPngQuant.c @@ -122,21 +122,4 @@ err: return result; } -#else - -/* Offer dummy implementation */ -int -quantize_pngquant( - Pixel *pixelData, - uint32_t width, - uint32_t height, - uint32_t quantPixels, - Pixel **palette, - uint32_t *paletteLength, - uint32_t **quantizedPixels, - int withAlpha) -{ - return 0; -} - #endif From c5278000c5fd66956e7902170f2fe78e102badd8 Mon Sep 17 00:00:00 2001 From: rr- Date: Fri, 6 May 2016 21:06:58 +0200 Subject: [PATCH 08/12] Skip libimagequant's test if it's unavailable --- Tests/test_image_quantize.py | 8 +++++++- winbuild/README.md | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py index 5e04ea9b0..225b02875 100644 --- a/Tests/test_image_quantize.py +++ b/Tests/test_image_quantize.py @@ -18,7 +18,13 @@ class TestImageQuantize(PillowTestCase): def test_libimagequant_quantize(self): im = hopper() - im = im.quantize(100, Image.LIBIMAGEQUANT) + try: + im = im.quantize(100, Image.LIBIMAGEQUANT) + except ValueError as ex: + if 'dependency' in str(ex).lower(): + self.skipTest('libimagepng support not available') + else: + raise self.assert_image(im, "P", im.size) assert len(im.getcolors()) == 100 diff --git a/winbuild/README.md b/winbuild/README.md index 918fb2ba3..89d60de62 100644 --- a/winbuild/README.md +++ b/winbuild/README.md @@ -15,4 +15,4 @@ For more extensive info, see the windows build instructions `docs/build.rst`. * `python test.py` runs the tests on Pillow in all the virtual envs. * Currently working with zlib, libjpeg, freetype, and libtiff on Python 2.7, 3.3, and 3.4, both 32 and 64 bit, on a local win7 pro machine and appveyor.com * Webp is built, not detected. -* LCMS and OpenJpeg are not building. +* LCMS, OpenJpeg and libimagequant are not building. From d471b354708fc8651015e4d222d33ae6f557ac0c Mon Sep 17 00:00:00 2001 From: rr- Date: Fri, 6 May 2016 23:29:53 +0200 Subject: [PATCH 09/12] Fix typo --- Tests/test_image_quantize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py index 225b02875..c19f4ccdf 100644 --- a/Tests/test_image_quantize.py +++ b/Tests/test_image_quantize.py @@ -22,7 +22,7 @@ class TestImageQuantize(PillowTestCase): im = im.quantize(100, Image.LIBIMAGEQUANT) except ValueError as ex: if 'dependency' in str(ex).lower(): - self.skipTest('libimagepng support not available') + self.skipTest('libimagequant support not available') else: raise self.assert_image(im, "P", im.size) From bc0e896b355f1bcb67f007b3e497f856a062f3b6 Mon Sep 17 00:00:00 2001 From: rr- Date: Sat, 21 May 2016 21:01:57 +0200 Subject: [PATCH 10/12] Change libimagequant facade signature --- libImaging/QuantPngQuant.c | 4 ++-- libImaging/QuantPngQuant.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libImaging/QuantPngQuant.c b/libImaging/QuantPngQuant.c index 8ae1fa901..aae18b895 100644 --- a/libImaging/QuantPngQuant.c +++ b/libImaging/QuantPngQuant.c @@ -35,8 +35,8 @@ int quantize_pngquant( Pixel *pixelData, - uint32_t width, - uint32_t height, + int width, + int height, uint32_t quantPixels, Pixel **palette, uint32_t *paletteLength, diff --git a/libImaging/QuantPngQuant.h b/libImaging/QuantPngQuant.h index dc8dd3cfa..d539a7a0d 100644 --- a/libImaging/QuantPngQuant.h +++ b/libImaging/QuantPngQuant.h @@ -4,8 +4,8 @@ #include "QuantTypes.h" int quantize_pngquant(Pixel *, - uint32_t, - uint32_t, + int, + int, uint32_t, Pixel **, uint32_t *, From 0430a4a834d278a4d4809f129829731c08fd3f0f Mon Sep 17 00:00:00 2001 From: rr- Date: Sat, 21 May 2016 21:11:54 +0200 Subject: [PATCH 11/12] Fix libimagequant facade header --- libImaging/QuantPngQuant.c | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/libImaging/QuantPngQuant.c b/libImaging/QuantPngQuant.c index aae18b895..a9a547540 100644 --- a/libImaging/QuantPngQuant.c +++ b/libImaging/QuantPngQuant.c @@ -1,27 +1,12 @@ -/* Copyright (c) 2010 Oliver Tonnhofer , Omniscale -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -*/ - /* -// This file implements a quantization using libimagequant, a part of pngquant. -*/ + * The Python Imaging Library + * $Id$ + * + * quantization using libimagequant, a part of pngquant. + * + * Copyright (c) 2016 Marcin Kurczewski + * + */ #include #include From db989ad99b62cd6443c687670ac327932d19ce33 Mon Sep 17 00:00:00 2001 From: rr- Date: Thu, 26 May 2016 17:23:43 +0200 Subject: [PATCH 12/12] Add perceptive comparisons to quantization tests --- Tests/test_image_quantize.py | 49 ++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py index c19f4ccdf..6ecc8dd7d 100644 --- a/Tests/test_image_quantize.py +++ b/Tests/test_image_quantize.py @@ -6,45 +6,46 @@ from PIL import Image class TestImageQuantize(PillowTestCase): def test_sanity(self): - im = hopper() + image = hopper() + converted = image.quantize() + self.assert_image(converted, 'P', converted.size) + self.assert_image_similar(converted.convert('RGB'), image, 10) - im = im.quantize() - self.assert_image(im, "P", im.size) - - im = hopper() - im = im.quantize(palette=hopper("P")) - self.assert_image(im, "P", im.size) + image = hopper() + converted = image.quantize(palette=hopper('P')) + self.assert_image(converted, 'P', converted.size) + self.assert_image_similar(converted.convert('RGB'), image, 60) def test_libimagequant_quantize(self): - im = hopper() - + image = hopper() try: - im = im.quantize(100, Image.LIBIMAGEQUANT) + converted = image.quantize(100, Image.LIBIMAGEQUANT) except ValueError as ex: if 'dependency' in str(ex).lower(): self.skipTest('libimagequant support not available') else: raise - self.assert_image(im, "P", im.size) - - assert len(im.getcolors()) == 100 + self.assert_image(converted, 'P', converted.size) + self.assert_image_similar(converted.convert('RGB'), image, 15) + assert len(converted.getcolors()) == 100 def test_octree_quantize(self): - im = hopper() - - im = im.quantize(100, Image.FASTOCTREE) - self.assert_image(im, "P", im.size) - - assert len(im.getcolors()) == 100 + image = hopper() + converted = image.quantize(100, Image.FASTOCTREE) + self.assert_image(converted, 'P', converted.size) + self.assert_image_similar(converted.convert('RGB'), image, 20) + assert len(converted.getcolors()) == 100 def test_rgba_quantize(self): - im = hopper('RGBA') - im.quantize() - self.assertRaises(Exception, lambda: im.quantize(method=0)) + image = hopper('RGBA') + image.quantize() + self.assertRaises(Exception, lambda: image.quantize(method=0)) def test_quantize(self): - im = Image.open('Tests/images/caption_6_33_22.png') - im.convert('RGB').quantize().convert('RGB') + image = Image.open('Tests/images/caption_6_33_22.png').convert('RGB') + converted = image.quantize() + self.assert_image(converted, 'P', converted.size) + self.assert_image_similar(converted.convert('RGB'), image, 1) if __name__ == '__main__':