mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-10-31 16:07:30 +03:00 
			
		
		
		
	find * -type f "-(" -name "*.bdf" -o -name "*.c" -o -name "*.h" -o -name "*.py" -o -name "*.rst" -o -name "*.txt" "-)" -exec sed -e "s/[[:space:]]*$//" -i {} \;
		
	
			
		
			
				
	
	
		
			1627 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1627 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * The Python Imaging Library
 | |
|  * $Id$
 | |
|  *
 | |
|  * image quantizer
 | |
|  *
 | |
|  * history:
 | |
|  * 1998-09-10 tjs  Contributed
 | |
|  * 1998-12-29 fl   Added to PIL 1.0b1
 | |
|  * 2004-02-21 fl   Fixed bogus free() on quantization error
 | |
|  * 2005-02-07 fl   Limit number of colors to 256
 | |
|  *
 | |
|  * Written by Toby J Sargeant <tjs@longford.cs.monash.edu.au>.
 | |
|  *
 | |
|  * Copyright (c) 1998 by Toby J Sargeant
 | |
|  * Copyright (c) 1998-2004 by Secret Labs AB.  All rights reserved.
 | |
|  *
 | |
|  * See the README file for information on usage and redistribution.
 | |
|  */
 | |
| 
 | |
| #include "Imaging.h"
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <memory.h>
 | |
| #include <time.h>
 | |
| 
 | |
| #include "QuantTypes.h"
 | |
| #include "QuantOctree.h"
 | |
| #include "QuantHash.h"
 | |
| #include "QuantHeap.h"
 | |
| 
 | |
| #define NO_OUTPUT
 | |
| 
 | |
| typedef struct {
 | |
|     uint32_t scale;
 | |
| } PixelHashData;
 | |
| 
 | |
| typedef struct _PixelList {
 | |
|     struct _PixelList *next[3],*prev[3];
 | |
|     Pixel p;
 | |
|     unsigned int flag:1;
 | |
|     int count;
 | |
| } PixelList;
 | |
| 
 | |
| typedef struct _BoxNode {
 | |
|     struct _BoxNode *l,*r;
 | |
|     PixelList *head[3],*tail[3];
 | |
|     int axis;
 | |
|     int volume;
 | |
|     uint32_t pixelCount;
 | |
| } BoxNode;
 | |
| 
 | |
| #define _SQR(x) ((x)*(x))
 | |
| #define _DISTSQR(p1,p2) \
 | |
|     _SQR((int)((p1)->c.r)-(int)((p2)->c.r))+ \
 | |
|     _SQR((int)((p1)->c.g)-(int)((p2)->c.g))+ \
 | |
|     _SQR((int)((p1)->c.b)-(int)((p2)->c.b))
 | |
| 
 | |
| #define MAX_HASH_ENTRIES 65536
 | |
| 
 | |
| #define PIXEL_HASH(r,g,b) \
 | |
|     (((unsigned int)(r)    )*463 ^ \
 | |
|      ((unsigned int)(g)<< 8)*10069 ^ \
 | |
|      ((unsigned int)(b)<<16)*64997)
 | |
| 
 | |
| #define PIXEL_UNSCALE(p,q,s) \
 | |
|     ((q)->c.r=(p)->c.r<<(s)), \
 | |
|     ((q)->c.g=(p)->c.g<<(s)), \
 | |
|     ((q)->c.b=(p)->c.b<<(s))
 | |
| 
 | |
| #define PIXEL_SCALE(p,q,s)\
 | |
|     ((q)->c.r=(p)->c.r>>(s)),              \
 | |
|     ((q)->c.g=(p)->c.g>>(s)),              \
 | |
|     ((q)->c.b=(p)->c.b>>(s))
 | |
| 
 | |
| static uint32_t
 | |
| unshifted_pixel_hash(const HashTable *h, const Pixel pixel)
 | |
| {
 | |
|    return PIXEL_HASH(pixel.c.r, pixel.c.g, pixel.c.b);
 | |
| }
 | |
| 
 | |
| static int
 | |
| unshifted_pixel_cmp(const HashTable *h, const Pixel pixel1, const Pixel pixel2)
 | |
| {
 | |
|     if (pixel1.c.r==pixel2.c.r) {
 | |
|         if (pixel1.c.g==pixel2.c.g) {
 | |
|             if (pixel1.c.b==pixel2.c.b) {
 | |
|                 return 0;
 | |
|             } else {
 | |
|                 return (int)(pixel1.c.b)-(int)(pixel2.c.b);
 | |
|             }
 | |
|         } else {
 | |
|             return (int)(pixel1.c.g)-(int)(pixel2.c.g);
 | |
|         }
 | |
|     } else {
 | |
|         return (int)(pixel1.c.r)-(int)(pixel2.c.r);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static uint32_t
 | |
| pixel_hash(const HashTable *h,const Pixel pixel)
 | |
| {
 | |
|     PixelHashData *d=(PixelHashData *)hashtable_get_user_data(h);
 | |
|     return PIXEL_HASH(pixel.c.r>>d->scale, pixel.c.g>>d->scale, pixel.c.b>>d->scale);
 | |
| }
 | |
| 
 | |
| static int
 | |
| pixel_cmp(const HashTable *h,const Pixel pixel1, const Pixel pixel2)
 | |
| {
 | |
|     PixelHashData *d=(PixelHashData *)hashtable_get_user_data(h);
 | |
|     uint32_t A,B;
 | |
|     A=PIXEL_HASH(pixel1.c.r>>d->scale, pixel1.c.g>>d->scale, pixel1.c.b>>d->scale);
 | |
|     B=PIXEL_HASH(pixel2.c.r>>d->scale, pixel2.c.g>>d->scale, pixel2.c.b>>d->scale);
 | |
|     return (A==B)?0:((A<B)?-1:1);
 | |
| }
 | |
| 
 | |
| static void
 | |
| exists_count_func(const HashTable *h, const Pixel key, uint32_t *val)
 | |
| {
 | |
|     *val+=1;
 | |
| }
 | |
| 
 | |
| static void
 | |
| new_count_func(const HashTable *h, const Pixel key, uint32_t *val)
 | |
| {
 | |
|     *val=1;
 | |
| }
 | |
| 
 | |
| static void
 | |
| rehash_collide(const HashTable *h,
 | |
|                Pixel *keyp,
 | |
|                uint32_t *valp,
 | |
|                Pixel newkey,
 | |
|                uint32_t newval)
 | |
| {
 | |
|     *valp += newval;
 | |
| }
 | |
| 
 | |
| /* %% */
 | |
| 
 | |
| static HashTable *
 | |
| create_pixel_hash(Pixel *pixelData,uint32_t nPixels)
 | |
| {
 | |
|    PixelHashData *d;
 | |
|    HashTable *hash;
 | |
|    uint32_t i;
 | |
| #ifndef NO_OUTPUT
 | |
|    uint32_t timer,timer2,timer3;
 | |
| #endif
 | |
| 
 | |
|    d=malloc(sizeof(PixelHashData));
 | |
|    if (!d) return NULL;
 | |
|    hash=hashtable_new(pixel_hash,pixel_cmp);
 | |
|    hashtable_set_user_data(hash,d);
 | |
|    d->scale=0;
 | |
| #ifndef NO_OUTPUT
 | |
|    timer=timer3=clock();
 | |
| #endif
 | |
|    for (i=0;i<nPixels;i++) {
 | |
|       if (!hashtable_insert_or_update_computed(hash,
 | |
|                                               pixelData[i],
 | |
|                                               new_count_func,
 | |
|                                               exists_count_func)) {;
 | |
|       }
 | |
|       while (hashtable_get_count(hash)>MAX_HASH_ENTRIES) {
 | |
|          d->scale++;
 | |
| #ifndef NO_OUTPUT
 | |
|          printf ("rehashing - new scale: %d\n",(int)d->scale);
 | |
|          timer2=clock();
 | |
| #endif
 | |
|          hashtable_rehash_compute(hash,rehash_collide);
 | |
| #ifndef NO_OUTPUT
 | |
|          timer2=clock()-timer2;
 | |
|          printf ("rehash took %f sec\n",timer2/(double)CLOCKS_PER_SEC);
 | |
|          timer+=timer2;
 | |
| #endif
 | |
|       }
 | |
|    }
 | |
| #ifndef NO_OUTPUT
 | |
|    printf ("inserts took %f sec\n",(clock()-timer)/(double)CLOCKS_PER_SEC);
 | |
| #endif
 | |
| #ifndef NO_OUTPUT
 | |
|    printf ("total        %f sec\n",(clock()-timer3)/(double)CLOCKS_PER_SEC);
 | |
| #endif
 | |
|    return hash;
 | |
| }
 | |
| 
 | |
| static void
 | |
| destroy_pixel_hash(HashTable *hash)
 | |
| {
 | |
|    PixelHashData *d=(PixelHashData *)hashtable_get_user_data(hash);
 | |
|    if (d) free(d);
 | |
|    hashtable_free(hash);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* 1. hash quantized pixels.                                                  */
 | |
| /* 2. create R,G,B lists of sorted quantized pixels.                          */
 | |
| /* 3. median cut.                                                             */
 | |
| /* 4. build hash table from median cut boxes.                                 */
 | |
| /* 5. for each pixel, compute entry in hash table, and hence median cut box.  */
 | |
| /* 6. compute median cut box pixel averages.                                  */
 | |
| /* 7. map each pixel to nearest average.                                      */
 | |
| 
 | |
| static int
 | |
| compute_box_volume(BoxNode *b)
 | |
| {
 | |
|    unsigned char rl,rh,gl,gh,bl,bh;
 | |
|    if (b->volume>=0) return b->volume;
 | |
|    if (!b->head[0]) {
 | |
|       b->volume=0;
 | |
|    } else {
 | |
|       rh=b->head[0]->p.c.r;
 | |
|       rl=b->tail[0]->p.c.r;
 | |
|       gh=b->head[1]->p.c.g;
 | |
|       gl=b->tail[1]->p.c.g;
 | |
|       bh=b->head[2]->p.c.b;
 | |
|       bl=b->tail[2]->p.c.b;
 | |
|       b->volume=(rh-rl+1)*(gh-gl+1)*(bh-bl+1);
 | |
|    }
 | |
|    return b->volume;
 | |
| }
 | |
| 
 | |
| static void
 | |
| hash_to_list(const HashTable *h, const Pixel pixel, const uint32_t count, void *u)
 | |
| {
 | |
|    PixelHashData *d=(PixelHashData *)hashtable_get_user_data(h);
 | |
|    PixelList **pl=(PixelList **)u;
 | |
|    PixelList *p;
 | |
|    int i;
 | |
|    Pixel q;
 | |
| 
 | |
|    PIXEL_SCALE(&pixel,&q,d->scale);
 | |
| 
 | |
|    p=malloc(sizeof(PixelList));
 | |
|    if (!p) return;
 | |
| 
 | |
|    p->flag=0;
 | |
|    p->p=q;
 | |
|    p->count=count;
 | |
|    for (i=0;i<3;i++) {
 | |
|       p->next[i]=pl[i];
 | |
|       p->prev[i]=NULL;
 | |
|       if (pl[i]) pl[i]->prev[i]=p;
 | |
|       pl[i]=p;
 | |
|    }
 | |
| }
 | |
| 
 | |
| static PixelList *
 | |
| mergesort_pixels(PixelList *head, int i)
 | |
| {
 | |
|    PixelList *c,*t,*a,*b,*p;
 | |
|    if (!head||!head->next[i]) {
 | |
|       if (head) {
 | |
|          head->next[i]=NULL;
 | |
|          head->prev[i]=NULL;
 | |
|       }
 | |
|       return head;
 | |
|    }
 | |
|    for (c=t=head;c&&t;c=c->next[i],t=(t->next[i])?t->next[i]->next[i]:NULL);
 | |
|    if (c) {
 | |
|       if (c->prev[i]) c->prev[i]->next[i]=NULL;
 | |
|       c->prev[i]=NULL;
 | |
|    }
 | |
|    a=mergesort_pixels(head,i);
 | |
|    b=mergesort_pixels(c,i);
 | |
|    head=NULL;
 | |
|    p=NULL;
 | |
|    while (a&&b) {
 | |
|       if (a->p.a.v[i]>b->p.a.v[i]) {
 | |
|          c=a;
 | |
|          a=a->next[i];
 | |
|       } else {
 | |
|          c=b;
 | |
|          b=b->next[i];
 | |
|       }
 | |
|       c->prev[i]=p;
 | |
|       c->next[i]=NULL;
 | |
|       if (p) p->next[i]=c;
 | |
|       p=c;
 | |
|       if (!head) head=c;
 | |
|    }
 | |
|    if (a) {
 | |
|       c->next[i]=a;
 | |
|       a->prev[i]=c;
 | |
|    } else if (b) {
 | |
|       c->next[i]=b;
 | |
|       b->prev[i]=c;
 | |
|    }
 | |
|    return head;
 | |
| }
 | |
| 
 | |
| #if defined(TEST_MERGESORT) || defined(TEST_SORTED)
 | |
| static int
 | |
| test_sorted(PixelList *pl[3])
 | |
| {
 | |
|    int i,n,l;
 | |
|    PixelList *t;
 | |
| 
 | |
|    for(i=0;i<3;i++) {
 | |
|       n=0;
 | |
|       l=256;
 | |
|       for (t=pl[i];t;t=t->next[i]) {
 | |
|          if (l<t->p.a.v[i]) return 0;
 | |
|          l=t->p.a.v[i];
 | |
|       }
 | |
|    }
 | |
|    return 1;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static int
 | |
| box_heap_cmp(const Heap *h, const void *A, const void *B)
 | |
| {
 | |
|    BoxNode *a=(BoxNode *)A;
 | |
|    BoxNode *b=(BoxNode *)B;
 | |
|    return (int)a->pixelCount-(int)b->pixelCount;
 | |
| }
 | |
| 
 | |
| #define LUMINANCE(p) (77*(p)->c.r+150*(p)->c.g+29*(p)->c.b)
 | |
| 
 | |
| static int
 | |
| splitlists(PixelList *h[3],
 | |
|            PixelList *t[3],
 | |
|            PixelList *nh[2][3],
 | |
|            PixelList *nt[2][3],
 | |
|            uint32_t nCount[2],
 | |
|            int axis,
 | |
|            uint32_t pixelCount)
 | |
| {
 | |
|    uint32_t left;
 | |
| 
 | |
|    PixelList *l,*r,*c,*n;
 | |
|    int i;
 | |
|    int nRight,nLeft;
 | |
|    int splitColourVal;
 | |
| 
 | |
| #ifdef TEST_SPLIT
 | |
|    {
 | |
|       PixelList *_prevTest,*_nextTest;
 | |
|       int _i,_nextCount[3],_prevCount[3];
 | |
|       for (_i=0;_i<3;_i++) {
 | |
|          for (_nextCount[_i]=0,_nextTest=h[_i];_nextTest&&_nextTest->next[_i];_nextTest=_nextTest->next[_i],_nextCount[_i]++);
 | |
|          for (_prevCount[_i]=0,_prevTest=t[_i];_prevTest&&_prevTest->prev[_i];_prevTest=_prevTest->prev[_i],_prevCount[_i]++);
 | |
|          if (_nextTest!=t[_i]) {
 | |
|             printf ("next-list of axis %d does not end at tail\n",_i);
 | |
|             exit(1);
 | |
|          }
 | |
|          if (_prevTest!=h[_i]) {
 | |
|             printf ("prev-list of axis %d does not end at head\n",_i);
 | |
|             exit(1);
 | |
|          }
 | |
|          for (;_nextTest&&_nextTest->prev[_i];_nextTest=_nextTest->prev[_i]);
 | |
|          for (;_prevTest&&_prevTest->next[_i];_prevTest=_prevTest->next[_i]);
 | |
|          if (_nextTest!=h[_i]) {
 | |
|             printf ("next-list of axis %d does not loop back to head\n",_i);
 | |
|             exit(1);
 | |
|          }
 | |
|          if (_prevTest!=t[_i]) {
 | |
|             printf ("prev-list of axis %d does not loop back to tail\n",_i);
 | |
|             exit(1);
 | |
|          }
 | |
|       }
 | |
|       for (_i=1;_i<3;_i++) {
 | |
|          if (_prevCount[_i]!=_prevCount[_i-1] ||
 | |
|              _nextCount[_i]!=_nextCount[_i-1] ||
 | |
|              _prevCount[_i]!=_nextCount[_i]) {
 | |
|             printf ("{%d %d %d} {%d %d %d}\n",
 | |
|                     _prevCount[0],
 | |
|                     _prevCount[1],
 | |
|                     _prevCount[2],
 | |
|                     _nextCount[0],
 | |
|                     _nextCount[1],
 | |
|                     _nextCount[2]);
 | |
|             exit(1);
 | |
|          }
 | |
|       }
 | |
|     }
 | |
| #endif
 | |
|    nCount[0]=nCount[1]=0;
 | |
|    nLeft=nRight=0;
 | |
|    for (left=0,c=h[axis];c;) {
 | |
|       left=left+c->count;
 | |
|       nCount[0]+=c->count;
 | |
|       c->flag=0;
 | |
|       nLeft++;
 | |
|       c=c->next[axis];
 | |
|       if (left*2>pixelCount) {
 | |
|          break;
 | |
|       }
 | |
|    }
 | |
|    if (c) {
 | |
|       splitColourVal=c->prev[axis]->p.a.v[axis];
 | |
|       for (;c;c=c->next[axis]) {
 | |
|          if (splitColourVal!=c->p.a.v[axis]) {
 | |
|             break;
 | |
|          }
 | |
|          c->flag=0;
 | |
|          nLeft++;
 | |
|          nCount[0]+=c->count;
 | |
|       }
 | |
|    }
 | |
|    for (;c;c=c->next[axis]) {
 | |
|       c->flag=1;
 | |
|       nRight++;
 | |
|       nCount[1]+=c->count;
 | |
|    }
 | |
|    if (!nRight) {
 | |
|       for (c=t[axis],splitColourVal=t[axis]->p.a.v[axis];c;c=c->prev[axis]) {
 | |
|          if (splitColourVal!=c->p.a.v[axis]) {
 | |
|             break;
 | |
|          }
 | |
|          c->flag=1;
 | |
|          nRight++;
 | |
|          nLeft--;
 | |
|          nCount[0]-=c->count;
 | |
|          nCount[1]+=c->count;
 | |
|       }
 | |
|    }
 | |
| #ifndef NO_OUTPUT
 | |
|    if (!nLeft) {
 | |
|       for (c=h[axis];c;c=c->next[axis]) {
 | |
|          printf ("[%d %d %d]\n",c->p.c.r,c->p.c.g,c->p.c.b);
 | |
|       }
 | |
|       printf ("warning... trivial split\n");
 | |
|    }
 | |
| #endif
 | |
| 
 | |
|    for (i=0;i<3;i++) {
 | |
|       l=r=NULL;
 | |
|       nh[0][i]=nt[0][i]=NULL;
 | |
|       nh[1][i]=nt[1][i]=NULL;
 | |
|       for (c=h[i];c;c=n) {
 | |
|          n=c->next[i];
 | |
|          if (c->flag) {    /* move pixel to right  list*/
 | |
|             if (r) r->next[i]=c; else nh[1][i]=c;
 | |
|             c->prev[i]=r;
 | |
|             r=c;
 | |
|          } else {          /* move pixel to left list */
 | |
|             if (l) l->next[i]=c; else nh[0][i]=c;
 | |
|             c->prev[i]=l;
 | |
|             l=c;
 | |
|          }
 | |
|       }
 | |
|       if (l) l->next[i]=NULL;
 | |
|       if (r) r->next[i]=NULL;
 | |
|       nt[0][i]=l;
 | |
|       nt[1][i]=r;
 | |
|    }
 | |
|    return 1;
 | |
| }
 | |
| 
 | |
| static int
 | |
| split(BoxNode *node)
 | |
| {
 | |
|    unsigned char rl,rh,gl,gh,bl,bh;
 | |
|    int f[3];
 | |
|    int best,axis;
 | |
|    int i;
 | |
|    PixelList *heads[2][3];
 | |
|    PixelList *tails[2][3];
 | |
|    uint32_t newCounts[2];
 | |
|    BoxNode *left,*right;
 | |
| 
 | |
|    rh=node->head[0]->p.c.r;
 | |
|    rl=node->tail[0]->p.c.r;
 | |
|    gh=node->head[1]->p.c.g;
 | |
|    gl=node->tail[1]->p.c.g;
 | |
|    bh=node->head[2]->p.c.b;
 | |
|    bl=node->tail[2]->p.c.b;
 | |
| #ifdef TEST_SPLIT
 | |
|    printf ("splitting node [%d %d %d] [%d %d %d] ",rl,gl,bl,rh,gh,bh);
 | |
| #endif
 | |
|    f[0]=(rh-rl)*77;
 | |
|    f[1]=(gh-gl)*150;
 | |
|    f[2]=(bh-bl)*29;
 | |
| 
 | |
|    best=f[0];
 | |
|    axis=0;
 | |
|    for (i=1;i<3;i++) {
 | |
|       if (best<f[i]) { best=f[i]; axis=i; }
 | |
|    }
 | |
| #ifdef TEST_SPLIT
 | |
|    printf ("along axis %d\n",axis+1);
 | |
| #endif
 | |
| 
 | |
| #ifdef TEST_SPLIT
 | |
|    {
 | |
|       PixelList *_prevTest,*_nextTest;
 | |
|       int _i,_nextCount[3],_prevCount[3];
 | |
|       for (_i=0;_i<3;_i++) {
 | |
|          if (node->tail[_i]->next[_i]) {
 | |
|             printf ("tail is not tail\n");
 | |
|             printf ("node->tail[%d]->next[%d]=%p\n",_i,_i,node->tail[_i]->next[_i]);
 | |
|          }
 | |
|          if (node->head[_i]->prev[_i]) {
 | |
|             printf ("head is not head\n");
 | |
|             printf ("node->head[%d]->prev[%d]=%p\n",_i,_i,node->head[_i]->prev[_i]);
 | |
|          }
 | |
|       }
 | |
| 
 | |
|       for (_i=0;_i<3;_i++) {
 | |
|          for (_nextCount[_i]=0,_nextTest=node->head[_i];_nextTest&&_nextTest->next[_i];_nextTest=_nextTest->next[_i],_nextCount[_i]++);
 | |
|          for (_prevCount[_i]=0,_prevTest=node->tail[_i];_prevTest&&_prevTest->prev[_i];_prevTest=_prevTest->prev[_i],_prevCount[_i]++);
 | |
|          if (_nextTest!=node->tail[_i]) {
 | |
|             printf ("next-list of axis %d does not end at tail\n",_i);
 | |
|          }
 | |
|          if (_prevTest!=node->head[_i]) {
 | |
|             printf ("prev-list of axis %d does not end at head\n",_i);
 | |
|          }
 | |
|          for (;_nextTest&&_nextTest->prev[_i];_nextTest=_nextTest->prev[_i]);
 | |
|          for (;_prevTest&&_prevTest->next[_i];_prevTest=_prevTest->next[_i]);
 | |
|          if (_nextTest!=node->head[_i]) {
 | |
|             printf ("next-list of axis %d does not loop back to head\n",_i);
 | |
|          }
 | |
|          if (_prevTest!=node->tail[_i]) {
 | |
|             printf ("prev-list of axis %d does not loop back to tail\n",_i);
 | |
|          }
 | |
|       }
 | |
|       for (_i=1;_i<3;_i++) {
 | |
|          if (_prevCount[_i]!=_prevCount[_i-1] ||
 | |
|              _nextCount[_i]!=_nextCount[_i-1] ||
 | |
|              _prevCount[_i]!=_nextCount[_i]) {
 | |
|             printf ("{%d %d %d} {%d %d %d}\n",
 | |
|                     _prevCount[0],
 | |
|                     _prevCount[1],
 | |
|                     _prevCount[2],
 | |
|                     _nextCount[0],
 | |
|                     _nextCount[1],
 | |
|                     _nextCount[2]);
 | |
|          }
 | |
|       }
 | |
|     }
 | |
| #endif
 | |
|    node->axis=axis;
 | |
|    if (!splitlists(node->head,
 | |
|                    node->tail,
 | |
|                    heads,
 | |
|                    tails,
 | |
|                    newCounts,
 | |
|                    axis,
 | |
|                    node->pixelCount)) {
 | |
| #ifndef NO_OUTPUT
 | |
|       printf ("list split failed.\n");
 | |
| #endif
 | |
|       return 0;
 | |
|    }
 | |
| #ifdef TEST_SPLIT
 | |
|    if (!test_sorted(heads[0])) {
 | |
|       printf ("bug in split");
 | |
|       exit(1);
 | |
|    }
 | |
|    if (!test_sorted(heads[1])) {
 | |
|       printf ("bug in split");
 | |
|       exit(1);
 | |
|    }
 | |
| #endif
 | |
|    left=malloc(sizeof(BoxNode));
 | |
|    right=malloc(sizeof(BoxNode));
 | |
|    if (!left||!right) {
 | |
|       return 0;
 | |
|    }
 | |
|    for(i=0;i<3;i++) {
 | |
|       left->head[i]=heads[0][i];
 | |
|       left->tail[i]=tails[0][i];
 | |
|       right->head[i]=heads[1][i];
 | |
|       right->tail[i]=tails[1][i];
 | |
|       node->head[i]=NULL;
 | |
|       node->tail[i]=NULL;
 | |
|    }
 | |
| #ifdef TEST_SPLIT
 | |
|    if (left->head[0]) {
 | |
|       rh=left->head[0]->p.c.r;
 | |
|       rl=left->tail[0]->p.c.r;
 | |
|       gh=left->head[1]->p.c.g;
 | |
|       gl=left->tail[1]->p.c.g;
 | |
|       bh=left->head[2]->p.c.b;
 | |
|       bl=left->tail[2]->p.c.b;
 | |
|       printf ("   left node  [%3d %3d %3d] [%3d %3d %3d]\n",rl,gl,bl,rh,gh,bh);
 | |
|    }
 | |
|    if (right->head[0]) {
 | |
|       rh=right->head[0]->p.c.r;
 | |
|       rl=right->tail[0]->p.c.r;
 | |
|       gh=right->head[1]->p.c.g;
 | |
|       gl=right->tail[1]->p.c.g;
 | |
|       bh=right->head[2]->p.c.b;
 | |
|       bl=right->tail[2]->p.c.b;
 | |
|       printf ("   right node [%3d %3d %3d] [%3d %3d %3d]\n",rl,gl,bl,rh,gh,bh);
 | |
|    }
 | |
| #endif
 | |
|    left->l=left->r=NULL;
 | |
|    right->l=right->r=NULL;
 | |
|    left->axis=right->axis=-1;
 | |
|    left->volume=right->volume=-1;
 | |
|    left->pixelCount=newCounts[0];
 | |
|    right->pixelCount=newCounts[1];
 | |
|    node->l=left;
 | |
|    node->r=right;
 | |
|    return 1;
 | |
| }
 | |
| 
 | |
| static BoxNode *
 | |
| median_cut(PixelList *hl[3],
 | |
|            uint32_t imPixelCount,
 | |
|            int nPixels)
 | |
| {
 | |
|    PixelList *tl[3];
 | |
|    int i;
 | |
|    BoxNode *root;
 | |
|    Heap* h;
 | |
|    BoxNode *thisNode;
 | |
| 
 | |
|    h=ImagingQuantHeapNew(box_heap_cmp);
 | |
|    root=malloc(sizeof(BoxNode));
 | |
|    if (!root) { ImagingQuantHeapFree(h); return NULL; }
 | |
|    for(i=0;i<3;i++) {
 | |
|       for (tl[i]=hl[i];tl[i]&&tl[i]->next[i];tl[i]=tl[i]->next[i]);
 | |
|       root->head[i]=hl[i];
 | |
|       root->tail[i]=tl[i];
 | |
|    }
 | |
|    root->l=root->r=NULL;
 | |
|    root->axis=-1;
 | |
|    root->volume=-1;
 | |
|    root->pixelCount=imPixelCount;
 | |
| 
 | |
|    ImagingQuantHeapAdd(h,(void *)root);
 | |
|    while (--nPixels) {
 | |
|       do {
 | |
|          if (!ImagingQuantHeapRemove(h,(void **)&thisNode)) {
 | |
|             goto done;
 | |
|          }
 | |
|       } while (compute_box_volume(thisNode)==1);
 | |
|       if (!split(thisNode)) {
 | |
| #ifndef NO_OUTPUT
 | |
|          printf ("Oops, split failed...\n");
 | |
| #endif
 | |
|          exit (1);
 | |
|       }
 | |
|       ImagingQuantHeapAdd(h,(void *)(thisNode->l));
 | |
|       ImagingQuantHeapAdd(h,(void *)(thisNode->r));
 | |
|    }
 | |
| done:
 | |
|    ImagingQuantHeapFree(h);
 | |
|    return root;
 | |
| }
 | |
| 
 | |
| static void
 | |
| free_box_tree(BoxNode *n)
 | |
| {
 | |
|    PixelList *p,*pp;
 | |
|    if (n->l) free_box_tree(n->l);
 | |
|    if (n->r) free_box_tree(n->r);
 | |
|    for (p=n->head[0];p;p=pp) {
 | |
|       pp=p->next[0];
 | |
|       free(p);
 | |
|    }
 | |
|    free(n);
 | |
| }
 | |
| 
 | |
| #ifdef TEST_SPLIT_INTEGRITY
 | |
| static int
 | |
| checkContained(BoxNode *n,Pixel *pp)
 | |
| {
 | |
|    if (n->l&&n->r) {
 | |
|       return checkContained(n->l,pp)+checkContained(n->r,pp);
 | |
|    }
 | |
|    if (n->l||n->r) {
 | |
| #ifndef NO_OUTPUT
 | |
|       printf ("box tree is dead\n");
 | |
| #endif
 | |
|       return 0;
 | |
|    }
 | |
|    if (
 | |
|        pp->c.r<=n->head[0]->p.c.r &&
 | |
|        pp->c.r>=n->tail[0]->p.c.r &&
 | |
|        pp->c.g<=n->head[1]->p.c.g &&
 | |
|        pp->c.g>=n->tail[1]->p.c.g &&
 | |
|        pp->c.b<=n->head[2]->p.c.b &&
 | |
|        pp->c.b>=n->tail[2]->p.c.b) {
 | |
|       return 1;
 | |
|    }
 | |
|    return 0;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static int
 | |
| annotate_hash_table(BoxNode *n,HashTable *h,uint32_t *box)
 | |
| {
 | |
|    PixelList *p;
 | |
|    PixelHashData *d=(PixelHashData *)hashtable_get_user_data(h);
 | |
|    Pixel q;
 | |
|    if (n->l&&n->r) {
 | |
|       return annotate_hash_table(n->l,h,box) && annotate_hash_table(n->r,h,box);
 | |
|    }
 | |
|    if (n->l||n->r) {
 | |
| #ifndef NO_OUTPUT
 | |
|       printf ("box tree is dead\n");
 | |
| #endif
 | |
|       return 0;
 | |
|    }
 | |
|    for (p=n->head[0];p;p=p->next[0]) {
 | |
|       PIXEL_UNSCALE(&(p->p),&q,d->scale);
 | |
|       if (!hashtable_insert(h,q,*box)) {
 | |
| #ifndef NO_OUTPUT
 | |
|          printf ("hashtable insert failed\n");
 | |
| #endif
 | |
|          return 0;
 | |
|       }
 | |
|    }
 | |
|    if (n->head[0]) (*box)++;
 | |
|    return 1;
 | |
| }
 | |
| 
 | |
| static int
 | |
| _sort_ulong_ptr_keys(const void *a, const void *b)
 | |
| {
 | |
|    uint32_t A=**(uint32_t **)a;
 | |
|    uint32_t B=**(uint32_t **)b;
 | |
|    return (A==B)?0:((A<B)?-1:+1);
 | |
| }
 | |
| 
 | |
| static int
 | |
| resort_distance_tables(uint32_t *avgDist,
 | |
|                        uint32_t **avgDistSortKey,
 | |
|                        Pixel *p,
 | |
|                        uint32_t nEntries)
 | |
| {
 | |
|    uint32_t i,j,k;
 | |
|    uint32_t **skRow;
 | |
|    uint32_t *skElt;
 | |
| 
 | |
|    for (i=0;i<nEntries;i++) {
 | |
|       avgDist[i*nEntries+i]=0;
 | |
|       for (j=0;j<i;j++) {
 | |
|          avgDist[j*nEntries+i]=
 | |
|          avgDist[i*nEntries+j]=_DISTSQR(p+i,p+j);
 | |
|       }
 | |
|    }
 | |
|    for (i=0;i<nEntries;i++) {
 | |
|       skRow=avgDistSortKey+i*nEntries;
 | |
|       for (j=1;j<nEntries;j++) {
 | |
|          skElt=skRow[j];
 | |
|          for (k=j;k&&(*(skRow[k-1])>*(skRow[k]));k--) {
 | |
|             skRow[k]=skRow[k-1];
 | |
|          }
 | |
|          if (k!=j) skRow[k]=skElt;
 | |
|       }
 | |
|    }
 | |
|    return 1;
 | |
| }
 | |
| 
 | |
| static int
 | |
| build_distance_tables(uint32_t *avgDist,
 | |
|                       uint32_t **avgDistSortKey,
 | |
|                       Pixel *p,
 | |
|                       uint32_t nEntries)
 | |
| {
 | |
|    uint32_t i,j;
 | |
| 
 | |
|    for (i=0;i<nEntries;i++) {
 | |
|       avgDist[i*nEntries+i]=0;
 | |
|       avgDistSortKey[i*nEntries+i]=&(avgDist[i*nEntries+i]);
 | |
|       for (j=0;j<i;j++) {
 | |
|          avgDist[j*nEntries+i]=
 | |
|          avgDist[i*nEntries+j]=_DISTSQR(p+i,p+j);
 | |
|          avgDistSortKey[j*nEntries+i]=&(avgDist[j*nEntries+i]);
 | |
|          avgDistSortKey[i*nEntries+j]=&(avgDist[i*nEntries+j]);
 | |
|       }
 | |
|    }
 | |
|    for (i=0;i<nEntries;i++) {
 | |
|       qsort(avgDistSortKey+i*nEntries,
 | |
|             nEntries,
 | |
|             sizeof(uint32_t *),
 | |
|             _sort_ulong_ptr_keys);
 | |
|    }
 | |
|    return 1;
 | |
| }
 | |
| 
 | |
| static int
 | |
| map_image_pixels(Pixel *pixelData,
 | |
|                  uint32_t nPixels,
 | |
|                  Pixel *paletteData,
 | |
|                  uint32_t nPaletteEntries,
 | |
|                  uint32_t *avgDist,
 | |
|                  uint32_t **avgDistSortKey,
 | |
|                  uint32_t *pixelArray)
 | |
| {
 | |
|    uint32_t *aD,**aDSK;
 | |
|    uint32_t idx;
 | |
|    uint32_t i,j;
 | |
|    uint32_t bestdist,bestmatch,dist;
 | |
|    uint32_t initialdist;
 | |
|    HashTable *h2;
 | |
| 
 | |
|    h2=hashtable_new(unshifted_pixel_hash,unshifted_pixel_cmp);
 | |
|    for (i=0;i<nPixels;i++) {
 | |
|       if (!hashtable_lookup(h2,pixelData[i],&bestmatch)) {
 | |
|          bestmatch=0;
 | |
|          initialdist=_DISTSQR(paletteData+bestmatch,pixelData+i);
 | |
|          bestdist=initialdist;
 | |
|          initialdist<<=2;
 | |
|          aDSK=avgDistSortKey+bestmatch*nPaletteEntries;
 | |
|          aD=avgDist+bestmatch*nPaletteEntries;
 | |
|          for (j=0;j<nPaletteEntries;j++) {
 | |
|             idx=aDSK[j]-aD;
 | |
|             if (*(aDSK[j])<=initialdist)  {
 | |
|                dist=_DISTSQR(paletteData+idx,pixelData+i);
 | |
|                if (dist<bestdist) {
 | |
|                   bestdist=dist;
 | |
|                   bestmatch=idx;
 | |
|                }
 | |
|             } else {
 | |
|                break;
 | |
|             }
 | |
|          }
 | |
|          hashtable_insert(h2,pixelData[i],bestmatch);
 | |
|       }
 | |
|       pixelArray[i]=bestmatch;
 | |
|    }
 | |
|    hashtable_free(h2);
 | |
|    return 1;
 | |
| }
 | |
| 
 | |
| static int
 | |
| map_image_pixels_from_quantized_pixels(
 | |
|     Pixel *pixelData,
 | |
|     uint32_t nPixels,
 | |
|     Pixel *paletteData,
 | |
|     uint32_t nPaletteEntries,
 | |
|     uint32_t *avgDist,
 | |
|     uint32_t **avgDistSortKey,
 | |
|     uint32_t *pixelArray,
 | |
|     uint32_t *avg[3],
 | |
|     uint32_t *count)
 | |
| {
 | |
|    uint32_t *aD,**aDSK;
 | |
|    uint32_t idx;
 | |
|    uint32_t i,j;
 | |
|    uint32_t bestdist,bestmatch,dist;
 | |
|    uint32_t initialdist;
 | |
|    HashTable *h2;
 | |
|    int changes=0;
 | |
| 
 | |
|    h2=hashtable_new(unshifted_pixel_hash,unshifted_pixel_cmp);
 | |
|    for (i=0;i<nPixels;i++) {
 | |
|       if (!hashtable_lookup(h2,pixelData[i],&bestmatch)) {
 | |
|          bestmatch=pixelArray[i];
 | |
|          initialdist=_DISTSQR(paletteData+bestmatch,pixelData+i);
 | |
|          bestdist=initialdist;
 | |
|          initialdist<<=2;
 | |
|          aDSK=avgDistSortKey+bestmatch*nPaletteEntries;
 | |
|          aD=avgDist+bestmatch*nPaletteEntries;
 | |
|          for (j=0;j<nPaletteEntries;j++) {
 | |
|             idx=aDSK[j]-aD;
 | |
|             if (*(aDSK[j])<=initialdist)  {
 | |
|                dist=_DISTSQR(paletteData+idx,pixelData+i);
 | |
|                if (dist<bestdist) {
 | |
|                   bestdist=dist;
 | |
|                   bestmatch=idx;
 | |
|                }
 | |
|             } else {
 | |
|                break;
 | |
|             }
 | |
|          }
 | |
|          hashtable_insert(h2,pixelData[i],bestmatch);
 | |
|       }
 | |
|       if (pixelArray[i]!=bestmatch) {
 | |
|          changes++;
 | |
|          avg[0][bestmatch]+=pixelData[i].c.r;
 | |
|          avg[1][bestmatch]+=pixelData[i].c.g;
 | |
|          avg[2][bestmatch]+=pixelData[i].c.b;
 | |
|          avg[0][pixelArray[i]]-=pixelData[i].c.r;
 | |
|          avg[1][pixelArray[i]]-=pixelData[i].c.g;
 | |
|          avg[2][pixelArray[i]]-=pixelData[i].c.b;
 | |
|          count[bestmatch]++;
 | |
|          count[pixelArray[i]]--;
 | |
|          pixelArray[i]=bestmatch;
 | |
|       }
 | |
|    }
 | |
|    hashtable_free(h2);
 | |
|    return changes;
 | |
| }
 | |
| 
 | |
| static int
 | |
| map_image_pixels_from_median_box(
 | |
|     Pixel *pixelData,
 | |
|     uint32_t nPixels,
 | |
|     Pixel *paletteData,
 | |
|     uint32_t nPaletteEntries,
 | |
|     HashTable *medianBoxHash,
 | |
|     uint32_t *avgDist,
 | |
|     uint32_t **avgDistSortKey,
 | |
|     uint32_t *pixelArray)
 | |
| {
 | |
|    uint32_t *aD,**aDSK;
 | |
|    uint32_t idx;
 | |
|    uint32_t i,j;
 | |
|    uint32_t bestdist,bestmatch,dist;
 | |
|    uint32_t initialdist;
 | |
|    HashTable *h2;
 | |
|    uint32_t pixelVal;
 | |
| 
 | |
|    h2=hashtable_new(unshifted_pixel_hash,unshifted_pixel_cmp);
 | |
|    for (i=0;i<nPixels;i++) {
 | |
|       if (hashtable_lookup(h2,pixelData[i],&pixelVal)) {
 | |
|          pixelArray[i]=pixelVal;
 | |
|          continue;
 | |
|       }
 | |
|       if (!hashtable_lookup(medianBoxHash,pixelData[i],&pixelVal)) {
 | |
| #ifndef NO_OUTPUT
 | |
|          printf ("pixel lookup failed\n");
 | |
| #endif
 | |
|          return 0;
 | |
|       }
 | |
|       initialdist=_DISTSQR(paletteData+pixelVal,pixelData+i);
 | |
|       bestdist=initialdist;
 | |
|       bestmatch=pixelVal;
 | |
|       initialdist<<=2;
 | |
|       aDSK=avgDistSortKey+pixelVal*nPaletteEntries;
 | |
|       aD=avgDist+pixelVal*nPaletteEntries;
 | |
|       for (j=0;j<nPaletteEntries;j++) {
 | |
|          idx=aDSK[j]-aD;
 | |
|          if (*(aDSK[j])<=initialdist)  {
 | |
|             dist=_DISTSQR(paletteData+idx,pixelData+i);
 | |
|             if (dist<bestdist) {
 | |
|                bestdist=dist;
 | |
|                bestmatch=idx;
 | |
|             }
 | |
|          } else {
 | |
|             break;
 | |
|          }
 | |
|       }
 | |
|       pixelArray[i]=bestmatch;
 | |
|       hashtable_insert(h2,pixelData[i],bestmatch);
 | |
|    }
 | |
|    hashtable_free(h2);
 | |
|    return 1;
 | |
| }
 | |
| 
 | |
| static int
 | |
| compute_palette_from_median_cut(
 | |
|     Pixel *pixelData,
 | |
|     uint32_t nPixels,
 | |
|     HashTable *medianBoxHash,
 | |
|     Pixel **palette,
 | |
|     uint32_t nPaletteEntries)
 | |
| {
 | |
|    uint32_t i;
 | |
|    uint32_t paletteEntry;
 | |
|    Pixel *p;
 | |
|    uint32_t *avg[3];
 | |
|    uint32_t *count;
 | |
| 
 | |
|    *palette=NULL;
 | |
|    if (!(count=malloc(sizeof(uint32_t)*nPaletteEntries))) {
 | |
|       return 0;
 | |
|    }
 | |
|    memset(count,0,sizeof(uint32_t)*nPaletteEntries);
 | |
|    for(i=0;i<3;i++) {
 | |
|       avg[i]=NULL;
 | |
|    }
 | |
|    for(i=0;i<3;i++) {
 | |
|       if (!(avg[i]=malloc(sizeof(uint32_t)*nPaletteEntries))) {
 | |
|          for(i=0;i<3;i++) {
 | |
|             if (avg[i]) free (avg[i]);
 | |
|          }
 | |
|          free(count);
 | |
|          return 0;
 | |
|       }
 | |
|    }
 | |
|    for(i=0;i<3;i++) {
 | |
|       memset(avg[i],0,sizeof(uint32_t)*nPaletteEntries);
 | |
|    }
 | |
|    for (i=0;i<nPixels;i++) {
 | |
| #ifdef TEST_SPLIT_INTEGRITY
 | |
|       if (!(i%100)) { printf ("%05d\r",i); fflush(stdout); }
 | |
|       if (checkContained(root,pixelData+i)>1) {
 | |
|          printf ("pixel in two boxes\n");
 | |
|          for(i=0;i<3;i++) free (avg[i]);
 | |
|          free(count);
 | |
|          return 0;
 | |
|       }
 | |
| #endif
 | |
|       if (!hashtable_lookup(medianBoxHash,pixelData[i],&paletteEntry)) {
 | |
| #ifndef NO_OUTPUT
 | |
|          printf ("pixel lookup failed\n");
 | |
| #endif
 | |
|          for(i=0;i<3;i++) free (avg[i]);
 | |
|          free(count);
 | |
|          return 0;
 | |
|       }
 | |
|       if (paletteEntry>=nPaletteEntries) {
 | |
| #ifndef NO_OUTPUT
 | |
|          printf ("panic - paletteEntry>=nPaletteEntries (%d>=%d)\n",(int)paletteEntry,(int)nPaletteEntries);
 | |
| #endif
 | |
|          for(i=0;i<3;i++) free (avg[i]);
 | |
|          free(count);
 | |
|          return 0;
 | |
|       }
 | |
|       avg[0][paletteEntry]+=pixelData[i].c.r;
 | |
|       avg[1][paletteEntry]+=pixelData[i].c.g;
 | |
|       avg[2][paletteEntry]+=pixelData[i].c.b;
 | |
|       count[paletteEntry]++;
 | |
|    }
 | |
|    p=malloc(sizeof(Pixel)*nPaletteEntries);
 | |
|    if (!p) {
 | |
|       for(i=0;i<3;i++) free (avg[i]);
 | |
|       free(count);
 | |
|       return 0;
 | |
|    }
 | |
|    for (i=0;i<nPaletteEntries;i++) {
 | |
|       p[i].c.r=(int)(.5+(double)avg[0][i]/(double)count[i]);
 | |
|       p[i].c.g=(int)(.5+(double)avg[1][i]/(double)count[i]);
 | |
|       p[i].c.b=(int)(.5+(double)avg[2][i]/(double)count[i]);
 | |
|    }
 | |
|    *palette=p;
 | |
|    for(i=0;i<3;i++) free (avg[i]);
 | |
|    free(count);
 | |
|    return 1;
 | |
| }
 | |
| 
 | |
| static int
 | |
| recompute_palette_from_averages(
 | |
|     Pixel *palette,
 | |
|     uint32_t nPaletteEntries,
 | |
|     uint32_t *avg[3],
 | |
|     uint32_t *count)
 | |
| {
 | |
|     uint32_t i;
 | |
| 
 | |
|     for (i=0;i<nPaletteEntries;i++) {
 | |
|         palette[i].c.r=(int)(.5+(double)avg[0][i]/(double)count[i]);
 | |
|         palette[i].c.g=(int)(.5+(double)avg[1][i]/(double)count[i]);
 | |
|         palette[i].c.b=(int)(.5+(double)avg[2][i]/(double)count[i]);
 | |
|     }
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| static int
 | |
| compute_palette_from_quantized_pixels(
 | |
|     Pixel *pixelData,
 | |
|     uint32_t nPixels,
 | |
|     Pixel *palette,
 | |
|     uint32_t nPaletteEntries,
 | |
|     uint32_t *avg[3],
 | |
|     uint32_t *count,
 | |
|     uint32_t *qp)
 | |
| {
 | |
|    uint32_t i;
 | |
| 
 | |
|    memset(count,0,sizeof(uint32_t)*nPaletteEntries);
 | |
|    for(i=0;i<3;i++) {
 | |
|       memset(avg[i],0,sizeof(uint32_t)*nPaletteEntries);
 | |
|    }
 | |
|    for (i=0;i<nPixels;i++) {
 | |
|       if (qp[i]>=nPaletteEntries) {
 | |
| #ifndef NO_OUTPUT
 | |
|          printf ("scream\n");
 | |
| #endif
 | |
|          return 0;
 | |
|       }
 | |
|       avg[0][qp[i]]+=pixelData[i].c.r;
 | |
|       avg[1][qp[i]]+=pixelData[i].c.g;
 | |
|       avg[2][qp[i]]+=pixelData[i].c.b;
 | |
|       count[qp[i]]++;
 | |
|    }
 | |
|    for (i=0;i<nPaletteEntries;i++) {
 | |
|       palette[i].c.r=(int)(.5+(double)avg[0][i]/(double)count[i]);
 | |
|       palette[i].c.g=(int)(.5+(double)avg[1][i]/(double)count[i]);
 | |
|       palette[i].c.b=(int)(.5+(double)avg[2][i]/(double)count[i]);
 | |
|    }
 | |
|    return 1;
 | |
| }
 | |
| 
 | |
| static int
 | |
| k_means(Pixel *pixelData,
 | |
|         uint32_t nPixels,
 | |
|         Pixel *paletteData,
 | |
|         uint32_t nPaletteEntries,
 | |
|         uint32_t *qp,
 | |
|         int threshold)
 | |
| {
 | |
|    uint32_t *avg[3];
 | |
|    uint32_t *count;
 | |
|    uint32_t i;
 | |
|    uint32_t *avgDist;
 | |
|    uint32_t **avgDistSortKey;
 | |
|    int changes;
 | |
|    int built=0;
 | |
| 
 | |
|    if (!(count=malloc(sizeof(uint32_t)*nPaletteEntries))) {
 | |
|       return 0;
 | |
|    }
 | |
|    for(i=0;i<3;i++) {
 | |
|       avg[i]=NULL;
 | |
|    }
 | |
|    for(i=0;i<3;i++) {
 | |
|       if (!(avg[i]=malloc(sizeof(uint32_t)*nPaletteEntries))) {
 | |
|          goto error_1;
 | |
|       }
 | |
|    }
 | |
|    avgDist=malloc(sizeof(uint32_t)*nPaletteEntries*nPaletteEntries);
 | |
|    if (!avgDist) { goto error_1; }
 | |
| 
 | |
|    avgDistSortKey=malloc(sizeof(uint32_t *)*nPaletteEntries*nPaletteEntries);
 | |
|    if (!avgDistSortKey) { goto error_2; }
 | |
| 
 | |
| #ifndef NO_OUTPUT
 | |
|    printf("[");fflush(stdout);
 | |
| #endif
 | |
|    while (1) {
 | |
|       if (!built) {
 | |
|          compute_palette_from_quantized_pixels(pixelData,nPixels,paletteData,nPaletteEntries,avg,count,qp);
 | |
|          build_distance_tables(avgDist,avgDistSortKey,paletteData,nPaletteEntries);
 | |
|          built=1;
 | |
|       } else {
 | |
|          recompute_palette_from_averages(paletteData,nPaletteEntries,avg,count);
 | |
|          resort_distance_tables(avgDist,avgDistSortKey,paletteData,nPaletteEntries);
 | |
|       }
 | |
|       changes=map_image_pixels_from_quantized_pixels(pixelData,
 | |
|                                                      nPixels,
 | |
|                                                      paletteData,
 | |
|                                                      nPaletteEntries,
 | |
|                                                      avgDist,
 | |
|                                                      avgDistSortKey,
 | |
|                                                      qp,
 | |
|                                                      avg,
 | |
|                                                      count);
 | |
|       if (changes<0) {
 | |
|          goto error_3;
 | |
|       }
 | |
| #ifndef NO_OUTPUT
 | |
|       printf (".(%d)",changes);fflush(stdout);
 | |
| #endif
 | |
|       if (changes<=threshold) break;
 | |
|    }
 | |
| #ifndef NO_OUTPUT
 | |
|    printf("]\n");
 | |
| #endif
 | |
|    if (avgDistSortKey) free(avgDistSortKey);
 | |
|    if (avgDist) free(avgDist);
 | |
|    for(i=0;i<3;i++) if (avg[i]) free (avg[i]);
 | |
|    if (count) free(count);
 | |
|    return 1;
 | |
| 
 | |
| error_3:
 | |
|    if (avgDistSortKey) free(avgDistSortKey);
 | |
| error_2:
 | |
|    if (avgDist) free(avgDist);
 | |
| error_1:
 | |
|    for(i=0;i<3;i++) if (avg[i]) free (avg[i]);
 | |
|    if (count) free(count);
 | |
|    return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| quantize(Pixel *pixelData,
 | |
|          uint32_t nPixels,
 | |
|          uint32_t nQuantPixels,
 | |
|          Pixel **palette,
 | |
|          uint32_t *paletteLength,
 | |
|          uint32_t **quantizedPixels,
 | |
|          int kmeans)
 | |
| {
 | |
|    PixelList *hl[3];
 | |
|    HashTable *h;
 | |
|    BoxNode *root;
 | |
|    uint32_t i;
 | |
|    uint32_t *qp;
 | |
|    uint32_t nPaletteEntries;
 | |
| 
 | |
|    uint32_t *avgDist;
 | |
|    uint32_t **avgDistSortKey;
 | |
|    Pixel *p;
 | |
| 
 | |
| #ifndef NO_OUTPUT
 | |
|    uint32_t timer,timer2;
 | |
| #endif
 | |
| 
 | |
| #ifndef NO_OUTPUT
 | |
|    timer2=clock();
 | |
|    printf ("create hash table..."); fflush(stdout); timer=clock();
 | |
| #endif
 | |
|    h=create_pixel_hash(pixelData,nPixels);
 | |
| #ifndef NO_OUTPUT
 | |
|    printf ("done (%f)\n",(clock()-timer)/(double)CLOCKS_PER_SEC);
 | |
| #endif
 | |
|    if (!h) {
 | |
|       goto error_0;
 | |
|    }
 | |
| 
 | |
| #ifndef NO_OUTPUT
 | |
|    printf ("create lists from hash table..."); fflush(stdout); timer=clock();
 | |
| #endif
 | |
|    hl[0]=hl[1]=hl[2]=NULL;
 | |
|    hashtable_foreach(h,hash_to_list,hl);
 | |
| #ifndef NO_OUTPUT
 | |
|    printf ("done (%f)\n",(clock()-timer)/(double)CLOCKS_PER_SEC);
 | |
| #endif
 | |
| 
 | |
|    if (!hl[0]) {
 | |
|       goto error_1;
 | |
|    }
 | |
| 
 | |
| #ifndef NO_OUTPUT
 | |
|    printf ("mergesort lists..."); fflush(stdout); timer=clock();
 | |
| #endif
 | |
|    for(i=0;i<3;i++) {
 | |
|       hl[i]=mergesort_pixels(hl[i],i);
 | |
|    }
 | |
| #ifdef TEST_MERGESORT
 | |
|    if (!test_sorted(hl)) {
 | |
|       printf ("bug in mergesort\n");
 | |
|       goto error_1;
 | |
|    }
 | |
| #endif
 | |
| #ifndef NO_OUTPUT
 | |
|    printf ("done (%f)\n",(clock()-timer)/(double)CLOCKS_PER_SEC);
 | |
| #endif
 | |
| 
 | |
| #ifndef NO_OUTPUT
 | |
|    printf ("median cut..."); fflush(stdout); timer=clock();
 | |
| #endif
 | |
|    root=median_cut(hl,nPixels,nQuantPixels);
 | |
| #ifndef NO_OUTPUT
 | |
|    printf ("done (%f)\n",(clock()-timer)/(double)CLOCKS_PER_SEC);
 | |
| #endif
 | |
|    if (!root) {
 | |
|       goto error_1;
 | |
|    }
 | |
|    nPaletteEntries=0;
 | |
| #ifndef NO_OUTPUT
 | |
|    printf ("median cut tree to hash table..."); fflush(stdout); timer=clock();
 | |
| #endif
 | |
|    annotate_hash_table(root,h,&nPaletteEntries);
 | |
| #ifndef NO_OUTPUT
 | |
|    printf ("done (%f)\n",(clock()-timer)/(double)CLOCKS_PER_SEC);
 | |
| #endif
 | |
| #ifndef NO_OUTPUT
 | |
|    printf ("compute palette...\n"); fflush(stdout); timer=clock();
 | |
| #endif
 | |
|    if (!compute_palette_from_median_cut(pixelData,nPixels,h,&p,nPaletteEntries)) {
 | |
|       goto error_3;
 | |
|    }
 | |
| #ifndef NO_OUTPUT
 | |
|    printf ("done (%f)\n",(clock()-timer)/(double)CLOCKS_PER_SEC);
 | |
| #endif
 | |
| 
 | |
|    free_box_tree(root);
 | |
|    root=NULL;
 | |
| 
 | |
|    qp=malloc(sizeof(uint32_t)*nPixels);
 | |
|    if (!qp) { goto error_4; }
 | |
| 
 | |
|    avgDist=malloc(sizeof(uint32_t)*nPaletteEntries*nPaletteEntries);
 | |
|    if (!avgDist) { goto error_5; }
 | |
| 
 | |
|    avgDistSortKey=malloc(sizeof(uint32_t *)*nPaletteEntries*nPaletteEntries);
 | |
|    if (!avgDistSortKey) { goto error_6; }
 | |
| 
 | |
|    if (!build_distance_tables(avgDist,avgDistSortKey,p,nPaletteEntries)) {
 | |
|       goto error_7;
 | |
|    }
 | |
| 
 | |
|    if (!map_image_pixels_from_median_box(pixelData,nPixels,p,nPaletteEntries,h,avgDist,avgDistSortKey,qp)) {
 | |
|       goto error_7;
 | |
|    }
 | |
| 
 | |
| #ifdef TEST_NEAREST_NEIGHBOUR
 | |
| #include <math.h>
 | |
|    {
 | |
|       uint32_t bestmatch,bestdist,dist;
 | |
|       HashTable *h2;
 | |
|       printf ("nearest neighbour search (full search)..."); fflush(stdout); timer=clock();
 | |
|       h2=hashtable_new(unshifted_pixel_hash,unshifted_pixel_cmp);
 | |
|       for (i=0;i<nPixels;i++) {
 | |
|          if (hashtable_lookup(h2,pixelData[i],&paletteEntry)) {
 | |
|             bestmatch=paletteEntry;
 | |
|          } else {
 | |
|             bestmatch=0;
 | |
|             bestdist=
 | |
|                _SQR(pixelData[i].c.r-p[0].c.r)+
 | |
|                _SQR(pixelData[i].c.g-p[0].c.g)+
 | |
|                _SQR(pixelData[i].c.b-p[0].c.b);
 | |
|             for (j=1;j<nPaletteEntries;j++) {
 | |
|                dist=
 | |
|                   _SQR(pixelData[i].c.r-p[j].c.r)+
 | |
|                   _SQR(pixelData[i].c.g-p[j].c.g)+
 | |
|                   _SQR(pixelData[i].c.b-p[j].c.b);
 | |
|                if (dist==bestdist && j==qp[i]) {
 | |
|                   bestmatch=j;
 | |
|                }
 | |
|                if (dist<bestdist) {
 | |
|                   bestdist=dist;
 | |
|                   bestmatch=j;
 | |
|                }
 | |
|             }
 | |
|             hashtable_insert(h2,pixelData[i],bestmatch);
 | |
|          }
 | |
|          if (qp[i]!=bestmatch ) {
 | |
|             printf ("discrepancy in matching algorithms pixel %d [%d %d] %f %f\n",
 | |
|                     i,qp[i],bestmatch,
 | |
|                     sqrt((double)(_SQR(pixelData[i].c.r-p[qp[i]].c.r)+
 | |
|                                   _SQR(pixelData[i].c.g-p[qp[i]].c.g)+
 | |
|                                   _SQR(pixelData[i].c.b-p[qp[i]].c.b))),
 | |
|                     sqrt((double)(_SQR(pixelData[i].c.r-p[bestmatch].c.r)+
 | |
|                                   _SQR(pixelData[i].c.g-p[bestmatch].c.g)+
 | |
|                                   _SQR(pixelData[i].c.b-p[bestmatch].c.b)))
 | |
|                    );
 | |
|          }
 | |
|       }
 | |
|       hashtable_free(h2);
 | |
|    }
 | |
| #endif
 | |
| #ifndef NO_OUTPUT
 | |
|    printf ("k means...\n"); fflush(stdout); timer=clock();
 | |
| #endif
 | |
|    if (kmeans) k_means(pixelData,nPixels,p,nPaletteEntries,qp,kmeans-1);
 | |
| #ifndef NO_OUTPUT
 | |
|    printf ("done (%f)\n",(clock()-timer)/(double)CLOCKS_PER_SEC);
 | |
| #endif
 | |
| 
 | |
|    *quantizedPixels=qp;
 | |
|    *palette=p;
 | |
|    *paletteLength=nPaletteEntries;
 | |
| 
 | |
| #ifndef NO_OUTPUT
 | |
|    printf ("cleanup..."); fflush(stdout); timer=clock();
 | |
| #endif
 | |
|    if (avgDist) free(avgDist);
 | |
|    if (avgDistSortKey) free(avgDistSortKey);
 | |
|    destroy_pixel_hash(h);
 | |
| #ifndef NO_OUTPUT
 | |
|    printf ("done (%f)\n",(clock()-timer)/(double)CLOCKS_PER_SEC);
 | |
|    printf ("-----\ntotal time %f\n",(clock()-timer2)/(double)CLOCKS_PER_SEC);
 | |
| #endif
 | |
|    return 1;
 | |
| 
 | |
| error_7:
 | |
|    if (avgDistSortKey) free(avgDistSortKey);
 | |
| error_6:
 | |
|    if (avgDist) free(avgDist);
 | |
| error_5:
 | |
|    if (qp) free(qp);
 | |
| error_4:
 | |
|    if (p) free(p);
 | |
| error_3:
 | |
|    if (root) free_box_tree(root);
 | |
| error_1:
 | |
|    destroy_pixel_hash(h);
 | |
| error_0:
 | |
|    *quantizedPixels=NULL;
 | |
|    *paletteLength=0;
 | |
|    *palette=NULL;
 | |
|    return 0;
 | |
| }
 | |
| 
 | |
| typedef struct {
 | |
|    Pixel new;
 | |
|    Pixel furthest;
 | |
|    uint32_t furthestDistance;
 | |
|    int secondPixel;
 | |
| } DistanceData;
 | |
| 
 | |
| static void
 | |
| compute_distances(const HashTable *h, const Pixel pixel, uint32_t *dist, void *u)
 | |
| {
 | |
|    DistanceData *data=(DistanceData *)u;
 | |
|    uint32_t oldDist=*dist;
 | |
|    uint32_t newDist;
 | |
|    newDist=_DISTSQR(&(data->new),&pixel);
 | |
|    if (data->secondPixel || newDist<oldDist) {
 | |
|       *dist=newDist;
 | |
|       oldDist=newDist;
 | |
|    }
 | |
|    if (oldDist>data->furthestDistance) {
 | |
|       data->furthestDistance=oldDist;
 | |
|       data->furthest.v=pixel.v;
 | |
|    }
 | |
| }
 | |
| 
 | |
| int
 | |
| quantize2(Pixel *pixelData,
 | |
|           uint32_t nPixels,
 | |
|           uint32_t nQuantPixels,
 | |
|           Pixel **palette,
 | |
|           uint32_t *paletteLength,
 | |
|           uint32_t **quantizedPixels,
 | |
|           int kmeans)
 | |
| {
 | |
|    HashTable *h;
 | |
|    uint32_t i;
 | |
|    uint32_t mean[3];
 | |
|    Pixel *p;
 | |
|    DistanceData data;
 | |
| 
 | |
|    uint32_t *qp;
 | |
|    uint32_t *avgDist;
 | |
|    uint32_t **avgDistSortKey;
 | |
| 
 | |
|    p=malloc(sizeof(Pixel)*nQuantPixels);
 | |
|    if (!p) return 0;
 | |
|    mean[0]=mean[1]=mean[2]=0;
 | |
|    h=hashtable_new(unshifted_pixel_hash,unshifted_pixel_cmp);
 | |
|    for (i=0;i<nPixels;i++) {
 | |
|       hashtable_insert(h,pixelData[i],0xffffffff);
 | |
|       mean[0]+=pixelData[i].c.r;
 | |
|       mean[1]+=pixelData[i].c.g;
 | |
|       mean[2]+=pixelData[i].c.b;
 | |
|    }
 | |
|    data.new.c.r=(int)(.5+(double)mean[0]/(double)nPixels);
 | |
|    data.new.c.g=(int)(.5+(double)mean[1]/(double)nPixels);
 | |
|    data.new.c.b=(int)(.5+(double)mean[2]/(double)nPixels);
 | |
|    for (i=0;i<nQuantPixels;i++) {
 | |
|       data.furthestDistance=0;
 | |
|       data.secondPixel=(i==1)?1:0;
 | |
|       hashtable_foreach_update(h,compute_distances,&data);
 | |
|       p[i].v=data.furthest.v;
 | |
|       data.new.v=data.furthest.v;
 | |
|    }
 | |
|    hashtable_free(h);
 | |
| 
 | |
|    qp=malloc(sizeof(uint32_t)*nPixels);
 | |
|    if (!qp) { goto error_1; }
 | |
| 
 | |
|    avgDist=malloc(sizeof(uint32_t)*nQuantPixels*nQuantPixels);
 | |
|    if (!avgDist) { goto error_2; }
 | |
| 
 | |
|    avgDistSortKey=malloc(sizeof(uint32_t *)*nQuantPixels*nQuantPixels);
 | |
|    if (!avgDistSortKey) { goto error_3; }
 | |
| 
 | |
|    if (!build_distance_tables(avgDist,avgDistSortKey,p,nQuantPixels)) {
 | |
|       goto error_4;
 | |
|    }
 | |
| 
 | |
|    if (!map_image_pixels(pixelData,nPixels,p,nQuantPixels,avgDist,avgDistSortKey,qp)) {
 | |
|       goto error_4;
 | |
|    }
 | |
|    if (kmeans) k_means(pixelData,nPixels,p,nQuantPixels,qp,kmeans-1);
 | |
| 
 | |
|    *paletteLength=nQuantPixels;
 | |
|    *palette=p;
 | |
|    *quantizedPixels=qp;
 | |
|    free(avgDistSortKey);
 | |
|    free(avgDist);
 | |
|    return 1;
 | |
| 
 | |
| error_4:
 | |
|    free(avgDistSortKey);
 | |
| error_3:
 | |
|    free(avgDist);
 | |
| error_2:
 | |
|    free(qp);
 | |
| error_1:
 | |
|    free(p);
 | |
|    return 0;
 | |
| }
 | |
| 
 | |
| Imaging
 | |
| ImagingQuantize(Imaging im, int colors, int mode, int kmeans)
 | |
| {
 | |
|     int i, j;
 | |
|     int x, y, v;
 | |
|     UINT8* pp;
 | |
|     Pixel* p;
 | |
|     Pixel* palette;
 | |
|     uint32_t paletteLength;
 | |
|     int result;
 | |
|     uint32_t* newData;
 | |
|     Imaging imOut;
 | |
|     int withAlpha = 0;
 | |
|     ImagingSectionCookie cookie;
 | |
| 
 | |
|     if (!im)
 | |
| 	return ImagingError_ModeError();
 | |
|     if (colors < 1 || colors > 256)
 | |
|         /* FIXME: for colors > 256, consider returning an RGB image
 | |
|            instead (see @PIL205) */
 | |
|         return (Imaging) ImagingError_ValueError("bad number of colors");
 | |
| 
 | |
|     if (strcmp(im->mode, "L") != 0 && strcmp(im->mode, "P") != 0 &&
 | |
|         strcmp(im->mode, "RGB") != 0 && strcmp(im->mode, "RGBA") !=0)
 | |
|         return ImagingError_ModeError();
 | |
| 
 | |
|     /* only octree supports RGBA */
 | |
|     if (!strcmp(im->mode, "RGBA") && mode != 2)
 | |
|        return ImagingError_ModeError();
 | |
| 
 | |
|     p = malloc(sizeof(Pixel) * im->xsize * im->ysize);
 | |
|     if (!p)
 | |
|         return ImagingError_MemoryError();
 | |
| 
 | |
|     /* collect statistics */
 | |
| 
 | |
|     /* FIXME: maybe we could load the hash tables directly from the
 | |
|        image data? */
 | |
| 
 | |
|     if (!strcmp(im->mode, "L")) {
 | |
|         /* greyscale */
 | |
| 
 | |
|         /* FIXME: converting a "L" image to "P" with 256 colors
 | |
|            should be done by a simple copy... */
 | |
| 
 | |
|         for (i = y = 0; y < im->ysize; y++)
 | |
|             for (x = 0; x < im->xsize; x++, i++)
 | |
|                 p[i].c.r = p[i].c.g = p[i].c.b = im->image8[y][x];
 | |
| 
 | |
|     } else if (!strcmp(im->mode, "P")) {
 | |
|         /* palette */
 | |
| 
 | |
|         pp = im->palette->palette;
 | |
| 
 | |
|         for (i = y = 0; y < im->ysize; y++)
 | |
|             for (x = 0; x < im->xsize; x++, i++) {
 | |
|                 v = im->image8[y][x];
 | |
|                 p[i].c.r = pp[v*4+0];
 | |
|                 p[i].c.g = pp[v*4+1];
 | |
|                 p[i].c.b = pp[v*4+2];
 | |
|             }
 | |
| 
 | |
|     } else if (!strcmp(im->mode, "RGB") || !strcmp(im->mode, "RGBA")) {
 | |
|         /* true colour */
 | |
| 
 | |
|         for (i = y = 0; y < im->ysize; y++)
 | |
|             for (x = 0; x < im->xsize; x++, i++)
 | |
|                 p[i].v = im->image32[y][x];
 | |
| 
 | |
|     } else {
 | |
|         free(p);
 | |
|         return (Imaging) ImagingError_ValueError("internal error");
 | |
|     }
 | |
| 
 | |
|     ImagingSectionEnter(&cookie);
 | |
| 
 | |
|     switch (mode) {
 | |
|     case 0:
 | |
|         /* median cut */
 | |
|         result = quantize(
 | |
|             p,
 | |
|             im->xsize*im->ysize,
 | |
|             colors,
 | |
|             &palette,
 | |
|             &paletteLength,
 | |
|             &newData,
 | |
|             kmeans
 | |
|             );
 | |
|         break;
 | |
|     case 1:
 | |
|         /* maximum coverage */
 | |
|         result = quantize2(
 | |
|             p,
 | |
|             im->xsize*im->ysize,
 | |
|             colors,
 | |
|             &palette,
 | |
|             &paletteLength,
 | |
|             &newData,
 | |
|             kmeans
 | |
|             );
 | |
|         break;
 | |
|     case 2:
 | |
|         if (!strcmp(im->mode, "RGBA")) {
 | |
|             withAlpha = 1;
 | |
|         }
 | |
|         result = quantize_octree(
 | |
|             p,
 | |
|             im->xsize*im->ysize,
 | |
|             colors,
 | |
|             &palette,
 | |
|             &paletteLength,
 | |
|             &newData,
 | |
|             withAlpha
 | |
|             );
 | |
|         break;
 | |
|     default:
 | |
|         result = 0;
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     free(p);
 | |
|     ImagingSectionLeave(&cookie);
 | |
| 
 | |
|     if (result) {
 | |
|         imOut = ImagingNew("P", im->xsize, im->ysize);
 | |
|         ImagingSectionEnter(&cookie);
 | |
| 
 | |
|         for (i = y = 0; y < im->ysize; y++)
 | |
|             for (x=0; x < im->xsize; x++)
 | |
|                 imOut->image8[y][x] = (unsigned char) newData[i++];
 | |
| 
 | |
|         free(newData);
 | |
| 
 | |
|         pp = imOut->palette->palette;
 | |
| 
 | |
|         for (i = j = 0; i < (int) paletteLength; i++) {
 | |
|             *pp++ = palette[i].c.r;
 | |
|             *pp++ = palette[i].c.g;
 | |
|             *pp++ = palette[i].c.b;
 | |
|             if (withAlpha) {
 | |
|                *pp++ = palette[i].c.a;
 | |
|             } else {
 | |
|                *pp++ = 255;
 | |
|             }
 | |
|         }
 | |
|         for (; i < 256; i++) {
 | |
|             *pp++ = 0;
 | |
|             *pp++ = 0;
 | |
|             *pp++ = 0;
 | |
|             *pp++ = 255;
 | |
|         }
 | |
| 
 | |
|         if (withAlpha) {
 | |
|             strcpy(imOut->palette->mode, "RGBA");
 | |
|         }
 | |
| 
 | |
|         free(palette);
 | |
|         ImagingSectionLeave(&cookie);
 | |
| 
 | |
|         return imOut;
 | |
| 
 | |
|     } else {
 | |
| 
 | |
|         return (Imaging) ImagingError_ValueError("quantization error");
 | |
| 
 | |
|     }
 | |
| }
 |