Initial revision
[imager.git] / quant.c
CommitLineData
02d1d628
AMH
1/* quant.c - provides general image quantization
2 currently only used by gif.c, but maybe we'll support producing
3 8-bit (or bigger indexed) png files at some point
4*/
5#include "image.h"
6
7static void makemap_addi(i_quantize *, i_img **imgs, int count);
8
9static
10void
11setcol(i_color *cl,unsigned char r,unsigned char g,unsigned char b,unsigned char a) {
12 cl->rgba.r=r;
13 cl->rgba.g=g;
14 cl->rgba.b=b;
15 cl->rgba.a=a;
16}
17
18
19
20/* make a colour map overwrites mc_existing/mc_count in quant Note
21 that i_makemap will be called once for each image if mc_perimage is
22 set and the format support multiple colour maps per image.
23
24 This means we don't need any special processing at this level to
25 handle multiple colour maps.
26*/
27
28void
29quant_makemap(i_quantize *quant, i_img **imgs, int count) {
30 i_color temp;
31#ifdef HAVE_LIBGIF
32 /* giflib does it's own color table generation */
33 if (quant->translate == pt_giflib)
34 return;
35#endif
36 switch (quant->make_colors & mc_mask) {
37 case mc_none:
38 /* use user's specified map */
39 break;
40 case mc_web_map:
41 {
42 int r, g, b;
43 int i = 0;
44 for (r = 0; r < 256; r+=0x33)
45 for (g = 0; g < 256; g+=0x33)
46 for (b = 0; b < 256; b += 0x33)
47 setcol(quant->mc_colors+i++, r, g, b, 0);
48 quant->mc_count = i;
49 }
50 break;
51
52 case mc_addi:
53 default:
54 makemap_addi(quant, imgs, count);
55 break;
56 }
57}
58
59#ifdef HAVE_LIBGIF
60static void translate_giflib(i_quantize *, i_img *, i_palidx *);
61#endif
62static void translate_closest(i_quantize *, i_img *, i_palidx *);
63static void translate_errdiff(i_quantize *, i_img *, i_palidx *);
64static void translate_addi(i_quantize *, i_img *, i_palidx *);
65
66/* Quantize the image given the palette in quant.
67
68 The giflib quantizer ignores the palette.
69*/
70i_palidx *quant_translate(i_quantize *quant, i_img *img) {
71 i_palidx *result = mymalloc(img->xsize * img->ysize);
72 switch (quant->translate) {
73#ifdef HAVE_LIBGIF
74 case pt_giflib:
75 translate_giflib(quant, img, result);
76 break;
77#endif
78
79 case pt_closest:
80 translate_closest(quant, img, result);
81 break;
82
83 case pt_errdiff:
84 translate_errdiff(quant, img, result);
85 break;
86
87 case pt_perturb:
88 default:
89 translate_addi(quant, img, result);
90 break;
91 }
92
93 return result;
94}
95
96#ifdef HAVE_LIBGIF
97#include "gif_lib.h"
98
99#define GET_RGB(im, x, y, ri, gi, bi, col) \
100 i_gpix((im),(x),(y),&(col)); (ri)=(col).rgb.r; \
101 if((im)->channels==3) { (bi)=(col).rgb.b; (gi)=(col).rgb.g; }
102
103static int
104quant_replicate(i_img *im, i_palidx *output, i_quantize *quant);
105
106/* Use the gif_lib quantization functions to quantize the image */
107static void translate_giflib(i_quantize *quant, i_img *img, i_palidx *out) {
108 int x,y,ColorMapSize,colours_in;
109 unsigned long Size;
110 int i;
111
112 GifByteType *RedBuffer = NULL, *GreenBuffer = NULL, *BlueBuffer = NULL;
113 GifByteType *RedP, *GreenP, *BlueP;
114 ColorMapObject *OutputColorMap = NULL;
115
116 i_color col;
117
118 /*mm_log((1,"i_writegif(0x%x, fd %d, colors %dbpp)\n",im,fd,colors));*/
119
120 /*if (!(im->channels==1 || im->channels==3)) { fprintf(stderr,"Unable to write gif, improper colorspace.\n"); exit(3); }*/
121
122 ColorMapSize = quant->mc_size;
123
124 Size = ((long) img->xsize) * img->ysize * sizeof(GifByteType);
125
126
127 if ((OutputColorMap = MakeMapObject(ColorMapSize, NULL)) == NULL)
128 m_fatal(0,"Failed to allocate memory for Output colormap.");
129 /* if ((OutputBuffer = (GifByteType *) mymalloc(im->xsize * im->ysize * sizeof(GifByteType))) == NULL)
130 m_fatal(0,"Failed to allocate memory for output buffer.");*/
131
132 /* ******************************************************* */
133 /* count the number of colours in the image */
134 colours_in=i_count_colors(img, OutputColorMap->ColorCount);
135
136 if(colours_in != -1) { /* less then the number wanted */
137 /* so we copy them over as-is */
138 mm_log((2,"image has %d colours, which fits in %d. Copying\n",
139 colours_in,ColorMapSize));
140 quant_replicate(img, out, quant);
141 /* saves the colors, so don't fall through */
142 return;
143 } else {
144
145 mm_log((2,"image has %d colours, more then %d. Quantizing\n",colours_in,ColorMapSize));
146
147 if (img->channels >= 3) {
148 if ((RedBuffer = (GifByteType *) mymalloc((unsigned int) Size)) == NULL) {
149 m_fatal(0,"Failed to allocate memory required, aborted.");
150 return;
151 }
152 if ((GreenBuffer = (GifByteType *) mymalloc((unsigned int) Size)) == NULL) {
153 m_fatal(0,"Failed to allocate memory required, aborted.");
154 free(RedBuffer);
155 return;
156 }
157
158 if ((BlueBuffer = (GifByteType *) mymalloc((unsigned int) Size)) == NULL) {
159 m_fatal(0,"Failed to allocate memory required, aborted.");
160 free(RedBuffer);
161 free(GreenBuffer);
162 return;
163 }
164
165 RedP = RedBuffer;
166 GreenP = GreenBuffer;
167 BlueP = BlueBuffer;
168
169 for (y=0; y< img->ysize; y++) for (x=0; x < img->xsize; x++) {
170 i_gpix(img,x,y,&col);
171 *RedP++ = col.rgb.r;
172 *GreenP++ = col.rgb.g;
173 *BlueP++ = col.rgb.b;
174 }
175
176 } else {
177
178 if ((RedBuffer = (GifByteType *) mymalloc((unsigned int) Size))==NULL) {
179 m_fatal(0,"Failed to allocate memory required, aborted.");
180 return;
181 }
182
183 GreenBuffer=BlueBuffer=RedBuffer;
184 RedP = RedBuffer;
185 for (y=0; y< img->ysize; y++) for (x=0; x < img->xsize; x++) {
186 i_gpix(img,x,y,&col);
187 *RedP++ = col.rgb.r;
188 }
189 }
190
191 if (QuantizeBuffer(img->xsize, img->ysize, &ColorMapSize, RedBuffer, GreenBuffer, BlueBuffer,
192 out, OutputColorMap->Colors) == GIF_ERROR) {
193 mm_log((1,"Error in QuantizeBuffer, unable to write image.\n"));
194 }
195 }
196
197 free(RedBuffer);
198 if (img->channels == 3) { free(GreenBuffer); free(BlueBuffer); }
199
200 /* copy over the color map */
201 for (i = 0; i < ColorMapSize; ++i) {
202 quant->mc_colors[i].rgb.r = OutputColorMap->Colors[i].Red;
203 quant->mc_colors[i].rgb.g = OutputColorMap->Colors[i].Green;
204 quant->mc_colors[i].rgb.b = OutputColorMap->Colors[i].Blue;
205 }
206 quant->mc_count = ColorMapSize;
207}
208
209static
210int
211quant_replicate(i_img *im, GifByteType *output, i_quantize *quant) {
212 int x, y, alloced, r, g=0, b=0, idx ;
213 i_color col;
214
215 alloced=0;
216 for(y=0; y<im->ysize; y++) {
217 for(x=0; x<im->xsize; x++) {
218
219 GET_RGB(im, x,y, r,g,b, col);
220
221 for(idx=0; idx<alloced; idx++) { /* linear search for an index */
222 if(quant->mc_colors[idx].rgb.r==r &&
223 quant->mc_colors[idx].rgb.g==g &&
224 quant->mc_colors[idx].rgb.b==b) {
225 break;
226 }
227 }
228
229 if(idx >= alloced) { /* if we haven't already, we */
230 idx=alloced++; /* add the colour to the map */
231 if(quant->mc_size < alloced) {
232 mm_log((1,"Tried to allocate more then %d colours.\n",
233 quant->mc_size));
234 return 0;
235 }
236 quant->mc_colors[idx].rgb.r=r;
237 quant->mc_colors[idx].rgb.g=g;
238 quant->mc_colors[idx].rgb.b=b;
239 }
240 *output=idx; /* fill output buffer */
241 output++; /* with colour indexes */
242 }
243 }
244 quant->mc_count = alloced;
245 return 1;
246}
247
248#endif
249
250static void translate_closest(i_quantize *quant, i_img *img, i_palidx *out) {
251 quant->perturb = 0;
252 translate_addi(quant, img, out);
253}
254
255#define PWR2(x) ((x)*(x))
256
257typedef int (*cmpfunc)(const void*, const void*);
258
259typedef struct {
260 unsigned char r,g,b;
261 char state;
262 int dr,dg,db;
263 int cdist;
264 int mcount;
265} cvec;
266
267typedef struct {
268 int cnt;
269 int vec[256];
270} hashbox;
271
272typedef struct {
273 int boxnum;
274 int pixcnt;
275 int cand;
276 int pdc;
277} pbox;
278
279static void prescan(i_img **im,int count, int cnum, cvec *clr);
280static void reorder(pbox prescan[512]);
281static int pboxcmp(const pbox *a,const pbox *b);
282static void boxcenter(int box,cvec *cv);
283static float frandn(void);
284static void boxrand(int box,cvec *cv);
285static void bbox(int box,int *r0,int *r1,int *g0,int *g1,int *b0,int *b1);
286static void cr_hashindex(cvec clr[256],int cnum,hashbox hb[512]);
287static int mindist(int boxnum,cvec *cv);
288static int maxdist(int boxnum,cvec *cv);
289
290/* Some of the simpler functions are kept here to aid the compiler -
291 maybe some of them will be inlined. */
292
293static int
294pixbox(i_color *ic) { return ((ic->channel[0] & 224)<<1)+ ((ic->channel[1]&224)>>2) + ((ic->channel[2] &224) >> 5); }
295
296static unsigned char
297g_sat(int in) {
298 if (in>255) { return 255; }
299 else if (in>0) return in;
300 return 0;
301}
302
303static
304float
305frand(void) {
306 return rand()/(RAND_MAX+1.0);
307}
308
309static
310int
311eucl_d(cvec* cv,i_color *cl) { return PWR2(cv->r-cl->channel[0])+PWR2(cv->g-cl->channel[1])+PWR2(cv->b-cl->channel[2]); }
312
313static
314int
315ceucl_d(i_color *c1, i_color *c2) { return PWR2(c1->channel[0]-c2->channel[0])+PWR2(c1->channel[1]-c2->channel[1])+PWR2(c1->channel[2]-c2->channel[2]); }
316
317/*
318
319This quantization algorithm and implementation routines are by Arnar
320M. Hrafnkelson. In case any new ideas are here they are mine since
321this was written from scratch.
322
323The algorithm uses local means in the following way:
324
325 For each point in the colormap we find which image points
326 have that point as it's closest point. We calculate the mean
327 of those points and in the next iteration it will be the new
328 entry in the colormap.
329
330In order to speed this process up (i.e. nearest neighbor problem) We
331divied the r,g,b space up in equally large 512 boxes. The boxes are
332numbered from 0 to 511. Their numbering is so that for a given vector
333it is known that it belongs to the box who is formed by concatenating the
3343 most significant bits from each component of the RGB triplet.
335
336For each box we find the list of points from the colormap who might be
337closest to any given point within the box. The exact solution
338involves finding the Voronoi map (or the dual the Delauny
339triangulation) and has many issues including numerical stability.
340
341So we use this approximation:
342
3431. Find which point has the shortest maximum distance to the box.
3442. Find all points that have a shorter minimum distance than that to the box
345
346This is a very simple task and is not computationally heavy if one
347takes into account that the minimum distances from a pixel to a box is
348always found by checking if it's inside the box or is closest to some
349side or a corner. Finding the maximum distance is also either a side
350or a corner.
351
352This approach results 2-3 times more than the actual points needed but
353is still a good gain over the complete space. Usually when one has a
354256 Colorcolor map a search over 30 is often obtained.
355
356A bit of an enhancement to this approach is to keep a seperate list
357for each side of the cube, but this will require even more memory.
358
359 Arnar M. Hrafnkelsson (addi@umich.edu);
360
361*/
362/*
363 Extracted from gifquant.c, removed dependencies on gif_lib,
364 and added support for multiple images.
365 starting from 1nov2000 by TonyC <tony@develop-help.com>.
366
367*/
368
369static void
370makemap_addi(i_quantize *quant, i_img **imgs, int count) {
371 cvec *clr;
372 int cnum, i, x, y, bst_idx=0, ld, cd, iter, currhb;
373 i_color val;
374 float dlt, accerr;
375 hashbox hb[512];
376
377 clr = (cvec *)mymalloc(sizeof(cvec) * quant->mc_size);
378 for (i=0; i < quant->mc_count; ++i) {
379 clr[i].r = quant->mc_colors[i].rgb.r;
380 clr[i].g = quant->mc_colors[i].rgb.g;
381 clr[i].b = quant->mc_colors[i].rgb.b;
382 clr[i].state = 1;
383 }
384 /* mymalloc doesn't clear memory, so I think we need this */
385 for (; i < quant->mc_size; ++i) {
386 clr[i].state = 0;
387 }
388 cnum = quant->mc_size;
389 dlt = 1;
390
391 prescan(imgs, count, cnum, clr);
392 cr_hashindex(clr, cnum, hb);
393
394 for(iter=0;iter<3;iter++) {
395 accerr=0.0;
396
397 for (i = 0; i < count; ++i) {
398 i_img *im = imgs[i];
399 for(y=0;y<im->ysize;y++) for(x=0;x<im->xsize;x++) {
400 ld=196608;
401 i_gpix(im,x,y,&val);
402 currhb=pixbox(&val);
403 /* printf("box = %d \n",currhb); */
404 for(i=0;i<hb[currhb].cnt;i++) {
405 /* printf("comparing: pix (%d,%d,%d) vec (%d,%d,%d)\n",val.channel[0],val.channel[1],val.channel[2],clr[hb[currhb].vec[i]].r,clr[hb[currhb].vec[i]].g,clr[hb[currhb].vec[i]].b); */
406
407 cd=eucl_d(&clr[hb[currhb].vec[i]],&val);
408 if (cd<ld) {
409 ld=cd; /* shortest distance yet */
410 bst_idx=hb[currhb].vec[i]; /* index of closest vector yet */
411 }
412 }
413
414 clr[bst_idx].mcount++;
415 accerr+=(ld);
416 clr[bst_idx].dr+=val.channel[0];
417 clr[bst_idx].dg+=val.channel[1];
418 clr[bst_idx].db+=val.channel[2];
419 }
420 }
421 for(i=0;i<cnum;i++) if (clr[i].mcount) { clr[i].dr/=clr[i].mcount; clr[i].dg/=clr[i].mcount; clr[i].db/=clr[i].mcount; }
422
423 /* for(i=0;i<cnum;i++) printf("vec(%d)=(%d,%d,%d) dest=(%d,%d,%d) matchcount=%d\n",
424 i,clr[i].r,clr[i].g,clr[i].b,clr[i].dr,clr[i].dg,clr[i].db,clr[i].mcount); */
425
426 /* printf("total error: %.2f\n",sqrt(accerr)); */
427
428 for(i=0;i<cnum;i++) {
429 if (clr[i].state) continue; /* skip reserved colors */
430
431 if (clr[i].mcount) {
432 clr[i].r=clr[i].r*(1-dlt)+dlt*clr[i].dr;
433 clr[i].g=clr[i].g*(1-dlt)+dlt*clr[i].dg;
434 clr[i].b=clr[i].b*(1-dlt)+dlt*clr[i].db;
435 } else {
436 /* I don't know why - TC */
437 clr[i].r=rand();
438 clr[i].g=rand();
439 clr[i].b=rand();
440 }
441
442 clr[i].dr=0;
443 clr[i].dg=0;
444 clr[i].db=0;
445 clr[i].mcount=0;
446 }
447 cr_hashindex(clr,cnum,hb);
448 }
449
450
451#ifdef NOTEF
452 for(i=0;i<cnum;i++) {
453 cd=eucl_d(&clr[i],&val);
454 if (cd<ld) {
455 ld=cd;
456 bst_idx=i;
457 }
458 }
459#endif
460
461 /* transfer the colors back */
462 for (i = 0; i < cnum; ++i) {
463 quant->mc_colors[i].rgb.r = clr[i].r;
464 quant->mc_colors[i].rgb.g = clr[i].g;
465 quant->mc_colors[i].rgb.b = clr[i].b;
466 }
467 quant->mc_count = cnum;
468
469 /* don't want to keep this */
470 myfree(clr);
471}
472
473#define pboxjump 32
474
475/* Define one of the following 4 symbols to choose a colour search method
476 The idea is to try these out, including benchmarking, to see which
477 is fastest in a good spread of circumstances.
478 I'd expect IM_CFLINSEARCH to be fastest for very small palettes, and
479 IM_CFHASHBOX for large images with large palettes.
480
481 Some other possibilities include:
482 - search over entries sorted by luminance
483
484 Initially I was planning on testing using the macros and then
485 integrating the code directly into each function, but this means if
486 we find a bug at a late stage we will need to update N copies of
487 the same code. Also, keeping the code in the macros means that the
488 code in the translation functions is much more to the point,
489 there's no distracting colour search code to remove attention from
490 what makes _this_ translation function different. It may be
491 advisable to move the setup code into functions at some point, but
492 it should be possible to do this fairly transparently.
493
494 If IM_CF_COPTS is defined then CFLAGS must have an appropriate
495 definition.
496
497 Each option needs to define 4 macros:
498 CF_VARS - variables to define in the function
499 CF_SETUP - code to setup for the colour search, eg. allocating and
500 initializing lookup tables
501 CF_FIND - code that looks for the color in val and puts the best
502 matching index in bst_idx
503 CF_CLEANUP - code to clean up, eg. releasing memory
504*/
505#ifndef IM_CF_COPTS
506/*#define IM_CFLINSEARCH*/
507#define IM_CFHASHBOX
508/*#define IM_CFSORTCHAN*/
509/*#define IM_CFRAND2DIST*/
510#endif
511
512#ifdef IM_CFHASHBOX
513
514/* The original version I wrote for this used the sort.
515 If this is defined then we use a sort to extract the indices for
516 the hashbox */
517#define HB_SORT
518
519/* assume i is available */
520#define CF_VARS hashbox hb[512]; \
521 int currhb; \
522 long ld, cd
523
524#ifdef HB_SORT
525
526static long *gdists; /* qsort is annoying */
527/* int might be smaller than long, so we need to do a real compare
528 rather than a subtraction*/
529static int distcomp(void const *a, void const *b) {
530 long ra = gdists[*(int const *)a];
531 long rb = gdists[*(int const *)b];
532 if (ra < rb)
533 return -1;
534 else if (ra > rb)
535 return 1;
536 else
537 return 0;
538}
539
540#endif
541
542/* for each hashbox build a list of colours that are in the hb or is closer
543 than other colours
544 This is pretty involved. The original gifquant generated the hashbox
545 as part of it's normal processing, but since the map generation is now
546 separated from the translation we need to do this on the spot.
547 Any optimizations, even if they don't produce perfect results would be
548 welcome.
549 */
550static void hbsetup(i_quantize *quant, hashbox *hb) {
551 long *dists, mind, maxd, cd;
552 int cr, cb, cg, hbnum, i;
553 i_color cenc;
554#ifdef HB_SORT
555 int *indices = mymalloc(quant->mc_count * sizeof(int));
556#endif
557
558 dists = mymalloc(quant->mc_count * sizeof(long));
559 for (cr = 0; cr < 8; ++cr) {
560 for (cg = 0; cg < 8; ++cg) {
561 for (cb = 0; cb < 8; ++cb) {
562 /* centre of the hashbox */
563 cenc.channel[0] = cr*pboxjump+pboxjump/2;
564 cenc.channel[1] = cg*pboxjump+pboxjump/2;
565 cenc.channel[2] = cb*pboxjump+pboxjump/2;
566 hbnum = pixbox(&cenc);
567 hb[hbnum].cnt = 0;
568 /* order indices in the order of distance from the hashbox */
569 for (i = 0; i < quant->mc_count; ++i) {
570#ifdef HB_SORT
571 indices[i] = i;
572#endif
573 dists[i] = ceucl_d(&cenc, quant->mc_colors+i);
574 }
575#ifdef HB_SORT
576 /* it should be possible to do this without a sort
577 but so far I'm too lazy */
578 gdists = dists;
579 qsort(indices, quant->mc_count, sizeof(int), distcomp);
580 /* any colors that can match are within mind+diagonal size of
581 a hashbox */
582 mind = dists[indices[0]];
583 i = 0;
584 maxd = (sqrt(mind)+pboxjump)*(sqrt(mind)+pboxjump);
585 while (i < quant->mc_count && dists[indices[i]] < maxd) {
586 hb[hbnum].vec[hb[hbnum].cnt++] = indices[i++];
587 }
588#else
589 /* work out the minimum */
590 mind = 256*256*3;
591 for (i = 0; i < quant->mc_count; ++i) {
592 if (dists[i] < mind) mind = dists[i];
593 }
594 /* transfer any colours that might be closest to a colour in
595 this hashbox */
596 maxd = (sqrt(mind)+pboxjump)*(sqrt(mind)+pboxjump);
597 for (i = 0; i < quant->mc_count; ++i) {
598 if (dists[i] < maxd)
599 hb[hbnum].vec[hb[hbnum].cnt++] = i;
600 }
601#endif
602 }
603 }
604 }
605#ifdef HB_SORT
606 myfree(indices);
607#endif
608 myfree(dists) ;
609}
610#define CF_SETUP hbsetup(quant, hb)
611
612#define CF_FIND \
613 currhb = pixbox(&val); \
614 ld = 196608; \
615 for (i = 0; i < hb[currhb].cnt; ++i) { \
616 cd = ceucl_d(quant->mc_colors+hb[currhb].vec[i], &val); \
617 if (cd < ld) { ld = cd; bst_idx = hb[currhb].vec[i]; } \
618 }
619
620#define CF_CLEANUP
621
622#endif
623
624#ifdef IM_CFLINSEARCH
625/* as simple as it gets */
626#define CF_VARS long ld, cd
627#define CF_SETUP /* none needed */
628#define CF_FIND \
629 ld = 196608; \
630 for (i = 0; i < quant->mc_count; ++i) { \
631 cd = ceucl_d(quant->mc_colors+i, &val); \
632 if (cd < ld) { ld = cd; bst_idx = i; } \
633 }
634#define CF_CLEANUP
635#endif
636
637#ifdef IM_CFSORTCHAN
638static int gsortchan;
639static i_quantize *gquant;
640static int chansort(void const *a, void const *b) {
641 return gquant->mc_colors[*(int const *)a].channel[gsortchan] -
642 gquant->mc_colors[*(int const *)b].channel[gsortchan];
643}
644#define CF_VARS int *indices, sortchan, diff; \
645 long ld, cd; \
646 int vindex[256] /* where to find value i of chan */
647
648static void chansetup(i_img *img, i_quantize *quant, int *csortchan,
649 int *vindex, int **cindices) {
650 int *indices, sortchan, chan, i, chval;
651 int chanmins[MAXCHANNELS], chanmaxs[MAXCHANNELS], maxrange;
652
653 /* find the channel with the maximum range */
654 /* the maximum stddev would probably be better */
655 for (chan = 0; chan < img->channels; ++chan) {
656 chanmins[chan] = 256; chanmaxs[chan] = 0;
657 for (i = 0; i < quant->mc_count; ++i) {
658 if (quant->mc_colors[i].channel[chan] < chanmins[chan])
659 chanmins[chan] = quant->mc_colors[i].channel[chan];
660 if (quant->mc_colors[i].channel[chan] > chanmaxs[chan])
661 chanmaxs[chan] = quant->mc_colors[i].channel[chan];
662 }
663 }
664 maxrange = -1;
665 for (chan = 0; chan < img->channels; ++chan) {
666 if (chanmaxs[chan]-chanmins[chan] > maxrange) {
667 maxrange = chanmaxs[chan]-chanmins[chan];
668 sortchan = chan;
669 }
670 }
671 indices = mymalloc(quant->mc_count * sizeof(int)) ;
672 for (i = 0; i < quant->mc_count; ++i) {
673 indices[i] = i;
674 }
675 gsortchan = sortchan;
676 gquant = quant;
677 qsort(indices, quant->mc_count, sizeof(int), chansort) ;
678 /* now a lookup table to find entries faster */
679 for (chval=0, i=0; i < quant->mc_count; ++i) {
680 while (chval < 256 &&
681 chval < quant->mc_colors[indices[i]].channel[sortchan]) {
682 vindex[chval++] = i;
683 }
684 }
685 while (chval < 256) {
686 vindex[chval++] = quant->mc_count-1;
687 }
688 *csortchan = sortchan;
689 *cindices = indices;
690}
691
692#define CF_SETUP \
693 chansetup(img, quant, &sortchan, vindex, &indices)
694
695int chanfind(i_color val, i_quantize *quant, int *indices, int *vindex,
696 int sortchan) {
697 int i, bst_idx, diff, maxdiff;
698 long ld, cd;
699
700 i = vindex[val.channel[sortchan]];
701 bst_idx = indices[i];
702 ld = 196608;
703 diff = 0;
704 maxdiff = quant->mc_count;
705 while (diff < maxdiff) {
706 if (i+diff < quant->mc_count) {
707 cd = ceucl_d(&val, quant->mc_colors+indices[i+diff]);
708 if (cd < ld) {
709 bst_idx = indices[i+diff];
710 ld = cd;
711 maxdiff = sqrt(ld);
712 }
713 }
714 if (i-diff >= 0) {
715 cd = ceucl_d(&val, quant->mc_colors+indices[i-diff]);
716 if (cd < ld) {
717 bst_idx = indices[i-diff];
718 ld = cd;
719 maxdiff = sqrt(ld);
720 }
721 }
722 ++diff;
723 }
724
725 return bst_idx;
726}
727
728#define CF_FIND \
729 bst_idx = chanfind(val, quant, indices, vindex, sortchan)
730
731
732#define CF_CLEANUP myfree(indices)
733
734#endif
735
736#ifdef IM_CFRAND2DIST
737
738/* This is based on a method described by Addi in the #imager channel
739 on the 28/2/2001. I was about 1am Sydney time at the time, so I
740 wasn't at my most cogent. Well, that's my excuse :)
741
742<TonyC> what I have at the moment is: hashboxes, with optimum hash box
743filling; simple linear search; and a lookup in the widest channel
744(currently the channel with the maximum range)
745<Addi> There is one more way that might be simple to implement.
746<Addi> You want to hear?
747<TonyC> what's that?
748<purl> somebody said that was not true
749<Addi> For each of the colors in the palette start by creating a
750sorted list of the form:
751<Addi> [distance, color]
752<Addi> Where they are sorted by distance.
753<TonyC> distance to where?
754<Addi> Where the elements in the lists are the distances and colors of
755the other colors in the palette
756<TonyC> ok
757<Addi> So if you are at color 0
758<Addi> ok - now to search for the closest color when you are creating
759the final image is done like this:
760<Addi> a) pick a random color from the palette
761<Addi> b) calculate the distance to it
762<Addi> c) only check the vectors that are within double the distance
763in the list of the color you picked from the palette.
764<Addi> Does that seem logical?
765<Addi> Lets imagine that we only have grayscale to make an example:
766<Addi> Our palette has 1 4 10 20 as colors.
767<Addi> And we want to quantize the color 11
768<Addi> lets say we picked 10 randomly
769<Addi> the double distance is 2
770<Addi> since abs(10-11)*2 is 2
771<Addi> And the list at vector 10 is this:
772<Addi> [0, 10], [6 4], [9, 1], [10, 20]
773<Addi> so we look at the first one (but not the second one since 6 is
774at a greater distance than 2.
775<Addi> Any of that make sense?
776<TonyC> yes, though are you suggesting another random jump to one of
777the colours with the possible choices? or an exhaustive search?
778<Addi> TonyC: It's possible to come up with a recursive/iterative
779enhancement but this is the 'basic' version.
780<Addi> Which would do an iterative search.
781<Addi> You can come up with conditions where it pays to switch to a new one.
782<Addi> And the 'random' start can be switched over to a small tree.
783<Addi> So you would have a little index at the start.
784<Addi> to get you into the general direction
785<Addi> Perhaps just an 8 split.
786<Addi> that is - split each dimension in half.
787<TonyC> yep
788<TonyC> I get the idea
789<Addi> But this would seem to be a good approach in our case since we
790usually have few codevectors.
791<Addi> So we only need 256*256 entries in a table.
792<Addi> We could even only index some of them that were deemed as good
793candidates.
794<TonyC> I was considering adding paletted output support for PNG and
795TIFF at some point, which support 16-bit palettes
796<Addi> ohh.
797<Addi> 'darn' ;)
798
799
800*/
801
802
803typedef struct i_dists {
804 int index;
805 long dist;
806} i_dists;
807
808#define CF_VARS \
809 i_dists *dists;
810
811static int dists_sort(void const *a, void const *b) {
812 return ((i_dists *)a)->dist - ((i_dists *)b)->dist;
813}
814
815static void rand2dist_setup(i_quantize *quant, i_dists **cdists) {
816 i_dists *dists =
817 mymalloc(sizeof(i_dists)*quant->mc_count*quant->mc_count);
818 int i, j;
819 long cd;
820 for (i = 0; i < quant->mc_count; ++i) {
821 i_dists *ldists = dists + quant->mc_count * i;
822 i_color val = quant->mc_colors[i];
823 for (j = 0; j < quant->mc_count; ++j) {
824 ldists[j].index = j;
825 ldists[j].dist = ceucl_d(&val, quant->mc_colors+j);
826 }
827 qsort(ldists, quant->mc_count, sizeof(i_dists), dists_sort);
828 }
829 *cdists = dists;
830}
831
832#define CF_SETUP \
833 bst_idx = rand() % quant->mc_count; \
834 rand2dist_setup(quant, &dists)
835
836static int rand2dist_find(i_color val, i_quantize *quant, i_dists *dists, int index) {
837 i_dists *cdists;
838 long cd, ld;
839 long maxld;
840 int i;
841 int bst_idx;
842
843 cdists = dists + index * quant->mc_count;
844 ld = 3 * 256 * 256;
845 maxld = 8 * ceucl_d(&val, quant->mc_colors+index);
846 for (i = 0; i < quant->mc_count && cdists[i].dist <= maxld; ++i) {
847 cd = ceucl_d(&val, quant->mc_colors+cdists[i].index);
848 if (cd < ld) {
849 bst_idx = cdists[i].index;
850 ld = cd;
851 }
852 }
853 return bst_idx;
854}
855
856#define CF_FIND bst_idx = rand2dist_find(val, quant, dists, bst_idx)
857
858#define CF_CLEANUP myfree(dists)
859
860
861#endif
862
863static void translate_addi(i_quantize *quant, i_img *img, i_palidx *out) {
864 int x, y, i, k, bst_idx;
865 i_color val;
866 int pixdev = quant->perturb;
867 CF_VARS;
868
869 CF_SETUP;
870
871 if (pixdev) {
872 k=0;
873 for(y=0;y<img->ysize;y++) for(x=0;x<img->xsize;x++) {
874 i_gpix(img,x,y,&val);
875 val.channel[0]=g_sat(val.channel[0]+(int)(pixdev*frandn()));
876 val.channel[1]=g_sat(val.channel[1]+(int)(pixdev*frandn()));
877 val.channel[2]=g_sat(val.channel[2]+(int)(pixdev*frandn()));
878 CF_FIND;
879 out[k++]=bst_idx;
880 }
881 } else {
882 k=0;
883 for(y=0;y<img->ysize;y++) for(x=0;x<img->xsize;x++) {
884 i_gpix(img,x,y,&val);
885 CF_FIND;
886 out[k++]=bst_idx;
887 }
888 }
889 CF_CLEANUP;
890}
891
892static int floyd_map[] =
893{
894 0, 0, 7,
895 3, 5, 1
896};
897
898static int jarvis_map[] =
899{
900 0, 0, 0, 7, 5,
901 3, 5, 7, 5, 3,
902 1, 3, 5, 3, 1
903};
904
905static int stucki_map[] =
906{
907 0, 0, 0, 8, 4,
908 2, 4, 8, 4, 2,
909 1, 2, 4, 2, 1
910};
911
912struct errdiff_map {
913 int *map;
914 int width, height, orig;
915};
916
917static struct errdiff_map maps[] =
918{
919 { floyd_map, 3, 2, 1 },
920 { jarvis_map, 5, 3, 2 },
921 { stucki_map, 5, 3, 2 },
922};
923
924typedef struct errdiff_tag {
925 int r, g, b;
926} errdiff_t;
927
928/* perform an error diffusion dither */
929static
930void
931translate_errdiff(i_quantize *quant, i_img *img, i_palidx *out) {
932 int *map;
933 int mapw, maph, mapo;
934 int i;
935 errdiff_t *err;
936 int errw;
937 int difftotal;
938 int x, y, dx, dy;
939 int minr, maxr, ming, maxg, minb, maxb, cr, cg, cb;
940 i_color find;
941 int bst_idx;
942 CF_VARS;
943
944 if ((quant->errdiff & ed_mask) == ed_custom) {
945 map = quant->ed_map;
946 mapw = quant->ed_width;
947 maph = quant->ed_height;
948 mapo = quant->ed_orig;
949 }
950 else {
951 int index = quant->errdiff & ed_mask;
952 if (index >= ed_custom) index = ed_floyd;
953 map = maps[index].map;
954 mapw = maps[index].width;
955 maph = maps[index].height;
956 mapo = maps[index].orig;
957 }
958
959 errw = img->xsize+mapw;
960 err = mymalloc(sizeof(*err) * maph * errw);
961 /*errp = err+mapo;*/
962 memset(err, 0, sizeof(*err) * maph * errw);
963
964 difftotal = 0;
965 for (i = 0; i < maph * mapw; ++i)
966 difftotal += map[i];
967 /*printf("map:\n");
968 for (dy = 0; dy < maph; ++dy) {
969 for (dx = 0; dx < mapw; ++dx) {
970 printf("%2d", map[dx+dy*mapw]);
971 }
972 putchar('\n');
973 }*/
974
975 CF_SETUP;
976
977 for (y = 0; y < img->ysize; ++y) {
978 for (x = 0; x < img->xsize; ++x) {
979 i_color val;
980 long ld, cd;
981 errdiff_t perr;
982 i_gpix(img, x, y, &val);
983 perr = err[x+mapo];
984 perr.r = perr.r < 0 ? -((-perr.r)/difftotal) : perr.r/difftotal;
985 perr.g = perr.g < 0 ? -((-perr.g)/difftotal) : perr.g/difftotal;
986 perr.b = perr.b < 0 ? -((-perr.b)/difftotal) : perr.b/difftotal;
987 /*printf("x %3d y %3d in(%3d, %3d, %3d) di(%4d,%4d,%4d)\n", x, y, val.channel[0], val.channel[1], val.channel[2], perr.r, perr.g, perr.b);*/
988 val.channel[0] = g_sat(val.channel[0]-perr.r);
989 val.channel[1] = g_sat(val.channel[1]-perr.g);
990 val.channel[2] = g_sat(val.channel[2]-perr.b);
991 CF_FIND;
992 /* save error */
993 perr.r = quant->mc_colors[bst_idx].channel[0] - val.channel[0];
994 perr.g = quant->mc_colors[bst_idx].channel[1] - val.channel[1];
995 perr.b = quant->mc_colors[bst_idx].channel[2] - val.channel[2];
996 /*printf(" out(%3d, %3d, %3d) er(%4d, %4d, %4d)\n", quant->mc_colors[bst_idx].channel[0], quant->mc_colors[bst_idx].channel[1], quant->mc_colors[bst_idx].channel[2], perr.r, perr.g, perr.b);*/
997 for (dx = 0; dx < mapw; ++dx) {
998 for (dy = 0; dy < maph; ++dy) {
999 err[x+dx+dy*errw].r += perr.r * map[dx+mapw*dy];
1000 err[x+dx+dy*errw].g += perr.g * map[dx+mapw*dy];
1001 err[x+dx+dy*errw].b += perr.b * map[dx+mapw*dy];
1002 }
1003 }
1004 *out++ = bst_idx;
1005 }
1006 /* shift up the error matrix */
1007 for (dy = 0; dy < maph-1; ++dy) {
1008 memcpy(err+dy*errw, err+(dy+1)*errw, sizeof(*err)*errw);
1009 }
1010 memset(err+(maph-1)*errw, 0, sizeof(*err)*errw);
1011 }
1012 CF_CLEANUP;
1013}
1014/* Prescan finds the boxes in the image that have the highest number of colors
1015 and that result is used as the initial value for the vectores */
1016
1017
1018static void prescan(i_img **imgs,int count, int cnum, cvec *clr) {
1019 int i,k,j,x,y;
1020 i_color val;
1021
1022 pbox prebox[512];
1023 for(i=0;i<512;i++) {
1024 prebox[i].boxnum=i;
1025 prebox[i].pixcnt=0;
1026 prebox[i].cand=1;
1027 }
1028
1029 /* process each image */
1030 for (i = 0; i < count; ++i) {
1031 i_img *im = imgs[i];
1032 for(y=0;y<im->ysize;y++) for(x=0;x<im->xsize;x++) {
1033 i_gpix(im,x,y,&val);
1034 prebox[pixbox(&val)].pixcnt++;
1035 }
1036 }
1037
1038 for(i=0;i<512;i++) prebox[i].pdc=prebox[i].pixcnt;
1039 qsort(prebox,512,sizeof(pbox),(cmpfunc)pboxcmp);
1040
1041 for(i=0;i<cnum;i++) {
1042 /* printf("Color %d\n",i);
1043 for(k=0;k<10;k++) { printf("box=%03d %04d %d %04d \n",prebox[k].boxnum,prebox[k].pixcnt,prebox[k].cand,prebox[k].pdc); }
1044 printf("\n\n"); */
1045 reorder(prebox);
1046 }
1047
1048 /* for(k=0;k<cnum;k++) { printf("box=%03d %04d %d %04d \n",prebox[k].boxnum,prebox[k].pixcnt,prebox[k].cand,prebox[k].pdc); } */
1049
1050 k=0;
1051 j=1;
1052 i=0;
1053 while(i<cnum) {
1054 /* printf("prebox[%d].cand=%d\n",k,prebox[k].cand); */
1055 if (clr[i].state) { i++; continue; } /* reserved go to next */
1056 if (j>=prebox[k].cand) { k++; j=1; } else {
1057 if (prebox[k].cand == 2) boxcenter(prebox[k].boxnum,&(clr[i]));
1058 else boxrand(prebox[k].boxnum,&(clr[i]));
1059 /* printf("(%d,%d) %d %d -> (%d,%d,%d)\n",k,j,prebox[k].boxnum,prebox[k].pixcnt,clr[i].r,clr[i].g,clr[i].b); */
1060 j++;
1061 i++;
1062 }
1063 }
1064}
1065
1066
1067static void reorder(pbox prescan[512]) {
1068 int nidx;
1069 pbox c;
1070
1071 nidx=0;
1072 c=prescan[0];
1073
1074 c.cand++;
1075 c.pdc=c.pixcnt/(c.cand*c.cand);
1076 /* c.pdc=c.pixcnt/c.cand; */
1077 while(c.pdc < prescan[nidx+1].pdc && nidx < 511) {
1078 prescan[nidx]=prescan[nidx+1];
1079 nidx++;
1080 }
1081 prescan[nidx]=c;
1082}
1083
1084static int
1085pboxcmp(const pbox *a,const pbox *b) {
1086 if (a->pixcnt > b->pixcnt) return -1;
1087 if (a->pixcnt < b->pixcnt) return 1;
1088 return 0;
1089}
1090
1091static void
1092boxcenter(int box,cvec *cv) {
1093 cv->r=15+((box&448)>>1);
1094 cv->g=15+((box&56)<<2);
1095 cv->b=15+((box&7)<<5);
1096}
1097
1098static void
1099bbox(int box,int *r0,int *r1,int *g0,int *g1,int *b0,int *b1) {
1100 *r0=(box&448)>>1;
1101 *r1=(*r0)|31;
1102 *g0=(box&56)<<2;
1103 *g1=(*g0)|31;
1104 *b0=(box&7)<<5;
1105 *b1=(*b0)|31;
1106}
1107
1108static void
1109boxrand(int box,cvec *cv) {
1110 cv->r=6+(rand()%25)+((box&448)>>1);
1111 cv->g=6+(rand()%25)+((box&56)<<2);
1112 cv->b=6+(rand()%25)+((box&7)<<5);
1113}
1114
1115static float
1116frandn(void) {
1117
1118 float u1,u2,w;
1119
1120 w=1;
1121
1122 while (w >= 1 || w == 0) {
1123 u1 = 2 * frand() - 1;
1124 u2 = 2 * frand() - 1;
1125 w = u1*u1 + u2*u2;
1126 }
1127
1128 w = sqrt((-2*log(w))/w);
1129 return u1*w;
1130}
1131
1132/* Create hash index */
1133static
1134void
1135cr_hashindex(cvec clr[256],int cnum,hashbox hb[512]) {
1136
1137 int bx,mind,cd,cumcnt,bst_idx,i;
1138/* printf("indexing... \n");*/
1139
1140 cumcnt=0;
1141 for(bx=0; bx<512; bx++) {
1142 mind=196608;
1143 for(i=0; i<cnum; i++) {
1144 cd = maxdist(bx,&clr[i]);
1145 if (cd < mind) { mind=cd; bst_idx=i; }
1146 }
1147
1148 hb[bx].cnt=0;
1149 for(i=0;i<cnum;i++) if (mindist(bx,&clr[i])<mind) hb[bx].vec[hb[bx].cnt++]=i;
1150 /*printf("box %d -> approx -> %d\n",bx,hb[bx].cnt); */
1151 /* statbox(bx,cnum,clr); */
1152 cumcnt+=hb[bx].cnt;
1153 }
1154
1155/* printf("Average search space: %d\n",cumcnt/512); */
1156}
1157
1158static int
1159maxdist(int boxnum,cvec *cv) {
1160 int r0,r1,g0,g1,b0,b1;
1161 int r,g,b,mr,mg,mb;
1162
1163 r=cv->r;
1164 g=cv->g;
1165 b=cv->b;
1166
1167 bbox(boxnum,&r0,&r1,&g0,&g1,&b0,&b1);
1168
1169 mr=max(abs(b-b0),abs(b-b1));
1170 mg=max(abs(g-g0),abs(g-g1));
1171 mb=max(abs(r-r0),abs(r-r1));
1172
1173 return PWR2(mr)+PWR2(mg)+PWR2(mb);
1174}
1175
1176static int
1177mindist(int boxnum,cvec *cv) {
1178 int r0,r1,g0,g1,b0,b1;
1179 int r,g,b,mr,mg,mb;
1180
1181 r=cv->r;
1182 g=cv->g;
1183 b=cv->b;
1184
1185 bbox(boxnum,&r0,&r1,&g0,&g1,&b0,&b1);
1186
1187 /* printf("box %d, (%d,%d,%d)-(%d,%d,%d) vec (%d,%d,%d) ",boxnum,r0,g0,b0,r1,g1,b1,r,g,b); */
1188
1189 if (r0<=r && r<=r1 && g0<=g && g<=g1 && b0<=b && b<=b1) return 0;
1190
1191 mr=min(abs(b-b0),abs(b-b1));
1192 mg=min(abs(g-g0),abs(g-g1));
1193 mb=min(abs(r-r0),abs(r-r1));
1194
1195 mr=PWR2(mr);
1196 mg=PWR2(mg);
1197 mb=PWR2(mb);
1198
1199 if (r0<=r && r<=r1 && g0<=g && g<=g1) return mb;
1200 if (r0<=r && r<=r1 && b0<=b && b<=b1) return mg;
1201 if (b0<=b && b<=b1 && g0<=g && g<=g1) return mr;
1202
1203 if (r0<=r && r<=r1) return mg+mb;
1204 if (g0<=g && g<=g1) return mr+mb;
1205 if (b0<=b && b<=b1) return mg+mr;
1206
1207 return mr+mg+mb;
1208}
1209
1210static void transparent_threshold(i_quantize *, i_palidx *, i_img *, i_palidx);
1211static void transparent_errdiff(i_quantize *, i_palidx *, i_img *, i_palidx);
1212static void transparent_ordered(i_quantize *, i_palidx *, i_img *, i_palidx);
1213
1214void quant_transparent(i_quantize *quant, i_palidx *data, i_img *img,
1215 i_palidx trans_index)
1216{
1217 switch (quant->transp) {
1218 case tr_none:
1219 break;
1220
1221 default:
1222 quant->tr_threshold = 128;
1223 /* fall through */
1224 case tr_threshold:
1225 transparent_threshold(quant, data, img, trans_index);
1226 break;
1227
1228 case tr_errdiff:
1229 transparent_errdiff(quant, data, img, trans_index);
1230 break;
1231
1232 case tr_ordered:
1233 transparent_ordered(quant, data, img, trans_index);
1234 break;
1235 }
1236}
1237
1238static void
1239transparent_threshold(i_quantize *quant, i_palidx *data, i_img *img,
1240 i_palidx trans_index)
1241{
1242 int x, y;
1243
1244 for (y = 0; y < img->ysize; ++y) {
1245 for (x = 0; x < img->xsize; ++x) {
1246 i_color val;
1247 i_gpix(img, x, y, &val);
1248 if (val.rgba.a < quant->tr_threshold)
1249 data[y*img->xsize+x] = trans_index;
1250 }
1251 }
1252}
1253
1254static void
1255transparent_errdiff(i_quantize *quant, i_palidx *data, i_img *img,
1256 i_palidx trans_index)
1257{
1258 int *map;
1259 int index;
1260 int mapw, maph, mapo;
1261 int errw, *err, *errp;
1262 int difftotal, out, error;
1263 int x, y, dx, dy, i;
1264
1265 /* no custom map for transparency (yet) */
1266 index = quant->tr_errdiff & ed_mask;
1267 if (index >= ed_custom) index = ed_floyd;
1268 map = maps[index].map;
1269 mapw = maps[index].width;
1270 maph = maps[index].height;
1271 mapo = maps[index].orig;
1272
1273 errw = img->xsize+mapw-1;
1274 err = mymalloc(sizeof(*err) * maph * errw);
1275 errp = err+mapo;
1276 memset(err, 0, sizeof(*err) * maph * errw);
1277
1278 difftotal = 0;
1279 for (i = 0; i < maph * mapw; ++i)
1280 difftotal += map[i];
1281 for (y = 0; y < img->ysize; ++y) {
1282 for (x = 0; x < img->xsize; ++x) {
1283 i_color val;
1284 i_gpix(img, x, y, &val);
1285 val.rgba.a = g_sat(val.rgba.a-errp[x]/difftotal);
1286 if (val.rgba.a < 128) {
1287 out = 0;
1288 data[y*img->xsize+x] = trans_index;
1289 }
1290 else {
1291 out = 255;
1292 }
1293 error = out - val.rgba.a;
1294 for (dx = 0; dx < mapw; ++dx) {
1295 for (dy = 0; dy < maph; ++dy) {
1296 errp[x+dx-mapo+dy*errw] += error * map[dx+mapw*dy];
1297 }
1298 }
1299 }
1300 /* shift up the error matrix */
1301 for (dy = 0; dy < maph-1; ++dy)
1302 memcpy(err+dy*errw, err+(dy+1)*errw, sizeof(*err)*errw);
1303 memset(err+(maph-1)*errw, 0, sizeof(*err)*errw);
1304 }
1305}
1306
1307/* builtin ordered dither maps */
1308unsigned char orddith_maps[][64] =
1309{
1310 { /* random
1311 this is purely random - it's pretty awful
1312 */
1313 48, 72, 196, 252, 180, 92, 108, 52,
1314 228, 176, 64, 8, 236, 40, 20, 164,
1315 120, 128, 84, 116, 24, 28, 172, 220,
1316 68, 0, 188, 124, 184, 224, 192, 104,
1317 132, 100, 240, 200, 152, 160, 244, 44,
1318 96, 204, 144, 16, 140, 56, 232, 216,
1319 208, 4, 76, 212, 136, 248, 80, 168,
1320 156, 88, 32, 112, 148, 12, 36, 60,
1321 },
1322 {
1323 /* dot8
1324 perl spot.perl '($x-3.5)*($x-3.5)+($y-3.5)*($y-3.5)'
1325 */
1326 240, 232, 200, 136, 140, 192, 228, 248,
1327 220, 148, 100, 76, 80, 104, 152, 212,
1328 180, 116, 56, 32, 36, 60, 120, 176,
1329 156, 64, 28, 0, 8, 44, 88, 160,
1330 128, 92, 24, 12, 4, 40, 68, 132,
1331 184, 96, 48, 20, 16, 52, 108, 188,
1332 216, 144, 112, 72, 84, 124, 164, 224,
1333 244, 236, 196, 168, 172, 204, 208, 252,
1334 },
1335 { /* dot4
1336 perl spot.perl \
1337 'min(dist(1.5, 1.5),dist(5.5,1.5),dist(1.5,5.5),dist(5.5,5.5))'
1338 */
1339 196, 72, 104, 220, 200, 80, 112, 224,
1340 76, 4, 24, 136, 84, 8, 32, 144,
1341 108, 28, 52, 168, 116, 36, 56, 176,
1342 216, 140, 172, 244, 228, 148, 180, 248,
1343 204, 92, 124, 236, 192, 68, 96, 208,
1344 88, 12, 44, 156, 64, 0, 16, 128,
1345 120, 40, 60, 188, 100, 20, 48, 160,
1346 232, 152, 184, 252, 212, 132, 164, 240,
1347 },
1348 { /* hline
1349 perl spot.perl '$y-3'
1350 */
1351 160, 164, 168, 172, 176, 180, 184, 188,
1352 128, 132, 136, 140, 144, 148, 152, 156,
1353 32, 36, 40, 44, 48, 52, 56, 60,
1354 0, 4, 8, 12, 16, 20, 24, 28,
1355 64, 68, 72, 76, 80, 84, 88, 92,
1356 96, 100, 104, 108, 112, 116, 120, 124,
1357 192, 196, 200, 204, 208, 212, 216, 220,
1358 224, 228, 232, 236, 240, 244, 248, 252,
1359 },
1360 { /* vline
1361 perl spot.perl '$x-3'
1362 */
1363 180, 100, 40, 12, 44, 104, 184, 232,
1364 204, 148, 60, 16, 64, 128, 208, 224,
1365 212, 144, 76, 8, 80, 132, 216, 244,
1366 160, 112, 68, 20, 84, 108, 172, 236,
1367 176, 96, 72, 28, 88, 152, 188, 228,
1368 200, 124, 92, 0, 32, 116, 164, 240,
1369 168, 120, 36, 24, 48, 136, 192, 248,
1370 196, 140, 52, 4, 56, 156, 220, 252,
1371 },
1372 { /* slashline
1373 perl spot.perl '$y+$x-7'
1374 */
1375 248, 232, 224, 192, 140, 92, 52, 28,
1376 240, 220, 196, 144, 108, 60, 12, 64,
1377 216, 180, 148, 116, 76, 20, 80, 128,
1378 204, 152, 104, 44, 16, 72, 100, 160,
1379 164, 96, 68, 24, 56, 112, 168, 176,
1380 124, 40, 8, 36, 88, 136, 184, 212,
1381 84, 4, 32, 120, 156, 188, 228, 236,
1382 0, 48, 132, 172, 200, 208, 244, 252,
1383 },
1384 { /* backline
1385 perl spot.perl '$y-$x'
1386 */
1387 0, 32, 116, 172, 184, 216, 236, 252,
1388 56, 8, 72, 132, 136, 200, 228, 240,
1389 100, 36, 12, 40, 92, 144, 204, 220,
1390 168, 120, 60, 16, 44, 96, 156, 176,
1391 180, 164, 112, 48, 28, 52, 128, 148,
1392 208, 192, 152, 88, 84, 20, 64, 104,
1393 232, 224, 196, 140, 108, 68, 24, 76,
1394 248, 244, 212, 188, 160, 124, 80, 4,
1395 },
1396};
1397
1398static void
1399transparent_ordered(i_quantize *quant, i_palidx *data, i_img *img,
1400 i_palidx trans_index)
1401{
1402 unsigned char *spot;
1403 int x, y;
1404 if (quant->tr_orddith == od_custom)
1405 spot = quant->tr_custom;
1406 else
1407 spot = orddith_maps[quant->tr_orddith];
1408 for (y = 0; y < img->ysize; ++y) {
1409 for (x = 0; x < img->xsize; ++x) {
1410 i_color val;
1411 i_gpix(img, x, y, &val);
1412 if (val.rgba.a < spot[(x&7)+(y&7)*8])
1413 data[x+y*img->xsize] = trans_index;
1414 }
1415 }
1416}