123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- /*
- * 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
- ImagingPaletteNew(const char* mode)
- {
- /* Create a palette object */
- int i;
- ImagingPalette palette;
- if (strcmp(mode, "RGB") && strcmp(mode, "RGBA"))
- return (ImagingPalette) ImagingError_ModeError();
- palette = calloc(1, sizeof(struct ImagingPaletteInstance));
- if (!palette)
- return (ImagingPalette) ImagingError_MemoryError();
- strncpy(palette->mode, mode, IMAGING_MODE_LENGTH-1);
- palette->mode[IMAGING_MODE_LENGTH-1] = 0;
- /* Initialize to ramp */
- for (i = 0; i < 256; i++) {
- palette->palette[i*4+0] =
- palette->palette[i*4+1] =
- palette->palette[i*4+2] = (UINT8) i;
- palette->palette[i*4+3] = 255; /* opaque */
- }
- return palette;
- }
- ImagingPalette
- ImagingPaletteNewBrowser(void)
- {
- /* Create a standard "browser" palette object */
- int i, r, g, b;
- ImagingPalette palette;
- palette = ImagingPaletteNew("RGB");
- if (!palette)
- return NULL;
- /* Blank out unused entries */
- /* FIXME: Add 10-level windows palette here? */
- for (i = 0; i < 10; i++) {
- palette->palette[i*4+0] =
- palette->palette[i*4+1] =
- palette->palette[i*4+2] = 0;
- }
- /* Simple 6x6x6 colour cube */
- for (b = 0; b < 256; b += 51)
- for (g = 0; g < 256; g += 51)
- for (r = 0; r < 256; r += 51) {
- palette->palette[i*4+0] = r;
- palette->palette[i*4+1] = g;
- palette->palette[i*4+2] = b;
- i++;
- }
- /* Blank out unused entries */
- /* FIXME: add 30-level greyscale wedge here? */
- for (; i < 256; i++) {
- palette->palette[i*4+0] =
- palette->palette[i*4+1] =
- palette->palette[i*4+2] = 0;
- }
- return palette;
- }
- ImagingPalette
- ImagingPaletteDuplicate(ImagingPalette palette)
- {
- /* Duplicate palette descriptor */
- ImagingPalette new_palette;
- if (!palette)
- return NULL;
- /* malloc check ok, small constant allocation */
- new_palette = malloc(sizeof(struct ImagingPaletteInstance));
- if (!new_palette)
- return (ImagingPalette) ImagingError_MemoryError();
- memcpy(new_palette, palette, sizeof(struct ImagingPaletteInstance));
- /* Don't share the cache */
- new_palette->cache = NULL;
- return new_palette;
- }
- void
- ImagingPaletteDelete(ImagingPalette palette)
- {
- /* Destroy palette object */
- if (palette) {
- if (palette->cache)
- free(palette->cache);
- free(palette);
- }
- }
- /* -------------------------------------------------------------------- */
- /* Colour mapping */
- /* -------------------------------------------------------------------- */
- /* 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. */
- #define DIST(a, b, s) (a - b) * (a - b) * s
- /* Colour weights (no scaling, for now) */
- #define RSCALE 1
- #define GSCALE 1
- #define BSCALE 1
- /* Calculated scaled distances */
- #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)
- /* Incremental steps */
- #define RSTEP (4 * RSCALE)
- #define GSTEP (4 * GSCALE)
- #define BSTEP (4 * BSCALE)
- #define BOX 8
- #define BOXVOLUME BOX*BOX*BOX
- void
- ImagingPaletteCacheUpdate(ImagingPalette palette, int r, int g, int b)
- {
- 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). */
- 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;
- /* 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. */
- dmax = (unsigned int) ~0;
- for (i = 0; i < 256; i++) {
- int r, g, b;
- unsigned int tmin, tmax;
- /* Find min and max distances to any point in the box */
- r = palette->palette[i*4+0];
- tmin = (r < r0) ? RDIST(r, r1) : (r > r1) ? RDIST(r, r0) : 0;
- tmax = (r <= rc) ? RDIST(r, r1) : RDIST(r, r0);
- g = palette->palette[i*4+1];
- tmin += (g < g0) ? GDIST(g, g1) : (g > g1) ? GDIST(g, g0) : 0;
- tmax += (g <= gc) ? GDIST(g, g1) : GDIST(g, g0);
- b = palette->palette[i*4+2];
- tmin += (b < b0) ? BDIST(b, b1) : (b > b1) ? BDIST(b, b0) : 0;
- tmax += (b <= bc) ? BDIST(b, b1) : BDIST(b, b0);
- dmin[i] = tmin;
- if (tmax < dmax)
- dmax = tmax; /* keep the smallest max distance only */
- }
- /* 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 */
- for (i = 0; i < BOXVOLUME; i++)
- d[i] = (unsigned int) ~0;
- for (i = 0; i < 256; i++)
- if (dmin[i] <= dmax) {
- int rd, gd, bd;
- int ri, gi, bi;
- int rx, gx, bx;
- ri = (r0 - palette->palette[i*4+0]) * RSCALE;
- gi = (g0 - palette->palette[i*4+1]) * GSCALE;
- bi = (b0 - palette->palette[i*4+2]) * BSCALE;
- rd = ri*ri + gi*gi + bi*bi;
- 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++) {
- gd = rd; gx = gi;
- for (g = 0; g < BOX; g++) {
- bd = gd; bx = bi;
- for (b = 0; b < BOX; b++) {
- if ((unsigned int) bd < d[j]) {
- d[j] = bd;
- c[j] = (UINT8) i;
- }
- bd += bx;
- bx += 2 * BSTEP * BSTEP;
- j++;
- }
- gd += gx;
- gx += 2 * GSTEP * GSTEP;
- }
- rd += rx;
- rx += 2 * RSTEP * RSTEP;
- }
- }
- /* Step 3 -- Update cache */
- /* The c array now contains the closest match for each
- * cache slot in the box. Update the cache. */
- j = 0;
- for (r = r0; r < r1; r+=4)
- for (g = g0; g < g1; g+=4)
- for (b = b0; b < b1; b+=4)
- ImagingPaletteCache(palette, r, g, b) = c[j++];
- }
- int
- ImagingPaletteCachePrepare(ImagingPalette palette)
- {
- /* Add a colour cache to a palette */
- int i;
- int entries = 64*64*64;
- if (palette->cache == NULL) {
- /* The cache is 512k. It might be a good idea to break it
- up into a pointer array (e.g. an 8-bit image?) */
- /* malloc check ok, small constant allocation */
- palette->cache = (INT16*) malloc(entries * sizeof(INT16));
- if (!palette->cache) {
- (void) ImagingError_MemoryError();
- return -1;
- }
- /* Mark all entries as empty */
- for (i = 0; i < entries; i++)
- palette->cache[i] = 0x100;
- }
- return 0;
- }
- void
- ImagingPaletteCacheDelete(ImagingPalette palette)
- {
- /* Release the colour cache, if any */
- if (palette && palette->cache) {
- free(palette->cache);
- palette->cache = NULL;
- }
- }
|