gbdk-2020 | GameBoy Development Kit |
| download: https://git.y1.nz/archives/gbdk.tar.gz | |
| README | Files | Log | Refs | LICENSE |
gbdk-support/png2hicolorgb/src/hicolor/Wu.c
1 /*
2 C Implementation of Wu's Color Quantizer (v2.0)
3 -- Xiaolin Wu.
4 From the Graphics Gems.
5
6 Memory hungry.
7
8 Some fixes and additional code by Benny.
9 */
10 /**********************************************************************
11 C Implementation of Wu's Color Quantizer (v. 2)
12 (see Graphics Gems vol. II, pp. 126-133)
13
14 Author: Xiaolin Wu
15 Dept. of Computer Science
16 Univ. of Western Ontario
17 London, Ontario N6A 5B7
18 wu@csd.uwo.ca
19
20 Algorithm: Greedy orthogonal bipartition of RGB space for variance
21 minimization aided by inclusion-exclusion tricks.
22 For speed no nearest neighbor search is done. Slightly
23 better performance can be expected by more sophisticated
24 but more expensive versions.
25
26 The author thanks Tom Lane at Tom_Lane@G.GP.CS.CMU.EDU for much of
27 additional documentation and a cure to a previous bug.
28
29 Free to distribute, comments and suggestions are appreciated.
30 **********************************************************************/
31
32 #include <stdio.h>
33 #include <string.h>
34 #include <stdbool.h>
35 #include <stdint.h>
36
37 #include "defines.h"
38 #include "hicolour.h"
39 #include "median.h"
40 #include "wu.h"
41
42
43 #define NOINVERT // RGB or BGR
44
45 //void error(char *s);
46
47
48 #define MAXCOLOR 256
49 #define RED 2
50 #define GREEN 1
51 #define BLUE 0
52
53
54 /* Histogram is in elements 1..HISTSIZE along each axis,
55 element 0 is for base or marginal value.
56 NB: these must start out 0! */
57 s32 wt[BOX][BOX][BOX],mr[BOX][BOX][BOX],mg[BOX][BOX][BOX],mb[BOX][BOX][BOX];
58 float m2[BOX][BOX][BOX];
59
60 s32 ImageSize; /* image size */
61 s32 PalSize; /* color look-up table size */
62
63 u16 *Qadd; // *must* be unsigned?
64 u8 *TrueColorPic;
65
66
67 // s16 AQadd[7*8*3*2]; // Signed in original code, type clash with QAdd and bit shifting
68 u16 AQadd[7*8*3*2];
69 u8 Atag[BOX * BOX * BOX];
70
71
72 /* build 3-D color histogram of counts, r/g/b, c^2 */
73 void Hist3d(s32 *vwt,s32 *vmr,s32 *vmg,s32 *vmb, float *m_2)
74 {
75 s32 ind,r,g,b;
76 s32 inr,ing,inb,table[256];
77 s32 i;
78
79 for (i = 0; i < 256; ++i)
80 table[i] = i * i;
81
82 for (i = 0; i < ImageSize; ++i)
83 {
84 r = TrueColorPic[i*3 ];
85 g = TrueColorPic[i*3+1];
86 b = TrueColorPic[i*3+2];
87
88 inr = (r >> 3) + 1;
89 ing = (g >> 3) + 1;
90 inb = (b >> 3) + 1;
91 Qadd[i] = ind = (inr << 10) + (inr << 6) + inr + (ing << 5) + ing + inb;
92 ++vwt[ind];
93 vmr[ind] += r;
94 vmg[ind] += g;
95 vmb[ind] += b;
96 m_2[ind] += (float) (table[r] + table[g] + table[b]);
97 }
98 }
99
100 /* At conclusion of the histogram step, we can interpret
101 * wt[r][g][b] = sum over voxel of P(c)
102 * mr[r][g][b] = sum over voxel of r*P(c) , similarly for mg, mb
103 * m2[r][g][b] = sum over voxel of c^2*P(c)
104 * Actually each of these should be divided by 'size' to give the usual
105 * interpretation of P() as ranging from 0 to 1, but we needn't do that here.
106 */
107
108 /* We now convert histogram into moments so that we can rapidly calculate
109 * the sums of the above quantities over any desired box.
110 */
111
112 void Momt3d(s32 *vwt, s32 *vmr, s32 *vmg, s32 *vmb, float *m_2)
113 {
114 u16 ind1,ind2;
115 u8 i,r,g,b;
116 s32 line,line_r,line_g,line_b,area[BOX],area_r[BOX],area_g[BOX],area_b[BOX];
117 float line2,area2[BOX];
118
119 for (r = 1; r <= 32; ++r)
120 {
121 for (i = 0; i <= 32; ++i)
122 {
123 area[i] = area_r[i] = area_g[i] = area_b[i] = 0;
124 area2[i] = 0.0;
125 }
126
127 for (g = 1; g <= 32; ++g)
128 {
129 line2 = 0.0;
130 line = line_r = line_g = line_b = 0;
131
132 for (b = 1; b <= 32; ++b)
133 {
134 ind1 = (r << 10) + (r << 6) + r + (g << 5) + g + b;
135 line += vwt[ind1];
136 line_r += vmr[ind1];
137 line_g += vmg[ind1];
138 line_b += vmb[ind1];
139 line2 += m_2[ind1];
140 area[b] += line;
141 area_r[b] += line_r;
142 area_g[b] += line_g;
143 area_b[b] += line_b;
144 area2[b] += line2;
145 ind2 = ind1 - 1089;
146 vwt[ind1] = vwt[ind2] + area[b];
147 vmr[ind1] = vmr[ind2] + area_r[b];
148 vmg[ind1] = vmg[ind2] + area_g[b];
149 vmb[ind1] = vmb[ind2] + area_b[b];
150 m_2[ind1] = m_2[ind2] + area2[b];
151 }
152 }
153 }
154 }
155
156 s32 Vol(struct box * cube, s32 mmt[BOX][BOX][BOX])
157 {
158 return (mmt[cube->r1][cube->g1][cube->b1] - mmt[cube->r1][cube->g1][cube->b0] - mmt[cube->r1][cube->g0][cube->b1]
159 + mmt[cube->r1][cube->g0][cube->b0] - mmt[cube->r0][cube->g1][cube->b1] + mmt[cube->r0][cube->g1][cube->b0]
160 + mmt[cube->r0][cube->g0][cube->b1] - mmt[cube->r0][cube->g0][cube->b0]);
161 }
162
163 /* The next two routines allow a slightly more efficient calculation
164 * of Vol() for a proposed subbox of a given box. The sum of Top()
165 * and Bottom() is the Vol() of a subbox split in the given direction
166 * and with the specified new upper bound.
167 */
168
169 /* Compute part of Vol(cube, mmt) that doesn't depend on r1, g1, or b1 */
170 /* (depending on dir) */
171 s32 Bottom(struct box * cube, u8 dir, s32 mmt[BOX][BOX][BOX])
172 {
173 switch (dir)
174 {
175
176 case RED:
177
178 return (-mmt[cube->r0][cube->g1][cube->b1] + mmt[cube->r0][cube->g1][cube->b0]
179 + mmt[cube->r0][cube->g0][cube->b1] - mmt[cube->r0][cube->g0][cube->b0]);
180 case GREEN:
181
182 return (-mmt[cube->r1][cube->g0][cube->b1] + mmt[cube->r1][cube->g0][cube->b0]
183 + mmt[cube->r0][cube->g0][cube->b1] - mmt[cube->r0][cube->g0][cube->b0]);
184
185 case BLUE:
186
187 return (-mmt[cube->r1][cube->g1][cube->b0] + mmt[cube->r1][cube->g0][cube->b0]
188 + mmt[cube->r0][cube->g1][cube->b0] - mmt[cube->r0][cube->g0][cube->b0]);
189 }
190
191 return 0;
192 }
193
194
195 s32 Top(struct box * cube, u8 dir, s32 pos, s32 mmt[BOX][BOX][BOX])
196 {
197 switch (dir)
198 {
199
200 case RED:
201
202 return (mmt[pos][cube->g1][cube->b1] - mmt[pos][cube->g1][cube->b0]
203 - mmt[pos][cube->g0][cube->b1] + mmt[pos][cube->g0][cube->b0]);
204
205 case GREEN:
206
207 return (mmt[cube->r1][pos][cube->b1] - mmt[cube->r1][pos][cube->b0]
208 - mmt[cube->r0][pos][cube->b1] + mmt[cube->r0][pos][cube->b0]);
209
210 case BLUE:
211
212 return (mmt[cube->r1][cube->g1][pos] - mmt[cube->r1][cube->g0][pos]
213 - mmt[cube->r0][cube->g1][pos] + mmt[cube->r0][cube->g0][pos]);
214 }
215
216 return 0;
217 }
218
219 /* Compute the weighted variance of a box */
220 /* NB: as with the raw statistics, this is really the variance * size */
221 float Var(struct box * cube)
222 {
223 float dr,dg,db,xx;
224
225 dr = (float)Vol(cube, mr);
226 dg = (float)Vol(cube, mg);
227 db = (float)Vol(cube, mb);
228 xx = m2[cube->r1][cube->g1][cube->b1] - m2[cube->r1][cube->g1][cube->b0] - m2[cube->r1][cube->g0][cube->b1]
229 + m2[cube->r1][cube->g0][cube->b0] - m2[cube->r0][cube->g1][cube->b1] + m2[cube->r0][cube->g1][cube->b0]
230 + m2[cube->r0][cube->g0][cube->b1] - m2[cube->r0][cube->g0][cube->b0];
231
232 return (xx - (dr * dr + dg * dg + db * db) / (float) Vol(cube, wt));
233 }
234
235 /* We want to minimize the sum of the variances of two subboxes.
236 * The sum(c^2) terms can be ignored since their sum over both subboxes
237 * is the same (the sum for the whole box) no matter where we split.
238 * The remaining terms have a minus sign in the variance formula,
239 * so we drop the minus sign and MAXIMIZE the sum of the two terms.
240 */
241
242 float Maximize(struct box *cube, u8 dir, s32 first, s32 last, s32 *cut, s32 whole_r, s32 whole_g, s32 whole_b, s32 whole_w)
243 {
244 s32 half_r,half_g,half_b,half_w;
245 s32 base_r,base_g,base_b,base_w;
246 s32 i;
247 float temp,max;
248
249 base_r = Bottom(cube, dir, mr);
250 base_g = Bottom(cube, dir, mg);
251 base_b = Bottom(cube, dir, mb);
252 base_w = Bottom(cube, dir, wt);
253
254 max = 0.0;
255 *cut = -1;
256
257 for (i = first; i < last; ++i)
258 {
259 half_r = base_r + Top(cube, dir, i, mr);
260 half_g = base_g + Top(cube, dir, i, mg);
261 half_b = base_b + Top(cube, dir, i, mb);
262 half_w = base_w + Top(cube, dir, i, wt);
263 // now half_x is sum over lower half of box, if split at i
264 if (half_w == 0)
265 {
266 // subbox could be empty of pixels!
267 // never split into an empty box
268 continue;
269 }
270 else
271 {
272 temp = ((float) half_r * half_r + (float) half_g * half_g + (float) half_b * half_b) / half_w;
273 }
274
275 half_r = whole_r - half_r;
276 half_g = whole_g - half_g;
277 half_b = whole_b - half_b;
278 half_w = whole_w - half_w;
279
280 if (half_w == 0)
281 {
282 // subbox could be empty of pixels!
283 // never split into an empty box
284 continue;
285 }
286 else
287 {
288 temp += ((float) half_r * half_r + (float) half_g * half_g + (float) half_b * half_b) / half_w;
289 }
290
291 if (temp > max)
292 {
293 max = temp;
294 *cut = i;
295 }
296 }
297
298 return (max);
299 }
300
301 s32 Cut(struct box * set1, struct box * set2)
302 {
303 u8 dir;
304 s32 cutr,cutg,cutb;
305 float maxr,maxg,maxb;
306 s32 whole_r,whole_g,whole_b,whole_w;
307
308 whole_r = Vol(set1, mr);
309 whole_g = Vol(set1, mg);
310 whole_b = Vol(set1, mb);
311 whole_w = Vol(set1, wt);
312
313 maxr = Maximize(set1, RED, set1->r0 + 1, set1->r1, &cutr, whole_r, whole_g, whole_b, whole_w);
314 maxg = Maximize(set1, GREEN, set1->g0 + 1, set1->g1, &cutg, whole_r, whole_g, whole_b, whole_w);
315 maxb = Maximize(set1, BLUE, set1->b0 + 1, set1->b1, &cutb, whole_r, whole_g, whole_b, whole_w);
316
317 if ((maxr >= maxg) && (maxr >= maxb))
318 {
319 dir = RED;
320
321 if (cutr < 0)
322 return 0; /* can't split the box */
323 }
324 else if ((maxg >= maxr) && (maxg >= maxb))
325 {
326 dir = GREEN;
327 }
328 else
329 {
330 dir = BLUE;
331 }
332
333 set2->r1 = set1->r1;
334 set2->g1 = set1->g1;
335 set2->b1 = set1->b1;
336
337 switch (dir)
338 {
339
340 case RED:
341
342 set2->r0 = set1->r1 = cutr;
343 set2->g0 = set1->g0;
344 set2->b0 = set1->b0;
345 break;
346
347 case GREEN:
348
349 set2->g0 = set1->g1 = cutg;
350 set2->r0 = set1->r0;
351 set2->b0 = set1->b0;
352 break;
353
354 case BLUE:
355
356 set2->b0 = set1->b1 = cutb;
357 set2->r0 = set1->r0;
358 set2->g0 = set1->g0;
359 break;
360 }
361
362 set1->vol = (set1->r1 - set1->r0) * (set1->g1 - set1->g0) * (set1->b1 - set1->b0);
363 set2->vol = (set2->r1 - set2->r0) * (set2->g1 - set2->g0) * (set2->b1 - set2->b0);
364
365 return 1;
366 }
367
368
369 void Mark(struct box *cube, s32 label, u8 *tag)
370 {
371 s32 r,g,b;
372
373 for (r = cube->r0 + 1; r <= cube->r1; ++r)
374 for (g = cube->g0 + 1; g <= cube->g1; ++g)
375 for (b = cube->b0 + 1; b <= cube->b1; ++b)
376 tag[(r << 10) + (r << 6) + r + (g << 5) + g + b] = label;
377 }
378
379
380
381 s32 wuReduce(u8 *RGBpic, s32 numcolors, s32 picsize)
382 {
383 struct box cube[MAXCOLOR];
384 u8 *tag = 0;
385 float vv[MAXCOLOR],temp = 0.;
386 s32 i = 0,weight = 0;
387 s32 next = 0;
388 s32 j = 0,k = 0,l = 0;
389
390 TrueColorPic = RGBpic;
391 ImageSize = picsize;
392 PalSize = numcolors;
393
394 for (j=0;j<BOX;j++)
395 for (k=0;k<BOX;k++)
396 for (l=0;l<BOX;l++)
397 {
398 wt[j][k][l] = 0;
399 mr[j][k][l] = 0;
400 mg[j][k][l] = 0;
401 mb[j][k][l] = 0;
402 m2[j][k][l] = 0.;
403 }
404
405 Qadd = AQadd;
406
407 // Hist3d((long *)&wt, (long *)&mr, (long *)&mg, (long *)&mb, (float *)&m2);
408 // Momt3d((long *)&wt, (long *)&mr, (long *)&mg, (long *)&mb, (float *)&m2);
409 Hist3d((s32 *)&wt, (s32 *)&mr, (s32 *)&mg, (s32 *)&mb, (float *)&m2);
410 // Histogram done
411 Momt3d((s32 *)&wt, (s32 *)&mr, (s32 *)&mg, (s32 *)&mb, (float *)&m2);
412 // Moments done
413
414 cube[0].r0 = cube[0].g0 = cube[0].b0 = 0;
415 cube[0].r1 = cube[0].g1 = cube[0].b1 = 32;
416 next = 0;
417
418 for (i = 1; i < PalSize; ++i)
419 {
420 if (Cut(&cube[next], &cube[i]))
421 {
422 // volume test ensures we won't try to cut one-cell box
423 vv[next] = (float)((cube[next].vol > 1) ? Var(&cube[next]) : 0.0);
424 vv[i] = (float)((cube[i].vol > 1) ? Var(&cube[i]) : 0.0);
425 }
426 else
427 {
428 vv[next] = 0.0;
429 i--;
430 }
431
432 next = 0;
433 temp = vv[0];
434
435 for (k = 1; k <= i; ++k)
436 {
437 if (vv[k] > temp)
438 {
439 temp = vv[k];
440 next = k;
441 }
442 }
443
444 if (temp <= 0.0)
445 {
446 PalSize = i + 1;
447 break;
448 }
449 }
450
451 tag = Atag;
452
453 for (k = 0; k < PalSize; ++k)
454 {
455 Mark(&cube[k], k, tag);
456 weight = Vol(&cube[k], wt);
457
458 if (weight)
459 {
460
461 QuantizedPalette[k][2] = (unsigned char)(Vol(&cube[k], mr) / weight);
462 QuantizedPalette[k][0] = (unsigned char)(Vol(&cube[k], mb) / weight);
463 QuantizedPalette[k][1] = (unsigned char)(Vol(&cube[k], mg) / weight);
464 }
465 else
466 {
467 QuantizedPalette[k][0] = QuantizedPalette[k][1] = QuantizedPalette[k][2] = 0;
468 }
469 }
470
471 for (i = 0; i < ImageSize; i++)
472 Picture256[i] = tag[Qadd[i]];
473
474 return 0;
475 }
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.