git.y1.nz

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.