diff options
author | Keshav Sharma <keshav7890@gmail.com> | 2011-07-06 18:54:41 (GMT) |
---|---|---|
committer | Keshav Sharma <keshav7890@gmail.com> | 2011-07-06 18:54:41 (GMT) |
commit | ff9e8108a9912e60e8d341272c443590eaadb890 (patch) | |
tree | 6fbbcb667816d92bc5f6e84c63afe78946451609 /Imaging/libImaging/Quant.c | |
parent | 17a7dee95319f511fcd82cd93e6b09ed3c02923c (diff) |
added changes in all files
Diffstat (limited to 'Imaging/libImaging/Quant.c')
-rw-r--r-- | Imaging/libImaging/Quant.c | 1606 |
1 files changed, 1606 insertions, 0 deletions
diff --git a/Imaging/libImaging/Quant.c b/Imaging/libImaging/Quant.c new file mode 100644 index 0000000..66130f6 --- /dev/null +++ b/Imaging/libImaging/Quant.c @@ -0,0 +1,1606 @@ +/* + * The Python Imaging Library + * $Id: Quant.c 2284 2005-02-07 20:58:58Z fredrik $ + * + * 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 "Quant.h" + +#include "QuantDefines.h" +#include "QuantHash.h" +#include "QuantHeap.h" + +#define NO_OUTPUT + +typedef struct { + unsigned long 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; + unsigned long 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 unsigned long +unshifted_pixel_hash(const HashTable h, const void *p) +{ + Pixel *pixel=(Pixel *)&p; + unsigned long hash=PIXEL_HASH(pixel->c.r, + pixel->c.g, + pixel->c.b); + return hash; +} + +static int +unshifted_pixel_cmp(const HashTable h, const void *a, const void *b) +{ + Pixel *pixel1=(Pixel *)&a; + Pixel *pixel2=(Pixel *)&b; + 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 unsigned long +pixel_hash(const HashTable h,const void *p) +{ + PixelHashData *d=(PixelHashData *)hashtable_get_user_data(h); + Pixel *pixel=(Pixel *)&p; + unsigned long hash=PIXEL_HASH(pixel->c.r>>d->scale, + pixel->c.g>>d->scale, + pixel->c.b>>d->scale); + return hash; +} + +static int +pixel_cmp(const HashTable h,const void *a,const void *b) +{ + PixelHashData *d=(PixelHashData *)hashtable_get_user_data(h); + Pixel *pixel1=(Pixel *)&a; + Pixel *pixel2=(Pixel *)&b; + unsigned long 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 void *key, void **val) +{ + (*(int *)val)+=1; +} + +static void +new_count_func(const HashTable h, const void *key, void **val) +{ + (*(int *)val)=1; +} + +static void +rehash_collide(HashTable h, + void **keyp, + void **valp, + void *newkey, + void *newval) +{ + *valp=(void *)((*(int *)valp)+(*(int *)&newval)); +} + +/* %% */ + +static HashTable +create_pixel_hash(Pixel *pixelData,unsigned long nPixels) +{ + PixelHashData *d; + HashTable *hash; + unsigned long i; + unsigned long timer,timer2,timer3; + + d=malloc(sizeof(PixelHashData)); + if (!d) return NULL; + hash=hashtable_new(pixel_hash,pixel_cmp); + hashtable_set_user_data(hash,d); + d->scale=0; + timer=timer3=clock(); + for (i=0;i<nPixels;i++) { + if (!hashtable_insert_or_update_computed(hash, + (void *)pixelData[i].v, + 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); +#endif + timer2=clock(); + hashtable_rehash_compute(hash,rehash_collide); + timer2=clock()-timer2; +#ifndef NO_OUTPUT + printf ("rehash took %f sec\n",timer2/(double)CLOCKS_PER_SEC); +#endif + timer+=timer2; + } + } +#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(HashTable h, const void *key, const void *val, void *u) +{ + PixelHashData *d=(PixelHashData *)hashtable_get_user_data(h); + PixelList **pl=(PixelList **)u; + PixelList *p; + Pixel *pixel=(Pixel *)&key; + int i; + Pixel q; + int count=*(int *)&val; + + 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; +} + +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; +} + +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], + unsigned long nCount[2], + int axis, + unsigned long pixelCount) +{ + unsigned long 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]; + unsigned long 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], + unsigned long 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)); + } + ImagingQuantHeapFree(h); +done: + 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); +} + +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; +} + +static int +annotate_hash_table(BoxNode *n,HashTable h,unsigned long *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,(void *)q.v,(void *)*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) +{ + unsigned long A=**(unsigned long **)a; + unsigned long B=**(unsigned long **)b; + return (A==B)?0:((A<B)?-1:+1); +} + +static int +resort_distance_tables(unsigned long *avgDist, + unsigned long **avgDistSortKey, + Pixel *p, + unsigned long nEntries) +{ + unsigned long i,j,k; + unsigned long **skRow; + unsigned long *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(unsigned long *avgDist, + unsigned long **avgDistSortKey, + Pixel *p, + unsigned long nEntries) +{ + unsigned long 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(unsigned long *), + _sort_ulong_ptr_keys); + } + return 1; +} + +static int +map_image_pixels(Pixel *pixelData, + unsigned long nPixels, + Pixel *paletteData, + unsigned long nPaletteEntries, + unsigned long *avgDist, + unsigned long **avgDistSortKey, + unsigned long *pixelArray) +{ + unsigned long *aD,**aDSK; + unsigned long idx; + unsigned long i,j; + unsigned long bestdist,bestmatch,dist; + unsigned long initialdist; + HashTable h2; + + h2=hashtable_new(unshifted_pixel_hash,unshifted_pixel_cmp); + for (i=0;i<nPixels;i++) { + if (!hashtable_lookup(h2,(void *)pixelData[i].v,(void **)&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,(void *)pixelData[i].v,(void *)bestmatch); + } + pixelArray[i]=bestmatch; + } + hashtable_free(h2); + return 1; +} + +static int +map_image_pixels_from_quantized_pixels( + Pixel *pixelData, + unsigned long nPixels, + Pixel *paletteData, + unsigned long nPaletteEntries, + unsigned long *avgDist, + unsigned long **avgDistSortKey, + unsigned long *pixelArray, + unsigned long *avg[3], + unsigned long *count) +{ + unsigned long *aD,**aDSK; + unsigned long idx; + unsigned long i,j; + unsigned long bestdist,bestmatch,dist; + unsigned long 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,(void *)pixelData[i].v,(void **)&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,(void *)pixelData[i].v,(void *)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, + unsigned long nPixels, + Pixel *paletteData, + unsigned long nPaletteEntries, + HashTable *medianBoxHash, + unsigned long *avgDist, + unsigned long **avgDistSortKey, + unsigned long *pixelArray) +{ + unsigned long *aD,**aDSK; + unsigned long idx; + unsigned long i,j; + unsigned long bestdist,bestmatch,dist; + unsigned long initialdist; + HashTable h2; + int pixelVal; + + h2=hashtable_new(unshifted_pixel_hash,unshifted_pixel_cmp); + for (i=0;i<nPixels;i++) { + if (hashtable_lookup(h2,(void *)pixelData[i].v,(void **)&pixelVal)) { + pixelArray[i]=pixelVal; + continue; + } + if (!hashtable_lookup(medianBoxHash,(void *)pixelData[i].v,(void **)&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,(void *)pixelData[i].v,(void *)bestmatch); + } + hashtable_free(h2); + return 1; +} + +static int +compute_palette_from_median_cut( + Pixel *pixelData, + unsigned long nPixels, + HashTable medianBoxHash, + Pixel **palette, + unsigned long nPaletteEntries) +{ + unsigned long i; + unsigned long paletteEntry; + Pixel *p; + unsigned long *avg[3]; + unsigned long *count; + + *palette=NULL; + if (!(count=malloc(sizeof(unsigned long)*nPaletteEntries))) { + return 0; + } + memset(count,0,sizeof(unsigned long)*nPaletteEntries); + for(i=0;i<3;i++) { + avg[i]=NULL; + } + for(i=0;i<3;i++) { + if (!(avg[i]=malloc(sizeof(unsigned long)*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(unsigned long)*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,(void *)pixelData[i].v,(void **)&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, + unsigned long nPaletteEntries, + unsigned long *avg[3], + unsigned long *count) +{ + unsigned long 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, + unsigned long nPixels, + Pixel *palette, + unsigned long nPaletteEntries, + unsigned long *avg[3], + unsigned long *count, + unsigned long *qp) +{ + unsigned long i; + + memset(count,0,sizeof(unsigned long)*nPaletteEntries); + for(i=0;i<3;i++) { + memset(avg[i],0,sizeof(unsigned long)*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, + unsigned long nPixels, + Pixel *paletteData, + unsigned long nPaletteEntries, + unsigned long *qp, + int threshold) +{ + unsigned long *avg[3]; + unsigned long *count; + unsigned long i; + unsigned long *avgDist; + unsigned long **avgDistSortKey; + int changes; + int built=0; + + if (!(count=malloc(sizeof(unsigned long)*nPaletteEntries))) { + return 0; + } + for(i=0;i<3;i++) { + avg[i]=NULL; + } + for(i=0;i<3;i++) { + if (!(avg[i]=malloc(sizeof(unsigned long)*nPaletteEntries))) { + goto error_1; + } + } + avgDist=malloc(sizeof(unsigned long)*nPaletteEntries*nPaletteEntries); + if (!avgDist) { goto error_1; } + + avgDistSortKey=malloc(sizeof(unsigned long *)*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, + unsigned long nPixels, + unsigned long nQuantPixels, + Pixel **palette, + unsigned long *paletteLength, + unsigned long **quantizedPixels, + int kmeans) +{ + PixelList *hl[3]; + HashTable h; + BoxNode *root; + unsigned long i; + unsigned long *qp; + unsigned long nPaletteEntries; + + unsigned long *avgDist; + unsigned long **avgDistSortKey; + Pixel *p; + +#ifndef NO_OUTPUT + unsigned long 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(unsigned long)*nPixels); + if (!qp) { goto error_4; } + + avgDist=malloc(sizeof(unsigned long)*nPaletteEntries*nPaletteEntries); + if (!avgDist) { goto error_5; } + + avgDistSortKey=malloc(sizeof(unsigned long *)*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> + { + unsigned long 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,(void *)pixelData[i].v,(void **)&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,(void *)pixelData[i].v,(void *)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; + unsigned long furthestDistance; + int secondPixel; +} DistanceData; + +static void +compute_distances(const HashTable h, const void *key, void **val, void *u) +{ + DistanceData *data=(DistanceData *)u; + Pixel *pixel=(Pixel *)&key; + unsigned long oldDist=*(unsigned long *)val; + unsigned long newDist; + newDist=_DISTSQR(&(data->new),pixel); + if (data->secondPixel || newDist<oldDist) { + *(unsigned long *)val=newDist; + oldDist=newDist; + } + if (oldDist>data->furthestDistance) { + data->furthestDistance=oldDist; + data->furthest.v=pixel->v; + } +} + +int +quantize2(Pixel *pixelData, + unsigned long nPixels, + unsigned long nQuantPixels, + Pixel **palette, + unsigned long *paletteLength, + unsigned long **quantizedPixels, + int kmeans) +{ + HashTable h; + unsigned long i; + unsigned long mean[3]; + Pixel *p; + DistanceData data; + + unsigned long *qp; + unsigned long *avgDist; + unsigned long **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,(void *)pixelData[i].v,(void *)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(unsigned long)*nPixels); + if (!qp) { goto error_1; } + + avgDist=malloc(sizeof(unsigned long)*nQuantPixels*nQuantPixels); + if (!avgDist) { goto error_2; } + + avgDistSortKey=malloc(sizeof(unsigned long *)*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; + unsigned long paletteLength; + int result; + unsigned long* newData; + Imaging imOut; + + 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")) + 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")) { + /* 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"); + } + + 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; + default: + result = 0; + break; + } + + free(p); + + if (result) { + + imOut = ImagingNew("P", im->xsize, im->ysize); + + 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; + *pp++ = 255; + } + for (; i < 256; i++) { + *pp++ = 0; + *pp++ = 0; + *pp++ = 0; + *pp++ = 255; + } + + free(palette); + + return imOut; + + } else { + + return (Imaging) ImagingError_ValueError("quantization error"); + + } +} |