12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859 |
- /*
- * 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;
- #ifndef NO_OUTPUT
- int nLeft;
- #endif
- 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;
- nRight = 0;
- #ifndef NO_OUTPUT
- nLeft = 0;
- #endif
- for (left = 0, c = h[axis]; c;) {
- left = left + c->count;
- nCount[0] += c->count;
- c->flag = 0;
- #ifndef NO_OUTPUT
- nLeft++;
- #endif
- 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;
- #ifndef NO_OUTPUT
- nLeft++;
- #endif
- 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++;
- #ifndef NO_OUTPUT
- nLeft--;
- #endif
- 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;
- }
- typedef struct {
- uint32_t *distance;
- uint32_t index;
- } DistanceWithIndex;
- static int
- _distance_index_cmp(const void *a, const void *b) {
- DistanceWithIndex *A = (DistanceWithIndex *)a;
- DistanceWithIndex *B = (DistanceWithIndex *)b;
- if (*A->distance == *B->distance) {
- return A->index < B->index ? -1 : +1;
- }
- return *A->distance < *B->distance ? -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;
- DistanceWithIndex *dwi;
- 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]);
- }
- }
- dwi = calloc(nEntries, sizeof(DistanceWithIndex));
- if (!dwi) {
- return 0;
- }
- for (i = 0; i < nEntries; i++) {
- for (j = 0; j < nEntries; j++) {
- dwi[j] = (DistanceWithIndex){
- &(avgDist[i * nEntries + j]),
- j
- };
- }
- qsort(
- dwi,
- nEntries,
- sizeof(DistanceWithIndex),
- _distance_index_cmp);
- for (j = 0; j < nEntries; j++) {
- avgDistSortKey[i * nEntries + j] = dwi[j].distance;
- }
- }
- free(dwi);
- 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);
- if (!build_distance_tables(
- avgDist, avgDistSortKey, paletteData, nPaletteEntries)) {
- goto error_3;
- }
- 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;
- }
- static 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;
- uint32_t furthestV;
- 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->furthestV = pixel.v;
- }
- }
- static 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.furthestV = pixelData[0].v;
- data.secondPixel = (i == 1) ? 1 : 0;
- hashtable_foreach_update(h, compute_distances, &data);
- p[i].v = data.furthestV;
- data.new.v = data.furthestV;
- }
- 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")) {
- /* grayscale */
- /* 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 */
- withAlpha = !strcmp(im->mode, "RGBA");
- int transparency = 0;
- unsigned char r = 0, g = 0, b = 0;
- for (i = y = 0; y < im->ysize; y++) {
- for (x = 0; x < im->xsize; x++, i++) {
- p[i].v = im->image32[y][x];
- if (withAlpha && p[i].c.a == 0) {
- if (transparency == 0) {
- transparency = 1;
- r = p[i].c.r;
- g = p[i].c.g;
- b = p[i].c.b;
- } else {
- /* Set all subsequent transparent pixels
- to the same colour as the first */
- p[i].c.r = r;
- p[i].c.g = g;
- p[i].c.b = b;
- }
- }
- }
- }
- } 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:
- result = quantize_octree(
- p,
- im->xsize * im->ysize,
- colors,
- &palette,
- &paletteLength,
- &newData,
- withAlpha);
- break;
- case 3:
- #ifdef HAVE_LIBIMAGEQUANT
- 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);
- imOut->palette->size = (int)paletteLength;
- 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;
- }
- pp++;
- }
- 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");
- }
- }
|