Coverage Report

Created: 2025-06-22 06:41

/src/freeimage-svn/FreeImage/trunk/Source/FreeImage/WuQuantizer.cpp
Line
Count
Source (jump to first uncovered line)
1
///////////////////////////////////////////////////////////////////////
2
//      C Implementation of Wu's Color Quantizer (v. 2)
3
//      (see Graphics Gems vol. II, pp. 126-133)
4
//
5
// Author:  Xiaolin Wu
6
// Dept. of Computer Science
7
// Univ. of Western Ontario
8
// London, Ontario N6A 5B7
9
// wu@csd.uwo.ca
10
// 
11
// Algorithm: Greedy orthogonal bipartition of RGB space for variance
12
//     minimization aided by inclusion-exclusion tricks.
13
//     For speed no nearest neighbor search is done. Slightly
14
//     better performance can be expected by more sophisticated
15
//     but more expensive versions.
16
// 
17
// The author thanks Tom Lane at Tom_Lane@G.GP.CS.CMU.EDU for much of
18
// additional documentation and a cure to a previous bug.
19
// 
20
// Free to distribute, comments and suggestions are appreciated.
21
///////////////////////////////////////////////////////////////////////
22
23
///////////////////////////////////////////////////////////////////////
24
// History
25
// -------
26
// July 2000:  C++ Implementation of Wu's Color Quantizer
27
//             and adaptation for the FreeImage 2 Library
28
//             Author: Hervé Drolon (drolon@infonie.fr)
29
// March 2004: Adaptation for the FreeImage 3 library (port to big endian processors)
30
//             Author: Hervé Drolon (drolon@infonie.fr)
31
///////////////////////////////////////////////////////////////////////
32
33
#include "Quantizers.h"
34
#include "FreeImage.h"
35
#include "Utilities.h"
36
37
///////////////////////////////////////////////////////////////////////
38
39
// Size of a 3D array : 33 x 33 x 33
40
0
#define SIZE_3D 35937
41
42
// 3D array indexation
43
0
#define INDEX(r, g, b)  ((r << 10) + (r << 6) + r + (g << 5) + g + b)
44
45
#define MAXCOLOR  256
46
47
// Constructor / Destructor
48
49
0
WuQuantizer::WuQuantizer(FIBITMAP *dib) {
50
0
  width = FreeImage_GetWidth(dib);
51
0
  height = FreeImage_GetHeight(dib);
52
0
  pitch = FreeImage_GetPitch(dib);
53
0
  m_dib = dib;
54
55
0
  gm2 = NULL;
56
0
  wt = mr = mg = mb = NULL;
57
0
  Qadd = NULL;
58
59
  // Allocate 3D arrays
60
0
  gm2 = (float*)malloc(SIZE_3D * sizeof(float));
61
0
  wt = (LONG*)malloc(SIZE_3D * sizeof(LONG));
62
0
  mr = (LONG*)malloc(SIZE_3D * sizeof(LONG));
63
0
  mg = (LONG*)malloc(SIZE_3D * sizeof(LONG));
64
0
  mb = (LONG*)malloc(SIZE_3D * sizeof(LONG));
65
66
  // Allocate Qadd
67
0
  Qadd = (WORD *)malloc(sizeof(WORD) * width * height);
68
69
0
  if(!gm2 || !wt || !mr || !mg || !mb || !Qadd) {
70
0
    if(gm2) free(gm2);
71
0
    if(wt) free(wt);
72
0
    if(mr) free(mr);
73
0
    if(mg) free(mg);
74
0
    if(mb) free(mb);
75
0
    if(Qadd)  free(Qadd);
76
0
    throw FI_MSG_ERROR_MEMORY;
77
0
  }
78
0
  memset(gm2, 0, SIZE_3D * sizeof(float));
79
0
  memset(wt, 0, SIZE_3D * sizeof(LONG));
80
0
  memset(mr, 0, SIZE_3D * sizeof(LONG));
81
0
  memset(mg, 0, SIZE_3D * sizeof(LONG));
82
0
  memset(mb, 0, SIZE_3D * sizeof(LONG));
83
0
  memset(Qadd, 0, sizeof(WORD) * width * height);
84
0
}
85
86
0
WuQuantizer::~WuQuantizer() {
87
0
  if(gm2) free(gm2);
88
0
  if(wt) free(wt);
89
0
  if(mr) free(mr);
90
0
  if(mg) free(mg);
91
0
  if(mb) free(mb);
92
0
  if(Qadd)  free(Qadd);
93
0
}
94
95
96
// Histogram is in elements 1..HISTSIZE along each axis,
97
// element 0 is for base or marginal value
98
// NB: these must start out 0!
99
100
// Build 3-D color histogram of counts, r/g/b, c^2
101
void 
102
0
WuQuantizer::Hist3D(LONG *vwt, LONG *vmr, LONG *vmg, LONG *vmb, float *m2, int ReserveSize, RGBQUAD *ReservePalette) {
103
0
  int ind = 0;
104
0
  int inr, ing, inb, table[256];
105
0
  int i;
106
0
  unsigned y, x;
107
108
0
  for(i = 0; i < 256; i++)
109
0
    table[i] = i * i;
110
111
0
  if (FreeImage_GetBPP(m_dib) == 24) {
112
0
    for(y = 0; y < height; y++) {
113
0
      BYTE *bits = FreeImage_GetScanLine(m_dib, y);
114
115
0
      for(x = 0; x < width; x++) {
116
0
        inr = (bits[FI_RGBA_RED] >> 3) + 1;
117
0
        ing = (bits[FI_RGBA_GREEN] >> 3) + 1;
118
0
        inb = (bits[FI_RGBA_BLUE] >> 3) + 1;
119
0
        ind = INDEX(inr, ing, inb);
120
0
        Qadd[y*width + x] = (WORD)ind;
121
        // [inr][ing][inb]
122
0
        vwt[ind]++;
123
0
        vmr[ind] += bits[FI_RGBA_RED];
124
0
        vmg[ind] += bits[FI_RGBA_GREEN];
125
0
        vmb[ind] += bits[FI_RGBA_BLUE];
126
0
        m2[ind] += (float)(table[bits[FI_RGBA_RED]] + table[bits[FI_RGBA_GREEN]] + table[bits[FI_RGBA_BLUE]]);
127
0
        bits += 3;
128
0
      }
129
0
    }
130
0
  } else {
131
0
    for(y = 0; y < height; y++) {
132
0
      BYTE *bits = FreeImage_GetScanLine(m_dib, y);
133
134
0
      for(x = 0; x < width; x++) {
135
0
        inr = (bits[FI_RGBA_RED] >> 3) + 1;
136
0
        ing = (bits[FI_RGBA_GREEN] >> 3) + 1;
137
0
        inb = (bits[FI_RGBA_BLUE] >> 3) + 1;
138
0
        ind = INDEX(inr, ing, inb);
139
0
        Qadd[y*width + x] = (WORD)ind;
140
        // [inr][ing][inb]
141
0
        vwt[ind]++;
142
0
        vmr[ind] += bits[FI_RGBA_RED];
143
0
        vmg[ind] += bits[FI_RGBA_GREEN];
144
0
        vmb[ind] += bits[FI_RGBA_BLUE];
145
0
        m2[ind] += (float)(table[bits[FI_RGBA_RED]] + table[bits[FI_RGBA_GREEN]] + table[bits[FI_RGBA_BLUE]]);
146
0
        bits += 4;
147
0
      }
148
0
    }
149
0
  }
150
151
0
  if( ReserveSize > 0 ) {
152
0
    int max = 0;
153
0
    for(i = 0; i < SIZE_3D; i++) {
154
0
      if( vwt[i] > max ) max = vwt[i];
155
0
    }
156
0
    max++;
157
0
    for(i = 0; i < ReserveSize; i++) {
158
0
      inr = (ReservePalette[i].rgbRed >> 3) + 1;
159
0
      ing = (ReservePalette[i].rgbGreen >> 3) + 1;
160
0
      inb = (ReservePalette[i].rgbBlue >> 3) + 1;
161
0
      ind = INDEX(inr, ing, inb);
162
0
      wt[ind] = max;
163
0
      mr[ind] = max * ReservePalette[i].rgbRed;
164
0
      mg[ind] = max * ReservePalette[i].rgbGreen;
165
0
      mb[ind] = max * ReservePalette[i].rgbBlue;
166
0
      gm2[ind] = (float)max * (float)(table[ReservePalette[i].rgbRed] + table[ReservePalette[i].rgbGreen] + table[ReservePalette[i].rgbBlue]);
167
0
    }
168
0
  }
169
0
}
170
171
172
// At conclusion of the histogram step, we can interpret
173
// wt[r][g][b] = sum over voxel of P(c)
174
// mr[r][g][b] = sum over voxel of r*P(c)  ,  similarly for mg, mb
175
// m2[r][g][b] = sum over voxel of c^2*P(c)
176
// Actually each of these should be divided by 'ImageSize' to give the usual
177
// interpretation of P() as ranging from 0 to 1, but we needn't do that here.
178
179
180
// We now convert histogram into moments so that we can rapidly calculate
181
// the sums of the above quantities over any desired box.
182
183
// Compute cumulative moments
184
void 
185
0
WuQuantizer::M3D(LONG *vwt, LONG *vmr, LONG *vmg, LONG *vmb, float *m2) {
186
0
  unsigned ind1, ind2;
187
0
  BYTE i, r, g, b;
188
0
  LONG line, line_r, line_g, line_b;
189
0
  LONG area[33], area_r[33], area_g[33], area_b[33];
190
0
  float line2, area2[33];
191
192
0
    for(r = 1; r <= 32; r++) {
193
0
    for(i = 0; i <= 32; i++) {
194
0
      area2[i] = 0;
195
0
      area[i] = area_r[i] = area_g[i] = area_b[i] = 0;
196
0
    }
197
0
    for(g = 1; g <= 32; g++) {
198
0
      line2 = 0;
199
0
      line = line_r = line_g = line_b = 0;
200
0
      for(b = 1; b <= 32; b++) {      
201
0
        ind1 = INDEX(r, g, b); // [r][g][b]
202
0
        line += vwt[ind1];
203
0
        line_r += vmr[ind1]; 
204
0
        line_g += vmg[ind1]; 
205
0
        line_b += vmb[ind1];
206
0
        line2 += m2[ind1];
207
0
        area[b] += line;
208
0
        area_r[b] += line_r;
209
0
        area_g[b] += line_g;
210
0
        area_b[b] += line_b;
211
0
        area2[b] += line2;
212
0
        ind2 = ind1 - 1089; // [r-1][g][b]
213
0
        vwt[ind1] = vwt[ind2] + area[b];
214
0
        vmr[ind1] = vmr[ind2] + area_r[b];
215
0
        vmg[ind1] = vmg[ind2] + area_g[b];
216
0
        vmb[ind1] = vmb[ind2] + area_b[b];
217
0
        m2[ind1] = m2[ind2] + area2[b];
218
0
      }
219
0
    }
220
0
  }
221
0
}
222
223
// Compute sum over a box of any given statistic
224
LONG 
225
0
WuQuantizer::Vol( Box *cube, LONG *mmt ) {
226
0
    return( mmt[INDEX(cube->r1, cube->g1, cube->b1)] 
227
0
      - mmt[INDEX(cube->r1, cube->g1, cube->b0)]
228
0
      - mmt[INDEX(cube->r1, cube->g0, cube->b1)]
229
0
      + mmt[INDEX(cube->r1, cube->g0, cube->b0)]
230
0
      - mmt[INDEX(cube->r0, cube->g1, cube->b1)]
231
0
      + mmt[INDEX(cube->r0, cube->g1, cube->b0)]
232
0
      + mmt[INDEX(cube->r0, cube->g0, cube->b1)]
233
0
      - mmt[INDEX(cube->r0, cube->g0, cube->b0)] );
234
0
}
235
236
// The next two routines allow a slightly more efficient calculation
237
// of Vol() for a proposed subbox of a given box.  The sum of Top()
238
// and Bottom() is the Vol() of a subbox split in the given direction
239
// and with the specified new upper bound.
240
241
242
// Compute part of Vol(cube, mmt) that doesn't depend on r1, g1, or b1
243
// (depending on dir)
244
245
LONG 
246
0
WuQuantizer::Bottom(Box *cube, BYTE dir, LONG *mmt) {
247
0
    switch(dir)
248
0
  {
249
0
    case FI_RGBA_RED:
250
0
      return( - mmt[INDEX(cube->r0, cube->g1, cube->b1)]
251
0
            + mmt[INDEX(cube->r0, cube->g1, cube->b0)]
252
0
          + mmt[INDEX(cube->r0, cube->g0, cube->b1)]
253
0
          - mmt[INDEX(cube->r0, cube->g0, cube->b0)] );
254
0
      break;
255
0
    case FI_RGBA_GREEN:
256
0
      return( - mmt[INDEX(cube->r1, cube->g0, cube->b1)]
257
0
            + mmt[INDEX(cube->r1, cube->g0, cube->b0)]
258
0
          + mmt[INDEX(cube->r0, cube->g0, cube->b1)]
259
0
          - mmt[INDEX(cube->r0, cube->g0, cube->b0)] );
260
0
      break;
261
0
    case FI_RGBA_BLUE:
262
0
      return( - mmt[INDEX(cube->r1, cube->g1, cube->b0)]
263
0
            + mmt[INDEX(cube->r1, cube->g0, cube->b0)]
264
0
          + mmt[INDEX(cube->r0, cube->g1, cube->b0)]
265
0
          - mmt[INDEX(cube->r0, cube->g0, cube->b0)] );
266
0
      break;
267
0
  }
268
269
0
  return 0;
270
0
}
271
272
273
// Compute remainder of Vol(cube, mmt), substituting pos for
274
// r1, g1, or b1 (depending on dir)
275
276
LONG 
277
0
WuQuantizer::Top(Box *cube, BYTE dir, int pos, LONG *mmt) {
278
0
    switch(dir)
279
0
  {
280
0
    case FI_RGBA_RED:
281
0
      return( mmt[INDEX(pos, cube->g1, cube->b1)] 
282
0
           -mmt[INDEX(pos, cube->g1, cube->b0)]
283
0
           -mmt[INDEX(pos, cube->g0, cube->b1)]
284
0
           +mmt[INDEX(pos, cube->g0, cube->b0)] );
285
0
      break;
286
0
    case FI_RGBA_GREEN:
287
0
      return( mmt[INDEX(cube->r1, pos, cube->b1)] 
288
0
           -mmt[INDEX(cube->r1, pos, cube->b0)]
289
0
           -mmt[INDEX(cube->r0, pos, cube->b1)]
290
0
           +mmt[INDEX(cube->r0, pos, cube->b0)] );
291
0
      break;
292
0
    case FI_RGBA_BLUE:
293
0
      return( mmt[INDEX(cube->r1, cube->g1, pos)]
294
0
           -mmt[INDEX(cube->r1, cube->g0, pos)]
295
0
           -mmt[INDEX(cube->r0, cube->g1, pos)]
296
0
           +mmt[INDEX(cube->r0, cube->g0, pos)] );
297
0
      break;
298
0
  }
299
300
0
  return 0;
301
0
}
302
303
// Compute the weighted variance of a box 
304
// NB: as with the raw statistics, this is really the variance * ImageSize 
305
306
float
307
0
WuQuantizer::Var(Box *cube) {
308
0
    float dr = (float) Vol(cube, mr); 
309
0
    float dg = (float) Vol(cube, mg); 
310
0
    float db = (float) Vol(cube, mb);
311
0
    float xx =  gm2[INDEX(cube->r1, cube->g1, cube->b1)] 
312
0
      -gm2[INDEX(cube->r1, cube->g1, cube->b0)]
313
0
       -gm2[INDEX(cube->r1, cube->g0, cube->b1)]
314
0
       +gm2[INDEX(cube->r1, cube->g0, cube->b0)]
315
0
       -gm2[INDEX(cube->r0, cube->g1, cube->b1)]
316
0
       +gm2[INDEX(cube->r0, cube->g1, cube->b0)]
317
0
       +gm2[INDEX(cube->r0, cube->g0, cube->b1)]
318
0
       -gm2[INDEX(cube->r0, cube->g0, cube->b0)];
319
320
0
    return (xx - (dr*dr+dg*dg+db*db)/(float)Vol(cube,wt));    
321
0
}
322
323
// We want to minimize the sum of the variances of two subboxes.
324
// The sum(c^2) terms can be ignored since their sum over both subboxes
325
// is the same (the sum for the whole box) no matter where we split.
326
// The remaining terms have a minus sign in the variance formula,
327
// so we drop the minus sign and MAXIMIZE the sum of the two terms.
328
329
float
330
0
WuQuantizer::Maximize(Box *cube, BYTE dir, int first, int last , int *cut, LONG whole_r, LONG whole_g, LONG whole_b, LONG whole_w) {
331
0
  LONG half_r, half_g, half_b, half_w;
332
0
  int i;
333
0
  float temp;
334
335
0
    LONG base_r = Bottom(cube, dir, mr);
336
0
    LONG base_g = Bottom(cube, dir, mg);
337
0
    LONG base_b = Bottom(cube, dir, mb);
338
0
    LONG base_w = Bottom(cube, dir, wt);
339
340
0
    float max = 0.0;
341
342
0
    *cut = -1;
343
344
0
    for (i = first; i < last; i++) {
345
0
    half_r = base_r + Top(cube, dir, i, mr);
346
0
    half_g = base_g + Top(cube, dir, i, mg);
347
0
    half_b = base_b + Top(cube, dir, i, mb);
348
0
    half_w = base_w + Top(cube, dir, i, wt);
349
350
        // now half_x is sum over lower half of box, if split at i
351
352
0
    if (half_w == 0) {   // subbox could be empty of pixels!
353
0
      continue;     // never split into an empty box
354
0
    } else {
355
0
      temp = ((float)half_r*half_r + (float)half_g*half_g + (float)half_b*half_b)/half_w;
356
0
    }
357
358
0
    half_r = whole_r - half_r;
359
0
    half_g = whole_g - half_g;
360
0
    half_b = whole_b - half_b;
361
0
    half_w = whole_w - half_w;
362
363
0
        if (half_w == 0) {   // subbox could be empty of pixels!
364
0
      continue;     // never split into an empty box
365
0
    } else {
366
0
      temp += ((float)half_r*half_r + (float)half_g*half_g + (float)half_b*half_b)/half_w;
367
0
    }
368
369
0
      if (temp > max) {
370
0
      max=temp;
371
0
      *cut=i;
372
0
    }
373
0
    }
374
375
0
    return max;
376
0
}
377
378
bool
379
0
WuQuantizer::Cut(Box *set1, Box *set2) {
380
0
  BYTE dir;
381
0
  int cutr, cutg, cutb;
382
383
0
    LONG whole_r = Vol(set1, mr);
384
0
    LONG whole_g = Vol(set1, mg);
385
0
    LONG whole_b = Vol(set1, mb);
386
0
    LONG whole_w = Vol(set1, wt);
387
388
0
    float maxr = Maximize(set1, FI_RGBA_RED, set1->r0+1, set1->r1, &cutr, whole_r, whole_g, whole_b, whole_w);    
389
0
  float maxg = Maximize(set1, FI_RGBA_GREEN, set1->g0+1, set1->g1, &cutg, whole_r, whole_g, whole_b, whole_w);    
390
0
  float maxb = Maximize(set1, FI_RGBA_BLUE, set1->b0+1, set1->b1, &cutb, whole_r, whole_g, whole_b, whole_w);
391
392
0
    if ((maxr >= maxg) && (maxr >= maxb)) {
393
0
    dir = FI_RGBA_RED;
394
395
0
    if (cutr < 0) {
396
0
      return false; // can't split the box
397
0
    }
398
0
    } else if ((maxg >= maxr) && (maxg>=maxb)) {
399
0
    dir = FI_RGBA_GREEN;
400
0
  } else {
401
0
    dir = FI_RGBA_BLUE;
402
0
  }
403
404
0
  set2->r1 = set1->r1;
405
0
    set2->g1 = set1->g1;
406
0
    set2->b1 = set1->b1;
407
408
0
    switch (dir) {
409
0
    case FI_RGBA_RED:
410
0
      set2->r0 = set1->r1 = cutr;
411
0
      set2->g0 = set1->g0;
412
0
      set2->b0 = set1->b0;
413
0
      break;
414
415
0
    case FI_RGBA_GREEN:
416
0
      set2->g0 = set1->g1 = cutg;
417
0
      set2->r0 = set1->r0;
418
0
      set2->b0 = set1->b0;
419
0
      break;
420
421
0
    case FI_RGBA_BLUE:
422
0
      set2->b0 = set1->b1 = cutb;
423
0
      set2->r0 = set1->r0;
424
0
      set2->g0 = set1->g0;
425
0
      break;
426
0
    }
427
428
0
    set1->vol = (set1->r1-set1->r0)*(set1->g1-set1->g0)*(set1->b1-set1->b0);
429
0
    set2->vol = (set2->r1-set2->r0)*(set2->g1-set2->g0)*(set2->b1-set2->b0);
430
431
0
    return true;
432
0
}
433
434
435
void
436
0
WuQuantizer::Mark(Box *cube, int label, BYTE *tag) {
437
0
    for (int r = cube->r0 + 1; r <= cube->r1; r++) {
438
0
    for (int g = cube->g0 + 1; g <= cube->g1; g++) {
439
0
      for (int b = cube->b0 + 1; b <= cube->b1; b++) {
440
0
        tag[INDEX(r, g, b)] = (BYTE)label;
441
0
      }
442
0
    }
443
0
  }
444
0
}
445
446
// Wu Quantization algorithm
447
FIBITMAP *
448
0
WuQuantizer::Quantize(int PaletteSize, int ReserveSize, RGBQUAD *ReservePalette) {
449
0
  BYTE *tag = NULL;
450
451
0
  try {
452
0
    Box cube[MAXCOLOR];
453
0
    int next;
454
0
    LONG i, weight;
455
0
    int k;
456
0
    float vv[MAXCOLOR], temp;
457
    
458
    // Compute 3D histogram
459
460
0
    Hist3D(wt, mr, mg, mb, gm2, ReserveSize, ReservePalette);
461
462
    // Compute moments
463
464
0
    M3D(wt, mr, mg, mb, gm2);
465
466
0
    cube[0].r0 = cube[0].g0 = cube[0].b0 = 0;
467
0
    cube[0].r1 = cube[0].g1 = cube[0].b1 = 32;
468
0
    next = 0;
469
470
0
    for (i = 1; i < PaletteSize; i++) {
471
0
      if(Cut(&cube[next], &cube[i])) {
472
        // volume test ensures we won't try to cut one-cell box
473
0
        vv[next] = (cube[next].vol > 1) ? Var(&cube[next]) : 0;
474
0
        vv[i] = (cube[i].vol > 1) ? Var(&cube[i]) : 0;
475
0
      } else {
476
0
          vv[next] = 0.0;   // don't try to split this box again
477
0
          i--;              // didn't create box i
478
0
      }
479
480
0
      next = 0; temp = vv[0];
481
482
0
      for (k = 1; k <= i; k++) {
483
0
        if (vv[k] > temp) {
484
0
          temp = vv[k]; next = k;
485
0
        }
486
0
      }
487
488
0
      if (temp <= 0.0) {
489
0
          PaletteSize = i + 1;
490
491
          // Error: "Only got 'PaletteSize' boxes"
492
493
0
          break;
494
0
      }
495
0
    }
496
497
    // Partition done
498
499
    // the space for array gm2 can be freed now
500
501
0
    free(gm2);
502
503
0
    gm2 = NULL;
504
505
    // Allocate a new dib
506
507
0
    FIBITMAP *new_dib = FreeImage_Allocate(width, height, 8);
508
509
0
    if (new_dib == NULL) {
510
0
      throw FI_MSG_ERROR_MEMORY;
511
0
    }
512
513
    // create an optimized palette
514
515
0
    RGBQUAD *new_pal = FreeImage_GetPalette(new_dib);
516
517
0
    tag = (BYTE*) malloc(SIZE_3D * sizeof(BYTE));
518
0
    if (tag == NULL) {
519
0
      throw FI_MSG_ERROR_MEMORY;
520
0
    }
521
0
    memset(tag, 0, SIZE_3D * sizeof(BYTE));
522
523
0
    for (k = 0; k < PaletteSize ; k++) {
524
0
      Mark(&cube[k], k, tag);
525
0
      weight = Vol(&cube[k], wt);
526
527
0
      if (weight) {
528
0
        new_pal[k].rgbRed = (BYTE)(((float)Vol(&cube[k], mr) / (float)weight) + 0.5f);
529
0
        new_pal[k].rgbGreen = (BYTE)(((float)Vol(&cube[k], mg) / (float)weight) + 0.5f);
530
0
        new_pal[k].rgbBlue  = (BYTE)(((float)Vol(&cube[k], mb) / (float)weight) + 0.5f);
531
0
      } else {
532
        // Error: bogus box 'k'
533
534
0
        new_pal[k].rgbRed = new_pal[k].rgbGreen = new_pal[k].rgbBlue = 0;   
535
0
      }
536
0
    }
537
538
0
    int npitch = FreeImage_GetPitch(new_dib);
539
540
0
    for (unsigned y = 0; y < height; y++) {
541
0
      BYTE *new_bits = FreeImage_GetBits(new_dib) + (y * npitch);
542
543
0
      for (unsigned x = 0; x < width; x++) {
544
0
        new_bits[x] = tag[Qadd[y*width + x]];
545
0
      }
546
0
    }
547
548
    // output 'new_pal' as color look-up table contents,
549
    // 'new_bits' as the quantized image (array of table addresses).
550
551
0
    free(tag);
552
553
0
    return (FIBITMAP*) new_dib;
554
0
  } catch(...) {
555
0
    free(tag);
556
0
  }
557
558
0
  return NULL;
559
0
}