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
7 static void makemap_addi(i_quantize *, i_img **imgs, int count);
8 static void makemap_mediancut(i_quantize *, i_img **imgs, int count);
12 setcol(i_color *cl,unsigned char r,unsigned char g,unsigned char b,unsigned char a) {
21 /* make a colour map overwrites mc_existing/mc_count in quant Note
22 that i_makemap will be called once for each image if mc_perimage is
23 set and the format support multiple colour maps per image.
25 This means we don't need any special processing at this level to
26 handle multiple colour maps.
30 quant_makemap(i_quantize *quant, i_img **imgs, int count) {
32 if (quant->translate == pt_giflib) {
33 /* giflib does it's own color table generation */
34 /* previously we used giflib's quantizer, but it didn't handle multiple
35 images, which made it hard to build a global color map
36 We've implemented our own median cut code so we can ignore
38 makemap_mediancut(quant, imgs, count);
42 switch (quant->make_colors & mc_mask) {
44 /* use user's specified map */
50 for (r = 0; r < 256; r+=0x33)
51 for (g = 0; g < 256; g+=0x33)
52 for (b = 0; b < 256; b += 0x33)
53 setcol(quant->mc_colors+i++, r, g, b, 255);
59 makemap_mediancut(quant, imgs, count);
64 makemap_addi(quant, imgs, count);
70 static void translate_giflib(i_quantize *, i_img *, i_palidx *);
72 static void translate_closest(i_quantize *, i_img *, i_palidx *);
73 static void translate_errdiff(i_quantize *, i_img *, i_palidx *);
74 static void translate_addi(i_quantize *, i_img *, i_palidx *);
76 /* Quantize the image given the palette in quant.
78 The giflib quantizer ignores the palette.
80 i_palidx *quant_translate(i_quantize *quant, i_img *img) {
82 mm_log((1, "quant_translate(quant %p, img %p)\n", quant, img));
84 result = mymalloc(img->xsize * img->ysize);
86 switch (quant->translate) {
89 translate_closest(quant, img, result);
93 translate_errdiff(quant, img, result);
98 translate_addi(quant, img, result);
105 #ifdef HAVE_LIBGIF_THIS_NOT_USED
109 #define GET_RGB(im, x, y, ri, gi, bi, col) \
110 i_gpix((im),(x),(y),&(col)); (ri)=(col).rgb.r; \
111 if((im)->channels==3) { (bi)=(col).rgb.b; (gi)=(col).rgb.g; }
114 quant_replicate(i_img *im, i_palidx *output, i_quantize *quant);
117 /* Use the gif_lib quantization functions to quantize the image */
118 static void translate_giflib(i_quantize *quant, i_img *img, i_palidx *out) {
119 int x,y,ColorMapSize,colours_in;
123 GifByteType *RedBuffer = NULL, *GreenBuffer = NULL, *BlueBuffer = NULL;
124 GifByteType *RedP, *GreenP, *BlueP;
125 ColorMapObject *OutputColorMap = NULL;
129 mm_log((1,"translate_giflib(quant %p, img %p, out %p)\n", quant, img, out));
131 /*if (!(im->channels==1 || im->channels==3)) { fprintf(stderr,"Unable to write gif, improper colorspace.\n"); exit(3); }*/
133 ColorMapSize = quant->mc_size;
135 Size = ((long) img->xsize) * img->ysize * sizeof(GifByteType);
138 if ((OutputColorMap = MakeMapObject(ColorMapSize, NULL)) == NULL)
139 m_fatal(0,"Failed to allocate memory for Output colormap.");
140 /* if ((OutputBuffer = (GifByteType *) mymalloc(im->xsize * im->ysize * sizeof(GifByteType))) == NULL)
141 m_fatal(0,"Failed to allocate memory for output buffer.");*/
143 /* ******************************************************* */
144 /* count the number of colours in the image */
145 colours_in=i_count_colors(img, OutputColorMap->ColorCount);
147 if(colours_in != -1) { /* less then the number wanted */
148 /* so we copy them over as-is */
149 mm_log((2,"image has %d colours, which fits in %d. Copying\n",
150 colours_in,ColorMapSize));
151 quant_replicate(img, out, quant);
152 /* saves the colors, so don't fall through */
156 mm_log((2,"image has %d colours, more then %d. Quantizing\n",colours_in,ColorMapSize));
158 if (img->channels >= 3) {
159 if ((RedBuffer = (GifByteType *) mymalloc((unsigned int) Size)) == NULL) {
160 m_fatal(0,"Failed to allocate memory required, aborted.");
163 if ((GreenBuffer = (GifByteType *) mymalloc((unsigned int) Size)) == NULL) {
164 m_fatal(0,"Failed to allocate memory required, aborted.");
169 if ((BlueBuffer = (GifByteType *) mymalloc((unsigned int) Size)) == NULL) {
170 m_fatal(0,"Failed to allocate memory required, aborted.");
177 GreenP = GreenBuffer;
180 for (y=0; y< img->ysize; y++) for (x=0; x < img->xsize; x++) {
181 i_gpix(img,x,y,&col);
183 *GreenP++ = col.rgb.g;
184 *BlueP++ = col.rgb.b;
189 if ((RedBuffer = (GifByteType *) mymalloc((unsigned int) Size))==NULL) {
190 m_fatal(0,"Failed to allocate memory required, aborted.");
194 GreenBuffer=BlueBuffer=RedBuffer;
196 for (y=0; y< img->ysize; y++) for (x=0; x < img->xsize; x++) {
197 i_gpix(img,x,y,&col);
202 if (QuantizeBuffer(img->xsize, img->ysize, &ColorMapSize, RedBuffer, GreenBuffer, BlueBuffer,
203 out, OutputColorMap->Colors) == GIF_ERROR) {
204 mm_log((1,"Error in QuantizeBuffer, unable to write image.\n"));
209 if (img->channels == 3) { myfree(GreenBuffer); myfree(BlueBuffer); }
211 /* copy over the color map */
212 for (i = 0; i < ColorMapSize; ++i) {
213 quant->mc_colors[i].rgb.r = OutputColorMap->Colors[i].Red;
214 quant->mc_colors[i].rgb.g = OutputColorMap->Colors[i].Green;
215 quant->mc_colors[i].rgb.b = OutputColorMap->Colors[i].Blue;
217 quant->mc_count = ColorMapSize;
222 quant_replicate(i_img *im, GifByteType *output, i_quantize *quant) {
223 int x, y, alloced, r, g=0, b=0, idx ;
227 for(y=0; y<im->ysize; y++) {
228 for(x=0; x<im->xsize; x++) {
230 GET_RGB(im, x,y, r,g,b, col);
232 for(idx=0; idx<alloced; idx++) { /* linear search for an index */
233 if(quant->mc_colors[idx].rgb.r==r &&
234 quant->mc_colors[idx].rgb.g==g &&
235 quant->mc_colors[idx].rgb.b==b) {
240 if(idx >= alloced) { /* if we haven't already, we */
241 idx=alloced++; /* add the colour to the map */
242 if(quant->mc_size < alloced) {
243 mm_log((1,"Tried to allocate more then %d colours.\n",
247 quant->mc_colors[idx].rgb.r=r;
248 quant->mc_colors[idx].rgb.g=g;
249 quant->mc_colors[idx].rgb.b=b;
251 *output=idx; /* fill output buffer */
252 output++; /* with colour indexes */
255 quant->mc_count = alloced;
261 static void translate_closest(i_quantize *quant, i_img *img, i_palidx *out) {
263 translate_addi(quant, img, out);
266 #define PWR2(x) ((x)*(x))
268 typedef int (*cmpfunc)(const void*, const void*);
291 static void prescan(i_img **im,int count, int cnum, cvec *clr, i_sample_t *line);
292 static void reorder(pbox prescan[512]);
293 static int pboxcmp(const pbox *a,const pbox *b);
294 static void boxcenter(int box,cvec *cv);
295 static float frandn(void);
296 static void boxrand(int box,cvec *cv);
297 static void bbox(int box,int *r0,int *r1,int *g0,int *g1,int *b0,int *b1);
298 static void cr_hashindex(cvec clr[256],int cnum,hashbox hb[512]);
299 static int mindist(int boxnum,cvec *cv);
300 static int maxdist(int boxnum,cvec *cv);
302 /* Some of the simpler functions are kept here to aid the compiler -
303 maybe some of them will be inlined. */
306 pixbox(i_color *ic) { return ((ic->channel[0] & 224)<<1)+ ((ic->channel[1]&224)>>2) + ((ic->channel[2] &224) >> 5); }
309 pixbox_ch(i_sample_t *chans) { return ((chans[0] & 224)<<1)+ ((chans[1]&224)>>2) + ((chans[2] &224) >> 5); }
313 if (in>255) { return 255; }
314 else if (in>0) return in;
321 return rand()/(RAND_MAX+1.0);
326 eucl_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]); }
330 eucl_d_ch(cvec* cv,i_sample_t *chans) {
331 return PWR2(cv->r - chans[0]) + PWR2(cv->g - chans[1])
332 + PWR2(cv->b - chans[2]);
337 ceucl_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]); }
340 gray_samples[] = { 0, 0, 0 };
344 This quantization algorithm and implementation routines are by Arnar
345 M. Hrafnkelson. In case any new ideas are here they are mine since
346 this was written from scratch.
348 The algorithm uses local means in the following way:
350 For each point in the colormap we find which image points
351 have that point as it's closest point. We calculate the mean
352 of those points and in the next iteration it will be the new
353 entry in the colormap.
355 In order to speed this process up (i.e. nearest neighbor problem) We
356 divied the r,g,b space up in equally large 512 boxes. The boxes are
357 numbered from 0 to 511. Their numbering is so that for a given vector
358 it is known that it belongs to the box who is formed by concatenating the
359 3 most significant bits from each component of the RGB triplet.
361 For each box we find the list of points from the colormap who might be
362 closest to any given point within the box. The exact solution
363 involves finding the Voronoi map (or the dual the Delauny
364 triangulation) and has many issues including numerical stability.
366 So we use this approximation:
368 1. Find which point has the shortest maximum distance to the box.
369 2. Find all points that have a shorter minimum distance than that to the box
371 This is a very simple task and is not computationally heavy if one
372 takes into account that the minimum distances from a pixel to a box is
373 always found by checking if it's inside the box or is closest to some
374 side or a corner. Finding the maximum distance is also either a side
377 This approach results 2-3 times more than the actual points needed but
378 is still a good gain over the complete space. Usually when one has a
379 256 Colorcolor map a search over 30 is often obtained.
381 A bit of an enhancement to this approach is to keep a seperate list
382 for each side of the cube, but this will require even more memory.
384 Arnar M. Hrafnkelsson (addi@umich.edu);
388 Extracted from gifquant.c, removed dependencies on gif_lib,
389 and added support for multiple images.
390 starting from 1nov2000 by TonyC <tony@develop-help.com>.
395 makemap_addi(i_quantize *quant, i_img **imgs, int count) {
397 int cnum, i, x, y, bst_idx=0, ld, cd, iter, currhb, img_num;
404 const int *sample_indices;
408 clr = i_mempool_alloc(&mp, sizeof(cvec) * quant->mc_size);
409 hb = i_mempool_alloc(&mp, sizeof(hashbox) * 512);
410 for (i=0; i < quant->mc_count; ++i) {
411 clr[i].r = quant->mc_colors[i].rgb.r;
412 clr[i].g = quant->mc_colors[i].rgb.g;
413 clr[i].b = quant->mc_colors[i].rgb.b;
417 /* mymalloc doesn't clear memory, so I think we need this */
418 for (; i < quant->mc_size; ++i) {
422 cnum = quant->mc_size;
425 for (img_num = 0; img_num < count; ++img_num) {
426 if (imgs[img_num]->xsize > maxwidth)
427 maxwidth = imgs[img_num]->xsize;
429 line = i_mempool_alloc(&mp, 3 * maxwidth * sizeof(*line));
431 prescan(imgs, count, cnum, clr, line);
432 cr_hashindex(clr, cnum, hb);
434 for(iter=0;iter<3;iter++) {
437 for (img_num = 0; img_num < count; ++img_num) {
438 i_img *im = imgs[img_num];
439 sample_indices = im->channels >= 3 ? NULL : gray_samples;
440 for(y=0;y<im->ysize;y++) {
441 i_gsamp(im, 0, im->xsize, y, line, sample_indices, 3);
443 for(x=0;x<im->xsize;x++) {
445 /*i_gpix(im,x,y,&val);*/
446 currhb=pixbox_ch(val);
447 /* printf("box = %d \n",currhb); */
448 for(i=0;i<hb[currhb].cnt;i++) {
449 /* 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); */
451 cd=eucl_d_ch(&clr[hb[currhb].vec[i]],val);
453 ld=cd; /* shortest distance yet */
454 bst_idx=hb[currhb].vec[i]; /* index of closest vector yet */
458 clr[bst_idx].mcount++;
460 clr[bst_idx].dr+=val[0];
461 clr[bst_idx].dg+=val[1];
462 clr[bst_idx].db+=val[2];
464 val += 3; /* next 3 samples (next pixel) */
471 clr[i].dr/=clr[i].mcount;
472 clr[i].dg/=clr[i].mcount;
473 clr[i].db/=clr[i].mcount;
476 /* for(i=0;i<cnum;i++) printf("vec(%d)=(%d,%d,%d) dest=(%d,%d,%d) matchcount=%d\n",
477 i,clr[i].r,clr[i].g,clr[i].b,clr[i].dr,clr[i].dg,clr[i].db,clr[i].mcount); */
479 /* printf("total error: %.2f\n",sqrt(accerr)); */
481 for(i=0;i<cnum;i++) {
482 if (clr[i].fixed) continue; /* skip reserved colors */
486 clr[i].r=clr[i].r*(1-dlt)+dlt*clr[i].dr;
487 clr[i].g=clr[i].g*(1-dlt)+dlt*clr[i].dg;
488 clr[i].b=clr[i].b*(1-dlt)+dlt*clr[i].db;
490 /* let's try something else */
502 cr_hashindex(clr,cnum,hb);
507 for(i=0;i<cnum;i++) {
508 cd=eucl_d(&clr[i],&val);
516 /* if defined, we only include colours with an mcount or that were
517 supplied in the fixed palette, giving us a smaller output palette */
518 #define ONLY_USE_USED
520 /* transfer the colors back */
522 for (i = 0; i < cnum; ++i) {
523 if (clr[i].fixed || clr[i].used) {
524 /*printf("Adding %d (%d,%d,%d)\n", i, clr[i].r, clr[i].g, clr[i].b);*/
525 quant->mc_colors[quant->mc_count].rgb.r = clr[i].r;
526 quant->mc_colors[quant->mc_count].rgb.g = clr[i].g;
527 quant->mc_colors[quant->mc_count].rgb.b = clr[i].b;
532 /* transfer the colors back */
533 for (i = 0; i < cnum; ++i) {
534 quant->mc_colors[i].rgb.r = clr[i].r;
535 quant->mc_colors[i].rgb.g = clr[i].g;
536 quant->mc_colors[i].rgb.b = clr[i].b;
538 quant->mc_count = cnum;
541 i_mempool_destroy(&mp);
549 #define MEDIAN_CUT_COLORS 32768
551 #define MED_CUT_INDEX(c) ((((c).rgb.r & 0xF8) << 7) | \
552 (((c).rgb.g & 0xF8) << 2) | (((c).rgb.b & 0xF8) >> 3))
554 #define MED_CUT_GRAY_INDEX(c) ((((c).rgb.r & 0xF8) << 7) | \
555 (((c).rgb.r & 0xF8) << 2) | (((c).rgb.r & 0xF8) >> 3))
557 /* scale these to cover the whole range */
558 #define MED_CUT_RED(index) ((((index) & 0x7C00) >> 10) * 255 / 31)
559 #define MED_CUT_GREEN(index) ((((index) & 0x3E0) >> 5) * 255 / 31)
560 #define MED_CUT_BLUE(index) (((index) & 0x1F) * 255 / 31)
563 i_sample_t min[3]; /* minimum for each channel */
564 i_sample_t max[3]; /* maximum for each channel */
565 i_sample_t width[3]; /* width for each channel */
566 int start, size; /* beginning and size of the partition */
567 int pixels; /* number of pixels represented by this partition */
571 =item calc_part(part, colors)
573 Calculates the new color limits for the given partition.
575 Giflib assumes that the limits for the non-split channels stay the
576 same, but this strikes me as incorrect, especially if the colors tend
579 Of course this could be optimized by not recalculating the channel we
580 just sorted on, but it's not worth the effort right now.
584 static void calc_part(medcut_partition *part, quant_color_entry *colors) {
587 for (ch = 0; ch < 3; ++ch) {
591 for (i = part->start; i < part->start + part->size; ++i) {
592 for (ch = 0; ch < 3; ++ch) {
593 if (part->min[ch] > colors[i].rgb[ch])
594 part->min[ch] = colors[i].rgb[ch];
595 if (part->max[ch] < colors[i].rgb[ch])
596 part->max[ch] = colors[i].rgb[ch];
599 for (ch = 0; ch < 3; ++ch) {
600 part->width[ch] = part->max[ch] - part->min[ch];
604 /* simple functions to sort by each channel - we could use a global, but
608 color_sort_red(void const *left, void const *right) {
609 return ((quant_color_entry *)left)->rgb[0] - ((quant_color_entry *)right)->rgb[0];
613 color_sort_green(void const *left, void const *right) {
614 return ((quant_color_entry *)left)->rgb[1] - ((quant_color_entry *)right)->rgb[1];
618 color_sort_blue(void const *left, void const *right) {
619 return ((quant_color_entry *)left)->rgb[2] - ((quant_color_entry *)right)->rgb[2];
622 static int (*sorters[])(void const *, void const *) =
630 makemap_mediancut(i_quantize *quant, i_img **imgs, int count) {
631 quant_color_entry *colors;
633 int imgn, x, y, i, ch;
638 medcut_partition *parts;
641 /* number of channels we search for the best channel to partition
642 this isn't terribly efficient, but it should work */
645 /*printf("images %d pal size %d\n", count, quant->mc_size);*/
649 colors = i_mempool_alloc(&mp, sizeof(*colors) * MEDIAN_CUT_COLORS);
650 for (i = 0; i < MEDIAN_CUT_COLORS; ++i) {
651 colors[i].rgb[0] = MED_CUT_RED(i);
652 colors[i].rgb[1] = MED_CUT_GREEN(i);
653 colors[i].rgb[2] = MED_CUT_BLUE(i);
658 for (imgn = 0; imgn < count; ++imgn) {
659 if (imgs[imgn]->xsize > max_width)
660 max_width = imgs[imgn]->xsize;
662 line = i_mempool_alloc(&mp, sizeof(i_color) * max_width);
664 /* build the stats */
666 chan_count = 1; /* assume we just have grayscale */
667 for (imgn = 0; imgn < count; ++imgn) {
668 total_pixels += imgs[imgn]->xsize * imgs[imgn]->ysize;
669 for (y = 0; y < imgs[imgn]->ysize; ++y) {
670 i_glin(imgs[imgn], 0, imgs[imgn]->xsize, y, line);
671 if (imgs[imgn]->channels > 2) {
673 for (x = 0; x < imgs[imgn]->xsize; ++x) {
674 printf("bumped entry %d\n", MED_CUT_INDEX(line[x]));
675 ++colors[MED_CUT_INDEX(line[x])].count;
679 /* a gray-scale image, just use the first channel */
680 for (x = 0; x < imgs[imgn]->xsize; ++x) {
681 ++colors[MED_CUT_GRAY_INDEX(line[x])].count;
687 /* eliminate the empty colors */
689 for (in = 0; in < MEDIAN_CUT_COLORS; ++in) {
690 if (colors[in].count) {
691 colors[out++] = colors[in];
694 /*printf("out %d\n", out);
696 for (i = 0; i < out; ++i) {
697 if (colors[i].count) {
698 printf("%d: (%d,%d,%d) -> %d\n", i, colors[i].rgb[0], colors[i].rgb[1],
699 colors[i].rgb[2], colors[i].count);
703 if (out < quant->mc_size) {
704 /* just copy them into the color table */
705 for (i = 0; i < out; ++i) {
706 for (ch = 0; ch < 3; ++ch) {
707 quant->mc_colors[i].channel[ch] = colors[i].rgb[ch];
710 quant->mc_count = out;
713 /* build the starting partition */
714 parts = i_mempool_alloc(&mp, sizeof(*parts) * quant->mc_size);
717 parts[0].pixels = total_pixels;
718 calc_part(parts, colors);
721 while (color_count < quant->mc_size) {
722 int max_index, max_ch; /* index/channel with biggest spread */
724 medcut_partition *workpart;
728 /* find the partition with the most biggest span with more than
731 for (i = 0; i < color_count; ++i) {
732 for (ch = 0; ch < chan_count; ++ch) {
733 if (parts[i].width[ch] > max_size
734 && parts[i].size > 1) {
737 max_size = parts[i].width[ch];
742 /* nothing else we can split */
746 workpart = parts+max_index;
747 /*printf("splitting partition %d (pixels %ld, start %d, size %d)\n", max_index, workpart->pixels, workpart->start, workpart->size);*/
748 qsort(colors + workpart->start, workpart->size, sizeof(*colors),
751 /* find the median or something like it we need to make sure both
752 sides of the split have at least one color in them, so we don't
753 test at the first or last entry */
755 cum_total = colors[i].count;
757 half = workpart->pixels / 2;
758 while (i < workpart->start + workpart->size - 1
759 && cum_total < half) {
760 cum_total += colors[i++].count;
762 /*printf("Split at %d to make %d (half %ld, cumtotal %ld)\n", i, color_count, half, cum_total);*/
764 /* found the spot to split */
765 parts[color_count].start = i;
766 parts[color_count].size = workpart->start + workpart->size - i;
767 workpart->size = i - workpart->start;
768 parts[color_count].pixels = workpart->pixels - cum_total;
769 workpart->pixels = cum_total;
771 /* recalculate the limits */
772 calc_part(workpart, colors);
773 calc_part(parts+color_count, colors);
777 /* fill in the color table - since we could still have partitions
778 that have more than one color, we need to average the colors */
779 for (part_num = 0; part_num < color_count; ++part_num) {
781 medcut_partition *workpart;
783 workpart = parts+part_num;
784 for (ch = 0; ch < 3; ++ch)
787 for (i = workpart->start; i < workpart->start + workpart->size; ++i) {
788 for (ch = 0; ch < 3; ++ch) {
789 sums[ch] += colors[i].rgb[ch] * colors[i].count;
792 for (ch = 0; ch < 3; ++ch) {
793 quant->mc_colors[part_num].channel[ch] = sums[ch] / workpart->pixels;
796 quant->mc_count = color_count;
798 /*printf("out %d colors\n", quant->mc_count);*/
799 i_mempool_destroy(&mp);
804 /* Define one of the following 4 symbols to choose a colour search method
805 The idea is to try these out, including benchmarking, to see which
806 is fastest in a good spread of circumstances.
807 I'd expect IM_CFLINSEARCH to be fastest for very small palettes, and
808 IM_CFHASHBOX for large images with large palettes.
810 Some other possibilities include:
811 - search over entries sorted by luminance
813 Initially I was planning on testing using the macros and then
814 integrating the code directly into each function, but this means if
815 we find a bug at a late stage we will need to update N copies of
816 the same code. Also, keeping the code in the macros means that the
817 code in the translation functions is much more to the point,
818 there's no distracting colour search code to remove attention from
819 what makes _this_ translation function different. It may be
820 advisable to move the setup code into functions at some point, but
821 it should be possible to do this fairly transparently.
823 If IM_CF_COPTS is defined then CFLAGS must have an appropriate
826 Each option needs to define 4 macros:
827 CF_VARS - variables to define in the function
828 CF_SETUP - code to setup for the colour search, eg. allocating and
829 initializing lookup tables
830 CF_FIND - code that looks for the color in val and puts the best
831 matching index in bst_idx
832 CF_CLEANUP - code to clean up, eg. releasing memory
835 /*#define IM_CFLINSEARCH*/
837 /*#define IM_CFSORTCHAN*/
838 /*#define IM_CFRAND2DIST*/
843 /* The original version I wrote for this used the sort.
844 If this is defined then we use a sort to extract the indices for
848 /* assume i is available */
849 #define CF_VARS hashbox *hb = mymalloc(sizeof(hashbox) * 512); \
855 static long *gdists; /* qsort is annoying */
856 /* int might be smaller than long, so we need to do a real compare
857 rather than a subtraction*/
858 static int distcomp(void const *a, void const *b) {
859 long ra = gdists[*(int const *)a];
860 long rb = gdists[*(int const *)b];
871 /* for each hashbox build a list of colours that are in the hb or is closer
873 This is pretty involved. The original gifquant generated the hashbox
874 as part of it's normal processing, but since the map generation is now
875 separated from the translation we need to do this on the spot.
876 Any optimizations, even if they don't produce perfect results would be
879 static void hbsetup(i_quantize *quant, hashbox *hb) {
880 long *dists, mind, maxd, cd;
881 int cr, cb, cg, hbnum, i;
884 int *indices = mymalloc(quant->mc_count * sizeof(int));
887 dists = mymalloc(quant->mc_count * sizeof(long));
888 for (cr = 0; cr < 8; ++cr) {
889 for (cg = 0; cg < 8; ++cg) {
890 for (cb = 0; cb < 8; ++cb) {
891 /* centre of the hashbox */
892 cenc.channel[0] = cr*pboxjump+pboxjump/2;
893 cenc.channel[1] = cg*pboxjump+pboxjump/2;
894 cenc.channel[2] = cb*pboxjump+pboxjump/2;
895 hbnum = pixbox(&cenc);
897 /* order indices in the order of distance from the hashbox */
898 for (i = 0; i < quant->mc_count; ++i) {
902 dists[i] = ceucl_d(&cenc, quant->mc_colors+i);
905 /* it should be possible to do this without a sort
906 but so far I'm too lazy */
908 qsort(indices, quant->mc_count, sizeof(int), distcomp);
909 /* any colors that can match are within mind+diagonal size of
911 mind = dists[indices[0]];
913 maxd = (sqrt(mind)+pboxjump)*(sqrt(mind)+pboxjump);
914 while (i < quant->mc_count && dists[indices[i]] < maxd) {
915 hb[hbnum].vec[hb[hbnum].cnt++] = indices[i++];
918 /* work out the minimum */
920 for (i = 0; i < quant->mc_count; ++i) {
921 if (dists[i] < mind) mind = dists[i];
923 /* transfer any colours that might be closest to a colour in
925 maxd = (sqrt(mind)+pboxjump)*(sqrt(mind)+pboxjump);
926 for (i = 0; i < quant->mc_count; ++i) {
928 hb[hbnum].vec[hb[hbnum].cnt++] = i;
939 #define CF_SETUP hbsetup(quant, hb)
942 currhb = pixbox(&val); \
944 for (i = 0; i < hb[currhb].cnt; ++i) { \
945 cd = ceucl_d(quant->mc_colors+hb[currhb].vec[i], &val); \
946 if (cd < ld) { ld = cd; bst_idx = hb[currhb].vec[i]; } \
949 #define CF_CLEANUP myfree(hb)
953 #ifdef IM_CFLINSEARCH
954 /* as simple as it gets */
955 #define CF_VARS long ld, cd
956 #define CF_SETUP /* none needed */
959 for (i = 0; i < quant->mc_count; ++i) { \
960 cd = ceucl_d(quant->mc_colors+i, &val); \
961 if (cd < ld) { ld = cd; bst_idx = i; } \
967 static int gsortchan;
968 static i_quantize *gquant;
969 static int chansort(void const *a, void const *b) {
970 return gquant->mc_colors[*(int const *)a].channel[gsortchan] -
971 gquant->mc_colors[*(int const *)b].channel[gsortchan];
973 #define CF_VARS int *indices, sortchan, diff; \
975 int vindex[256] /* where to find value i of chan */
977 static void chansetup(i_img *img, i_quantize *quant, int *csortchan,
978 int *vindex, int **cindices) {
979 int *indices, sortchan, chan, i, chval;
980 int chanmins[MAXCHANNELS], chanmaxs[MAXCHANNELS], maxrange;
982 /* find the channel with the maximum range */
983 /* the maximum stddev would probably be better */
984 for (chan = 0; chan < img->channels; ++chan) {
985 chanmins[chan] = 256; chanmaxs[chan] = 0;
986 for (i = 0; i < quant->mc_count; ++i) {
987 if (quant->mc_colors[i].channel[chan] < chanmins[chan])
988 chanmins[chan] = quant->mc_colors[i].channel[chan];
989 if (quant->mc_colors[i].channel[chan] > chanmaxs[chan])
990 chanmaxs[chan] = quant->mc_colors[i].channel[chan];
994 for (chan = 0; chan < img->channels; ++chan) {
995 if (chanmaxs[chan]-chanmins[chan] > maxrange) {
996 maxrange = chanmaxs[chan]-chanmins[chan];
1000 indices = mymalloc(quant->mc_count * sizeof(int)) ;
1001 for (i = 0; i < quant->mc_count; ++i) {
1004 gsortchan = sortchan;
1006 qsort(indices, quant->mc_count, sizeof(int), chansort) ;
1007 /* now a lookup table to find entries faster */
1008 for (chval=0, i=0; i < quant->mc_count; ++i) {
1009 while (chval < 256 &&
1010 chval < quant->mc_colors[indices[i]].channel[sortchan]) {
1011 vindex[chval++] = i;
1014 while (chval < 256) {
1015 vindex[chval++] = quant->mc_count-1;
1017 *csortchan = sortchan;
1018 *cindices = indices;
1022 chansetup(img, quant, &sortchan, vindex, &indices)
1024 int chanfind(i_color val, i_quantize *quant, int *indices, int *vindex,
1026 int i, bst_idx, diff, maxdiff;
1029 i = vindex[val.channel[sortchan]];
1030 bst_idx = indices[i];
1033 maxdiff = quant->mc_count;
1034 while (diff < maxdiff) {
1035 if (i+diff < quant->mc_count) {
1036 cd = ceucl_d(&val, quant->mc_colors+indices[i+diff]);
1038 bst_idx = indices[i+diff];
1044 cd = ceucl_d(&val, quant->mc_colors+indices[i-diff]);
1046 bst_idx = indices[i-diff];
1058 bst_idx = chanfind(val, quant, indices, vindex, sortchan)
1061 #define CF_CLEANUP myfree(indices)
1065 #ifdef IM_CFRAND2DIST
1067 /* This is based on a method described by Addi in the #imager channel
1068 on the 28/2/2001. I was about 1am Sydney time at the time, so I
1069 wasn't at my most cogent. Well, that's my excuse :)
1071 <TonyC> what I have at the moment is: hashboxes, with optimum hash box
1072 filling; simple linear search; and a lookup in the widest channel
1073 (currently the channel with the maximum range)
1074 <Addi> There is one more way that might be simple to implement.
1075 <Addi> You want to hear?
1076 <TonyC> what's that?
1077 <purl> somebody said that was not true
1078 <Addi> For each of the colors in the palette start by creating a
1079 sorted list of the form:
1080 <Addi> [distance, color]
1081 <Addi> Where they are sorted by distance.
1082 <TonyC> distance to where?
1083 <Addi> Where the elements in the lists are the distances and colors of
1084 the other colors in the palette
1086 <Addi> So if you are at color 0
1087 <Addi> ok - now to search for the closest color when you are creating
1088 the final image is done like this:
1089 <Addi> a) pick a random color from the palette
1090 <Addi> b) calculate the distance to it
1091 <Addi> c) only check the vectors that are within double the distance
1092 in the list of the color you picked from the palette.
1093 <Addi> Does that seem logical?
1094 <Addi> Lets imagine that we only have grayscale to make an example:
1095 <Addi> Our palette has 1 4 10 20 as colors.
1096 <Addi> And we want to quantize the color 11
1097 <Addi> lets say we picked 10 randomly
1098 <Addi> the double distance is 2
1099 <Addi> since abs(10-11)*2 is 2
1100 <Addi> And the list at vector 10 is this:
1101 <Addi> [0, 10], [6 4], [9, 1], [10, 20]
1102 <Addi> so we look at the first one (but not the second one since 6 is
1103 at a greater distance than 2.
1104 <Addi> Any of that make sense?
1105 <TonyC> yes, though are you suggesting another random jump to one of
1106 the colours with the possible choices? or an exhaustive search?
1107 <Addi> TonyC: It's possible to come up with a recursive/iterative
1108 enhancement but this is the 'basic' version.
1109 <Addi> Which would do an iterative search.
1110 <Addi> You can come up with conditions where it pays to switch to a new one.
1111 <Addi> And the 'random' start can be switched over to a small tree.
1112 <Addi> So you would have a little index at the start.
1113 <Addi> to get you into the general direction
1114 <Addi> Perhaps just an 8 split.
1115 <Addi> that is - split each dimension in half.
1117 <TonyC> I get the idea
1118 <Addi> But this would seem to be a good approach in our case since we
1119 usually have few codevectors.
1120 <Addi> So we only need 256*256 entries in a table.
1121 <Addi> We could even only index some of them that were deemed as good
1123 <TonyC> I was considering adding paletted output support for PNG and
1124 TIFF at some point, which support 16-bit palettes
1132 typedef struct i_dists {
1140 static int dists_sort(void const *a, void const *b) {
1141 return ((i_dists *)a)->dist - ((i_dists *)b)->dist;
1144 static void rand2dist_setup(i_quantize *quant, i_dists **cdists) {
1146 mymalloc(sizeof(i_dists)*quant->mc_count*quant->mc_count);
1149 for (i = 0; i < quant->mc_count; ++i) {
1150 i_dists *ldists = dists + quant->mc_count * i;
1151 i_color val = quant->mc_colors[i];
1152 for (j = 0; j < quant->mc_count; ++j) {
1153 ldists[j].index = j;
1154 ldists[j].dist = ceucl_d(&val, quant->mc_colors+j);
1156 qsort(ldists, quant->mc_count, sizeof(i_dists), dists_sort);
1162 bst_idx = rand() % quant->mc_count; \
1163 rand2dist_setup(quant, &dists)
1165 static int rand2dist_find(i_color val, i_quantize *quant, i_dists *dists, int index) {
1172 cdists = dists + index * quant->mc_count;
1174 maxld = 8 * ceucl_d(&val, quant->mc_colors+index);
1175 for (i = 0; i < quant->mc_count && cdists[i].dist <= maxld; ++i) {
1176 cd = ceucl_d(&val, quant->mc_colors+cdists[i].index);
1178 bst_idx = cdists[i].index;
1185 #define CF_FIND bst_idx = rand2dist_find(val, quant, dists, bst_idx)
1187 #define CF_CLEANUP myfree(dists)
1192 static void translate_addi(i_quantize *quant, i_img *img, i_palidx *out) {
1193 int x, y, i, k, bst_idx;
1195 int pixdev = quant->perturb;
1200 if (img->channels >= 3) {
1203 for(y=0;y<img->ysize;y++) for(x=0;x<img->xsize;x++) {
1204 i_gpix(img,x,y,&val);
1205 val.channel[0]=g_sat(val.channel[0]+(int)(pixdev*frandn()));
1206 val.channel[1]=g_sat(val.channel[1]+(int)(pixdev*frandn()));
1207 val.channel[2]=g_sat(val.channel[2]+(int)(pixdev*frandn()));
1213 for(y=0;y<img->ysize;y++) for(x=0;x<img->xsize;x++) {
1214 i_gpix(img,x,y,&val);
1223 for(y=0;y<img->ysize;y++) for(x=0;x<img->xsize;x++) {
1224 i_gpix(img,x,y,&val);
1225 val.channel[1] = val.channel[2] =
1226 val.channel[0]=g_sat(val.channel[0]+(int)(pixdev*frandn()));
1232 for(y=0;y<img->ysize;y++) for(x=0;x<img->xsize;x++) {
1233 i_gpix(img,x,y,&val);
1234 val.channel[1] = val.channel[2] = val.channel[0];
1243 static int floyd_map[] =
1249 static int jarvis_map[] =
1256 static int stucki_map[] =
1263 struct errdiff_map {
1265 int width, height, orig;
1268 static struct errdiff_map maps[] =
1270 { floyd_map, 3, 2, 1 },
1271 { jarvis_map, 5, 3, 2 },
1272 { stucki_map, 5, 3, 2 },
1275 typedef struct errdiff_tag {
1279 /* perform an error diffusion dither */
1282 translate_errdiff(i_quantize *quant, i_img *img, i_palidx *out) {
1284 int mapw, maph, mapo;
1290 int minr, maxr, ming, maxg, minb, maxb, cr, cg, cb;
1295 if ((quant->errdiff & ed_mask) == ed_custom) {
1296 map = quant->ed_map;
1297 mapw = quant->ed_width;
1298 maph = quant->ed_height;
1299 mapo = quant->ed_orig;
1302 int index = quant->errdiff & ed_mask;
1303 if (index >= ed_custom) index = ed_floyd;
1304 map = maps[index].map;
1305 mapw = maps[index].width;
1306 maph = maps[index].height;
1307 mapo = maps[index].orig;
1310 errw = img->xsize+mapw;
1311 err = mymalloc(sizeof(*err) * maph * errw);
1312 /*errp = err+mapo;*/
1313 memset(err, 0, sizeof(*err) * maph * errw);
1316 for (i = 0; i < maph * mapw; ++i)
1317 difftotal += map[i];
1319 for (dy = 0; dy < maph; ++dy) {
1320 for (dx = 0; dx < mapw; ++dx) {
1321 printf("%2d", map[dx+dy*mapw]);
1328 for (y = 0; y < img->ysize; ++y) {
1329 for (x = 0; x < img->xsize; ++x) {
1333 i_gpix(img, x, y, &val);
1334 if (img->channels < 3) {
1335 val.channel[1] = val.channel[2] = val.channel[0];
1338 perr.r = perr.r < 0 ? -((-perr.r)/difftotal) : perr.r/difftotal;
1339 perr.g = perr.g < 0 ? -((-perr.g)/difftotal) : perr.g/difftotal;
1340 perr.b = perr.b < 0 ? -((-perr.b)/difftotal) : perr.b/difftotal;
1341 /*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);*/
1342 val.channel[0] = g_sat(val.channel[0]-perr.r);
1343 val.channel[1] = g_sat(val.channel[1]-perr.g);
1344 val.channel[2] = g_sat(val.channel[2]-perr.b);
1347 perr.r = quant->mc_colors[bst_idx].channel[0] - val.channel[0];
1348 perr.g = quant->mc_colors[bst_idx].channel[1] - val.channel[1];
1349 perr.b = quant->mc_colors[bst_idx].channel[2] - val.channel[2];
1350 /*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);*/
1351 for (dx = 0; dx < mapw; ++dx) {
1352 for (dy = 0; dy < maph; ++dy) {
1353 err[x+dx+dy*errw].r += perr.r * map[dx+mapw*dy];
1354 err[x+dx+dy*errw].g += perr.g * map[dx+mapw*dy];
1355 err[x+dx+dy*errw].b += perr.b * map[dx+mapw*dy];
1360 /* shift up the error matrix */
1361 for (dy = 0; dy < maph-1; ++dy) {
1362 memcpy(err+dy*errw, err+(dy+1)*errw, sizeof(*err)*errw);
1364 memset(err+(maph-1)*errw, 0, sizeof(*err)*errw);
1369 /* Prescan finds the boxes in the image that have the highest number of colors
1370 and that result is used as the initial value for the vectores */
1373 static void prescan(i_img **imgs,int count, int cnum, cvec *clr, i_sample_t *line) {
1379 for(i=0;i<512;i++) {
1385 /* process each image */
1386 for (i = 0; i < count; ++i) {
1387 i_img *im = imgs[i];
1388 chans = im->channels >= 3 ? NULL : gray_samples;
1389 for(y=0;y<im->ysize;y++) {
1390 i_gsamp(im, 0, im->xsize, y, line, chans, 3);
1392 for(x=0;x<im->xsize;x++) {
1393 prebox[pixbox_ch(val)].pixcnt++;
1398 for(i=0;i<512;i++) prebox[i].pdc=prebox[i].pixcnt;
1399 qsort(prebox,512,sizeof(pbox),(cmpfunc)pboxcmp);
1401 for(i=0;i<cnum;i++) {
1402 /* printf("Color %d\n",i);
1403 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); }
1408 /* 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); } */
1414 /* printf("prebox[%d].cand=%d\n",k,prebox[k].cand); */
1415 if (clr[i].fixed) { i++; continue; } /* reserved go to next */
1416 if (j>=prebox[k].cand) { k++; j=1; } else {
1417 if (prebox[k].cand == 2) boxcenter(prebox[k].boxnum,&(clr[i]));
1418 else boxrand(prebox[k].boxnum,&(clr[i]));
1419 /* 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); */
1427 static void reorder(pbox prescan[512]) {
1435 c.pdc=c.pixcnt/(c.cand*c.cand);
1436 /* c.pdc=c.pixcnt/c.cand; */
1437 while(c.pdc < prescan[nidx+1].pdc && nidx < 511) {
1438 prescan[nidx]=prescan[nidx+1];
1445 pboxcmp(const pbox *a,const pbox *b) {
1446 if (a->pixcnt > b->pixcnt) return -1;
1447 if (a->pixcnt < b->pixcnt) return 1;
1452 boxcenter(int box,cvec *cv) {
1453 cv->r=15+((box&448)>>1);
1454 cv->g=15+((box&56)<<2);
1455 cv->b=15+((box&7)<<5);
1459 bbox(int box,int *r0,int *r1,int *g0,int *g1,int *b0,int *b1) {
1469 boxrand(int box,cvec *cv) {
1470 cv->r=6+(rand()%25)+((box&448)>>1);
1471 cv->g=6+(rand()%25)+((box&56)<<2);
1472 cv->b=6+(rand()%25)+((box&7)<<5);
1482 while (w >= 1 || w == 0) {
1483 u1 = 2 * frand() - 1;
1484 u2 = 2 * frand() - 1;
1488 w = sqrt((-2*log(w))/w);
1492 /* Create hash index */
1495 cr_hashindex(cvec clr[256],int cnum,hashbox hb[512]) {
1497 int bx,mind,cd,cumcnt,bst_idx,i;
1498 /* printf("indexing... \n");*/
1501 for(bx=0; bx<512; bx++) {
1503 for(i=0; i<cnum; i++) {
1504 cd = maxdist(bx,&clr[i]);
1505 if (cd < mind) { mind=cd; bst_idx=i; }
1509 for(i=0;i<cnum;i++) if (mindist(bx,&clr[i])<mind) hb[bx].vec[hb[bx].cnt++]=i;
1510 /*printf("box %d -> approx -> %d\n",bx,hb[bx].cnt); */
1511 /* statbox(bx,cnum,clr); */
1515 /* printf("Average search space: %d\n",cumcnt/512); */
1519 maxdist(int boxnum,cvec *cv) {
1520 int r0,r1,g0,g1,b0,b1;
1527 bbox(boxnum,&r0,&r1,&g0,&g1,&b0,&b1);
1529 mr=i_max(abs(b-b0),abs(b-b1));
1530 mg=i_max(abs(g-g0),abs(g-g1));
1531 mb=i_max(abs(r-r0),abs(r-r1));
1533 return PWR2(mr)+PWR2(mg)+PWR2(mb);
1537 mindist(int boxnum,cvec *cv) {
1538 int r0,r1,g0,g1,b0,b1;
1545 bbox(boxnum,&r0,&r1,&g0,&g1,&b0,&b1);
1547 /* printf("box %d, (%d,%d,%d)-(%d,%d,%d) vec (%d,%d,%d) ",boxnum,r0,g0,b0,r1,g1,b1,r,g,b); */
1549 if (r0<=r && r<=r1 && g0<=g && g<=g1 && b0<=b && b<=b1) return 0;
1551 mr=i_min(abs(b-b0),abs(b-b1));
1552 mg=i_min(abs(g-g0),abs(g-g1));
1553 mb=i_min(abs(r-r0),abs(r-r1));
1559 if (r0<=r && r<=r1 && g0<=g && g<=g1) return mb;
1560 if (r0<=r && r<=r1 && b0<=b && b<=b1) return mg;
1561 if (b0<=b && b<=b1 && g0<=g && g<=g1) return mr;
1563 if (r0<=r && r<=r1) return mg+mb;
1564 if (g0<=g && g<=g1) return mr+mb;
1565 if (b0<=b && b<=b1) return mg+mr;
1570 static void transparent_threshold(i_quantize *, i_palidx *, i_img *, i_palidx);
1571 static void transparent_errdiff(i_quantize *, i_palidx *, i_img *, i_palidx);
1572 static void transparent_ordered(i_quantize *, i_palidx *, i_img *, i_palidx);
1574 void quant_transparent(i_quantize *quant, i_palidx *data, i_img *img,
1575 i_palidx trans_index)
1577 switch (quant->transp) {
1582 quant->tr_threshold = 128;
1585 transparent_threshold(quant, data, img, trans_index);
1589 transparent_errdiff(quant, data, img, trans_index);
1593 transparent_ordered(quant, data, img, trans_index);
1599 transparent_threshold(i_quantize *quant, i_palidx *data, i_img *img,
1600 i_palidx trans_index)
1603 i_sample_t *line = mymalloc(img->xsize * sizeof(i_sample_t));
1604 int trans_chan = img->channels > 2 ? 3 : 1;
1606 for (y = 0; y < img->ysize; ++y) {
1607 i_gsamp(img, 0, img->xsize, y, line, &trans_chan, 1);
1608 for (x = 0; x < img->xsize; ++x) {
1609 if (line[x] < quant->tr_threshold)
1610 data[y*img->xsize+x] = trans_index;
1617 transparent_errdiff(i_quantize *quant, i_palidx *data, i_img *img,
1618 i_palidx trans_index)
1622 int mapw, maph, mapo;
1623 int errw, *err, *errp;
1624 int difftotal, out, error;
1625 int x, y, dx, dy, i;
1627 int trans_chan = img->channels > 2 ? 3 : 1;
1629 /* no custom map for transparency (yet) */
1630 index = quant->tr_errdiff & ed_mask;
1631 if (index >= ed_custom) index = ed_floyd;
1632 map = maps[index].map;
1633 mapw = maps[index].width;
1634 maph = maps[index].height;
1635 mapo = maps[index].orig;
1637 errw = img->xsize+mapw-1;
1638 err = mymalloc(sizeof(*err) * maph * errw);
1640 memset(err, 0, sizeof(*err) * maph * errw);
1642 line = mymalloc(img->xsize * sizeof(i_sample_t));
1644 for (i = 0; i < maph * mapw; ++i)
1645 difftotal += map[i];
1646 for (y = 0; y < img->ysize; ++y) {
1647 i_gsamp(img, 0, img->xsize, y, line, &trans_chan, 1);
1648 for (x = 0; x < img->xsize; ++x) {
1649 line[x] = g_sat(line[x]-errp[x]/difftotal);
1650 if (line[x] < 128) {
1652 data[y*img->xsize+x] = trans_index;
1657 error = out - line[x];
1658 for (dx = 0; dx < mapw; ++dx) {
1659 for (dy = 0; dy < maph; ++dy) {
1660 errp[x+dx-mapo+dy*errw] += error * map[dx+mapw*dy];
1664 /* shift up the error matrix */
1665 for (dy = 0; dy < maph-1; ++dy)
1666 memcpy(err+dy*errw, err+(dy+1)*errw, sizeof(*err)*errw);
1667 memset(err+(maph-1)*errw, 0, sizeof(*err)*errw);
1673 /* builtin ordered dither maps */
1674 static unsigned char
1675 orddith_maps[][64] =
1678 this is purely random - it's pretty awful
1680 48, 72, 196, 252, 180, 92, 108, 52,
1681 228, 176, 64, 8, 236, 40, 20, 164,
1682 120, 128, 84, 116, 24, 28, 172, 220,
1683 68, 0, 188, 124, 184, 224, 192, 104,
1684 132, 100, 240, 200, 152, 160, 244, 44,
1685 96, 204, 144, 16, 140, 56, 232, 216,
1686 208, 4, 76, 212, 136, 248, 80, 168,
1687 156, 88, 32, 112, 148, 12, 36, 60,
1691 perl spot.perl '($x-3.5)*($x-3.5)+($y-3.5)*($y-3.5)'
1693 240, 232, 200, 136, 140, 192, 228, 248,
1694 220, 148, 100, 76, 80, 104, 152, 212,
1695 180, 116, 56, 32, 36, 60, 120, 176,
1696 156, 64, 28, 0, 8, 44, 88, 160,
1697 128, 92, 24, 12, 4, 40, 68, 132,
1698 184, 96, 48, 20, 16, 52, 108, 188,
1699 216, 144, 112, 72, 84, 124, 164, 224,
1700 244, 236, 196, 168, 172, 204, 208, 252,
1704 'min(dist(1.5, 1.5),dist(5.5,1.5),dist(1.5,5.5),dist(5.5,5.5))'
1706 196, 72, 104, 220, 200, 80, 112, 224,
1707 76, 4, 24, 136, 84, 8, 32, 144,
1708 108, 28, 52, 168, 116, 36, 56, 176,
1709 216, 140, 172, 244, 228, 148, 180, 248,
1710 204, 92, 124, 236, 192, 68, 96, 208,
1711 88, 12, 44, 156, 64, 0, 16, 128,
1712 120, 40, 60, 188, 100, 20, 48, 160,
1713 232, 152, 184, 252, 212, 132, 164, 240,
1716 perl spot.perl '$y-3'
1718 160, 164, 168, 172, 176, 180, 184, 188,
1719 128, 132, 136, 140, 144, 148, 152, 156,
1720 32, 36, 40, 44, 48, 52, 56, 60,
1721 0, 4, 8, 12, 16, 20, 24, 28,
1722 64, 68, 72, 76, 80, 84, 88, 92,
1723 96, 100, 104, 108, 112, 116, 120, 124,
1724 192, 196, 200, 204, 208, 212, 216, 220,
1725 224, 228, 232, 236, 240, 244, 248, 252,
1728 perl spot.perl '$x-3'
1730 180, 100, 40, 12, 44, 104, 184, 232,
1731 204, 148, 60, 16, 64, 128, 208, 224,
1732 212, 144, 76, 8, 80, 132, 216, 244,
1733 160, 112, 68, 20, 84, 108, 172, 236,
1734 176, 96, 72, 28, 88, 152, 188, 228,
1735 200, 124, 92, 0, 32, 116, 164, 240,
1736 168, 120, 36, 24, 48, 136, 192, 248,
1737 196, 140, 52, 4, 56, 156, 220, 252,
1740 perl spot.perl '$y+$x-7'
1742 248, 232, 224, 192, 140, 92, 52, 28,
1743 240, 220, 196, 144, 108, 60, 12, 64,
1744 216, 180, 148, 116, 76, 20, 80, 128,
1745 204, 152, 104, 44, 16, 72, 100, 160,
1746 164, 96, 68, 24, 56, 112, 168, 176,
1747 124, 40, 8, 36, 88, 136, 184, 212,
1748 84, 4, 32, 120, 156, 188, 228, 236,
1749 0, 48, 132, 172, 200, 208, 244, 252,
1752 perl spot.perl '$y-$x'
1754 0, 32, 116, 172, 184, 216, 236, 252,
1755 56, 8, 72, 132, 136, 200, 228, 240,
1756 100, 36, 12, 40, 92, 144, 204, 220,
1757 168, 120, 60, 16, 44, 96, 156, 176,
1758 180, 164, 112, 48, 28, 52, 128, 148,
1759 208, 192, 152, 88, 84, 20, 64, 104,
1760 232, 224, 196, 140, 108, 68, 24, 76,
1761 248, 244, 212, 188, 160, 124, 80, 4,
1765 good for display, bad for print
1768 0, 128, 32, 192, 8, 136, 40, 200,
1769 224, 64, 160, 112, 232, 72, 168, 120,
1770 48, 144, 16, 208, 56, 152, 24, 216,
1771 176, 96, 240, 80, 184, 104, 248, 88,
1772 12, 140, 44, 204, 4, 132, 36, 196,
1773 236, 76, 172, 124, 228, 68, 164, 116,
1774 60, 156, 28, 220, 52, 148, 20, 212,
1775 188, 108, 252, 92, 180, 100, 244, 84,
1780 transparent_ordered(i_quantize *quant, i_palidx *data, i_img *img,
1781 i_palidx trans_index)
1783 unsigned char *spot;
1786 int trans_chan = img->channels > 2 ? 3 : 1;
1787 if (quant->tr_orddith == od_custom)
1788 spot = quant->tr_custom;
1790 spot = orddith_maps[quant->tr_orddith];
1792 line = mymalloc(img->xsize * sizeof(i_sample_t));
1793 for (y = 0; y < img->ysize; ++y) {
1794 i_gsamp(img, 0, img->xsize, y, line, &trans_chan, 1);
1795 for (x = 0; x < img->xsize; ++x) {
1796 if (line[x] < spot[(x&7)+(y&7)*8])
1797 data[x+y*img->xsize] = trans_index;