123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695 |
- /*
- * 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 "QuantPngQuant.h"
- #include "QuantHash.h"
- #include "QuantHeap.h"
- /* MSVC9.0 */
- #ifndef UINT32_MAX
- #define UINT32_MAX 0xffffffff
- #endif
- #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
- /* malloc check ok, small constant allocation */
- 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);
- /* malloc check ok, small constant allocation */
- 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
- /* malloc check ok, small constant allocation */
- left=malloc(sizeof(BoxNode));
- right=malloc(sizeof(BoxNode));
- if (!left||!right) {
- free(left);
- free(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);
- /* malloc check ok, small constant allocation */
- 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;
- /* malloc check ok, using calloc */
- if (!(count=calloc(nPaletteEntries, sizeof(uint32_t)))) {
- return 0;
- }
- for(i=0;i<3;i++) {
- avg[i]=NULL;
- }
- for(i=0;i<3;i++) {
- /* malloc check ok, using calloc */
- if (!(avg[i]=calloc(nPaletteEntries, sizeof(uint32_t)))) {
- for(i=0;i<3;i++) {
- if (avg[i]) free (avg[i]);
- }
- free(count);
- return 0;
- }
- }
- 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]++;
- }
- /* malloc check ok, using calloc */
- p=calloc(nPaletteEntries, sizeof(Pixel));
- 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 (nPaletteEntries > UINT32_MAX / (sizeof(uint32_t))) {
- return 0;
- }
- /* malloc check ok, using calloc */
- if (!(count=calloc(nPaletteEntries, sizeof(uint32_t)))) {
- return 0;
- }
- for(i=0;i<3;i++) {
- avg[i]=NULL;
- }
- for(i=0;i<3;i++) {
- /* malloc check ok, using calloc */
- if (!(avg[i]=calloc(nPaletteEntries, sizeof(uint32_t)))) {
- goto error_1;
- }
- }
- /* this is enough of a check, since the multiplication n*size is done above */
- if (nPaletteEntries > UINT32_MAX / nPaletteEntries) {
- goto error_1;
- }
- /* malloc check ok, using calloc, checking n*n above */
- avgDist=calloc(nPaletteEntries*nPaletteEntries, sizeof(uint32_t));
- if (!avgDist) { goto error_1; }
- /* malloc check ok, using calloc, checking n*n above */
- avgDistSortKey=calloc(nPaletteEntries*nPaletteEntries, sizeof(uint32_t *));
- 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;
- /* malloc check ok, using calloc for overflow */
- qp=calloc(nPixels, sizeof(uint32_t));
- if (!qp) { goto error_4; }
- if (nPaletteEntries > UINT32_MAX / nPaletteEntries ) {
- goto error_5;
- }
- /* malloc check ok, using calloc for overflow, check of n*n above */
- avgDist=calloc(nPaletteEntries*nPaletteEntries, sizeof(uint32_t));
- if (!avgDist) { goto error_5; }
- /* malloc check ok, using calloc for overflow, check of n*n above */
- avgDistSortKey=calloc(nPaletteEntries*nPaletteEntries, sizeof(uint32_t *));
- 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;
- /* malloc check ok, using calloc */
- p=calloc(nQuantPixels, sizeof(Pixel));
- 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);
- /* malloc check ok, using calloc */
- qp=calloc(nPixels, sizeof(uint32_t));
- if (!qp) { goto error_1; }
- if (nQuantPixels > UINT32_MAX / nQuantPixels ) {
- goto error_2;
- }
- /* malloc check ok, using calloc for overflow, check of n*n above */
- avgDist=calloc(nQuantPixels*nQuantPixels, sizeof(uint32_t));
- if (!avgDist) { goto error_2; }
- /* malloc check ok, using calloc for overflow, check of n*n above */
- avgDistSortKey=calloc(nQuantPixels*nQuantPixels, sizeof(uint32_t *));
- 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 and imagequant supports RGBA */
- if (!strcmp(im->mode, "RGBA") && mode != 2 && mode != 3)
- return ImagingError_ModeError();
- if (im->xsize > INT_MAX / im->ysize) {
- return ImagingError_MemoryError();
- }
- /* malloc check ok, using calloc for final overflow, x*y above */
- p = calloc(im->xsize * im->ysize, sizeof(Pixel));
- 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];
- p[i].c.a = 255;
- }
- } 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];
- p[i].c.a = pp[v*4+3];
- }
- } 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;
- case 3:
- #ifdef HAVE_LIBIMAGEQUANT
- if (!strcmp(im->mode, "RGBA")) {
- withAlpha = 1;
- }
- result = quantize_pngquant(
- p,
- im->xsize,
- im->ysize,
- colors,
- &palette,
- &paletteLength,
- &newData,
- withAlpha
- );
- #else
- result = -1;
- #endif
- break;
- default:
- result = 0;
- break;
- }
- free(p);
- ImagingSectionLeave(&cookie);
- if (result > 0) {
- imOut = ImagingNewDirty("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 {
- if (result == -1) {
- return (Imaging) ImagingError_ValueError(
- "dependency required by this method was not "
- "enabled at compile time");
- }
- return (Imaging) ImagingError_ValueError("quantization error");
- }
- }
|