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
8 static void makemap_addi(i_quantize *, i_img **imgs, int count);
9 static void makemap_mediancut(i_quantize *, i_img **imgs, int count);
10 static void makemap_mono(i_quantize *);
14 setcol(i_color *cl,unsigned char r,unsigned char g,unsigned char b,unsigned char a) {
23 /* make a colour map overwrites mc_existing/mc_count in quant Note
24 that i_makemap will be called once for each image if mc_perimage is
25 set and the format support multiple colour maps per image.
27 This means we don't need any special processing at this level to
28 handle multiple colour maps.
32 =item i_quant_makemap(C<quant>, C<imgs>, C<count>)
34 =category Image quantization
36 Analyzes the C<count> images in C<imgs> according to the rules in
37 C<quant> to build a color map (optimal or not depending on
38 C<< quant->make_colors >>).
44 i_quant_makemap(i_quantize *quant, i_img **imgs, int count) {
46 if (quant->translate == pt_giflib) {
47 /* giflib does it's own color table generation */
48 /* previously we used giflib's quantizer, but it didn't handle multiple
49 images, which made it hard to build a global color map
50 We've implemented our own median cut code so we can ignore
52 makemap_mediancut(quant, imgs, count);
56 switch (quant->make_colors & mc_mask) {
58 /* use user's specified map */
64 for (r = 0; r < 256; r+=0x33)
65 for (g = 0; g < 256; g+=0x33)
66 for (b = 0; b < 256; b += 0x33)
67 setcol(quant->mc_colors+i++, r, g, b, 255);
73 makemap_mediancut(quant, imgs, count);
82 makemap_addi(quant, imgs, count);
87 static void translate_closest(i_quantize *, i_img *, i_palidx *);
88 static void translate_errdiff(i_quantize *, i_img *, i_palidx *);
89 static void translate_addi(i_quantize *, i_img *, i_palidx *);
92 =item i_quant_translate(C<quant>, C<img>)
94 =category Image quantization
96 Quantize the image given the palette in C<quant>.
98 On success returns a pointer to a memory block of C<< img->xsize *
99 img->ysize >> C<i_palidx> entries.
101 On failure returns NULL.
103 You should call myfree() on the returned block when you're done with
106 This function will fail if the supplied palette contains no colors.
111 i_quant_translate(i_quantize *quant, i_img *img) {
115 mm_log((1, "quant_translate(quant %p, img %p)\n", quant, img));
117 /* there must be at least one color in the paletted (though even that
119 if (quant->mc_count == 0) {
120 i_push_error(0, "no colors available for translation");
124 bytes = img->xsize * img->ysize;
125 if (bytes / img->ysize != img->xsize) {
126 i_push_error(0, "integer overflow calculating memory allocation");
129 result = mymalloc(bytes);
131 switch (quant->translate) {
134 translate_closest(quant, img, result);
138 translate_errdiff(quant, img, result);
143 translate_addi(quant, img, result);
150 static void translate_closest(i_quantize *quant, i_img *img, i_palidx *out) {
152 translate_addi(quant, img, out);
155 #define PWR2(x) ((x)*(x))
157 typedef int (*cmpfunc)(const void*, const void*);
180 static void prescan(i_img **im,int count, int cnum, cvec *clr, i_sample_t *line);
181 static void reorder(pbox prescan[512]);
182 static int pboxcmp(const pbox *a,const pbox *b);
183 static void boxcenter(int box,cvec *cv);
184 static float frandn(void);
185 static void boxrand(int box,cvec *cv);
186 static void bbox(int box,int *r0,int *r1,int *g0,int *g1,int *b0,int *b1);
187 static void cr_hashindex(cvec clr[256],int cnum,hashbox hb[512]);
188 static int mindist(int boxnum,cvec *cv);
189 static int maxdist(int boxnum,cvec *cv);
191 /* Some of the simpler functions are kept here to aid the compiler -
192 maybe some of them will be inlined. */
195 pixbox(i_color *ic) { return ((ic->channel[0] & 224)<<1)+ ((ic->channel[1]&224)>>2) + ((ic->channel[2] &224) >> 5); }
198 pixbox_ch(i_sample_t *chans) { return ((chans[0] & 224)<<1)+ ((chans[1]&224)>>2) + ((chans[2] &224) >> 5); }
202 if (in>255) { return 255; }
203 else if (in>0) return in;
210 return rand()/(RAND_MAX+1.0);
216 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]); }
221 eucl_d_ch(cvec* cv,i_sample_t *chans) {
222 return PWR2(cv->r - chans[0]) + PWR2(cv->g - chans[1])
223 + PWR2(cv->b - chans[2]);
228 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]); }
231 gray_samples[] = { 0, 0, 0 };
235 This quantization algorithm and implementation routines are by Arnar
236 M. Hrafnkelson. In case any new ideas are here they are mine since
237 this was written from scratch.
239 The algorithm uses local means in the following way:
241 For each point in the colormap we find which image points
242 have that point as it's closest point. We calculate the mean
243 of those points and in the next iteration it will be the new
244 entry in the colormap.
246 In order to speed this process up (i.e. nearest neighbor problem) We
247 divied the r,g,b space up in equally large 512 boxes. The boxes are
248 numbered from 0 to 511. Their numbering is so that for a given vector
249 it is known that it belongs to the box who is formed by concatenating the
250 3 most significant bits from each component of the RGB triplet.
252 For each box we find the list of points from the colormap who might be
253 closest to any given point within the box. The exact solution
254 involves finding the Voronoi map (or the dual the Delauny
255 triangulation) and has many issues including numerical stability.
257 So we use this approximation:
259 1. Find which point has the shortest maximum distance to the box.
260 2. Find all points that have a shorter minimum distance than that to the box
262 This is a very simple task and is not computationally heavy if one
263 takes into account that the minimum distances from a pixel to a box is
264 always found by checking if it's inside the box or is closest to some
265 side or a corner. Finding the maximum distance is also either a side
268 This approach results 2-3 times more than the actual points needed but
269 is still a good gain over the complete space. Usually when one has a
270 256 Colorcolor map a search over 30 is often obtained.
272 A bit of an enhancement to this approach is to keep a seperate list
273 for each side of the cube, but this will require even more memory.
275 Arnar M. Hrafnkelsson (addi@umich.edu);
279 Extracted from gifquant.c, removed dependencies on gif_lib,
280 and added support for multiple images.
281 starting from 1nov2000 by TonyC <tony@develop-help.com>.
286 makemap_addi(i_quantize *quant, i_img **imgs, int count) {
288 int cnum, i, x, y, bst_idx=0, ld, cd, iter, currhb, img_num;
295 const int *sample_indices;
297 mm_log((1, "makemap_addi(quant %p { mc_count=%d, mc_colors=%p }, imgs %p, count %d)\n",
298 quant, quant->mc_count, quant->mc_colors, imgs, count));
302 clr = i_mempool_alloc(&mp, sizeof(cvec) * quant->mc_size);
303 hb = i_mempool_alloc(&mp, sizeof(hashbox) * 512);
304 for (i=0; i < quant->mc_count; ++i) {
305 clr[i].r = quant->mc_colors[i].rgb.r;
306 clr[i].g = quant->mc_colors[i].rgb.g;
307 clr[i].b = quant->mc_colors[i].rgb.b;
311 /* mymalloc doesn't clear memory, so I think we need this */
312 for (; i < quant->mc_size; ++i) {
313 /*clr[i].r = clr[i].g = clr[i].b = 0;*/
320 cnum = quant->mc_size;
323 for (img_num = 0; img_num < count; ++img_num) {
324 if (imgs[img_num]->xsize > maxwidth)
325 maxwidth = imgs[img_num]->xsize;
327 line = i_mempool_alloc(&mp, 3 * maxwidth * sizeof(*line));
329 prescan(imgs, count, cnum, clr, line);
330 cr_hashindex(clr, cnum, hb);
332 for(iter=0;iter<3;iter++) {
335 for (img_num = 0; img_num < count; ++img_num) {
336 i_img *im = imgs[img_num];
337 sample_indices = im->channels >= 3 ? NULL : gray_samples;
338 for(y=0;y<im->ysize;y++) {
339 i_gsamp(im, 0, im->xsize, y, line, sample_indices, 3);
341 for(x=0;x<im->xsize;x++) {
343 /*i_gpix(im,x,y,&val);*/
344 currhb=pixbox_ch(val);
345 /* printf("box = %d \n",currhb); */
346 for(i=0;i<hb[currhb].cnt;i++) {
347 /* 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); */
349 cd=eucl_d_ch(&clr[hb[currhb].vec[i]],val);
351 ld=cd; /* shortest distance yet */
352 bst_idx=hb[currhb].vec[i]; /* index of closest vector yet */
356 clr[bst_idx].mcount++;
358 clr[bst_idx].dr+=val[0];
359 clr[bst_idx].dg+=val[1];
360 clr[bst_idx].db+=val[2];
362 val += 3; /* next 3 samples (next pixel) */
369 clr[i].dr/=clr[i].mcount;
370 clr[i].dg/=clr[i].mcount;
371 clr[i].db/=clr[i].mcount;
374 /* for(i=0;i<cnum;i++) printf("vec(%d)=(%d,%d,%d) dest=(%d,%d,%d) matchcount=%d\n",
375 i,clr[i].r,clr[i].g,clr[i].b,clr[i].dr,clr[i].dg,clr[i].db,clr[i].mcount); */
377 /* printf("total error: %.2f\n",sqrt(accerr)); */
379 for(i=0;i<cnum;i++) {
380 if (clr[i].fixed) continue; /* skip reserved colors */
384 clr[i].r=clr[i].r*(1-dlt)+dlt*clr[i].dr;
385 clr[i].g=clr[i].g*(1-dlt)+dlt*clr[i].dg;
386 clr[i].b=clr[i].b*(1-dlt)+dlt*clr[i].db;
388 /* let's try something else */
400 cr_hashindex(clr,cnum,hb);
405 for(i=0;i<cnum;i++) {
406 cd=eucl_d(&clr[i],&val);
414 /* if defined, we only include colours with an mcount or that were
415 supplied in the fixed palette, giving us a smaller output palette */
416 #define ONLY_USE_USED
418 /* transfer the colors back */
420 for (i = 0; i < cnum; ++i) {
421 if (clr[i].fixed || clr[i].used) {
422 /*printf("Adding %d (%d,%d,%d)\n", i, clr[i].r, clr[i].g, clr[i].b);*/
423 quant->mc_colors[quant->mc_count].rgb.r = clr[i].r;
424 quant->mc_colors[quant->mc_count].rgb.g = clr[i].g;
425 quant->mc_colors[quant->mc_count].rgb.b = clr[i].b;
430 /* transfer the colors back */
431 for (i = 0; i < cnum; ++i) {
432 quant->mc_colors[i].rgb.r = clr[i].r;
433 quant->mc_colors[i].rgb.g = clr[i].g;
434 quant->mc_colors[i].rgb.b = clr[i].b;
436 quant->mc_count = cnum;
440 mm_log((1, "makemap_addi returns - quant.mc_count = %d\n", quant->mc_count));
441 for (i = 0; i < quant->mc_count; ++i)
442 mm_log((5, " map entry %d: (%d, %d, %d)\n", i, clr[i].r, clr[i].g, clr[i].b));
445 i_mempool_destroy(&mp);
453 #define MEDIAN_CUT_COLORS 32768
455 #define MED_CUT_INDEX(c) ((((c).rgb.r & 0xF8) << 7) | \
456 (((c).rgb.g & 0xF8) << 2) | (((c).rgb.b & 0xF8) >> 3))
458 #define MED_CUT_GRAY_INDEX(c) ((((c).rgb.r & 0xF8) << 7) | \
459 (((c).rgb.r & 0xF8) << 2) | (((c).rgb.r & 0xF8) >> 3))
461 /* scale these to cover the whole range */
462 #define MED_CUT_RED(index) ((((index) & 0x7C00) >> 10) * 255 / 31)
463 #define MED_CUT_GREEN(index) ((((index) & 0x3E0) >> 5) * 255 / 31)
464 #define MED_CUT_BLUE(index) (((index) & 0x1F) * 255 / 31)
467 i_sample_t min[3]; /* minimum for each channel */
468 i_sample_t max[3]; /* maximum for each channel */
469 i_sample_t width[3]; /* width for each channel */
470 int start, size; /* beginning and size of the partition */
471 int pixels; /* number of pixels represented by this partition */
475 =item calc_part(part, colors)
477 Calculates the new color limits for the given partition.
479 Giflib assumes that the limits for the non-split channels stay the
480 same, but this strikes me as incorrect, especially if the colors tend
483 Of course this could be optimized by not recalculating the channel we
484 just sorted on, but it's not worth the effort right now.
488 static void calc_part(medcut_partition *part, quant_color_entry *colors) {
491 for (ch = 0; ch < 3; ++ch) {
495 for (i = part->start; i < part->start + part->size; ++i) {
496 for (ch = 0; ch < 3; ++ch) {
497 if (part->min[ch] > colors[i].rgb[ch])
498 part->min[ch] = colors[i].rgb[ch];
499 if (part->max[ch] < colors[i].rgb[ch])
500 part->max[ch] = colors[i].rgb[ch];
503 for (ch = 0; ch < 3; ++ch) {
504 part->width[ch] = part->max[ch] - part->min[ch];
508 /* simple functions to sort by each channel - we could use a global, but
512 color_sort_red(void const *left, void const *right) {
513 return ((quant_color_entry *)left)->rgb[0] - ((quant_color_entry *)right)->rgb[0];
517 color_sort_green(void const *left, void const *right) {
518 return ((quant_color_entry *)left)->rgb[1] - ((quant_color_entry *)right)->rgb[1];
522 color_sort_blue(void const *left, void const *right) {
523 return ((quant_color_entry *)left)->rgb[2] - ((quant_color_entry *)right)->rgb[2];
526 static int (*sorters[])(void const *, void const *) =
534 makemap_mediancut(i_quantize *quant, i_img **imgs, int count) {
535 quant_color_entry *colors;
537 int imgn, x, y, i, ch;
542 medcut_partition *parts;
545 /* number of channels we search for the best channel to partition
546 this isn't terribly efficient, but it should work */
549 /*printf("images %d pal size %d\n", count, quant->mc_size);*/
553 colors = i_mempool_alloc(&mp, sizeof(*colors) * MEDIAN_CUT_COLORS);
554 for (i = 0; i < MEDIAN_CUT_COLORS; ++i) {
555 colors[i].rgb[0] = MED_CUT_RED(i);
556 colors[i].rgb[1] = MED_CUT_GREEN(i);
557 colors[i].rgb[2] = MED_CUT_BLUE(i);
562 for (imgn = 0; imgn < count; ++imgn) {
563 if (imgs[imgn]->xsize > max_width)
564 max_width = imgs[imgn]->xsize;
566 line = i_mempool_alloc(&mp, sizeof(i_color) * max_width);
568 /* build the stats */
570 chan_count = 1; /* assume we just have grayscale */
571 for (imgn = 0; imgn < count; ++imgn) {
572 total_pixels += imgs[imgn]->xsize * imgs[imgn]->ysize;
573 for (y = 0; y < imgs[imgn]->ysize; ++y) {
574 i_glin(imgs[imgn], 0, imgs[imgn]->xsize, y, line);
575 if (imgs[imgn]->channels > 2) {
577 for (x = 0; x < imgs[imgn]->xsize; ++x) {
578 ++colors[MED_CUT_INDEX(line[x])].count;
582 /* a gray-scale image, just use the first channel */
583 for (x = 0; x < imgs[imgn]->xsize; ++x) {
584 ++colors[MED_CUT_GRAY_INDEX(line[x])].count;
590 /* eliminate the empty colors */
592 for (in = 0; in < MEDIAN_CUT_COLORS; ++in) {
593 if (colors[in].count) {
594 colors[out++] = colors[in];
597 /*printf("out %d\n", out);
599 for (i = 0; i < out; ++i) {
600 if (colors[i].count) {
601 printf("%d: (%d,%d,%d) -> %d\n", i, colors[i].rgb[0], colors[i].rgb[1],
602 colors[i].rgb[2], colors[i].count);
606 if (out < quant->mc_size) {
607 /* just copy them into the color table */
608 for (i = 0; i < out; ++i) {
609 for (ch = 0; ch < 3; ++ch) {
610 quant->mc_colors[i].channel[ch] = colors[i].rgb[ch];
613 quant->mc_count = out;
616 /* build the starting partition */
617 parts = i_mempool_alloc(&mp, sizeof(*parts) * quant->mc_size);
620 parts[0].pixels = total_pixels;
621 calc_part(parts, colors);
624 while (color_count < quant->mc_size) {
625 /* initialized to avoid compiler warnings */
626 int max_index = 0, max_ch = 0; /* index/channel with biggest spread */
628 medcut_partition *workpart;
632 /* find the partition with the most biggest span with more than
635 for (i = 0; i < color_count; ++i) {
636 for (ch = 0; ch < chan_count; ++ch) {
637 if (parts[i].width[ch] > max_size
638 && parts[i].size > 1) {
641 max_size = parts[i].width[ch];
646 /* nothing else we can split */
650 workpart = parts+max_index;
651 /*printf("splitting partition %d (pixels %ld, start %d, size %d)\n", max_index, workpart->pixels, workpart->start, workpart->size);*/
652 qsort(colors + workpart->start, workpart->size, sizeof(*colors),
655 /* find the median or something like it we need to make sure both
656 sides of the split have at least one color in them, so we don't
657 test at the first or last entry */
659 cum_total = colors[i].count;
661 half = workpart->pixels / 2;
662 while (i < workpart->start + workpart->size - 1
663 && cum_total < half) {
664 cum_total += colors[i++].count;
666 /*printf("Split at %d to make %d (half %ld, cumtotal %ld)\n", i, color_count, half, cum_total);*/
668 /* found the spot to split */
669 parts[color_count].start = i;
670 parts[color_count].size = workpart->start + workpart->size - i;
671 workpart->size = i - workpart->start;
672 parts[color_count].pixels = workpart->pixels - cum_total;
673 workpart->pixels = cum_total;
675 /* recalculate the limits */
676 calc_part(workpart, colors);
677 calc_part(parts+color_count, colors);
681 /* fill in the color table - since we could still have partitions
682 that have more than one color, we need to average the colors */
683 for (part_num = 0; part_num < color_count; ++part_num) {
685 medcut_partition *workpart;
687 workpart = parts+part_num;
688 for (ch = 0; ch < 3; ++ch)
691 for (i = workpart->start; i < workpart->start + workpart->size; ++i) {
692 for (ch = 0; ch < 3; ++ch) {
693 sums[ch] += colors[i].rgb[ch] * colors[i].count;
696 for (ch = 0; ch < 3; ++ch) {
697 quant->mc_colors[part_num].channel[ch] = sums[ch] / workpart->pixels;
700 quant->mc_count = color_count;
702 /*printf("out %d colors\n", quant->mc_count);*/
703 i_mempool_destroy(&mp);
707 makemap_mono(i_quantize *quant) {
708 quant->mc_colors[0].rgba.r = 0;
709 quant->mc_colors[0].rgba.g = 0;
710 quant->mc_colors[0].rgba.b = 0;
711 quant->mc_colors[0].rgba.a = 255;
712 quant->mc_colors[1].rgba.r = 255;
713 quant->mc_colors[1].rgba.g = 255;
714 quant->mc_colors[1].rgba.b = 255;
715 quant->mc_colors[1].rgba.a = 255;
721 /* Define one of the following 4 symbols to choose a colour search method
722 The idea is to try these out, including benchmarking, to see which
723 is fastest in a good spread of circumstances.
724 I'd expect IM_CFLINSEARCH to be fastest for very small palettes, and
725 IM_CFHASHBOX for large images with large palettes.
727 Some other possibilities include:
728 - search over entries sorted by luminance
730 Initially I was planning on testing using the macros and then
731 integrating the code directly into each function, but this means if
732 we find a bug at a late stage we will need to update N copies of
733 the same code. Also, keeping the code in the macros means that the
734 code in the translation functions is much more to the point,
735 there's no distracting colour search code to remove attention from
736 what makes _this_ translation function different. It may be
737 advisable to move the setup code into functions at some point, but
738 it should be possible to do this fairly transparently.
740 If IM_CF_COPTS is defined then CFLAGS must have an appropriate
743 Each option needs to define 4 macros:
744 CF_VARS - variables to define in the function
745 CF_SETUP - code to setup for the colour search, eg. allocating and
746 initializing lookup tables
747 CF_FIND - code that looks for the color in val and puts the best
748 matching index in bst_idx
749 CF_CLEANUP - code to clean up, eg. releasing memory
752 /*#define IM_CFLINSEARCH*/
754 /*#define IM_CFSORTCHAN*/
755 /*#define IM_CFRAND2DIST*/
760 /* The original version I wrote for this used the sort.
761 If this is defined then we use a sort to extract the indices for
765 /* assume i is available */
766 #define CF_VARS hashbox *hb = mymalloc(sizeof(hashbox) * 512); \
772 static long *gdists; /* qsort is annoying */
773 /* int might be smaller than long, so we need to do a real compare
774 rather than a subtraction*/
775 static int distcomp(void const *a, void const *b) {
776 long ra = gdists[*(int const *)a];
777 long rb = gdists[*(int const *)b];
788 /* for each hashbox build a list of colours that are in the hb or is closer
790 This is pretty involved. The original gifquant generated the hashbox
791 as part of it's normal processing, but since the map generation is now
792 separated from the translation we need to do this on the spot.
793 Any optimizations, even if they don't produce perfect results would be
796 static void hbsetup(i_quantize *quant, hashbox *hb) {
797 long *dists, mind, maxd;
798 int cr, cb, cg, hbnum, i;
801 int *indices = mymalloc(quant->mc_count * sizeof(int));
804 dists = mymalloc(quant->mc_count * sizeof(long));
805 for (cr = 0; cr < 8; ++cr) {
806 for (cg = 0; cg < 8; ++cg) {
807 for (cb = 0; cb < 8; ++cb) {
808 /* centre of the hashbox */
809 cenc.channel[0] = cr*pboxjump+pboxjump/2;
810 cenc.channel[1] = cg*pboxjump+pboxjump/2;
811 cenc.channel[2] = cb*pboxjump+pboxjump/2;
812 hbnum = pixbox(&cenc);
814 /* order indices in the order of distance from the hashbox */
815 for (i = 0; i < quant->mc_count; ++i) {
819 dists[i] = ceucl_d(&cenc, quant->mc_colors+i);
822 /* it should be possible to do this without a sort
823 but so far I'm too lazy */
825 qsort(indices, quant->mc_count, sizeof(int), distcomp);
826 /* any colors that can match are within mind+diagonal size of
828 mind = dists[indices[0]];
830 maxd = (sqrt(mind)+pboxjump)*(sqrt(mind)+pboxjump);
831 while (i < quant->mc_count && dists[indices[i]] < maxd) {
832 hb[hbnum].vec[hb[hbnum].cnt++] = indices[i++];
835 /* work out the minimum */
837 for (i = 0; i < quant->mc_count; ++i) {
838 if (dists[i] < mind) mind = dists[i];
840 /* transfer any colours that might be closest to a colour in
842 maxd = (sqrt(mind)+pboxjump)*(sqrt(mind)+pboxjump);
843 for (i = 0; i < quant->mc_count; ++i) {
845 hb[hbnum].vec[hb[hbnum].cnt++] = i;
856 #define CF_SETUP hbsetup(quant, hb)
859 currhb = pixbox(&val); \
861 for (i = 0; i < hb[currhb].cnt; ++i) { \
862 cd = ceucl_d(quant->mc_colors+hb[currhb].vec[i], &val); \
863 if (cd < ld) { ld = cd; bst_idx = hb[currhb].vec[i]; } \
866 #define CF_CLEANUP myfree(hb)
870 #ifdef IM_CFLINSEARCH
871 /* as simple as it gets */
872 #define CF_VARS long ld, cd
873 #define CF_SETUP /* none needed */
876 for (i = 0; i < quant->mc_count; ++i) { \
877 cd = ceucl_d(quant->mc_colors+i, &val); \
878 if (cd < ld) { ld = cd; bst_idx = i; } \
884 static int gsortchan;
885 static i_quantize *gquant;
886 static int chansort(void const *a, void const *b) {
887 return gquant->mc_colors[*(int const *)a].channel[gsortchan] -
888 gquant->mc_colors[*(int const *)b].channel[gsortchan];
890 #define CF_VARS int *indices, sortchan, diff; \
892 int vindex[256] /* where to find value i of chan */
894 static void chansetup(i_img *img, i_quantize *quant, int *csortchan,
895 int *vindex, int **cindices) {
896 int *indices, sortchan, chan, i, chval;
897 int chanmins[MAXCHANNELS], chanmaxs[MAXCHANNELS], maxrange;
899 /* find the channel with the maximum range */
900 /* the maximum stddev would probably be better */
901 for (chan = 0; chan < img->channels; ++chan) {
902 chanmins[chan] = 256; chanmaxs[chan] = 0;
903 for (i = 0; i < quant->mc_count; ++i) {
904 if (quant->mc_colors[i].channel[chan] < chanmins[chan])
905 chanmins[chan] = quant->mc_colors[i].channel[chan];
906 if (quant->mc_colors[i].channel[chan] > chanmaxs[chan])
907 chanmaxs[chan] = quant->mc_colors[i].channel[chan];
911 for (chan = 0; chan < img->channels; ++chan) {
912 if (chanmaxs[chan]-chanmins[chan] > maxrange) {
913 maxrange = chanmaxs[chan]-chanmins[chan];
917 indices = mymalloc(quant->mc_count * sizeof(int)) ;
918 for (i = 0; i < quant->mc_count; ++i) {
921 gsortchan = sortchan;
923 qsort(indices, quant->mc_count, sizeof(int), chansort) ;
924 /* now a lookup table to find entries faster */
925 for (chval=0, i=0; i < quant->mc_count; ++i) {
926 while (chval < 256 &&
927 chval < quant->mc_colors[indices[i]].channel[sortchan]) {
931 while (chval < 256) {
932 vindex[chval++] = quant->mc_count-1;
934 *csortchan = sortchan;
939 chansetup(img, quant, &sortchan, vindex, &indices)
941 int chanfind(i_color val, i_quantize *quant, int *indices, int *vindex,
943 int i, bst_idx, diff, maxdiff;
946 i = vindex[val.channel[sortchan]];
947 bst_idx = indices[i];
950 maxdiff = quant->mc_count;
951 while (diff < maxdiff) {
952 if (i+diff < quant->mc_count) {
953 cd = ceucl_d(&val, quant->mc_colors+indices[i+diff]);
955 bst_idx = indices[i+diff];
961 cd = ceucl_d(&val, quant->mc_colors+indices[i-diff]);
963 bst_idx = indices[i-diff];
975 bst_idx = chanfind(val, quant, indices, vindex, sortchan)
978 #define CF_CLEANUP myfree(indices)
982 #ifdef IM_CFRAND2DIST
984 /* This is based on a method described by Addi in the #imager channel
985 on the 28/2/2001. I was about 1am Sydney time at the time, so I
986 wasn't at my most cogent. Well, that's my excuse :)
988 <TonyC> what I have at the moment is: hashboxes, with optimum hash box
989 filling; simple linear search; and a lookup in the widest channel
990 (currently the channel with the maximum range)
991 <Addi> There is one more way that might be simple to implement.
992 <Addi> You want to hear?
994 <purl> somebody said that was not true
995 <Addi> For each of the colors in the palette start by creating a
996 sorted list of the form:
997 <Addi> [distance, color]
998 <Addi> Where they are sorted by distance.
999 <TonyC> distance to where?
1000 <Addi> Where the elements in the lists are the distances and colors of
1001 the other colors in the palette
1003 <Addi> So if you are at color 0
1004 <Addi> ok - now to search for the closest color when you are creating
1005 the final image is done like this:
1006 <Addi> a) pick a random color from the palette
1007 <Addi> b) calculate the distance to it
1008 <Addi> c) only check the vectors that are within double the distance
1009 in the list of the color you picked from the palette.
1010 <Addi> Does that seem logical?
1011 <Addi> Lets imagine that we only have grayscale to make an example:
1012 <Addi> Our palette has 1 4 10 20 as colors.
1013 <Addi> And we want to quantize the color 11
1014 <Addi> lets say we picked 10 randomly
1015 <Addi> the double distance is 2
1016 <Addi> since abs(10-11)*2 is 2
1017 <Addi> And the list at vector 10 is this:
1018 <Addi> [0, 10], [6 4], [9, 1], [10, 20]
1019 <Addi> so we look at the first one (but not the second one since 6 is
1020 at a greater distance than 2.
1021 <Addi> Any of that make sense?
1022 <TonyC> yes, though are you suggesting another random jump to one of
1023 the colours with the possible choices? or an exhaustive search?
1024 <Addi> TonyC: It's possible to come up with a recursive/iterative
1025 enhancement but this is the 'basic' version.
1026 <Addi> Which would do an iterative search.
1027 <Addi> You can come up with conditions where it pays to switch to a new one.
1028 <Addi> And the 'random' start can be switched over to a small tree.
1029 <Addi> So you would have a little index at the start.
1030 <Addi> to get you into the general direction
1031 <Addi> Perhaps just an 8 split.
1032 <Addi> that is - split each dimension in half.
1034 <TonyC> I get the idea
1035 <Addi> But this would seem to be a good approach in our case since we
1036 usually have few codevectors.
1037 <Addi> So we only need 256*256 entries in a table.
1038 <Addi> We could even only index some of them that were deemed as good
1040 <TonyC> I was considering adding paletted output support for PNG and
1041 TIFF at some point, which support 16-bit palettes
1049 typedef struct i_dists {
1057 static int dists_sort(void const *a, void const *b) {
1058 return ((i_dists *)a)->dist - ((i_dists *)b)->dist;
1061 static void rand2dist_setup(i_quantize *quant, i_dists **cdists) {
1063 mymalloc(sizeof(i_dists)*quant->mc_count*quant->mc_count);
1066 for (i = 0; i < quant->mc_count; ++i) {
1067 i_dists *ldists = dists + quant->mc_count * i;
1068 i_color val = quant->mc_colors[i];
1069 for (j = 0; j < quant->mc_count; ++j) {
1070 ldists[j].index = j;
1071 ldists[j].dist = ceucl_d(&val, quant->mc_colors+j);
1073 qsort(ldists, quant->mc_count, sizeof(i_dists), dists_sort);
1079 bst_idx = rand() % quant->mc_count; \
1080 rand2dist_setup(quant, &dists)
1082 static int rand2dist_find(i_color val, i_quantize *quant, i_dists *dists, int index) {
1089 cdists = dists + index * quant->mc_count;
1091 maxld = 8 * ceucl_d(&val, quant->mc_colors+index);
1092 for (i = 0; i < quant->mc_count && cdists[i].dist <= maxld; ++i) {
1093 cd = ceucl_d(&val, quant->mc_colors+cdists[i].index);
1095 bst_idx = cdists[i].index;
1102 #define CF_FIND bst_idx = rand2dist_find(val, quant, dists, bst_idx)
1104 #define CF_CLEANUP myfree(dists)
1109 static void translate_addi(i_quantize *quant, i_img *img, i_palidx *out) {
1110 int x, y, i, k, bst_idx = 0;
1112 int pixdev = quant->perturb;
1117 if (img->channels >= 3) {
1120 for(y=0;y<img->ysize;y++) for(x=0;x<img->xsize;x++) {
1121 i_gpix(img,x,y,&val);
1122 val.channel[0]=g_sat(val.channel[0]+(int)(pixdev*frandn()));
1123 val.channel[1]=g_sat(val.channel[1]+(int)(pixdev*frandn()));
1124 val.channel[2]=g_sat(val.channel[2]+(int)(pixdev*frandn()));
1130 for(y=0;y<img->ysize;y++) for(x=0;x<img->xsize;x++) {
1131 i_gpix(img,x,y,&val);
1140 for(y=0;y<img->ysize;y++) for(x=0;x<img->xsize;x++) {
1141 i_gpix(img,x,y,&val);
1142 val.channel[1] = val.channel[2] =
1143 val.channel[0]=g_sat(val.channel[0]+(int)(pixdev*frandn()));
1149 for(y=0;y<img->ysize;y++) for(x=0;x<img->xsize;x++) {
1150 i_gpix(img,x,y,&val);
1151 val.channel[1] = val.channel[2] = val.channel[0];
1160 static int floyd_map[] =
1166 static int jarvis_map[] =
1173 static int stucki_map[] =
1180 struct errdiff_map {
1182 int width, height, orig;
1185 static struct errdiff_map maps[] =
1187 { floyd_map, 3, 2, 1 },
1188 { jarvis_map, 5, 3, 2 },
1189 { stucki_map, 5, 3, 2 },
1192 typedef struct errdiff_tag {
1196 /* perform an error diffusion dither */
1199 translate_errdiff(i_quantize *quant, i_img *img, i_palidx *out) {
1201 int mapw, maph, mapo;
1210 if ((quant->errdiff & ed_mask) == ed_custom) {
1211 map = quant->ed_map;
1212 mapw = quant->ed_width;
1213 maph = quant->ed_height;
1214 mapo = quant->ed_orig;
1217 int index = quant->errdiff & ed_mask;
1218 if (index >= ed_custom) index = ed_floyd;
1219 map = maps[index].map;
1220 mapw = maps[index].width;
1221 maph = maps[index].height;
1222 mapo = maps[index].orig;
1225 errw = img->xsize+mapw;
1226 err = mymalloc(sizeof(*err) * maph * errw);
1227 /*errp = err+mapo;*/
1228 memset(err, 0, sizeof(*err) * maph * errw);
1231 for (i = 0; i < maph * mapw; ++i)
1232 difftotal += map[i];
1234 for (dy = 0; dy < maph; ++dy) {
1235 for (dx = 0; dx < mapw; ++dx) {
1236 printf("%2d", map[dx+dy*mapw]);
1243 for (y = 0; y < img->ysize; ++y) {
1244 for (x = 0; x < img->xsize; ++x) {
1248 i_gpix(img, x, y, &val);
1249 if (img->channels < 3) {
1250 val.channel[1] = val.channel[2] = val.channel[0];
1253 perr.r = perr.r < 0 ? -((-perr.r)/difftotal) : perr.r/difftotal;
1254 perr.g = perr.g < 0 ? -((-perr.g)/difftotal) : perr.g/difftotal;
1255 perr.b = perr.b < 0 ? -((-perr.b)/difftotal) : perr.b/difftotal;
1256 /*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);*/
1257 val.channel[0] = g_sat(val.channel[0]-perr.r);
1258 val.channel[1] = g_sat(val.channel[1]-perr.g);
1259 val.channel[2] = g_sat(val.channel[2]-perr.b);
1262 perr.r = quant->mc_colors[bst_idx].channel[0] - val.channel[0];
1263 perr.g = quant->mc_colors[bst_idx].channel[1] - val.channel[1];
1264 perr.b = quant->mc_colors[bst_idx].channel[2] - val.channel[2];
1265 /*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);*/
1266 for (dx = 0; dx < mapw; ++dx) {
1267 for (dy = 0; dy < maph; ++dy) {
1268 err[x+dx+dy*errw].r += perr.r * map[dx+mapw*dy];
1269 err[x+dx+dy*errw].g += perr.g * map[dx+mapw*dy];
1270 err[x+dx+dy*errw].b += perr.b * map[dx+mapw*dy];
1275 /* shift up the error matrix */
1276 for (dy = 0; dy < maph-1; ++dy) {
1277 memcpy(err+dy*errw, err+(dy+1)*errw, sizeof(*err)*errw);
1279 memset(err+(maph-1)*errw, 0, sizeof(*err)*errw);
1284 /* Prescan finds the boxes in the image that have the highest number of colors
1285 and that result is used as the initial value for the vectores */
1288 static void prescan(i_img **imgs,int count, int cnum, cvec *clr, i_sample_t *line) {
1294 for(i=0;i<512;i++) {
1300 /* process each image */
1301 for (i = 0; i < count; ++i) {
1302 i_img *im = imgs[i];
1303 chans = im->channels >= 3 ? NULL : gray_samples;
1304 for(y=0;y<im->ysize;y++) {
1305 i_gsamp(im, 0, im->xsize, y, line, chans, 3);
1307 for(x=0;x<im->xsize;x++) {
1308 prebox[pixbox_ch(val)].pixcnt++;
1313 for(i=0;i<512;i++) prebox[i].pdc=prebox[i].pixcnt;
1314 qsort(prebox,512,sizeof(pbox),(cmpfunc)pboxcmp);
1316 for(i=0;i<cnum;i++) {
1317 /* printf("Color %d\n",i);
1318 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); }
1323 /* 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); } */
1329 /* printf("prebox[%d].cand=%d\n",k,prebox[k].cand); */
1330 if (clr[i].fixed) { i++; continue; } /* reserved go to next */
1331 if (j>=prebox[k].cand) { k++; j=1; } else {
1332 if (prebox[k].cand == 2) boxcenter(prebox[k].boxnum,&(clr[i]));
1333 else boxrand(prebox[k].boxnum,&(clr[i]));
1334 /* 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); */
1342 static void reorder(pbox prescan[512]) {
1350 c.pdc=c.pixcnt/(c.cand*c.cand);
1351 /* c.pdc=c.pixcnt/c.cand; */
1352 while(c.pdc < prescan[nidx+1].pdc && nidx < 511) {
1353 prescan[nidx]=prescan[nidx+1];
1360 pboxcmp(const pbox *a,const pbox *b) {
1361 if (a->pixcnt > b->pixcnt) return -1;
1362 if (a->pixcnt < b->pixcnt) return 1;
1367 boxcenter(int box,cvec *cv) {
1368 cv->r=15+((box&448)>>1);
1369 cv->g=15+((box&56)<<2);
1370 cv->b=15+((box&7)<<5);
1374 bbox(int box,int *r0,int *r1,int *g0,int *g1,int *b0,int *b1) {
1384 boxrand(int box,cvec *cv) {
1385 cv->r=6+(rand()%25)+((box&448)>>1);
1386 cv->g=6+(rand()%25)+((box&56)<<2);
1387 cv->b=6+(rand()%25)+((box&7)<<5);
1397 while (w >= 1 || w == 0) {
1398 u1 = 2 * frand() - 1;
1399 u2 = 2 * frand() - 1;
1403 w = sqrt((-2*log(w))/w);
1407 /* Create hash index */
1410 cr_hashindex(cvec clr[256],int cnum,hashbox hb[512]) {
1412 int bx,mind,cd,cumcnt,bst_idx,i;
1413 /* printf("indexing... \n");*/
1416 for(bx=0; bx<512; bx++) {
1418 for(i=0; i<cnum; i++) {
1419 cd = maxdist(bx,&clr[i]);
1420 if (cd < mind) { mind=cd; bst_idx=i; }
1424 for(i=0;i<cnum;i++) if (mindist(bx,&clr[i])<mind) hb[bx].vec[hb[bx].cnt++]=i;
1425 /*printf("box %d -> approx -> %d\n",bx,hb[bx].cnt); */
1426 /* statbox(bx,cnum,clr); */
1430 /* printf("Average search space: %d\n",cumcnt/512); */
1434 maxdist(int boxnum,cvec *cv) {
1435 int r0,r1,g0,g1,b0,b1;
1442 bbox(boxnum,&r0,&r1,&g0,&g1,&b0,&b1);
1444 mr=i_max(abs(b-b0),abs(b-b1));
1445 mg=i_max(abs(g-g0),abs(g-g1));
1446 mb=i_max(abs(r-r0),abs(r-r1));
1448 return PWR2(mr)+PWR2(mg)+PWR2(mb);
1452 mindist(int boxnum,cvec *cv) {
1453 int r0,r1,g0,g1,b0,b1;
1460 bbox(boxnum,&r0,&r1,&g0,&g1,&b0,&b1);
1462 /* printf("box %d, (%d,%d,%d)-(%d,%d,%d) vec (%d,%d,%d) ",boxnum,r0,g0,b0,r1,g1,b1,r,g,b); */
1464 if (r0<=r && r<=r1 && g0<=g && g<=g1 && b0<=b && b<=b1) return 0;
1466 mr=i_min(abs(b-b0),abs(b-b1));
1467 mg=i_min(abs(g-g0),abs(g-g1));
1468 mb=i_min(abs(r-r0),abs(r-r1));
1474 if (r0<=r && r<=r1 && g0<=g && g<=g1) return mb;
1475 if (r0<=r && r<=r1 && b0<=b && b<=b1) return mg;
1476 if (b0<=b && b<=b1 && g0<=g && g<=g1) return mr;
1478 if (r0<=r && r<=r1) return mg+mb;
1479 if (g0<=g && g<=g1) return mr+mb;
1480 if (b0<=b && b<=b1) return mg+mr;
1485 static void transparent_threshold(i_quantize *, i_palidx *, i_img *, i_palidx);
1486 static void transparent_errdiff(i_quantize *, i_palidx *, i_img *, i_palidx);
1487 static void transparent_ordered(i_quantize *, i_palidx *, i_img *, i_palidx);
1490 =item i_quant_transparent(C<quant>, C<data>, C<img>, C<trans_index>)
1492 =category Image quantization
1494 Dither the alpha channel on C<img> into the palette indexes in
1495 C<data>. Pixels to be transparent are replaced with C<trans_pixel>.
1497 The method used depends on the tr_* members of C<quant>.
1503 i_quant_transparent(i_quantize *quant, i_palidx *data, i_img *img,
1504 i_palidx trans_index)
1506 switch (quant->transp) {
1511 quant->tr_threshold = 128;
1514 transparent_threshold(quant, data, img, trans_index);
1518 transparent_errdiff(quant, data, img, trans_index);
1522 transparent_ordered(quant, data, img, trans_index);
1528 transparent_threshold(i_quantize *quant, i_palidx *data, i_img *img,
1529 i_palidx trans_index)
1532 i_sample_t *line = mymalloc(img->xsize * sizeof(i_sample_t));
1533 int trans_chan = img->channels > 2 ? 3 : 1;
1535 for (y = 0; y < img->ysize; ++y) {
1536 i_gsamp(img, 0, img->xsize, y, line, &trans_chan, 1);
1537 for (x = 0; x < img->xsize; ++x) {
1538 if (line[x] < quant->tr_threshold)
1539 data[y*img->xsize+x] = trans_index;
1546 transparent_errdiff(i_quantize *quant, i_palidx *data, i_img *img,
1547 i_palidx trans_index)
1551 int mapw, maph, mapo;
1552 int errw, *err, *errp;
1553 int difftotal, out, error;
1554 int x, y, dx, dy, i;
1556 int trans_chan = img->channels > 2 ? 3 : 1;
1558 /* no custom map for transparency (yet) */
1559 index = quant->tr_errdiff & ed_mask;
1560 if (index >= ed_custom) index = ed_floyd;
1561 map = maps[index].map;
1562 mapw = maps[index].width;
1563 maph = maps[index].height;
1564 mapo = maps[index].orig;
1566 errw = img->xsize+mapw-1;
1567 err = mymalloc(sizeof(*err) * maph * errw);
1569 memset(err, 0, sizeof(*err) * maph * errw);
1571 line = mymalloc(img->xsize * sizeof(i_sample_t));
1573 for (i = 0; i < maph * mapw; ++i)
1574 difftotal += map[i];
1575 for (y = 0; y < img->ysize; ++y) {
1576 i_gsamp(img, 0, img->xsize, y, line, &trans_chan, 1);
1577 for (x = 0; x < img->xsize; ++x) {
1578 line[x] = g_sat(line[x]-errp[x]/difftotal);
1579 if (line[x] < 128) {
1581 data[y*img->xsize+x] = trans_index;
1586 error = out - line[x];
1587 for (dx = 0; dx < mapw; ++dx) {
1588 for (dy = 0; dy < maph; ++dy) {
1589 errp[x+dx-mapo+dy*errw] += error * map[dx+mapw*dy];
1593 /* shift up the error matrix */
1594 for (dy = 0; dy < maph-1; ++dy)
1595 memcpy(err+dy*errw, err+(dy+1)*errw, sizeof(*err)*errw);
1596 memset(err+(maph-1)*errw, 0, sizeof(*err)*errw);
1602 /* builtin ordered dither maps */
1603 static unsigned char
1604 orddith_maps[][64] =
1607 this is purely random - it's pretty awful
1609 48, 72, 196, 252, 180, 92, 108, 52,
1610 228, 176, 64, 8, 236, 40, 20, 164,
1611 120, 128, 84, 116, 24, 28, 172, 220,
1612 68, 0, 188, 124, 184, 224, 192, 104,
1613 132, 100, 240, 200, 152, 160, 244, 44,
1614 96, 204, 144, 16, 140, 56, 232, 216,
1615 208, 4, 76, 212, 136, 248, 80, 168,
1616 156, 88, 32, 112, 148, 12, 36, 60,
1620 perl spot.perl '($x-3.5)*($x-3.5)+($y-3.5)*($y-3.5)'
1622 240, 232, 200, 136, 140, 192, 228, 248,
1623 220, 148, 100, 76, 80, 104, 152, 212,
1624 180, 116, 56, 32, 36, 60, 120, 176,
1625 156, 64, 28, 0, 8, 44, 88, 160,
1626 128, 92, 24, 12, 4, 40, 68, 132,
1627 184, 96, 48, 20, 16, 52, 108, 188,
1628 216, 144, 112, 72, 84, 124, 164, 224,
1629 244, 236, 196, 168, 172, 204, 208, 252,
1633 'min(dist(1.5, 1.5),dist(5.5,1.5),dist(1.5,5.5),dist(5.5,5.5))'
1635 196, 72, 104, 220, 200, 80, 112, 224,
1636 76, 4, 24, 136, 84, 8, 32, 144,
1637 108, 28, 52, 168, 116, 36, 56, 176,
1638 216, 140, 172, 244, 228, 148, 180, 248,
1639 204, 92, 124, 236, 192, 68, 96, 208,
1640 88, 12, 44, 156, 64, 0, 16, 128,
1641 120, 40, 60, 188, 100, 20, 48, 160,
1642 232, 152, 184, 252, 212, 132, 164, 240,
1645 perl spot.perl '$y-3'
1647 160, 164, 168, 172, 176, 180, 184, 188,
1648 128, 132, 136, 140, 144, 148, 152, 156,
1649 32, 36, 40, 44, 48, 52, 56, 60,
1650 0, 4, 8, 12, 16, 20, 24, 28,
1651 64, 68, 72, 76, 80, 84, 88, 92,
1652 96, 100, 104, 108, 112, 116, 120, 124,
1653 192, 196, 200, 204, 208, 212, 216, 220,
1654 224, 228, 232, 236, 240, 244, 248, 252,
1657 perl spot.perl '$x-3'
1659 180, 100, 40, 12, 44, 104, 184, 232,
1660 204, 148, 60, 16, 64, 128, 208, 224,
1661 212, 144, 76, 8, 80, 132, 216, 244,
1662 160, 112, 68, 20, 84, 108, 172, 236,
1663 176, 96, 72, 28, 88, 152, 188, 228,
1664 200, 124, 92, 0, 32, 116, 164, 240,
1665 168, 120, 36, 24, 48, 136, 192, 248,
1666 196, 140, 52, 4, 56, 156, 220, 252,
1669 perl spot.perl '$y+$x-7'
1671 248, 232, 224, 192, 140, 92, 52, 28,
1672 240, 220, 196, 144, 108, 60, 12, 64,
1673 216, 180, 148, 116, 76, 20, 80, 128,
1674 204, 152, 104, 44, 16, 72, 100, 160,
1675 164, 96, 68, 24, 56, 112, 168, 176,
1676 124, 40, 8, 36, 88, 136, 184, 212,
1677 84, 4, 32, 120, 156, 188, 228, 236,
1678 0, 48, 132, 172, 200, 208, 244, 252,
1681 perl spot.perl '$y-$x'
1683 0, 32, 116, 172, 184, 216, 236, 252,
1684 56, 8, 72, 132, 136, 200, 228, 240,
1685 100, 36, 12, 40, 92, 144, 204, 220,
1686 168, 120, 60, 16, 44, 96, 156, 176,
1687 180, 164, 112, 48, 28, 52, 128, 148,
1688 208, 192, 152, 88, 84, 20, 64, 104,
1689 232, 224, 196, 140, 108, 68, 24, 76,
1690 248, 244, 212, 188, 160, 124, 80, 4,
1694 good for display, bad for print
1697 0, 128, 32, 192, 8, 136, 40, 200,
1698 224, 64, 160, 112, 232, 72, 168, 120,
1699 48, 144, 16, 208, 56, 152, 24, 216,
1700 176, 96, 240, 80, 184, 104, 248, 88,
1701 12, 140, 44, 204, 4, 132, 36, 196,
1702 236, 76, 172, 124, 228, 68, 164, 116,
1703 60, 156, 28, 220, 52, 148, 20, 212,
1704 188, 108, 252, 92, 180, 100, 244, 84,
1709 transparent_ordered(i_quantize *quant, i_palidx *data, i_img *img,
1710 i_palidx trans_index)
1712 unsigned char *spot;
1715 int trans_chan = img->channels > 2 ? 3 : 1;
1716 if (quant->tr_orddith == od_custom)
1717 spot = quant->tr_custom;
1719 spot = orddith_maps[quant->tr_orddith];
1721 line = mymalloc(img->xsize * sizeof(i_sample_t));
1722 for (y = 0; y < img->ysize; ++y) {
1723 i_gsamp(img, 0, img->xsize, y, line, &trans_chan, 1);
1724 for (x = 0; x < img->xsize; ++x) {
1725 if (line[x] < spot[(x&7)+(y&7)*8])
1726 data[x+y*img->xsize] = trans_index;