mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-27 17:54:32 +03:00
Add method=Image.LIBIMAGEQUANT for quantize()
This commit is contained in:
parent
12bd44a6fb
commit
eb354be7c4
|
@ -185,6 +185,7 @@ ADAPTIVE = 1
|
||||||
MEDIANCUT = 0
|
MEDIANCUT = 0
|
||||||
MAXCOVERAGE = 1
|
MAXCOVERAGE = 1
|
||||||
FASTOCTREE = 2
|
FASTOCTREE = 2
|
||||||
|
LIBIMAGEQUANT = 3
|
||||||
|
|
||||||
# categories
|
# categories
|
||||||
NORMAL = 0
|
NORMAL = 0
|
||||||
|
@ -967,6 +968,7 @@ class Image(object):
|
||||||
:param method: 0 = median cut
|
:param method: 0 = median cut
|
||||||
1 = maximum coverage
|
1 = maximum coverage
|
||||||
2 = fast octree
|
2 = fast octree
|
||||||
|
3 = libimagequant
|
||||||
:param kmeans: Integer
|
:param kmeans: Integer
|
||||||
:param palette: Quantize to the :py:class:`PIL.ImagingPalette` palette.
|
:param palette: Quantize to the :py:class:`PIL.ImagingPalette` palette.
|
||||||
:returns: A new image
|
:returns: A new image
|
||||||
|
@ -981,10 +983,11 @@ class Image(object):
|
||||||
if self.mode == 'RGBA':
|
if self.mode == 'RGBA':
|
||||||
method = 2
|
method = 2
|
||||||
|
|
||||||
if self.mode == 'RGBA' and method != 2:
|
if self.mode == 'RGBA' and method not in (2, 3):
|
||||||
# Caller specified an invalid mode.
|
# Caller specified an invalid mode.
|
||||||
raise ValueError('Fast Octree (method == 2) is the ' +
|
raise ValueError(
|
||||||
' only valid method for quantizing RGBA images')
|
'Fast Octree (method == 2) and libimagequant (method == 3) ' +
|
||||||
|
'are the only valid methods for quantizing RGBA images')
|
||||||
|
|
||||||
if palette:
|
if palette:
|
||||||
# use palette from reference image
|
# use palette from reference image
|
||||||
|
|
|
@ -15,6 +15,14 @@ class TestImageQuantize(PillowTestCase):
|
||||||
im = im.quantize(palette=hopper("P"))
|
im = im.quantize(palette=hopper("P"))
|
||||||
self.assert_image(im, "P", im.size)
|
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):
|
def test_octree_quantize(self):
|
||||||
im = hopper()
|
im = hopper()
|
||||||
|
|
||||||
|
|
|
@ -1484,7 +1484,7 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans)
|
||||||
return ImagingError_ModeError();
|
return ImagingError_ModeError();
|
||||||
|
|
||||||
/* only octree supports RGBA */
|
/* only octree supports RGBA */
|
||||||
if (!strcmp(im->mode, "RGBA") && mode != 2)
|
if (!strcmp(im->mode, "RGBA") && mode != 2 && mode != 3)
|
||||||
return ImagingError_ModeError();
|
return ImagingError_ModeError();
|
||||||
|
|
||||||
p = malloc(sizeof(Pixel) * im->xsize * im->ysize);
|
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... */
|
should be done by a simple copy... */
|
||||||
|
|
||||||
for (i = y = 0; y < im->ysize; y++)
|
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.r = p[i].c.g = p[i].c.b = im->image8[y][x];
|
||||||
|
p[i].c.a = 255;
|
||||||
|
}
|
||||||
|
|
||||||
} else if (!strcmp(im->mode, "P")) {
|
} else if (!strcmp(im->mode, "P")) {
|
||||||
/* palette */
|
/* 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.r = pp[v*4+0];
|
||||||
p[i].c.g = pp[v*4+1];
|
p[i].c.g = pp[v*4+1];
|
||||||
p[i].c.b = pp[v*4+2];
|
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")) {
|
} else if (!strcmp(im->mode, "RGB") || !strcmp(im->mode, "RGBA")) {
|
||||||
|
@ -1572,6 +1575,21 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans)
|
||||||
withAlpha
|
withAlpha
|
||||||
);
|
);
|
||||||
break;
|
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:
|
default:
|
||||||
result = 0;
|
result = 0;
|
||||||
break;
|
break;
|
||||||
|
|
141
libImaging/QuantPngQuant.c
Normal file
141
libImaging/QuantPngQuant.c
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
/* Copyright (c) 2010 Oliver Tonnhofer <olt@bogosoft.com>, 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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#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
|
15
libImaging/QuantPngQuant.h
Normal file
15
libImaging/QuantPngQuant.h
Normal file
|
@ -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
|
19
setup.py
19
setup.py
|
@ -35,7 +35,7 @@ _LIB_IMAGING = (
|
||||||
"RankFilter", "RawDecode", "RawEncode", "Storage", "SunRleDecode",
|
"RankFilter", "RawDecode", "RawEncode", "Storage", "SunRleDecode",
|
||||||
"TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask", "XbmDecode",
|
"TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask", "XbmDecode",
|
||||||
"XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode", "Incremental",
|
"XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode", "Incremental",
|
||||||
"Jpeg2KDecode", "Jpeg2KEncode", "BoxBlur")
|
"Jpeg2KDecode", "Jpeg2KEncode", "BoxBlur", "QuantPngQuant")
|
||||||
|
|
||||||
DEBUG = False
|
DEBUG = False
|
||||||
|
|
||||||
|
@ -114,6 +114,7 @@ TCL_ROOT = None
|
||||||
JPEG_ROOT = None
|
JPEG_ROOT = None
|
||||||
JPEG2K_ROOT = None
|
JPEG2K_ROOT = None
|
||||||
ZLIB_ROOT = None
|
ZLIB_ROOT = None
|
||||||
|
IMAGEQUANT_ROOT = None
|
||||||
TIFF_ROOT = None
|
TIFF_ROOT = None
|
||||||
FREETYPE_ROOT = None
|
FREETYPE_ROOT = None
|
||||||
LCMS_ROOT = None
|
LCMS_ROOT = None
|
||||||
|
@ -122,7 +123,7 @@ LCMS_ROOT = None
|
||||||
class pil_build_ext(build_ext):
|
class pil_build_ext(build_ext):
|
||||||
class feature:
|
class feature:
|
||||||
features = ['zlib', 'jpeg', 'tiff', 'freetype', 'tcl', 'tk', 'lcms',
|
features = ['zlib', 'jpeg', 'tiff', 'freetype', 'tcl', 'tk', 'lcms',
|
||||||
'webp', 'webpmux', 'jpeg2000']
|
'webp', 'webpmux', 'jpeg2000', 'imagequant']
|
||||||
|
|
||||||
required = set(['jpeg', 'zlib'])
|
required = set(['jpeg', 'zlib'])
|
||||||
|
|
||||||
|
@ -185,7 +186,7 @@ class pil_build_ext(build_ext):
|
||||||
# add configured kits
|
# add configured kits
|
||||||
|
|
||||||
for root in (TCL_ROOT, JPEG_ROOT, JPEG2K_ROOT, TIFF_ROOT, ZLIB_ROOT,
|
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(())):
|
if isinstance(root, type(())):
|
||||||
lib_root, include_root = root
|
lib_root, include_root = root
|
||||||
else:
|
else:
|
||||||
|
@ -478,6 +479,14 @@ class pil_build_ext(build_ext):
|
||||||
feature.openjpeg_version = '.'.join([str(x) for x in
|
feature.openjpeg_version = '.'.join([str(x) for x in
|
||||||
best_version])
|
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'):
|
if feature.want('tiff'):
|
||||||
_dbg('Looking for tiff')
|
_dbg('Looking for tiff')
|
||||||
if _find_include_file(self, 'tiff.h'):
|
if _find_include_file(self, 'tiff.h'):
|
||||||
|
@ -591,6 +600,9 @@ class pil_build_ext(build_ext):
|
||||||
if feature.zlib:
|
if feature.zlib:
|
||||||
libs.append(feature.zlib)
|
libs.append(feature.zlib)
|
||||||
defs.append(("HAVE_LIBZ", None))
|
defs.append(("HAVE_LIBZ", None))
|
||||||
|
if feature.imagequant:
|
||||||
|
libs.append(feature.imagequant)
|
||||||
|
defs.append(("HAVE_LIBIMAGEQUANT", None))
|
||||||
if feature.tiff:
|
if feature.tiff:
|
||||||
libs.append(feature.tiff)
|
libs.append(feature.tiff)
|
||||||
defs.append(("HAVE_LIBTIFF", None))
|
defs.append(("HAVE_LIBTIFF", None))
|
||||||
|
@ -698,6 +710,7 @@ class pil_build_ext(build_ext):
|
||||||
(feature.jpeg2000, "OPENJPEG (JPEG2000)",
|
(feature.jpeg2000, "OPENJPEG (JPEG2000)",
|
||||||
feature.openjpeg_version),
|
feature.openjpeg_version),
|
||||||
(feature.zlib, "ZLIB (PNG/ZIP)"),
|
(feature.zlib, "ZLIB (PNG/ZIP)"),
|
||||||
|
(feature.imagequant, "LIBIMAGEQUANT"),
|
||||||
(feature.tiff, "LIBTIFF"),
|
(feature.tiff, "LIBTIFF"),
|
||||||
(feature.freetype, "FREETYPE2"),
|
(feature.freetype, "FREETYPE2"),
|
||||||
(feature.lcms, "LITTLECMS2"),
|
(feature.lcms, "LITTLECMS2"),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user