Merge pull request #95 from olt/pil-png-transp-backport

Backport PNG/ZIP improvements from PIL repo
This commit is contained in:
Alex Clark ☺ 2013-03-12 03:43:06 -07:00
commit d6f597a1ad
13 changed files with 739 additions and 142 deletions

View File

@ -143,11 +143,23 @@ FLOYDSTEINBERG = 3 # default
WEB = 0
ADAPTIVE = 1
MEDIANCUT = 0
MAXCOVERAGE = 1
FASTOCTREE = 2
# categories
NORMAL = 0
SEQUENCE = 1
CONTAINER = 2
if hasattr(core, 'DEFAULT_STRATEGY'):
DEFAULT_STRATEGY = core.DEFAULT_STRATEGY
FILTERED = core.FILTERED
HUFFMAN_ONLY = core.HUFFMAN_ONLY
RLE = core.RLE
FIXED = core.FIXED
# --------------------------------------------------------------------
# Registries
@ -616,18 +628,12 @@ class Image:
self.palette.mode = "RGB"
self.palette.rawmode = None
if "transparency" in self.info:
# XXX Not sure how this ever worked:
# if self.info["transparency_palette"]:
# Should probably be:
if "transparency_palette" in self.info:
# amirite?
self.im.putpalettealpha(0, 0, self.info["transparency_palette"])
if isinstance(self.info["transparency"], str):
self.im.putpalettealphas(self.info["transparency"])
else:
self.im.putpalettealpha(self.info["transparency"], 0)
self.palette.mode = "RGBA"
if self.im:
return self.im.pixel_access(self.readonly)
@ -724,6 +730,7 @@ class Image:
# methods:
# 0 = median cut
# 1 = maximum coverage
# 2 = fast octree
# NOTE: this functionality will be moved to the extended
# quantizer interface in a later version of PIL.

View File

@ -70,6 +70,8 @@ _MODES = {
}
_simple_palette = re.compile(b'^\xff+\x00+$')
# --------------------------------------------------------------------
# Support classes. Suitable for PNG and related formats like MNG etc.
@ -251,10 +253,12 @@ class PngStream(ChunkStream):
# transparency
s = ImageFile._safe_read(self.fp, len)
if self.im_mode == "P":
i = s.find(b"\0")
if i >= 0:
self.im_info["transparency"] = i
self.im_info["transparency_palette"] = s
if _simple_palette.match(s):
i = s.find(b"\0")
if i >= 0:
self.im_info["transparency"] = i
else:
self.im_info["transparency"] = s
elif self.im_mode == "L":
self.im_info["transparency"] = i16(s)
elif self.im_mode == "RGB":
@ -514,7 +518,10 @@ def _save(im, fp, filename, chunk=putchunk, check=0):
else:
dictionary = b""
im.encoderconfig = ("optimize" in im.encoderinfo, dictionary)
im.encoderconfig = ("optimize" in im.encoderinfo,
im.encoderinfo.get("compress_level", -1),
im.encoderinfo.get("compress_type", -1),
dictionary)
# get the corresponding PNG mode
try:
@ -538,12 +545,16 @@ def _save(im, fp, filename, chunk=putchunk, check=0):
b'\0') # 12: interlace flag
if im.mode == "P":
chunk(fp, b"PLTE", im.im.getpalette("RGB"))
palette_bytes = (2 ** bits) * 3
chunk(fp, b"PLTE", im.im.getpalette("RGB")[:palette_bytes])
if "transparency" in im.encoderinfo:
if im.mode == "P":
transparency = max(0, min(255, im.encoderinfo["transparency"]))
chunk(fp, b"tRNS", b'\xFF' * transparency + b'\0')
alpha = b'\xFF' * transparency + b'\0'
# limit to actual palette size
alpha_bytes = 2**bits
chunk(fp, b"tRNS", alpha[:alpha_bytes])
elif im.mode == "L":
transparency = max(0, min(65535, im.encoderinfo["transparency"]))
chunk(fp, b"tRNS", o16(transparency))
@ -552,6 +563,11 @@ def _save(im, fp, filename, chunk=putchunk, check=0):
chunk(fp, b"tRNS", o16(red) + o16(green) + o16(blue))
else:
raise IOError("cannot use transparency for this mode")
else:
if im.mode == "P" and im.im.getpalettemode() == "RGBA":
alpha = im.im.getpalette("RGBA", "A")
alpha_bytes = 2**bits
chunk(fp, b"tRNS", alpha[:alpha_bytes])
if 0:
# FIXME: to be supported some day

View File

@ -117,6 +117,17 @@ def test_interlace():
assert_no_exception(lambda: im.load())
def test_load_transparent_p():
file = "Tests/images/pil123p.png"
im = Image.open(file)
assert_image(im, "P", (162, 150))
im = im.convert("RGBA")
assert_image(im, "RGBA", (162, 150))
# image has 124 uniqe qlpha values
assert_equal(len(im.split()[3].getcolors()), 124)
def test_load_verify():
# Check open/load/verify exception (@PIL150)

View File

@ -13,3 +13,10 @@ def test_sanity():
im = im.quantize(palette=lena("P"))
assert_image(im, "P", im.size)
def test_octree_quantize():
im = lena()
im = im.quantize(100, Image.FASTOCTREE)
assert_image(im, "P", im.size)
assert len(im.getcolors()) == 100

View File

@ -65,7 +65,7 @@
* 2005-10-02 fl Added access proxy
* 2006-06-18 fl Always draw last point in polyline
*
* Copyright (c) 1997-2006 by Secret Labs AB
* Copyright (c) 1997-2006 by Secret Labs AB
* Copyright (c) 1995-2006 by Fredrik Lundh
*
* See the README file for information on usage and redistribution.
@ -157,7 +157,7 @@ typedef struct {
static PyTypeObject PixelAccess_Type;
PyObject*
PyObject*
PyImagingNew(Imaging imOut)
{
ImagingObject* imagep;
@ -584,7 +584,7 @@ getink(PyObject* color, Imaging im, char* ink)
/* FACTORIES */
/* -------------------------------------------------------------------- */
static PyObject*
static PyObject*
_fill(PyObject* self, PyObject* args)
{
char* mode;
@ -592,7 +592,7 @@ _fill(PyObject* self, PyObject* args)
PyObject* color;
char buffer[4];
Imaging im;
xsize = ysize = 256;
color = NULL;
@ -616,7 +616,7 @@ _fill(PyObject* self, PyObject* args)
return PyImagingNew(im);
}
static PyObject*
static PyObject*
_new(PyObject* self, PyObject* args)
{
char* mode;
@ -628,7 +628,7 @@ _new(PyObject* self, PyObject* args)
return PyImagingNew(ImagingNew(mode, xsize, ysize));
}
static PyObject*
static PyObject*
_new_array(PyObject* self, PyObject* args)
{
char* mode;
@ -640,7 +640,7 @@ _new_array(PyObject* self, PyObject* args)
return PyImagingNew(ImagingNewArray(mode, xsize, ysize));
}
static PyObject*
static PyObject*
_new_block(PyObject* self, PyObject* args)
{
char* mode;
@ -652,7 +652,7 @@ _new_block(PyObject* self, PyObject* args)
return PyImagingNew(ImagingNewBlock(mode, xsize, ysize));
}
static PyObject*
static PyObject*
_getcount(PyObject* self, PyObject* args)
{
if (!PyArg_ParseTuple(args, ":getcount"))
@ -661,7 +661,7 @@ _getcount(PyObject* self, PyObject* args)
return PyInt_FromLong(ImagingNewCount);
}
static PyObject*
static PyObject*
_linear_gradient(PyObject* self, PyObject* args)
{
char* mode;
@ -672,7 +672,7 @@ _linear_gradient(PyObject* self, PyObject* args)
return PyImagingNew(ImagingFillLinearGradient(mode));
}
static PyObject*
static PyObject*
_radial_gradient(PyObject* self, PyObject* args)
{
char* mode;
@ -683,7 +683,7 @@ _radial_gradient(PyObject* self, PyObject* args)
return PyImagingNew(ImagingFillRadialGradient(mode));
}
static PyObject*
static PyObject*
_open_ppm(PyObject* self, PyObject* args)
{
char* filename;
@ -708,13 +708,13 @@ _alpha_composite(ImagingObject* self, PyObject* args)
return PyImagingNew(ImagingAlphaComposite(imagep1->image, imagep2->image));
}
static PyObject*
static PyObject*
_blend(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep1;
ImagingObject* imagep2;
double alpha;
alpha = 0.5;
if (!PyArg_ParseTuple(args, "O!O!|d",
&Imaging_Type, &imagep1,
@ -730,7 +730,7 @@ _blend(ImagingObject* self, PyObject* args)
/* METHODS */
/* -------------------------------------------------------------------- */
static PyObject*
static PyObject*
_convert(ImagingObject* self, PyObject* args)
{
char* mode;
@ -741,20 +741,20 @@ _convert(ImagingObject* self, PyObject* args)
return NULL;
if (paletteimage != NULL) {
if (!PyImaging_Check(paletteimage)) {
PyObject_Print((PyObject *)paletteimage, stderr, 0);
PyErr_SetString(PyExc_ValueError, "palette argument must be image with mode 'P'");
return NULL;
PyObject_Print((PyObject *)paletteimage, stderr, 0);
PyErr_SetString(PyExc_ValueError, "palette argument must be image with mode 'P'");
return NULL;
}
if (paletteimage->image->palette == NULL) {
PyErr_SetString(PyExc_ValueError, "null palette");
return NULL;
PyErr_SetString(PyExc_ValueError, "null palette");
return NULL;
}
}
return PyImagingNew(ImagingConvert(self->image, mode, paletteimage ? paletteimage->image->palette : NULL, dither));
}
static PyObject*
static PyObject*
_convert2(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep1;
@ -771,7 +771,7 @@ _convert2(ImagingObject* self, PyObject* args)
return Py_None;
}
static PyObject*
static PyObject*
_convert_matrix(ImagingObject* self, PyObject* args)
{
char* mode;
@ -788,7 +788,7 @@ _convert_matrix(ImagingObject* self, PyObject* args)
return PyImagingNew(ImagingConvertMatrix(self->image, mode, m));
}
static PyObject*
static PyObject*
_copy(ImagingObject* self, PyObject* args)
{
if (!PyArg_ParseTuple(args, ""))
@ -797,7 +797,7 @@ _copy(ImagingObject* self, PyObject* args)
return PyImagingNew(ImagingCopy(self->image));
}
static PyObject*
static PyObject*
_copy2(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep1;
@ -814,7 +814,7 @@ _copy2(ImagingObject* self, PyObject* args)
return Py_None;
}
static PyObject*
static PyObject*
_crop(ImagingObject* self, PyObject* args)
{
int x0, y0, x1, y1;
@ -824,7 +824,7 @@ _crop(ImagingObject* self, PyObject* args)
return PyImagingNew(ImagingCrop(self->image, x0, y0, x1, y1));
}
static PyObject*
static PyObject*
_expand(ImagingObject* self, PyObject* args)
{
int x, y;
@ -835,7 +835,7 @@ _expand(ImagingObject* self, PyObject* args)
return PyImagingNew(ImagingExpand(self->image, x, y, mode));
}
static PyObject*
static PyObject*
_filter(ImagingObject* self, PyObject* args)
{
PyObject* imOut;
@ -848,7 +848,7 @@ _filter(ImagingObject* self, PyObject* args)
if (!PyArg_ParseTuple(args, "(ii)ffO", &xsize, &ysize,
&divisor, &offset, &kernel))
return NULL;
/* get user-defined kernel */
kerneldata = getlist(kernel, &kernelsize, NULL, TYPE_FLOAT32);
if (!kerneldata)
@ -868,7 +868,7 @@ _filter(ImagingObject* self, PyObject* args)
}
#ifdef WITH_UNSHARPMASK
static PyObject*
static PyObject*
_gaussian_blur(ImagingObject* self, PyObject* args)
{
Imaging imIn;
@ -890,7 +890,7 @@ _gaussian_blur(ImagingObject* self, PyObject* args)
}
#endif
static PyObject*
static PyObject*
_getpalette(ImagingObject* self, PyObject* args)
{
PyObject* palette;
@ -924,6 +924,17 @@ _getpalette(ImagingObject* self, PyObject* args)
return palette;
}
static PyObject*
_getpalettemode(ImagingObject* self, PyObject* args)
{
if (!self->image->palette) {
PyErr_SetString(PyExc_ValueError, no_palette);
return NULL;
}
return PyString_FromString(self->image->palette->mode);
}
static inline int
_getxy(PyObject* xy, int* x, int *y)
{
@ -931,7 +942,7 @@ _getxy(PyObject* xy, int* x, int *y)
if (!PyTuple_Check(xy) || PyTuple_GET_SIZE(xy) != 2)
goto badarg;
value = PyTuple_GET_ITEM(xy, 0);
if (PyInt_Check(value))
*x = PyInt_AS_LONG(value);
@ -965,7 +976,7 @@ _getxy(PyObject* xy, int* x, int *y)
return -1;
}
static PyObject*
static PyObject*
_getpixel(ImagingObject* self, PyObject* args)
{
PyObject* xy;
@ -1065,7 +1076,7 @@ _histogram(ImagingObject* self, PyObject* args)
}
#ifdef WITH_MODEFILTER
static PyObject*
static PyObject*
_modefilter(ImagingObject* self, PyObject* args)
{
int size;
@ -1076,7 +1087,7 @@ _modefilter(ImagingObject* self, PyObject* args)
}
#endif
static PyObject*
static PyObject*
_offset(ImagingObject* self, PyObject* args)
{
int xoffset, yoffset;
@ -1086,7 +1097,7 @@ _offset(ImagingObject* self, PyObject* args)
return PyImagingNew(ImagingOffset(self->image, xoffset, yoffset));
}
static PyObject*
static PyObject*
_paste(ImagingObject* self, PyObject* args)
{
int status;
@ -1251,7 +1262,7 @@ _putdata(ImagingObject* self, PyObject* args)
x = image->xsize;
memcpy(image->image8[y], p+i, x);
}
else
else
/* Scaled and clipped string data */
for (i = x = y = 0; i < n; i++) {
image->image8[y][x] = CLIP((int) (p[i] * scale + offset));
@ -1355,7 +1366,7 @@ _putdata(ImagingObject* self, PyObject* args)
#ifdef WITH_QUANTIZE
#include "Quant.h"
static PyObject*
static PyObject*
_quantize(ImagingObject* self, PyObject* args)
{
int colours = 256;
@ -1375,7 +1386,7 @@ _quantize(ImagingObject* self, PyObject* args)
}
#endif
static PyObject*
static PyObject*
_putpalette(ImagingObject* self, PyObject* args)
{
ImagingShuffler unpack;
@ -1410,14 +1421,12 @@ _putpalette(ImagingObject* self, PyObject* args)
return Py_None;
}
static PyObject*
static PyObject*
_putpalettealpha(ImagingObject* self, PyObject* args)
{
int index;
int alpha = 0;
char* tpalette = NULL;
int tpaletteSize = 0;
if (!PyArg_ParseTuple(args, "i|i"PY_ARG_BYTES_LENGTH, &index, &alpha, &tpalette, &tpaletteSize))
if (!PyArg_ParseTuple(args, "i|i", &index, &alpha))
return NULL;
if (!self->image->palette) {
@ -1429,23 +1438,43 @@ _putpalettealpha(ImagingObject* self, PyObject* args)
PyErr_SetString(PyExc_ValueError, outside_palette);
return NULL;
}
strcpy(self->image->palette->mode, "RGBA");
if (tpaletteSize > 0) {
for (index = 0; index < tpaletteSize; index++) {
self->image->palette->palette[index*4+3] = (UINT8) tpalette[index];
}
strcpy(self->image->palette->mode, "RGBA");
self->image->palette->palette[index*4+3] = (UINT8) alpha;
Py_INCREF(Py_None);
return Py_None;
}
static PyObject*
_putpalettealphas(ImagingObject* self, PyObject* args)
{
int i;
UINT8 *values;
int length;
if (!PyArg_ParseTuple(args, "s#", &values, &length))
return NULL;
if (!self->image->palette) {
PyErr_SetString(PyExc_ValueError, no_palette);
return NULL;
}
else {
self->image->palette->palette[index*4+3] = (UINT8) alpha;
if (length > 256) {
PyErr_SetString(PyExc_ValueError, outside_palette);
return NULL;
}
strcpy(self->image->palette->mode, "RGBA");
for (i=0; i<length; i++) {
self->image->palette->palette[i*4+3] = (UINT8) values[i];
}
Py_INCREF(Py_None);
return Py_None;
}
static PyObject*
static PyObject*
_putpixel(ImagingObject* self, PyObject* args)
{
Imaging im;
@ -1457,7 +1486,7 @@ _putpixel(ImagingObject* self, PyObject* args)
return NULL;
im = self->image;
if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) {
PyErr_SetString(PyExc_IndexError, outside_image);
return NULL;
@ -1474,7 +1503,7 @@ _putpixel(ImagingObject* self, PyObject* args)
}
#ifdef WITH_RANKFILTER
static PyObject*
static PyObject*
_rankfilter(ImagingObject* self, PyObject* args)
{
int size, rank;
@ -1485,7 +1514,7 @@ _rankfilter(ImagingObject* self, PyObject* args)
}
#endif
static PyObject*
static PyObject*
_resize(ImagingObject* self, PyObject* args)
{
Imaging imIn;
@ -1501,16 +1530,16 @@ _resize(ImagingObject* self, PyObject* args)
imOut = ImagingNew(imIn->mode, xsize, ysize);
if (imOut)
(void) ImagingResize(imOut, imIn, filter);
return PyImagingNew(imOut);
}
static PyObject*
static PyObject*
_rotate(ImagingObject* self, PyObject* args)
{
Imaging imOut;
Imaging imIn;
double theta;
int filter = IMAGING_TRANSFORM_NEAREST;
if (!PyArg_ParseTuple(args, "d|i", &theta, &filter))
@ -1556,7 +1585,7 @@ _rotate(ImagingObject* self, PyObject* args)
#define IS_RGB(mode)\
(!strcmp(mode, "RGB") || !strcmp(mode, "RGBA") || !strcmp(mode, "RGBX"))
static PyObject*
static PyObject*
im_setmode(ImagingObject* self, PyObject* args)
{
/* attempt to modify the mode of an image in place */
@ -1594,7 +1623,7 @@ im_setmode(ImagingObject* self, PyObject* args)
return Py_None;
}
static PyObject*
static PyObject*
_stretch(ImagingObject* self, PyObject* args)
{
Imaging imIn;
@ -1611,7 +1640,7 @@ _stretch(ImagingObject* self, PyObject* args)
/* two-pass resize: minimize size of intermediate image */
if (imIn->xsize * ysize < xsize * imIn->ysize)
imTemp = ImagingNew(imIn->mode, imIn->xsize, ysize);
else
else
imTemp = ImagingNew(imIn->mode, xsize, imIn->ysize);
if (!imTemp)
return NULL;
@ -1636,11 +1665,11 @@ _stretch(ImagingObject* self, PyObject* args)
}
ImagingDelete(imTemp);
return PyImagingNew(imOut);
}
static PyObject*
static PyObject*
_transform2(ImagingObject* self, PyObject* args)
{
static const char* wrong_number = "wrong number of matrix entries";
@ -1715,7 +1744,7 @@ _transform2(ImagingObject* self, PyObject* args)
return Py_None;
}
static PyObject*
static PyObject*
_transpose(ImagingObject* self, PyObject* args)
{
Imaging imIn;
@ -1726,7 +1755,7 @@ _transpose(ImagingObject* self, PyObject* args)
return NULL;
imIn = self->image;
switch (op) {
case 0: /* flip left right */
case 1: /* flip top bottom */
@ -1765,7 +1794,7 @@ _transpose(ImagingObject* self, PyObject* args)
}
#ifdef WITH_UNSHARPMASK
static PyObject*
static PyObject*
_unsharp_mask(ImagingObject* self, PyObject* args)
{
Imaging imIn;
@ -1791,13 +1820,13 @@ _unsharp_mask(ImagingObject* self, PyObject* args)
/* -------------------------------------------------------------------- */
static PyObject*
static PyObject*
_isblock(ImagingObject* self, PyObject* args)
{
return PyInt_FromLong((long) self->image->block);
}
static PyObject*
static PyObject*
_getbbox(ImagingObject* self, PyObject* args)
{
int bbox[4];
@ -1809,7 +1838,7 @@ _getbbox(ImagingObject* self, PyObject* args)
return Py_BuildValue("iiii", bbox[0], bbox[1], bbox[2], bbox[3]);
}
static PyObject*
static PyObject*
_getcolors(ImagingObject* self, PyObject* args)
{
ImagingColorItem* items;
@ -1843,7 +1872,7 @@ _getcolors(ImagingObject* self, PyObject* args)
return out;
}
static PyObject*
static PyObject*
_getextrema(ImagingObject* self, PyObject* args)
{
union {
@ -1852,7 +1881,7 @@ _getextrema(ImagingObject* self, PyObject* args)
FLOAT32 f[2];
} extrema;
int status;
status = ImagingGetExtrema(self->image, &extrema);
if (status < 0)
return NULL;
@ -1871,7 +1900,7 @@ _getextrema(ImagingObject* self, PyObject* args)
return Py_None;
}
static PyObject*
static PyObject*
_getprojection(ImagingObject* self, PyObject* args)
{
unsigned char* xprofile;
@ -1901,7 +1930,7 @@ _getprojection(ImagingObject* self, PyObject* args)
/* -------------------------------------------------------------------- */
static PyObject*
static PyObject*
_getband(ImagingObject* self, PyObject* args)
{
int band;
@ -1912,7 +1941,7 @@ _getband(ImagingObject* self, PyObject* args)
return PyImagingNew(ImagingGetBand(self->image, band));
}
static PyObject*
static PyObject*
_fillband(ImagingObject* self, PyObject* args)
{
int band;
@ -1923,12 +1952,12 @@ _fillband(ImagingObject* self, PyObject* args)
if (!ImagingFillBand(self->image, band, color))
return NULL;
Py_INCREF(Py_None);
return Py_None;
}
static PyObject*
static PyObject*
_putband(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
@ -1949,13 +1978,13 @@ _putband(ImagingObject* self, PyObject* args)
#ifdef WITH_IMAGECHOPS
static PyObject*
static PyObject*
_chop_invert(ImagingObject* self, PyObject* args)
{
return PyImagingNew(ImagingNegative(self->image));
}
static PyObject*
static PyObject*
_chop_lighter(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
@ -1966,7 +1995,7 @@ _chop_lighter(ImagingObject* self, PyObject* args)
return PyImagingNew(ImagingChopLighter(self->image, imagep->image));
}
static PyObject*
static PyObject*
_chop_darker(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
@ -1977,7 +2006,7 @@ _chop_darker(ImagingObject* self, PyObject* args)
return PyImagingNew(ImagingChopDarker(self->image, imagep->image));
}
static PyObject*
static PyObject*
_chop_difference(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
@ -1988,7 +2017,7 @@ _chop_difference(ImagingObject* self, PyObject* args)
return PyImagingNew(ImagingChopDifference(self->image, imagep->image));
}
static PyObject*
static PyObject*
_chop_multiply(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
@ -1999,7 +2028,7 @@ _chop_multiply(ImagingObject* self, PyObject* args)
return PyImagingNew(ImagingChopMultiply(self->image, imagep->image));
}
static PyObject*
static PyObject*
_chop_screen(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
@ -2010,7 +2039,7 @@ _chop_screen(ImagingObject* self, PyObject* args)
return PyImagingNew(ImagingChopScreen(self->image, imagep->image));
}
static PyObject*
static PyObject*
_chop_add(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
@ -2028,7 +2057,7 @@ _chop_add(ImagingObject* self, PyObject* args)
scale, offset));
}
static PyObject*
static PyObject*
_chop_subtract(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
@ -2046,7 +2075,7 @@ _chop_subtract(ImagingObject* self, PyObject* args)
scale, offset));
}
static PyObject*
static PyObject*
_chop_and(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
@ -2057,7 +2086,7 @@ _chop_and(ImagingObject* self, PyObject* args)
return PyImagingNew(ImagingChopAnd(self->image, imagep->image));
}
static PyObject*
static PyObject*
_chop_or(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
@ -2068,7 +2097,7 @@ _chop_or(ImagingObject* self, PyObject* args)
return PyImagingNew(ImagingChopOr(self->image, imagep->image));
}
static PyObject*
static PyObject*
_chop_xor(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
@ -2079,7 +2108,7 @@ _chop_xor(ImagingObject* self, PyObject* args)
return PyImagingNew(ImagingChopXor(self->image, imagep->image));
}
static PyObject*
static PyObject*
_chop_add_modulo(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
@ -2090,7 +2119,7 @@ _chop_add_modulo(ImagingObject* self, PyObject* args)
return PyImagingNew(ImagingChopAddModulo(self->image, imagep->image));
}
static PyObject*
static PyObject*
_chop_subtract_modulo(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
@ -2284,7 +2313,7 @@ _draw_dealloc(ImagingDrawObject* self)
extern int PyPath_Flatten(PyObject* data, double **xy);
static PyObject*
static PyObject*
_draw_ink(ImagingDrawObject* self, PyObject* args)
{
INT32 ink = 0;
@ -2299,7 +2328,7 @@ _draw_ink(ImagingDrawObject* self, PyObject* args)
return PyInt_FromLong((int) ink);
}
static PyObject*
static PyObject*
_draw_arc(ImagingDrawObject* self, PyObject* args)
{
int x0, y0, x1, y1;
@ -2319,7 +2348,7 @@ _draw_arc(ImagingDrawObject* self, PyObject* args)
return Py_None;
}
static PyObject*
static PyObject*
_draw_bitmap(ImagingDrawObject* self, PyObject* args)
{
double *xy;
@ -2356,7 +2385,7 @@ _draw_bitmap(ImagingDrawObject* self, PyObject* args)
return Py_None;
}
static PyObject*
static PyObject*
_draw_chord(ImagingDrawObject* self, PyObject* args)
{
int x0, y0, x1, y1;
@ -2374,7 +2403,7 @@ _draw_chord(ImagingDrawObject* self, PyObject* args)
return Py_None;
}
static PyObject*
static PyObject*
_draw_ellipse(ImagingDrawObject* self, PyObject* args)
{
double* xy;
@ -2401,7 +2430,7 @@ _draw_ellipse(ImagingDrawObject* self, PyObject* args)
self->image->image, (int) xy[0], (int) xy[1], (int) xy[2], (int) xy[3],
&ink, fill, self->blend
);
free(xy);
if (n < 0)
@ -2411,7 +2440,7 @@ _draw_ellipse(ImagingDrawObject* self, PyObject* args)
return Py_None;
}
static PyObject*
static PyObject*
_draw_line(ImagingDrawObject* self, PyObject* args)
{
int x0, y0, x1, y1;
@ -2427,7 +2456,7 @@ _draw_line(ImagingDrawObject* self, PyObject* args)
return Py_None;
}
static PyObject*
static PyObject*
_draw_lines(ImagingDrawObject* self, PyObject* args)
{
double *xy;
@ -2480,7 +2509,7 @@ _draw_lines(ImagingDrawObject* self, PyObject* args)
return Py_None;
}
static PyObject*
static PyObject*
_draw_point(ImagingDrawObject* self, PyObject* args)
{
int x, y;
@ -2495,7 +2524,7 @@ _draw_point(ImagingDrawObject* self, PyObject* args)
return Py_None;
}
static PyObject*
static PyObject*
_draw_points(ImagingDrawObject* self, PyObject* args)
{
double *xy;
@ -2530,7 +2559,7 @@ _draw_points(ImagingDrawObject* self, PyObject* args)
/* from outline.c */
extern ImagingOutline PyOutline_AsOutline(PyObject* outline);
static PyObject*
static PyObject*
_draw_outline(ImagingDrawObject* self, PyObject* args)
{
ImagingOutline outline;
@ -2557,7 +2586,7 @@ _draw_outline(ImagingDrawObject* self, PyObject* args)
#endif
static PyObject*
static PyObject*
_draw_pieslice(ImagingDrawObject* self, PyObject* args)
{
int x0, y0, x1, y1;
@ -2575,7 +2604,7 @@ _draw_pieslice(ImagingDrawObject* self, PyObject* args)
return Py_None;
}
static PyObject*
static PyObject*
_draw_polygon(ImagingDrawObject* self, PyObject* args)
{
double *xy;
@ -2621,7 +2650,7 @@ _draw_polygon(ImagingDrawObject* self, PyObject* args)
return Py_None;
}
static PyObject*
static PyObject*
_draw_rectangle(ImagingDrawObject* self, PyObject* args)
{
double* xy;
@ -2648,7 +2677,7 @@ _draw_rectangle(ImagingDrawObject* self, PyObject* args)
self->image->image, (int) xy[0], (int) xy[1],
(int) xy[2], (int) xy[3], &ink, fill, self->blend
);
free(xy);
if (n < 0)
@ -2759,7 +2788,7 @@ pixel_access_setitem(PixelAccessObject *self, PyObject *xy, PyObject *color)
#ifdef WITH_EFFECTS
static PyObject*
static PyObject*
_effect_mandelbrot(ImagingObject* self, PyObject* args)
{
int xsize = 512;
@ -2778,7 +2807,7 @@ _effect_mandelbrot(ImagingObject* self, PyObject* args)
return PyImagingNew(ImagingEffectMandelbrot(xsize, ysize, extent, quality));
}
static PyObject*
static PyObject*
_effect_noise(ImagingObject* self, PyObject* args)
{
int xsize, ysize;
@ -2789,7 +2818,7 @@ _effect_noise(ImagingObject* self, PyObject* args)
return PyImagingNew(ImagingEffectNoise(xsize, ysize, sigma));
}
static PyObject*
static PyObject*
_effect_spread(ImagingObject* self, PyObject* args)
{
int dist;
@ -2806,7 +2835,7 @@ _effect_spread(ImagingObject* self, PyObject* args)
/* UTILITIES */
/* -------------------------------------------------------------------- */
static PyObject*
static PyObject*
_crc32(PyObject* self, PyObject* args)
{
unsigned char* buffer;
@ -2827,7 +2856,7 @@ _crc32(PyObject* self, PyObject* args)
return Py_BuildValue("ii", (crc >> 16) & 0xFFFF, crc & 0xFFFF);
}
static PyObject*
static PyObject*
_getcodecstatus(PyObject* self, PyObject* args)
{
int status;
@ -2861,7 +2890,7 @@ _getcodecstatus(PyObject* self, PyObject* args)
#ifdef WITH_DEBUG
static PyObject*
static PyObject*
_save_ppm(ImagingObject* self, PyObject* args)
{
char* filename;
@ -2935,10 +2964,12 @@ static struct PyMethodDef methods[] = {
{"fillband", (PyCFunction)_fillband, 1},
{"setmode", (PyCFunction)im_setmode, 1},
{"getpalette", (PyCFunction)_getpalette, 1},
{"getpalettemode", (PyCFunction)_getpalettemode, 1},
{"putpalette", (PyCFunction)_putpalette, 1},
{"putpalettealpha", (PyCFunction)_putpalettealpha, 1},
{"putpalettealphas", (PyCFunction)_putpalettealphas, 1},
#ifdef WITH_IMAGECHOPS
/* Channel operations (ImageChops) */
@ -3334,7 +3365,7 @@ static PyMethodDef functions[] = {
#ifdef WITH_IMAGEPATH
{"path", (PyCFunction)PyPath_Create, 1},
#endif
/* Experimental arrow graphics stuff */
#ifdef WITH_ARROW
{"outline", (PyCFunction)PyOutline_Create, 1},
@ -3371,6 +3402,13 @@ setup_module(PyObject* m) {
#endif
#ifdef HAVE_LIBZ
#include "zlib.h"
/* zip encoding strategies */
PyModule_AddIntConstant(m, "DEFAULT_STRATEGY", Z_DEFAULT_STRATEGY);
PyModule_AddIntConstant(m, "FILTERED", Z_FILTERED);
PyModule_AddIntConstant(m, "HUFFMAN_ONLY", Z_HUFFMAN_ONLY);
PyModule_AddIntConstant(m, "RLE", Z_RLE);
PyModule_AddIntConstant(m, "FIXED", Z_FIXED);
{
extern const char* ImagingZipVersion(void);
PyDict_SetItemString(d, "zlib_version", PyUnicode_FromString(ImagingZipVersion()));
@ -3409,4 +3447,3 @@ init_imaging(void)
}
#endif

View File

@ -445,10 +445,14 @@ PyImaging_ZipEncoderNew(PyObject* self, PyObject* args)
char* mode;
char* rawmode;
int optimize = 0;
int compress_level = -1;
int compress_type = -1;
char* dictionary = NULL;
int dictionary_size = 0;
if (!PyArg_ParseTuple(args, "ss|i"PY_ARG_BYTES_LENGTH, &mode, &rawmode,
&optimize, &dictionary, &dictionary_size))
if (!PyArg_ParseTuple(args, "ss|iii"PY_ARG_BYTES_LENGTH, &mode, &rawmode,
&optimize,
&compress_level, &compress_type,
&dictionary, &dictionary_size))
return NULL;
/* Copy to avoid referencing Python's memory, but there's no mechanism to
@ -477,6 +481,8 @@ PyImaging_ZipEncoderNew(PyObject* self, PyObject* args)
((ZIPSTATE*)encoder->state.context)->mode = ZIP_PNG_PALETTE;
((ZIPSTATE*)encoder->state.context)->optimize = optimize;
((ZIPSTATE*)encoder->state.context)->compress_level = compress_level;
((ZIPSTATE*)encoder->state.context)->compress_type = compress_type;
((ZIPSTATE*)encoder->state.context)->dictionary = dictionary;
((ZIPSTATE*)encoder->state.context)->dictionary_size = dictionary_size;
@ -512,16 +518,16 @@ static unsigned int** get_qtables_arrays(PyObject* qtables) {
PyObject* table_data;
int i, j, num_tables;
unsigned int **qarrays;
if (qtables == Py_None) {
return NULL;
}
if (!PySequence_Check(qtables)) {
PyErr_SetString(PyExc_ValueError, "Invalid quantization tables");
return NULL;
}
tables = PySequence_Fast(qtables, "expected a sequence");
num_tables = PySequence_Size(qtables);
if (num_tables < 2 || num_tables > NUM_QUANT_TBLS) {

View File

@ -26,6 +26,7 @@
#include <time.h>
#include "Quant.h"
#include "QuantOctree.h"
#include "QuantDefines.h"
#include "QuantHash.h"
@ -1485,6 +1486,8 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans)
int result;
unsigned long* newData;
Imaging imOut;
int withAlpha = 0;
ImagingSectionCookie cookie;
if (!im)
return ImagingError_ModeError();
@ -1494,9 +1497,13 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans)
return (Imaging) ImagingError_ValueError("bad number of colors");
if (strcmp(im->mode, "L") != 0 && strcmp(im->mode, "P") != 0 &&
strcmp(im->mode, "RGB"))
strcmp(im->mode, "RGB") != 0 && strcmp(im->mode, "RGBA") !=0)
return ImagingError_ModeError();
/* only octree supports RGBA */
if (!strcmp(im->mode, "RGBA") && mode != 2)
return ImagingError_ModeError();
p = malloc(sizeof(Pixel) * im->xsize * im->ysize);
if (!p)
return ImagingError_MemoryError();
@ -1529,7 +1536,7 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans)
p[i].c.b = pp[v*4+2];
}
} else if (!strcmp(im->mode, "RGB")) {
} else if (!strcmp(im->mode, "RGB") || !strcmp(im->mode, "RGBA")) {
/* true colour */
for (i = y = 0; y < im->ysize; y++)
@ -1541,6 +1548,8 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans)
return (Imaging) ImagingError_ValueError("internal error");
}
ImagingSectionEnter(&cookie);
switch (mode) {
case 0:
/* median cut */
@ -1566,16 +1575,31 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans)
kmeans
);
break;
case 2:
if (!strcmp(im->mode, "RGBA")) {
withAlpha = 1;
}
result = quantize_octree(
p,
im->xsize*im->ysize,
colors,
&palette,
&paletteLength,
&newData,
withAlpha
);
break;
default:
result = 0;
break;
}
free(p);
ImagingSectionLeave(&cookie);
if (result) {
imOut = ImagingNew("P", im->xsize, im->ysize);
ImagingSectionEnter(&cookie);
for (i = y = 0; y < im->ysize; y++)
for (x=0; x < im->xsize; x++)
@ -1589,7 +1613,11 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans)
*pp++ = palette[i].c.r;
*pp++ = palette[i].c.g;
*pp++ = palette[i].c.b;
*pp++ = 255;
if (withAlpha) {
*pp++ = palette[i].c.a;
} else {
*pp++ = 255;
}
}
for (; i < 256; i++) {
*pp++ = 0;
@ -1598,7 +1626,12 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans)
*pp++ = 255;
}
if (withAlpha) {
strcpy(imOut->palette->mode, "RGBA");
}
free(palette);
ImagingSectionLeave(&cookie);
return imOut;

454
libImaging/QuantOctree.c Normal file
View File

@ -0,0 +1,454 @@
/* 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 variation of the octree color quantization algorithm.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Quant.h"
typedef struct _ColorBucket{
/* contains palette index when used for look up cube */
unsigned long count;
unsigned long r;
unsigned long g;
unsigned long b;
unsigned long a;
} *ColorBucket;
typedef struct _ColorCube{
unsigned int rBits, gBits, bBits, aBits;
unsigned int rWidth, gWidth, bWidth, aWidth;
unsigned int rOffset, gOffset, bOffset, aOffset;
long size;
ColorBucket buckets;
} *ColorCube;
#define MAX(a, b) (a)>(b) ? (a) : (b)
static ColorCube
new_color_cube(int r, int g, int b, int a) {
ColorCube cube;
cube = malloc(sizeof(struct _ColorCube));
if (!cube) return NULL;
cube->rBits = MAX(r, 0);
cube->gBits = MAX(g, 0);
cube->bBits = MAX(b, 0);
cube->aBits = MAX(a, 0);
/* the width of the cube for each dimension */
cube->rWidth = 1<<cube->rBits;
cube->gWidth = 1<<cube->gBits;
cube->bWidth = 1<<cube->bBits;
cube->aWidth = 1<<cube->aBits;
/* the offsets of each color */
cube->rOffset = cube->gBits + cube->bBits + cube->aBits;
cube->gOffset = cube->bBits + cube->aBits;
cube->bOffset = cube->aBits;
cube->aOffset = 0;
/* the number of color buckets */
cube->size = cube->rWidth * cube->gWidth * cube->bWidth * cube->aWidth;
cube->buckets = calloc(cube->size, sizeof(struct _ColorBucket));
if (!cube->buckets) {
free(cube);
return NULL;
}
return cube;
}
static void
free_color_cube(ColorCube cube) {
if (cube != NULL) {
free(cube->buckets);
free(cube);
}
}
static long
color_bucket_offset_pos(const ColorCube cube,
unsigned int r, unsigned int g, unsigned int b, unsigned int a)
{
return r<<cube->rOffset | g<<cube->gOffset | b<<cube->bOffset | a<<cube->aOffset;
}
static long
color_bucket_offset(const ColorCube cube, const Pixel *p) {
unsigned int r = p->c.r>>(8-cube->rBits);
unsigned int g = p->c.g>>(8-cube->gBits);
unsigned int b = p->c.b>>(8-cube->bBits);
unsigned int a = p->c.a>>(8-cube->aBits);
return color_bucket_offset_pos(cube, r, g, b, a);
}
static ColorBucket
color_bucket_from_cube(const ColorCube cube, const Pixel *p) {
unsigned int offset = color_bucket_offset(cube, p);
return &cube->buckets[offset];
}
static void
add_color_to_color_cube(const ColorCube cube, const Pixel *p) {
ColorBucket bucket = color_bucket_from_cube(cube, p);
bucket->count += 1;
bucket->r += p->c.r;
bucket->g += p->c.g;
bucket->b += p->c.b;
bucket->a += p->c.a;
}
static long
count_used_color_buckets(const ColorCube cube) {
long usedBuckets = 0;
long i;
for (i=0; i < cube->size; i++) {
if (cube->buckets[i].count > 0) {
usedBuckets += 1;
}
}
return usedBuckets;
}
static void
avg_color_from_color_bucket(const ColorBucket bucket, Pixel *dst) {
float count = bucket->count;
dst->c.r = (int)(bucket->r / count);
dst->c.g = (int)(bucket->g / count);
dst->c.b = (int)(bucket->b / count);
dst->c.a = (int)(bucket->a / count);
}
static int
compare_bucket_count(const ColorBucket a, const ColorBucket b) {
return b->count - a->count;
}
static ColorBucket
create_sorted_color_palette(const ColorCube cube) {
ColorBucket buckets;
buckets = malloc(sizeof(struct _ColorBucket)*cube->size);
if (!buckets) return NULL;
memcpy(buckets, cube->buckets, sizeof(struct _ColorBucket)*cube->size);
qsort(buckets, cube->size, sizeof(struct _ColorBucket),
(int (*)(void const *, void const *))&compare_bucket_count);
return buckets;
}
void add_bucket_values(ColorBucket src, ColorBucket dst) {
dst->count += src->count;
dst->r += src->r;
dst->g += src->g;
dst->b += src->b;
dst->a += src->a;
}
/* expand or shrink a given cube to level */
static ColorCube copy_color_cube(const ColorCube cube,
int rBits, int gBits, int bBits, int aBits)
{
unsigned int r, g, b, a;
long src_pos, dst_pos;
unsigned int src_reduce[4] = {0}, dst_reduce[4] = {0};
unsigned int width[4];
ColorCube result;
result = new_color_cube(rBits, gBits, bBits, aBits);
if (!result) return NULL;
if (cube->rBits > rBits) {
dst_reduce[0] = cube->rBits - result->rBits;
width[0] = cube->rWidth;
} else {
src_reduce[0] = result->rBits - cube->rBits;
width[0] = result->rWidth;
}
if (cube->gBits > gBits) {
dst_reduce[1] = cube->gBits - result->gBits;
width[1] = cube->gWidth;
} else {
src_reduce[1] = result->gBits - cube->gBits;
width[1] = result->gWidth;
}
if (cube->bBits > bBits) {
dst_reduce[2] = cube->bBits - result->bBits;
width[2] = cube->bWidth;
} else {
src_reduce[2] = result->bBits - cube->bBits;
width[2] = result->bWidth;
}
if (cube->aBits > aBits) {
dst_reduce[3] = cube->aBits - result->aBits;
width[3] = cube->aWidth;
} else {
src_reduce[3] = result->aBits - cube->aBits;
width[3] = result->aWidth;
}
for (r=0; r<width[0]; r++) {
for (g=0; g<width[1]; g++) {
for (b=0; b<width[2]; b++) {
for (a=0; a<width[3]; a++) {
src_pos = color_bucket_offset_pos(cube,
r>>src_reduce[0],
g>>src_reduce[1],
b>>src_reduce[2],
a>>src_reduce[3]);
dst_pos = color_bucket_offset_pos(result,
r>>dst_reduce[0],
g>>dst_reduce[1],
b>>dst_reduce[2],
a>>dst_reduce[3]);
add_bucket_values(
&cube->buckets[src_pos],
&result->buckets[dst_pos]
);
}
}
}
}
return result;
}
void
subtract_color_buckets(ColorCube cube, ColorBucket buckets, long nBuckets) {
ColorBucket minuend, subtrahend;
long i;
Pixel p;
for (i=0; i<nBuckets; i++) {
subtrahend = &buckets[i];
avg_color_from_color_bucket(subtrahend, &p);
minuend = color_bucket_from_cube(cube, &p);
minuend->count -= subtrahend->count;
minuend->r -= subtrahend->r;
minuend->g -= subtrahend->g;
minuend->b -= subtrahend->b;
minuend->a -= subtrahend->a;
}
}
static void
set_lookup_value(const ColorCube cube, const Pixel *p, long value) {
ColorBucket bucket = color_bucket_from_cube(cube, p);
bucket->count = value;
}
unsigned long
lookup_color(const ColorCube cube, const Pixel *p) {
ColorBucket bucket = color_bucket_from_cube(cube, p);
return bucket->count;
}
void add_lookup_buckets(ColorCube cube, ColorBucket palette, long nColors, long offset) {
long i;
Pixel p;
for (i=offset; i<offset+nColors; i++) {
avg_color_from_color_bucket(&palette[i], &p);
set_lookup_value(cube, &p, i);
}
}
ColorBucket
combined_palette(ColorBucket bucketsA, long nBucketsA, ColorBucket bucketsB, long nBucketsB) {
ColorBucket result;
result = malloc(sizeof(struct _ColorBucket)*(nBucketsA+nBucketsB));
memcpy(result, bucketsA, sizeof(struct _ColorBucket) * nBucketsA);
memcpy(&result[nBucketsA], bucketsB, sizeof(struct _ColorBucket) * nBucketsB);
return result;
}
static Pixel *
create_palette_array(const ColorBucket palette, unsigned int paletteLength) {
Pixel *paletteArray;
unsigned int i;
paletteArray = malloc(sizeof(Pixel)*paletteLength);
if (!paletteArray) return NULL;
for (i=0; i<paletteLength; i++) {
avg_color_from_color_bucket(&palette[i], &paletteArray[i]);
}
return paletteArray;
}
static void
map_image_pixels(const Pixel *pixelData,
unsigned long nPixels,
const ColorCube lookupCube,
unsigned long *pixelArray)
{
long i;
for (i=0; i<nPixels; i++) {
pixelArray[i] = lookup_color(lookupCube, &pixelData[i]);
}
}
const int CUBE_LEVELS[8] = {4, 4, 4, 0, 2, 2, 2, 0};
const int CUBE_LEVELS_ALPHA[8] = {3, 4, 3, 3, 2, 2, 2, 2};
int quantize_octree(Pixel *pixelData,
unsigned long nPixels,
unsigned long nQuantPixels,
Pixel **palette,
unsigned long *paletteLength,
unsigned long **quantizedPixels,
int withAlpha)
{
ColorCube fineCube = NULL;
ColorCube coarseCube = NULL;
ColorCube lookupCube = NULL;
ColorCube coarseLookupCube = NULL;
ColorBucket paletteBucketsCoarse = NULL;
ColorBucket paletteBucketsFine = NULL;
ColorBucket paletteBuckets = NULL;
unsigned long *qp = NULL;
long i;
long nCoarseColors, nFineColors, nAlreadySubtracted;
const int *cubeBits;
if (withAlpha) {
cubeBits = CUBE_LEVELS_ALPHA;
}
else {
cubeBits = CUBE_LEVELS;
}
/*
Create two color cubes, one fine grained with 8x16x8=1024
colors buckets and a coarse with 4x4x4=64 color buckets.
The coarse one guarantes that there are color buckets available for
the whole color range (assuming nQuantPixels > 64).
For a quantization to 256 colors all 64 coarse colors will be used
plus the 192 most used color buckets from the fine color cube.
The average of all colors within one bucket is used as the actual
color for that bucket.
For images with alpha the cubes gets a forth dimension,
8x16x8x8 and 4x4x4x4.
*/
/* create fine cube */
fineCube = new_color_cube(cubeBits[0], cubeBits[1],
cubeBits[2], cubeBits[3]);
if (!fineCube) goto error;
for (i=0; i<nPixels; i++) {
add_color_to_color_cube(fineCube, &pixelData[i]);
}
/* create coarse cube */
coarseCube = copy_color_cube(fineCube, cubeBits[4], cubeBits[5],
cubeBits[6], cubeBits[7]);
if (!coarseCube) goto error;
nCoarseColors = count_used_color_buckets(coarseCube);
/* limit to nQuantPixels */
if (nCoarseColors > nQuantPixels)
nCoarseColors = nQuantPixels;
/* how many space do we have in our palette for fine colors? */
nFineColors = nQuantPixels - nCoarseColors;
/* create fine color palette */
paletteBucketsFine = create_sorted_color_palette(fineCube);
if (!paletteBucketsFine) goto error;
/* remove the used fine colors from the coarse cube */
subtract_color_buckets(coarseCube, paletteBucketsFine, nFineColors);
/* did the substraction cleared one or more coarse bucket? */
while (nCoarseColors > count_used_color_buckets(coarseCube)) {
/* then we can use the free buckets for fine colors */
nAlreadySubtracted = nFineColors;
nCoarseColors = count_used_color_buckets(coarseCube);
nFineColors = nQuantPixels - nCoarseColors;
subtract_color_buckets(coarseCube, &paletteBucketsFine[nAlreadySubtracted],
nFineColors-nAlreadySubtracted);
}
/* create our palette buckets with fine and coarse combined */
paletteBucketsCoarse = create_sorted_color_palette(coarseCube);
if (!paletteBucketsCoarse) goto error;
paletteBuckets = combined_palette(paletteBucketsCoarse, nCoarseColors,
paletteBucketsFine, nFineColors);
free(paletteBucketsFine);
paletteBucketsFine = NULL;
free(paletteBucketsCoarse);
paletteBucketsCoarse = NULL;
/* add all coarse colors to our coarse lookup cube. */
coarseLookupCube = new_color_cube(cubeBits[4], cubeBits[5],
cubeBits[6], cubeBits[7]);
if (!coarseLookupCube) goto error;
add_lookup_buckets(coarseLookupCube, paletteBuckets, nCoarseColors, 0);
/* expand coarse cube (64) to larger fine cube (4k). the value of each
coarse bucket is then present in the according 64 fine buckets. */
lookupCube = copy_color_cube(coarseLookupCube, cubeBits[0], cubeBits[1],
cubeBits[2], cubeBits[3]);
if (!lookupCube) goto error;
/* add fine colors to the lookup cube */
add_lookup_buckets(lookupCube, paletteBuckets, nFineColors, nCoarseColors);
/* create result pixles and map palatte indices */
qp = malloc(sizeof(Pixel)*nPixels);
if (!qp) goto error;
map_image_pixels(pixelData, nPixels, lookupCube, qp);
/* convert palette buckets to RGB pixel palette */
*palette = create_palette_array(paletteBuckets, nQuantPixels);
if (!(*palette)) goto error;
*quantizedPixels = qp;
*paletteLength = nQuantPixels;
free_color_cube(coarseCube);
free_color_cube(fineCube);
free_color_cube(lookupCube);
free_color_cube(coarseLookupCube);
free(paletteBuckets);
return 1;
error:
/* everything is initialized to NULL
so we are safe to call free */
free(qp);
free_color_cube(lookupCube);
free_color_cube(coarseLookupCube);
free(paletteBucketsCoarse);
free(paletteBucketsFine);
free_color_cube(coarseCube);
free_color_cube(fineCube);
return 0;
}

12
libImaging/QuantOctree.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef __QUANT_OCTREE_H__
#define __QUANT_OCTREE_H__
int quantize_octree(Pixel *,
unsigned long,
unsigned long,
Pixel **,
unsigned long *,
unsigned long **,
int);
#endif

View File

@ -27,6 +27,11 @@ typedef struct {
/* Optimize (max compression) SLOW!!! */
int optimize;
/* 0 no compression, 9 best compression, -1 default compression */
int compress_level;
/* compression strategy Z_XXX */
int compress_type;
/* Predefined dictionary (experimental) */
char* dictionary;

View File

@ -26,6 +26,7 @@ ImagingZipEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
{
ZIPSTATE* context = (ZIPSTATE*) state->context;
int err;
int compress_level, compress_type;
UINT8* ptr;
int i, bpp, s, sum;
ImagingSectionCookie cookie;
@ -73,17 +74,25 @@ ImagingZipEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
context->z_stream.next_in = 0;
context->z_stream.avail_in = 0;
compress_level = (context->optimize) ? Z_BEST_COMPRESSION
: context->compress_level;
if (context->compress_type == -1) {
compress_type = (context->mode == ZIP_PNG) ? Z_FILTERED
: Z_DEFAULT_STRATEGY;
} else {
compress_type = context->compress_type;
}
err = deflateInit2(&context->z_stream,
/* compression level */
(context->optimize) ? Z_BEST_COMPRESSION
: Z_DEFAULT_COMPRESSION,
compress_level,
/* compression method */
Z_DEFLATED,
/* compression memory resources */
15, 9,
/* compression strategy (image data are filtered)*/
(context->mode == ZIP_PNG) ? Z_FILTERED
: Z_DEFAULT_STRATEGY);
compress_type);
if (err < 0) {
state->errcode = IMAGING_CODEC_CONFIG;
return -1;

View File

@ -157,7 +157,7 @@ def testimage():
def check_module(feature, module):
try:
__import__(module)
__import__("PIL." + module)
except ImportError:
print("***", feature, "support not installed")
else:

View File

@ -22,7 +22,7 @@ _LIB_IMAGING = (
"Geometry", "GetBBox", "GifDecode", "GifEncode", "HexDecode",
"Histo", "JpegDecode", "JpegEncode", "LzwDecode", "Matrix",
"ModeFilter", "MspDecode", "Negative", "Offset", "Pack",
"PackDecode", "Palette", "Paste", "Quant", "QuantHash",
"PackDecode", "Palette", "Paste", "Quant", "QuantOctree", "QuantHash",
"QuantHeap", "PcdDecode", "PcxDecode", "PcxEncode", "Point",
"RankFilter", "RawDecode", "RawEncode", "Storage", "SunRleDecode",
"TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask", "XbmDecode",