1 #include "palette_creater.h"
2 
3 #ifdef _DEBUG
4 #define new DEBUG_NEW
5 #else
6 #define STUDIOX_EVAL_BUILD
7 #endif
8 
palette_creater()9 palette_creater::palette_creater()
10 {
11     histogram = NULL;
12     boxlist = NULL;
13 }
14 
15 
~palette_creater()16 palette_creater::~palette_creater()
17 {
18 }
19 
20 ///////////////////////////////////////////////////////////////////////////////
21 // Note:
22 // The color quantization code is implemented based on Heckbert's Median Cut
23 // Algorithm that described in the following paper:
24 //
25 // Heckbert, Paul.  "Color Image Quantization for Frame Buffer Display",
26 // *Proc.SIGGRAPH '82, Computer Graphics v.16 #3 (July 1982), pp 297-304.
27 //
28 // The concept behind the median cut algorithm is to partition the color space
29 // into a three-dimensional rectangle box. Next separated the selected box into
30 // two halves recursively untill we get a desired number of boxes.
31 ///////////////////////////////////////////////////////////////////////////////
CreatePaletteForOnePixelmap(res_info * resource,palette_info * info,int requested_size)32 void palette_creater::CreatePaletteForOnePixelmap(res_info *resource, palette_info *info, int requested_size)
33 {
34     CreatePaletteForPixelmaps(resource, info, FALSE, requested_size);
35 }
36 
37 
CreatePaletteForPixelmaps(res_info * resource,palette_info * info,BOOL recursive,int requested_size)38 void palette_creater::CreatePaletteForPixelmaps(res_info *resource, palette_info *info, BOOL recursive, int requested_size)
39 {
40     int boxes_num;
41 
42     ULONG hist_size = (1 << (HIST_RED_BITS + HIST_GREEN_BITS + HIST_BLUE_BITS));
43 
44     histogram = new ULONG[hist_size];
45     memset(histogram, 0, hist_size * sizeof(UINT));
46 
47     if (requested_size == -1)
48     {
49         requested_size = 255;
50     }
51 
52     boxlist = new BOX[requested_size];
53     memset(boxlist, 0, requested_size * sizeof(BOX));
54 
55     // Accumulate the color histogram of all images under
56     // the specified pixelmap folder, which showing the
57     // usage count of each possible color
58     if (!recursive)
59     {
60         AccumulateOneMapHistogram(resource);
61     }
62     else
63     {
64         AccumulateSharedHistogram(resource);
65     }
66 
67     // Initiate one box that containing whole space.
68     // Here we properly decrease the precision of the input color by
69     // Remaining 5 bits for red, 6 bits for green and 5 bits for blue.
70     // By doing this, we get a smaller histogram size that contains
71     // 65536 colors.
72     boxlist[0].rmin = 0;
73     boxlist[0].rmax = (1 << HIST_RED_BITS) - 1;
74     boxlist[0].gmin = 0;
75     boxlist[0].gmax = (1 << HIST_GREEN_BITS) - 1;
76     boxlist[0].bmin = 0;
77     boxlist[0].bmax = (1 << HIST_BLUE_BITS) - 1;
78 
79     // Update some parametersi of the first box that containing whole space
80     UpdateBox(&boxlist[0]);
81 
82     // Process median cut algorithm to produce the desired number of boxes
83     boxes_num = median_cut(requested_size);
84 
85     info->palette = new GX_COLOR[requested_size];
86     memset(info->palette, 0, requested_size * sizeof(GX_COLOR));
87     info->total_size = requested_size;
88     info->used_size = boxes_num;
89 
90     // Calculate the average color of each box which comes to one entry of
91     // the optimized palette
92     for (int index = 0; index < boxes_num; index++)
93     {
94         ComputeColor(info, &boxlist[index], index);
95     }
96 
97     if (histogram)
98     {
99         delete histogram;
100         histogram = NULL;
101     }
102 
103     if (boxlist)
104     {
105         delete boxlist;
106         boxlist = NULL;
107     }
108 }
109 
110 // Find the splittable BOX with the largest color population.
FindLargestColorPop(int numboxes)111 BOX *palette_creater::FindLargestColorPop(int numboxes)
112 {
113     BOX *which = NULL;
114     long maxc = 0;
115     BOX *boxp = boxlist;
116 
117     for (int i = 0; i < numboxes; i++)
118     {
119         if ((boxp->color_count > maxc) && (boxp->volume > 0))
120         {
121             which = boxp;
122             maxc = boxp->color_count;
123         }
124 
125         boxp++;
126     }
127 
128     return which;
129 }
130 
131 // Find the splittable BOX with the largest color volume.
FindLargestColorVolume(int numboxes)132 BOX *palette_creater::FindLargestColorVolume(int numboxes)
133 {
134     BOX *which = NULL;
135     long maxc = 0;
136     BOX *boxp = boxlist;
137 
138     for (int i = 0; i < numboxes; i++)
139     {
140         if ((boxp->volume > maxc) && (boxp->color_count > 0))
141         {
142             which = boxp;
143             maxc = boxp->volume;
144         }
145 
146         boxp++;
147     }
148 
149     return which;
150 }
151 
UpdateBox(BOX * boxp)152 void palette_creater::UpdateBox(BOX *boxp)
153 {
154     // Shrink the min and max bounds of a BOX to include only nonzero pixels
155     int rmin, rmax;
156     int gmin, gmax;
157     int bmin, bmax;
158 
159     rmin = (1 << HIST_RED_BITS) - 1;
160     rmax = 0;
161     gmin = (1 << HIST_GREEN_BITS) - 1;
162     gmax = 0;
163     bmin = (1 << HIST_BLUE_BITS) - 1;
164     bmax = 0;
165 
166     int r, g, b;
167     int color_count = 0;
168     ULONG *histp;
169     int pos;
170 
171     for (r = boxp->rmin; r <= boxp->rmax; r++)
172     {
173         for (g = boxp->gmin; g <= boxp->gmax; g++)
174         {
175             for (b = boxp->bmin; b <= boxp->bmax; b++)
176             {
177                 pos = ASSEMBLECOLOR_16BPP(r, g, b);
178                 histp = &histogram[pos];
179 
180                 if (*histp != 0)
181                 {
182                     color_count++;
183 
184                     if (r < rmin)
185                     {
186                         rmin = r;
187                     }
188 
189                     if (r > rmax)
190                     {
191                         rmax = r;
192                     }
193 
194                     if (g < gmin)
195                     {
196                         gmin = g;
197                     }
198 
199                     if (g > gmax)
200                     {
201                         gmax = g;
202                     }
203 
204                     if (b < bmin)
205                     {
206                         bmin = b;
207                     }
208 
209                     if (b > bmax)
210                     {
211                         bmax = b;
212                     }
213                 }
214             }
215         }
216     }
217 
218     boxp->color_count = color_count;
219     boxp->rmin = rmin;
220     boxp->rmax = rmax;
221     boxp->gmin = gmin;
222     boxp->gmax = gmax;
223     boxp->bmin = bmin;
224     boxp->bmax = bmax;
225 
226     int rdist = ((rmax - rmin) << HIST_RED_SHIFT) * RED_SCALE;
227     int gdist = ((gmax - gmin) << HIST_GREEN_SHIFT) * GREEN_SCALE;
228     int bdist = ((bmax - bmin) << HIST_BLUE_SHIFT) * BLUE_SCALE;
229 
230     boxp->volume = rdist * rdist + gdist * gdist + bdist * bdist;
231 }
232 
233 // Accumulate color histogram of all pixelmaps under info that are
234 // using shared palette
AccumulateSharedHistogram(res_info * info)235 void palette_creater::AccumulateSharedHistogram(res_info *info)
236 {
237     while (info)
238     {
239         if (info->type == RES_TYPE_PIXELMAP)
240         {
241             if (info->palette_type == PALETTE_TYPE_SHARED)
242             {
243                 AccumulateOneMapHistogram(info);
244             }
245         }
246         if (info->child)
247         {
248             AccumulateSharedHistogram(info->child);
249         }
250         info = info->next;
251     }
252 }
253 
254 // Accumulate one map color histogram
AccumulateOneMapHistogram(res_info * info)255 void palette_creater::AccumulateOneMapHistogram(res_info *info)
256 {
257     if (info->type != RES_TYPE_PIXELMAP)
258     {
259         return;
260     }
261 
262     GX_COLOR *getdata;
263     int width;
264     int height;
265     int red, green, blue;
266     ULONG *histp;
267     USHORT pos;
268     res_info *pixinfo;
269 
270     pixinfo = new res_info(NULL, *info, FALSE);
271     pixinfo->output_color_format = GX_COLOR_FORMAT_24XRGB;
272     pixinfo->keep_alpha = FALSE;
273     pixinfo->compress = FALSE;
274     GetOpenProject()->InitializeOnePixelmap(pixinfo, NULL, GX_COLOR_FORMAT_24XRGB);
275 
276     GX_PIXELMAP *map;
277 
278     for (int index = 0; index < pixinfo->GetPixelmapFrameCount(); index++)
279     {
280         map = pixinfo->GetPixelmap(index);
281 
282         if (map && map->gx_pixelmap_data)
283         {
284             getdata = (GX_COLOR*)map->gx_pixelmap_data;
285             width = map->gx_pixelmap_width;
286             height = map->gx_pixelmap_height;
287 
288             for (int index = 0; index < width * height; index++)
289             {
290                 red = (*getdata) >> 16;
291                 green = ((*getdata) & 0xff00) >> 8;
292                 blue = ((*getdata) & 0xff);
293 
294                 red >>= HIST_RED_SHIFT;
295                 green >>= HIST_GREEN_SHIFT;
296                 blue >>= HIST_BLUE_SHIFT;
297 
298                 pos = ASSEMBLECOLOR_16BPP(red, green, blue);
299                 histp = &histogram[pos];
300 
301                 if (++(*histp) <= 0)
302                 {
303                     (*histp)--;
304                 }
305 
306                 getdata++;
307             }
308         }
309     }
310 
311     delete pixinfo;
312 }
313 
314 // Median cut color quantization algorithm that split one box that containing
315 // the whole color space into desired number of boxes.
median_cut(int desired_colors_num)316 int palette_creater::median_cut(int desired_colors_num)
317 {
318     int boxes_num = 1;
319     BOX *b1, *b2;
320 
321     while (boxes_num < desired_colors_num)
322     {
323         if (boxes_num * 2 <= desired_colors_num)
324         {
325             // select the box with the largest color population
326             b1 = FindLargestColorPop(boxes_num);
327         }
328         else
329         {
330             // select the box with the largets color volumn
331             b1 = FindLargestColorVolume(boxes_num);
332         }
333 
334         if (b1 == NULL)
335         {
336             break;
337         }
338 
339         b2 = &boxlist[boxes_num];
340 
341         /* initiate new BOX. */
342         (*b2) = (*b1);
343 
344         int rdist = ((b1->rmax - b1->rmin) << HIST_RED_SHIFT) * RED_SCALE;
345         int gdist = ((b1->gmax - b1->gmin) << HIST_GREEN_SHIFT) * GREEN_SCALE;
346         int bdist = ((b1->bmax - b1->bmin) << HIST_BLUE_SHIFT) * BLUE_SCALE;
347 
348         // split the selected box along the longges axis
349         if ((rdist >= gdist) && (rdist >= bdist))
350         {
351             //split BOX along red axis
352             b1->rmax = (b1->rmax + b1->rmin) / 2;
353             b2->rmin = (b1->rmax + 1);
354         }
355         else if ((gdist >= rdist) && (gdist >= bdist))
356         {
357             //split BOX along green axis
358             b1->gmax = (b1->gmax + b1->gmin) / 2;
359             b2->gmin = (b1->gmax + 1);
360         }
361         else
362         {
363             //split BOX along blue axis
364             b1->bmax = (b1->bmax + b1->bmin) / 2;
365             b2->bmin = (b1->bmax + 1);
366         }
367 
368         UpdateBox(b1);
369         UpdateBox(b2);
370 
371         boxes_num++;
372     }
373 
374     return boxes_num;
375 }
376 
377 // Calcualte the average color value of one box
ComputeColor(palette_info * control,BOX * boxp,int index)378 void palette_creater::ComputeColor(palette_info *control, BOX *boxp, int index)
379 {
380     int r, g, b;
381     int count;
382     ULONG *histp;
383     ULONG total = 0;
384     ULONG totalred = 0;
385     ULONG totalgreen = 0;
386     ULONG totalblue = 0;
387 
388     for (r = boxp->rmin; r <= boxp->rmax; r++)
389     {
390         for (g = boxp->gmin; g <= boxp->gmax; g++)
391         {
392             for (b = boxp->bmin; b <= boxp->bmax; b++)
393             {
394                 histp = &histogram[ASSEMBLECOLOR_16BPP(r, g, b)];
395                 count = *histp;
396 
397                 if (count)
398                 {
399                     total += count;
400 
401                     totalred += (r << HIST_RED_SHIFT) * count;
402                     totalgreen += (g << HIST_GREEN_SHIFT) * count;
403                     totalblue += (b << HIST_BLUE_SHIFT) * count;
404                 }
405             }
406         }
407     }
408 
409     if (total)
410     {
411         r = totalred / total;
412         g = totalgreen / total;
413         b = totalblue / total;
414 
415         control->palette[index] = 0xff000000 | ASSEMBLECOLOR_24BPP(r, g, b);
416     }
417 }