mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-26 01:46:18 +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
|
||||
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
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
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",
|
||||
"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"),
|
||||
|
|
Loading…
Reference in New Issue
Block a user