Skip to content

Commit 47daccc

Browse files
committed
Move palette cache functions to ConvertToPalette.c
They were used nowhere else. Keeping them together, as a private API, allows for future improvements to the palette cache's shape.
1 parent b70da5e commit 47daccc

3 files changed

Lines changed: 174 additions & 209 deletions

File tree

src/libImaging/ConvertToPalette.c

Lines changed: 174 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* The Python Imaging Library
33
*
4-
* See Convert.c for legacy history of this file.
4+
* See Convert.c and Palette.c for legacy history of this file.
55
*
66
* Copyright (c) 1997-2005 by Secret Labs AB.
77
* Copyright (c) 1995-1997 by Fredrik Lundh.
@@ -11,6 +11,174 @@
1111

1212
#include "Imaging.h"
1313

14+
/*
15+
* The coarse colour mapping is loosely based on the corresponding code in
16+
* the IJG JPEG library by Thomas G. Lane. Original algorithms by
17+
* Paul Heckbert and Spencer W. Thomas.
18+
*/
19+
20+
#define DIST(a, b, s) (a - b) * (a - b) * s
21+
22+
#define RSCALE 1
23+
#define GSCALE 1
24+
#define BSCALE 1
25+
26+
#define RDIST(a, b) DIST(a, b, RSCALE *RSCALE)
27+
#define GDIST(a, b) DIST(a, b, GSCALE *GSCALE)
28+
#define BDIST(a, b) DIST(a, b, BSCALE *BSCALE)
29+
30+
#define RSTEP (4 * RSCALE)
31+
#define GSTEP (4 * GSCALE)
32+
#define BSTEP (4 * BSCALE)
33+
34+
#define BOX 8
35+
#define BOXVOLUME (BOX * BOX * BOX)
36+
37+
inline static INT16 *
38+
palette_cache(ImagingPalette palette, int r, int g, int b) {
39+
return &palette->cache[(r >> 2) + (g >> 2) * 64 + (b >> 2) * 64 * 64];
40+
}
41+
42+
static void
43+
palette_cache_update(ImagingPalette palette, int r, int g, int b) {
44+
int i, j;
45+
unsigned int dmin[IMAGING_PALETTE_MAX_ENTRIES], dmax;
46+
int r0, g0, b0;
47+
int r1, g1, b1;
48+
int rc, gc, bc;
49+
unsigned int d[BOXVOLUME];
50+
UINT8 c[BOXVOLUME];
51+
52+
/* Get box boundaries for the given (r,g,b)-triplet. Each box
53+
covers eight cache slots (32 colour values, that is). */
54+
55+
r0 = r & 0xe0;
56+
r1 = r0 + 0x1f;
57+
rc = (r0 + r1) / 2;
58+
g0 = g & 0xe0;
59+
g1 = g0 + 0x1f;
60+
gc = (g0 + g1) / 2;
61+
b0 = b & 0xe0;
62+
b1 = b0 + 0x1f;
63+
bc = (b0 + b1) / 2;
64+
65+
/* Step 1 -- Select relevant palette entries (after Heckbert) */
66+
67+
/* For each palette entry, calculate the min and max distances to
68+
* any position in the box given by the colour we're looking for. */
69+
70+
dmax = (unsigned int)~0;
71+
72+
for (i = 0; i < palette->size; i++) {
73+
int r, g, b;
74+
unsigned int tmin, tmax;
75+
76+
/* Find min and max distances to any point in the box */
77+
r = palette->palette[i * 4 + 0];
78+
tmin = (r < r0) ? RDIST(r, r0) : (r > r1) ? RDIST(r, r1) : 0;
79+
tmax = (r <= rc) ? RDIST(r, r1) : RDIST(r, r0);
80+
81+
g = palette->palette[i * 4 + 1];
82+
tmin += (g < g0) ? GDIST(g, g0) : (g > g1) ? GDIST(g, g1) : 0;
83+
tmax += (g <= gc) ? GDIST(g, g1) : GDIST(g, g0);
84+
85+
b = palette->palette[i * 4 + 2];
86+
tmin += (b < b0) ? BDIST(b, b0) : (b > b1) ? BDIST(b, b1) : 0;
87+
tmax += (b <= bc) ? BDIST(b, b1) : BDIST(b, b0);
88+
89+
dmin[i] = tmin;
90+
if (tmax < dmax) {
91+
dmax = tmax; /* keep the smallest max distance only */
92+
}
93+
}
94+
95+
/* Step 2 -- Incrementally update cache slot (after Thomas) */
96+
97+
/* Find the box containing the nearest palette entry, and update
98+
* all slots in that box. We only check boxes for which the min
99+
* distance is less than or equal the smallest max distance */
100+
101+
for (i = 0; i < BOXVOLUME; i++) {
102+
d[i] = (unsigned int)~0;
103+
}
104+
105+
for (i = 0; i < palette->size; i++) {
106+
if (dmin[i] <= dmax) {
107+
int rd, gd, bd;
108+
int ri, gi, bi;
109+
int rx, gx, bx;
110+
111+
ri = (r0 - palette->palette[i * 4 + 0]) * RSCALE;
112+
gi = (g0 - palette->palette[i * 4 + 1]) * GSCALE;
113+
bi = (b0 - palette->palette[i * 4 + 2]) * BSCALE;
114+
115+
rd = ri * ri + gi * gi + bi * bi;
116+
117+
ri = ri * (2 * RSTEP) + RSTEP * RSTEP;
118+
gi = gi * (2 * GSTEP) + GSTEP * GSTEP;
119+
bi = bi * (2 * BSTEP) + BSTEP * BSTEP;
120+
121+
rx = ri;
122+
for (r = j = 0; r < BOX; r++) {
123+
gd = rd;
124+
gx = gi;
125+
for (g = 0; g < BOX; g++) {
126+
bd = gd;
127+
bx = bi;
128+
for (b = 0; b < BOX; b++) {
129+
if ((unsigned int)bd < d[j]) {
130+
d[j] = bd;
131+
c[j] = (UINT8)i;
132+
}
133+
bd += bx;
134+
bx += 2 * BSTEP * BSTEP;
135+
j++;
136+
}
137+
gd += gx;
138+
gx += 2 * GSTEP * GSTEP;
139+
}
140+
rd += rx;
141+
rx += 2 * RSTEP * RSTEP;
142+
}
143+
}
144+
}
145+
146+
/* Step 3 -- Update cache */
147+
148+
/* The c array now contains the closest match for each
149+
* cache slot in the box. Update the cache. */
150+
151+
j = 0;
152+
for (r = r0; r < r1; r += 4) {
153+
for (g = g0; g < g1; g += 4) {
154+
for (b = b0; b < b1; b += 4) {
155+
*palette_cache(palette, r, g, b) = c[j++];
156+
}
157+
}
158+
}
159+
}
160+
161+
static int
162+
palette_cache_prepare(ImagingPalette palette) {
163+
int entries = 64 * 64 * 64;
164+
165+
if (palette->cache == NULL) {
166+
/* malloc check ok, small constant allocation */
167+
palette->cache = (INT16 *)malloc(entries * sizeof(INT16));
168+
if (!palette->cache) {
169+
(void)ImagingError_MemoryError();
170+
return -1;
171+
}
172+
173+
/* Mark all entries as empty */
174+
for (int i = 0; i < entries; i++) {
175+
palette->cache[i] = 0x100;
176+
}
177+
}
178+
179+
return 0;
180+
}
181+
14182
// The hash will always have up to 50% fill factor.
15183
#define EXACT_COLOR_HASH_SIZE (IMAGING_PALETTE_MAX_ENTRIES * 2)
16184

@@ -151,9 +319,9 @@ topalette_colour_floyd_steinberg(
151319
int palette_index = find_exact_color(ech, r, g, b);
152320
if (palette_index < 0) {
153321
/* get closest colour */
154-
INT16 *cache = &ImagingPaletteCache(palette, r, g, b);
322+
INT16 *cache = palette_cache(palette, r, g, b);
155323
if (cache[0] == 0x100) {
156-
ImagingPaletteCacheUpdate(palette, r, g, b);
324+
palette_cache_update(palette, r, g, b);
157325
}
158326
palette_index = cache[0];
159327
}
@@ -229,9 +397,9 @@ topalette_colour_closest(
229397
int palette_index = find_exact_color(ech, r, g, b);
230398
if (palette_index < 0) {
231399
/* get closest colour */
232-
INT16 *cache = &ImagingPaletteCache(palette, r, g, b);
400+
INT16 *cache = palette_cache(palette, r, g, b);
233401
if (cache[0] == 0x100) {
234-
ImagingPaletteCacheUpdate(palette, r, g, b);
402+
palette_cache_update(palette, r, g, b);
235403
}
236404
palette_index = cache[0];
237405
}
@@ -303,7 +471,7 @@ topalette(
303471
struct ExactColorHashInstance exact_color_hash;
304472
prepare_exact_color_hash(palette, &exact_color_hash);
305473

306-
if (ImagingPaletteCachePrepare(palette) < 0) {
474+
if (palette_cache_prepare(palette) < 0) {
307475
// Failed allocation for cache
308476
ImagingDelete(imOut);
309477
imOut = NULL;

src/libImaging/Imaging.h

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -240,16 +240,6 @@ ImagingPaletteDuplicate(ImagingPalette palette);
240240
extern void
241241
ImagingPaletteDelete(ImagingPalette palette);
242242

243-
extern int
244-
ImagingPaletteCachePrepare(ImagingPalette palette);
245-
extern void
246-
ImagingPaletteCacheUpdate(ImagingPalette palette, int r, int g, int b);
247-
extern void
248-
ImagingPaletteCacheDelete(ImagingPalette palette);
249-
250-
#define ImagingPaletteCache(p, r, g, b) \
251-
p->cache[(r >> 2) + (g >> 2) * 64 + (b >> 2) * 64 * 64]
252-
253243
extern Imaging
254244
ImagingQuantize(Imaging im, int colours, int mode, int kmeans);
255245

0 commit comments

Comments
 (0)