+static char hasAlpha(unsigned char*_image, int size)
+{
+ COL*image = (COL*)_image;
+ int t;
+ for(t=0;t<size;t++) {
+ if(image[t].a!=255)
+ return 1;
+ }
+ return 0;
+}
+
+typedef struct {
+ u32 num;
+ u32 color;
+} colornum_t;
+
+int compare_colors(const void*_c1, const void*_c2) {
+ colornum_t*c1 = (colornum_t*)_c1;
+ colornum_t*c2 = (colornum_t*)_c2;
+ return c1->num - c2->num;
+}
+
+static colornum_t* getColors(COL*image, int size, int*num)
+{
+ unsigned char*colexists = malloc((256*256*256)/8);
+ memset(colexists, 0, (256*256*256)/8);
+ int t;
+ int count=0;
+
+ /* find all different colors in the image */
+ for(t=0;t<size;t++) {
+ int index = (image[t].r)|(image[t].g)<<8|(image[t].b)<<16;
+ if(!(colexists[index/8]&(1<<(index&7)))) {
+ count++;
+ colexists[index/8]|=(1<<(index&7));
+ }
+ }
+
+ /* now store them in an array */
+ colornum_t*colors=(colornum_t*)malloc(sizeof(colornum_t)*count);
+ int pos=0;
+ for(t=0;t<256*256*256;t++) {
+ if(colexists[t/8]&(1<<(t&7))) {
+ colors[pos].color = t;
+ colors[pos].num = 0;
+ pos++;
+ }
+ }
+
+ /* next, count how often each color occurs */
+ for(t=0;t<size;t++) {
+ int col = (image[t].r)|(image[t].g)<<8|(image[t].b)<<16;
+ int min,max,i,l;
+ for(min=0, max=count, i=count/2, l=count; i != l; l=i,i=(min+max)/2) {
+ if(colors[i].color >= col) max=i;
+ else min=i+1;
+ }
+ assert(colors[i].color==col);
+ colors[i].num++;
+ }
+ free(colexists);
+ *num = count;
+ return colors;
+}
+
+static COL* getOptimalPalette(COL*image, int size, int palettesize)
+{
+ int num;
+ COL* ret = malloc(sizeof(COL)*palettesize);
+ memset(ret, 0, sizeof(COL)*palettesize);
+ colornum_t*colors = getColors(image, size, &num);
+
+ assert(palettesize<=256);
+
+ qsort(colors, num, sizeof(colornum_t), compare_colors);
+
+ if(num<=palettesize) {
+ /* if there are not more than palettesize different colors in
+ the image anyway, we are done */
+ int t;
+ for(t=0;t<num;t++) {
+ ret[t].r = colors[t].color;
+ ret[t].g = colors[t].color>>8;
+ ret[t].b = colors[t].color>>16;
+ ret[t].a = 255;
+ }
+ return ret;
+ }
+
+ colornum_t*centers = malloc(sizeof(colornum_t)*palettesize);
+ int t;
+ for(t=0;t<palettesize;t++) {
+ centers[t].color = colors[t].color;
+ }
+ unsigned char*belongsto = (unsigned char*)malloc(num);
+ memset(belongsto, 0, num);
+ /* do a k-means clustering on the colors */
+ char change = 1;
+ int tries = 0;
+ while(change) {
+ if(tries++ >= (palettesize+num)*2) {
+ fprintf(stderr, "Warning: didn't find optimal palette\n");
+ break;
+ }
+ change = 0;
+ int s,t;
+ for(s=0;s<palettesize;s++) {
+ centers[s].num = 0;
+ }
+ for(t=0;t<num;t++) {
+ int best=0x7fffffff;
+ int bestpos=0;
+ for(s=0;s<palettesize;s++) {
+ int distance = 0;
+ distance += abs((centers[s].color>>0&0xff) - (colors[t].color>>0&0xff));
+ distance += abs((centers[s].color>>8&0xff) - (colors[t].color>>8&0xff));
+ distance += abs((centers[s].color>>16&0xff) - (colors[t].color>>16&0xff));
+ distance *= colors[t].num;
+ if(distance<best) {
+ best = distance;
+ bestpos = s;
+ }
+ }
+ if(bestpos!=belongsto[t])
+ change = 1;
+ belongsto[t] = bestpos;
+ }
+ for(s=0;s<palettesize;s++) {
+ int r=0;
+ int g=0;
+ int b=0;
+ int count=0;
+ for(t=0;t<num;t++) {
+ if(belongsto[t]==s) {
+ r += ((colors[t].color>>0)&0xff)*colors[t].num;
+ g += ((colors[t].color>>8)&0xff)*colors[t].num;
+ b += ((colors[t].color>>16)&0xff)*colors[t].num;
+ count+=colors[t].num;
+ }
+ }
+ if(!count) {
+ int random = lrand48()%num;
+ centers[s].color = colors[random].color;
+ centers[s].num = 0;
+ change = 1;
+ } else {
+ r /= count;
+ g /= count;
+ b /= count;
+ centers[s].color = r|g<<8|b<<16;
+ centers[s].num = count;
+ }
+ }
+ }
+ free(belongsto);
+ free(colors);
+ for(t=0;t<palettesize;t++) {
+ ret[t].r = centers[t].color;
+ ret[t].g = centers[t].color>>8;
+ ret[t].b = centers[t].color>>16;
+ ret[t].a = 255;
+ }
+ free(centers);
+ return ret;
+}
+
+static int sqr(const int x) {return x*x;}
+
+static void quantizeImage(unsigned char*_image, int size, int numcolors, unsigned char**newimage, COL**palette)
+{
+ COL*image = (COL*)_image;
+ COL*pal= getOptimalPalette(image, size, numcolors);
+ *palette = pal;
+ *newimage = (unsigned char*)malloc(size);
+ int t;
+ for(t=0;t<size;t++) {
+ int best=0x7fffffff;
+ int bestcol = 0;
+ int s;
+ for(s=0;s<numcolors;s++) {
+ int distance = 0;
+ distance += sqr((pal[s].r) - (image[t].r))*5;
+ distance += sqr((pal[s].g) - (image[t].g))*6;
+ distance += sqr((pal[s].b) - (image[t].b))*4;
+ if(distance<best) {
+ best = distance;
+ bestcol = s;
+ }
+ }
+ //image[t] = pal[bestcol];
+ (*newimage)[t] = bestcol;
+ }
+}
+