Pillow/src/libImaging/Palette.c

322 lines
8.5 KiB
C
Raw Normal View History

2010-07-31 06:52:47 +04:00
/*
* The Python Imaging Library
* $Id$
*
* imaging palette object
*
* history:
* 1996-05-05 fl Added to library
* 1996-05-27 fl Added colour mapping stuff
* 1997-05-12 fl Support RGBA palettes
* 2005-02-09 fl Removed grayscale entries from web palette
*
* Copyright (c) Secret Labs AB 1997-2005. All rights reserved.
* Copyright (c) Fredrik Lundh 1995-1997.
*
* See the README file for information on usage and redistribution.
*/
#include "Imaging.h"
#include <math.h>
ImagingPalette
2021-01-03 06:17:51 +03:00
ImagingPaletteNew(const char *mode) {
2010-07-31 06:52:47 +04:00
/* Create a palette object */
int i;
ImagingPalette palette;
2020-05-10 12:56:36 +03:00
if (strcmp(mode, "RGB") && strcmp(mode, "RGBA")) {
2021-01-03 06:17:51 +03:00
return (ImagingPalette)ImagingError_ModeError();
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
palette = calloc(1, sizeof(struct ImagingPaletteInstance));
2020-05-10 12:56:36 +03:00
if (!palette) {
2021-01-03 06:17:51 +03:00
return (ImagingPalette)ImagingError_MemoryError();
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
2021-01-03 06:17:51 +03:00
strncpy(palette->mode, mode, IMAGING_MODE_LENGTH - 1);
palette->mode[IMAGING_MODE_LENGTH - 1] = 0;
2010-07-31 06:52:47 +04:00
/* Initialize to ramp */
palette->size = 256;
2010-07-31 06:52:47 +04:00
for (i = 0; i < 256; i++) {
2021-01-03 06:17:51 +03:00
palette->palette[i * 4 + 0] = palette->palette[i * 4 + 1] =
palette->palette[i * 4 + 2] = (UINT8)i;
palette->palette[i * 4 + 3] = 255; /* opaque */
2010-07-31 06:52:47 +04:00
}
return palette;
}
ImagingPalette
2021-01-03 06:17:51 +03:00
ImagingPaletteNewBrowser(void) {
2010-07-31 06:52:47 +04:00
/* Create a standard "browser" palette object */
int i, r, g, b;
ImagingPalette palette;
palette = ImagingPaletteNew("RGB");
2020-05-10 12:56:36 +03:00
if (!palette) {
2016-03-16 14:23:45 +03:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
/* Blank out unused entries */
/* FIXME: Add 10-level windows palette here? */
for (i = 0; i < 10; i++) {
2021-01-03 06:17:51 +03:00
palette->palette[i * 4 + 0] = palette->palette[i * 4 + 1] =
palette->palette[i * 4 + 2] = 0;
2010-07-31 06:52:47 +04:00
}
/* Simple 6x6x6 colour cube */
2020-05-10 12:56:36 +03:00
for (b = 0; b < 256; b += 51) {
for (g = 0; g < 256; g += 51) {
2016-03-16 14:23:45 +03:00
for (r = 0; r < 256; r += 51) {
2021-01-03 06:17:51 +03:00
palette->palette[i * 4 + 0] = r;
palette->palette[i * 4 + 1] = g;
palette->palette[i * 4 + 2] = b;
2016-03-16 14:23:45 +03:00
i++;
}
2020-05-10 12:56:36 +03:00
}
}
2010-07-31 06:52:47 +04:00
/* Blank out unused entries */
/* FIXME: add 30-level greyscale wedge here? */
for (; i < 256; i++) {
2021-01-03 06:17:51 +03:00
palette->palette[i * 4 + 0] = palette->palette[i * 4 + 1] =
palette->palette[i * 4 + 2] = 0;
2010-07-31 06:52:47 +04:00
}
return palette;
}
ImagingPalette
2021-01-03 06:17:51 +03:00
ImagingPaletteDuplicate(ImagingPalette palette) {
2010-07-31 06:52:47 +04:00
/* Duplicate palette descriptor */
ImagingPalette new_palette;
2020-05-10 12:56:36 +03:00
if (!palette) {
2016-03-16 14:23:45 +03:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2016-03-16 14:47:18 +03:00
/* malloc check ok, small constant allocation */
2010-07-31 06:52:47 +04:00
new_palette = malloc(sizeof(struct ImagingPaletteInstance));
2020-05-10 12:56:36 +03:00
if (!new_palette) {
2021-01-03 06:17:51 +03:00
return (ImagingPalette)ImagingError_MemoryError();
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
memcpy(new_palette, palette, sizeof(struct ImagingPaletteInstance));
/* Don't share the cache */
new_palette->cache = NULL;
return new_palette;
}
void
2021-01-03 06:17:51 +03:00
ImagingPaletteDelete(ImagingPalette palette) {
2010-07-31 06:52:47 +04:00
/* Destroy palette object */
if (palette) {
2020-05-10 12:56:36 +03:00
if (palette->cache) {
2016-03-16 14:23:45 +03:00
free(palette->cache);
2020-05-10 12:56:36 +03:00
}
2016-03-16 14:23:45 +03:00
free(palette);
2010-07-31 06:52:47 +04:00
}
}
/* -------------------------------------------------------------------- */
2016-03-16 14:23:45 +03:00
/* Colour mapping */
2010-07-31 06:52:47 +04:00
/* -------------------------------------------------------------------- */
/* This code is used to map RGB triplets to palette indices, using
a palette index cache. */
/*
* This implementation is loosely based on the corresponding code in
* the IJG JPEG library by Thomas G. Lane. Original algorithms by
* Paul Heckbert and Spencer W. Thomas.
*
* The IJG JPEG library is copyright (C) 1991-1995, Thomas G. Lane. */
2016-03-16 14:23:45 +03:00
#define DIST(a, b, s) (a - b) * (a - b) * s
2010-07-31 06:52:47 +04:00
/* Colour weights (no scaling, for now) */
2021-01-03 06:17:51 +03:00
#define RSCALE 1
#define GSCALE 1
#define BSCALE 1
2010-07-31 06:52:47 +04:00
/* Calculated scaled distances */
2021-01-03 06:17:51 +03:00
#define RDIST(a, b) DIST(a, b, RSCALE *RSCALE)
#define GDIST(a, b) DIST(a, b, GSCALE *GSCALE)
#define BDIST(a, b) DIST(a, b, BSCALE *BSCALE)
2010-07-31 06:52:47 +04:00
/* Incremental steps */
2021-01-03 06:17:51 +03:00
#define RSTEP (4 * RSCALE)
#define GSTEP (4 * GSCALE)
#define BSTEP (4 * BSCALE)
2010-07-31 06:52:47 +04:00
2021-01-03 06:17:51 +03:00
#define BOX 8
2010-07-31 06:52:47 +04:00
2021-01-03 06:17:51 +03:00
#define BOXVOLUME BOX *BOX *BOX
2010-07-31 06:52:47 +04:00
void
2021-01-03 06:17:51 +03:00
ImagingPaletteCacheUpdate(ImagingPalette palette, int r, int g, int b) {
2010-07-31 06:52:47 +04:00
int i, j;
unsigned int dmin[256], dmax;
int r0, g0, b0;
int r1, g1, b1;
int rc, gc, bc;
unsigned int d[BOXVOLUME];
UINT8 c[BOXVOLUME];
/* Get box boundaries for the given (r,g,b)-triplet. Each box
covers eight cache slots (32 colour values, that is). */
2021-01-03 06:17:51 +03:00
r0 = r & 0xe0;
r1 = r0 + 0x1f;
rc = (r0 + r1) / 2;
g0 = g & 0xe0;
g1 = g0 + 0x1f;
gc = (g0 + g1) / 2;
b0 = b & 0xe0;
b1 = b0 + 0x1f;
bc = (b0 + b1) / 2;
2010-07-31 06:52:47 +04:00
/* Step 1 -- Select relevant palette entries (after Heckbert) */
/* For each palette entry, calculate the min and max distances to
* any position in the box given by the colour we're looking for. */
2021-01-03 06:17:51 +03:00
dmax = (unsigned int)~0;
2010-07-31 06:52:47 +04:00
for (i = 0; i < palette->size; i++) {
2016-03-16 14:23:45 +03:00
int r, g, b;
unsigned int tmin, tmax;
2010-07-31 06:52:47 +04:00
2016-03-16 14:23:45 +03:00
/* Find min and max distances to any point in the box */
2021-01-03 06:17:51 +03:00
r = palette->palette[i * 4 + 0];
2016-03-16 14:23:45 +03:00
tmin = (r < r0) ? RDIST(r, r1) : (r > r1) ? RDIST(r, r0) : 0;
tmax = (r <= rc) ? RDIST(r, r1) : RDIST(r, r0);
2010-07-31 06:52:47 +04:00
2021-01-03 06:17:51 +03:00
g = palette->palette[i * 4 + 1];
2016-03-16 14:23:45 +03:00
tmin += (g < g0) ? GDIST(g, g1) : (g > g1) ? GDIST(g, g0) : 0;
tmax += (g <= gc) ? GDIST(g, g1) : GDIST(g, g0);
2010-07-31 06:52:47 +04:00
2021-01-03 06:17:51 +03:00
b = palette->palette[i * 4 + 2];
2016-03-16 14:23:45 +03:00
tmin += (b < b0) ? BDIST(b, b1) : (b > b1) ? BDIST(b, b0) : 0;
tmax += (b <= bc) ? BDIST(b, b1) : BDIST(b, b0);
2010-07-31 06:52:47 +04:00
2016-03-16 14:23:45 +03:00
dmin[i] = tmin;
2020-05-10 12:56:36 +03:00
if (tmax < dmax) {
2016-03-16 14:23:45 +03:00
dmax = tmax; /* keep the smallest max distance only */
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
}
/* Step 2 -- Incrementally update cache slot (after Thomas) */
/* Find the box containing the nearest palette entry, and update
* all slots in that box. We only check boxes for which the min
* distance is less than or equal the smallest max distance */
2020-05-10 12:56:36 +03:00
for (i = 0; i < BOXVOLUME; i++) {
2021-01-03 06:17:51 +03:00
d[i] = (unsigned int)~0;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
for (i = 0; i < palette->size; i++) {
2016-03-16 14:23:45 +03:00
if (dmin[i] <= dmax) {
int rd, gd, bd;
int ri, gi, bi;
int rx, gx, bx;
2021-01-03 06:17:51 +03:00
ri = (r0 - palette->palette[i * 4 + 0]) * RSCALE;
gi = (g0 - palette->palette[i * 4 + 1]) * GSCALE;
bi = (b0 - palette->palette[i * 4 + 2]) * BSCALE;
2016-03-16 14:23:45 +03:00
2021-01-03 06:17:51 +03:00
rd = ri * ri + gi * gi + bi * bi;
2016-03-16 14:23:45 +03:00
ri = ri * (2 * RSTEP) + RSTEP * RSTEP;
gi = gi * (2 * GSTEP) + GSTEP * GSTEP;
bi = bi * (2 * BSTEP) + BSTEP * BSTEP;
rx = ri;
for (r = j = 0; r < BOX; r++) {
2021-01-03 06:17:51 +03:00
gd = rd;
gx = gi;
2016-03-16 14:23:45 +03:00
for (g = 0; g < BOX; g++) {
2021-01-03 06:17:51 +03:00
bd = gd;
bx = bi;
2016-03-16 14:23:45 +03:00
for (b = 0; b < BOX; b++) {
2021-01-03 06:17:51 +03:00
if ((unsigned int)bd < d[j]) {
2016-03-16 14:23:45 +03:00
d[j] = bd;
2021-01-03 06:17:51 +03:00
c[j] = (UINT8)i;
2016-03-16 14:23:45 +03:00
}
bd += bx;
bx += 2 * BSTEP * BSTEP;
j++;
}
gd += gx;
gx += 2 * GSTEP * GSTEP;
}
rd += rx;
rx += 2 * RSTEP * RSTEP;
}
}
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
/* Step 3 -- Update cache */
/* The c array now contains the closest match for each
* cache slot in the box. Update the cache. */
j = 0;
2021-01-03 06:17:51 +03:00
for (r = r0; r < r1; r += 4) {
for (g = g0; g < g1; g += 4) {
for (b = b0; b < b1; b += 4) {
2016-03-16 14:23:45 +03:00
ImagingPaletteCache(palette, r, g, b) = c[j++];
2020-05-10 12:56:36 +03:00
}
}
}
2010-07-31 06:52:47 +04:00
}
int
2021-01-03 06:17:51 +03:00
ImagingPaletteCachePrepare(ImagingPalette palette) {
2010-07-31 06:52:47 +04:00
/* Add a colour cache to a palette */
int i;
2021-01-03 06:17:51 +03:00
int entries = 64 * 64 * 64;
2010-07-31 06:52:47 +04:00
if (palette->cache == NULL) {
2016-03-16 14:23:45 +03:00
/* The cache is 512k. It might be a good idea to break it
up into a pointer array (e.g. an 8-bit image?) */
2010-07-31 06:52:47 +04:00
2016-03-16 14:47:18 +03:00
/* malloc check ok, small constant allocation */
2021-01-03 06:17:51 +03:00
palette->cache = (INT16 *)malloc(entries * sizeof(INT16));
2016-03-16 14:23:45 +03:00
if (!palette->cache) {
2021-01-03 06:17:51 +03:00
(void)ImagingError_MemoryError();
2016-03-16 14:23:45 +03:00
return -1;
}
2010-07-31 06:52:47 +04:00
2016-03-16 14:23:45 +03:00
/* Mark all entries as empty */
2020-05-10 12:56:36 +03:00
for (i = 0; i < entries; i++) {
2016-03-16 14:23:45 +03:00
palette->cache[i] = 0x100;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
}
return 0;
}
void
2021-01-03 06:17:51 +03:00
ImagingPaletteCacheDelete(ImagingPalette palette) {
2010-07-31 06:52:47 +04:00
/* Release the colour cache, if any */
if (palette && palette->cache) {
2016-03-16 14:23:45 +03:00
free(palette->cache);
palette->cache = NULL;
2010-07-31 06:52:47 +04:00
}
}