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) {
84 mm_log((1, "quant_translate(quant %p, img %p)\n", quant, img));
86 /* there must be at least one color in the paletted (though even that
88 if (quant->mc_count == 0) {
89 i_push_error(0, "no colors available for translation");
93 bytes = img->xsize * img->ysize;
94 if (bytes / img->ysize != img->xsize) {
95 i_push_error(0, "integer overflow calculating memory allocation");
98 result = mymalloc(bytes);
100 switch (quant->translate) {
103 translate_closest(quant, img, result);
107 translate_errdiff(quant, img, result);
112 translate_addi(quant, img, result);
119 static void translate_closest(i_quantize *quant, i_img *img, i_palidx *out) {
121 translate_addi(quant, img, out);
124 #define PWR2(x) ((x)*(x))
126 typedef int (*cmpfunc)(const void*, const void*);
149 static void prescan(i_img **im,int count, int cnum, cvec *clr, i_sample_t *line);
150 static void reorder(pbox prescan[512]);
151 static int pboxcmp(const pbox *a,const pbox *b);
152 static void boxcenter(int box,cvec *cv);
153 static float frandn(void);
154 static void boxrand(int box,cvec *cv);
155 static void bbox(int box,int *r0,int *r1,int *g0,int *g1,int *b0,int *b1);
156 static void cr_hashindex(cvec clr[256],int cnum,hashbox hb[512]);
157 static int mindist(int boxnum,cvec *cv);
158 static int maxdist(int boxnum,cvec *cv);
160 /* Some of the simpler functions are kept here to aid the compiler -
161 maybe some of them will be inlined. */
164 pixbox(i_color *ic) { return ((ic->channel[0] & 224)<<1)+ ((ic->channel[1]&224)>>2) + ((ic->channel[2] &224) >> 5); }
167 pixbox_ch(i_sample_t *chans) { return ((chans[0] & 224)<<1)+ ((chans[1]&224)>>2) + ((chans[2] &224) >> 5); }
171 if (in>255) { return 255; }
172 else if (in>0) return in;
179 return rand()/(RAND_MAX+1.0);
184 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]); }
188 eucl_d_ch(cvec* cv,i_sample_t *chans) {
189 return PWR2(cv->r - chans[0]) + PWR2(cv->g - chans[1])
190 + PWR2(cv->b - chans[2]);
195 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]); }
198 gray_samples[] = { 0, 0, 0 };
202 This quantization algorithm and implementation routines are by Arnar
203 M. Hrafnkelson. In case any new ideas are here they are mine since
204 this was written from scratch.
206 The algorithm uses local means in the following way:
208 For each point in the colormap we find which image points
209 have that point as it's closest point. We calculate the mean
210 of those points and in the next iteration it will be the new
211 entry in the colormap.
213 In order to speed this process up (i.e. nearest neighbor problem) We
214 divied the r,g,b space up in equally large 512 boxes. The boxes are
215 numbered from 0 to 511. Their numbering is so that for a given vector
216 it is known that it belongs to the box who is formed by concatenating the
217 3 most significant bits from each component of the RGB triplet.
219 For each box we find the list of points from the colormap who might be
220 closest to any given point within the box. The exact solution
221 involves finding the Voronoi map (or the dual the Delauny
222 triangulation) and has many issues including numerical stability.
224 So we use this approximation:
226 1. Find which point has the shortest maximum distance to the box.
227 2. Find all points that have a shorter minimum distance than that to the box
229 This is a very simple task and is not computationally heavy if one
230 takes into account that the minimum distances from a pixel to a box is
231 always found by checking if it's inside the box or is closest to some
232 side or a corner. Finding the maximum distance is also either a side
235 This approach results 2-3 times more than the actual points needed but
236 is still a good gain over the complete space. Usually when one has a
237 256 Colorcolor map a search over 30 is often obtained.
239 A bit of an enhancement to this approach is to keep a seperate list
240 for each side of the cube, but this will require even more memory.
242 Arnar M. Hrafnkelsson (addi@umich.edu);
246 Extracted from gifquant.c, removed dependencies on gif_lib,
247 and added support for multiple images.
248 starting from 1nov2000 by TonyC <tony@develop-help.com>.
253 makemap_addi(i_quantize *quant, i_img **imgs, int count) {
255 int cnum, i, x, y, bst_idx=0, ld, cd, iter, currhb, img_num;
262 const int *sample_indices;
264 mm_log((1, "makemap_addi(quant %p { mc_count=%d, mc_colors=%p }, imgs %p, count %d)\n",
265 quant, quant->mc_count, quant->mc_colors, imgs, count));
269 clr = i_mempool_alloc(&mp, sizeof(cvec) * quant->mc_size);
270 hb = i_mempool_alloc(&mp, sizeof(hashbox) * 512);
271 for (i=0; i < quant->mc_count; ++i) {
272 clr[i].r = quant->mc_colors[i].rgb.r;
273 clr[i].g = quant->mc_colors[i].rgb.g;
274 clr[i].b = quant->mc_colors[i].rgb.b;
278 /* mymalloc doesn't clear memory, so I think we need this */
279 for (; i < quant->mc_size; ++i) {
280 /*clr[i].r = clr[i].g = clr[i].b = 0;*/
287 cnum = quant->mc_size;
290 for (img_num = 0; img_num < count; ++img_num) {
291 if (imgs[img_num]->xsize > maxwidth)
292 maxwidth = imgs[img_num]->xsize;
294 line = i_mempool_alloc(&mp, 3 * maxwidth * sizeof(*line));
296 prescan(imgs, count, cnum, clr, line);
297 cr_hashindex(clr, cnum, hb);
299 for(iter=0;iter<3;iter++) {
302 for (img_num = 0; img_num < count; ++img_num) {
303 i_img *im = imgs[img_num];
304 sample_indices = im->channels >= 3 ? NULL : gray_samples;
305 for(y=0;y<im->ysize;y++) {
306 i_gsamp(im, 0, im->xsize, y, line, sample_indices, 3);
308 for(x=0;x<im->xsize;x++) {
310 /*i_gpix(im,x,y,&val);*/
311 currhb=pixbox_ch(val);
312 /* printf("box = %d \n",currhb); */
313 for(i=0;i<hb[currhb].cnt;i++) {
314 /* 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); */
316 cd=eucl_d_ch(&clr[hb[currhb].vec[i]],val);
318 ld=cd; /* shortest distance yet */
319 bst_idx=hb[currhb].vec[i]; /* index of closest vector yet */
323 clr[bst_idx].mcount++;
325 clr[bst_idx].dr+=val[0];
326 clr[bst_idx].dg+=val[1];
327 clr[bst_idx].db+=val[2];
329 val += 3; /* next 3 samples (next pixel) */
336 clr[i].dr/=clr[i].mcount;
337 clr[i].dg/=clr[i].mcount;
338 clr[i].db/=clr[i].mcount;
341 /* for(i=0;i<cnum;i++) printf("vec(%d)=(%d,%d,%d) dest=(%d,%d,%d) matchcount=%d\n",
342 i,clr[i].r,clr[i].g,clr[i].b,clr[i].dr,clr[i].dg,clr[i].db,clr[i].mcount); */
344 /* printf("total error: %.2f\n",sqrt(accerr)); */
346 for(i=0;i<cnum;i++) {
347 if (clr[i].fixed) continue; /* skip reserved colors */
351 clr[i].r=clr[i].r*(1-dlt)+dlt*clr[i].dr;
352 clr[i].g=clr[i].g*(1-dlt)+dlt*clr[i].dg;
353 clr[i].b=clr[i].b*(1-dlt)+dlt*clr[i].db;
355 /* let's try something else */
367 cr_hashindex(clr,cnum,hb);
372 for(i=0;i<cnum;i++) {
373 cd=eucl_d(&clr[i],&val);
381 /* if defined, we only include colours with an mcount or that were
382 supplied in the fixed palette, giving us a smaller output palette */
383 #define ONLY_USE_USED
385 /* transfer the colors back */
387 for (i = 0; i < cnum; ++i) {
388 if (clr[i].fixed || clr[i].used) {
389 /*printf("Adding %d (%d,%d,%d)\n", i, clr[i].r, clr[i].g, clr[i].b);*/
390 quant->mc_colors[quant->mc_count].rgb.r = clr[i].r;
391 quant->mc_colors[quant->mc_count].rgb.g = clr[i].g;
392 quant->mc_colors[quant->mc_count].rgb.b = clr[i].b;
397 /* transfer the colors back */
398 for (i = 0; i < cnum; ++i) {
399 quant->mc_colors[i].rgb.r = clr[i].r;
400 quant->mc_colors[i].rgb.g = clr[i].g;
401 quant->mc_colors[i].rgb.b = clr[i].b;
403 quant->mc_count = cnum;
407 mm_log((1, "makemap_addi returns - quant.mc_count = %d\n", quant->mc_count));
408 for (i = 0; i < quant->mc_count; ++i)
409 mm_log((5, " map entry %d: (%d, %d, %d)\n", i, clr[i].r, clr[i].g, clr[i].b));
412 i_mempool_destroy(&mp);
420 #define MEDIAN_CUT_COLORS 32768
422 #define MED_CUT_INDEX(c) ((((c).rgb.r & 0xF8) << 7) | \
423 (((c).rgb.g & 0xF8) << 2) | (((c).rgb.b & 0xF8) >> 3))
425 #define MED_CUT_GRAY_INDEX(c) ((((c).rgb.r & 0xF8) << 7) | \
426 (((c).rgb.r & 0xF8) << 2) | (((c).rgb.r & 0xF8) >> 3))
428 /* scale these to cover the whole range */
429 #define MED_CUT_RED(index) ((((index) & 0x7C00) >> 10) * 255 / 31)
430 #define MED_CUT_GREEN(index) ((((index) & 0x3E0) >> 5) * 255 / 31)
431 #define MED_CUT_BLUE(index) (((index) & 0x1F) * 255 / 31)
434 i_sample_t min[3]; /* minimum for each channel */
435 i_sample_t max[3]; /* maximum for each channel */
436 i_sample_t width[3]; /* width for each channel */
437 int start, size; /* beginning and size of the partition */
438 int pixels; /* number of pixels represented by this partition */
442 =item calc_part(part, colors)
444 Calculates the new color limits for the given partition.
446 Giflib assumes that the limits for the non-split channels stay the
447 same, but this strikes me as incorrect, especially if the colors tend
450 Of course this could be optimized by not recalculating the channel we
451 just sorted on, but it's not worth the effort right now.
455 static void calc_part(medcut_partition *part, quant_color_entry *colors) {
458 for (ch = 0; ch < 3; ++ch) {
462 for (i = part->start; i < part->start + part->size; ++i) {
463 for (ch = 0; ch < 3; ++ch) {
464 if (part->min[ch] > colors[i].rgb[ch])
465 part->min[ch] = colors[i].rgb[ch];
466 if (part->max[ch] < colors[i].rgb[ch])
467 part->max[ch] = colors[i].rgb[ch];
470 for (ch = 0; ch < 3; ++ch) {
471 part->width[ch] = part->max[ch] - part->min[ch];
475 /* simple functions to sort by each channel - we could use a global, but
479 color_sort_red(void const *left, void const *right) {
480 return ((quant_color_entry *)left)->rgb[0] - ((quant_color_entry *)right)->rgb[0];
484 color_sort_green(void const *left, void const *right) {
485 return ((quant_color_entry *)left)->rgb[1] - ((quant_color_entry *)right)->rgb[1];
489 color_sort_blue(void const *left, void const *right) {
490 return ((quant_color_entry *)left)->rgb[2] - ((quant_color_entry *)right)->rgb[2];
493 static int (*sorters[])(void const *, void const *) =
501 makemap_mediancut(i_quantize *quant, i_img **imgs, int count) {
502 quant_color_entry *colors;
504 int imgn, x, y, i, ch;
509 medcut_partition *parts;
512 /* number of channels we search for the best channel to partition
513 this isn't terribly efficient, but it should work */
516 /*printf("images %d pal size %d\n", count, quant->mc_size);*/
520 colors = i_mempool_alloc(&mp, sizeof(*colors) * MEDIAN_CUT_COLORS);
521 for (i = 0; i < MEDIAN_CUT_COLORS; ++i) {
522 colors[i].rgb[0] = MED_CUT_RED(i);
523 colors[i].rgb[1] = MED_CUT_GREEN(i);
524 colors[i].rgb[2] = MED_CUT_BLUE(i);
529 for (imgn = 0; imgn < count; ++imgn) {
530 if (imgs[imgn]->xsize > max_width)
531 max_width = imgs[imgn]->xsize;
533 line = i_mempool_alloc(&mp, sizeof(i_color) * max_width);
535 /* build the stats */
537 chan_count = 1; /* assume we just have grayscale */
538 for (imgn = 0; imgn < count; ++imgn) {
539 total_pixels += imgs[imgn]->xsize * imgs[imgn]->ysize;
540 for (y = 0; y < imgs[imgn]->ysize; ++y) {
541 i_glin(imgs[imgn], 0, imgs[imgn]->xsize, y, line);
542 if (imgs[imgn]->channels > 2) {
544 for (x = 0; x < imgs[imgn]->xsize; ++x) {
545 ++colors[MED_CUT_INDEX(line[x])].count;
549 /* a gray-scale image, just use the first channel */
550 for (x = 0; x < imgs[imgn]->xsize; ++x) {
551 ++colors[MED_CUT_GRAY_INDEX(line[x])].count;
557 /* eliminate the empty colors */
559 for (in = 0; in < MEDIAN_CUT_COLORS; ++in) {
560 if (colors[in].count) {
561 colors[out++] = colors[in];
564 /*printf("out %d\n", out);
566 for (i = 0; i < out; ++i) {
567 if (colors[i].count) {
568 printf("%d: (%d,%d,%d) -> %d\n", i, colors[i].rgb[0], colors[i].rgb[1],
569 colors[i].rgb[2], colors[i].count);
573 if (out < quant->mc_size) {
574 /* just copy them into the color table */
575 for (i = 0; i < out; ++i) {
576 for (ch = 0; ch < 3; ++ch) {
577 quant->mc_colors[i].channel[ch] = colors[i].rgb[ch];
580 quant->mc_count = out;
583 /* build the starting partition */
584 parts = i_mempool_alloc(&mp, sizeof(*parts) * quant->mc_size);
587 parts[0].pixels = total_pixels;
588 calc_part(parts, colors);
591 while (color_count < quant->mc_size) {
592 int max_index, max_ch; /* index/channel with biggest spread */
594 medcut_partition *workpart;
598 /* find the partition with the most biggest span with more than
601 for (i = 0; i < color_count; ++i) {
602 for (ch = 0; ch < chan_count; ++ch) {
603 if (parts[i].width[ch] > max_size
604 && parts[i].size > 1) {
607 max_size = parts[i].width[ch];
612 /* nothing else we can split */
616 workpart = parts+max_index;
617 /*printf("splitting partition %d (pixels %ld, start %d, size %d)\n", max_index, workpart->pixels, workpart->start, workpart->size);*/
618 qsort(colors + workpart->start, workpart->size, sizeof(*colors),
621 /* find the median or something like it we need to make sure both
622 sides of the split have at least one color in them, so we don't
623 test at the first or last entry */
625 cum_total = colors[i].count;
627 half = workpart->pixels / 2;
628 while (i < workpart->start + workpart->size - 1
629 && cum_total < half) {
630 cum_total += colors[i++].count;
632 /*printf("Split at %d to make %d (half %ld, cumtotal %ld)\n", i, color_count, half, cum_total);*/
634 /* found the spot to split */
635 parts[color_count].start = i;
636 parts[color_count].size = workpart->start + workpart->size - i;
637 workpart->size = i - workpart->start;
638 parts[color_count].pixels = workpart->pixels - cum_total;
639 workpart->pixels = cum_total;
641 /* recalculate the limits */
642 calc_part(workpart, colors);
643 calc_part(parts+color_count, colors);
647 /* fill in the color table - since we could still have partitions
648 that have more than one color, we need to average the colors */
649 for (part_num = 0; part_num < color_count; ++part_num) {
651 medcut_partition *workpart;
653 workpart = parts+part_num;
654 for (ch = 0; ch < 3; ++ch)
657 for (i = workpart->start; i < workpart->start + workpart->size; ++i) {
658 for (ch = 0; ch < 3; ++ch) {
659 sums[ch] += colors[i].rgb[ch] * colors[i].count;
662 for (ch = 0; ch < 3; ++ch) {
663 quant->mc_colors[part_num].channel[ch] = sums[ch] / workpart->pixels;
666 quant->mc_count = color_count;
668 /*printf("out %d colors\n", quant->mc_count);*/
669 i_mempool_destroy(&mp);
674 /* Define one of the following 4 symbols to choose a colour search method
675 The idea is to try these out, including benchmarking, to see which
676 is fastest in a good spread of circumstances.
677 I'd expect IM_CFLINSEARCH to be fastest for very small palettes, and
678 IM_CFHASHBOX for large images with large palettes.
680 Some other possibilities include:
681 - search over entries sorted by luminance
683 Initially I was planning on testing using the macros and then
684 integrating the code directly into each function, but this means if
685 we find a bug at a late stage we will need to update N copies of
686 the same code. Also, keeping the code in the macros means that the
687 code in the translation functions is much more to the point,
688 there's no distracting colour search code to remove attention from
689 what makes _this_ translation function different. It may be
690 advisable to move the setup code into functions at some point, but
691 it should be possible to do this fairly transparently.
693 If IM_CF_COPTS is defined then CFLAGS must have an appropriate
696 Each option needs to define 4 macros:
697 CF_VARS - variables to define in the function
698 CF_SETUP - code to setup for the colour search, eg. allocating and
699 initializing lookup tables
700 CF_FIND - code that looks for the color in val and puts the best
701 matching index in bst_idx
702 CF_CLEANUP - code to clean up, eg. releasing memory
705 /*#define IM_CFLINSEARCH*/
707 /*#define IM_CFSORTCHAN*/
708 /*#define IM_CFRAND2DIST*/
713 /* The original version I wrote for this used the sort.
714 If this is defined then we use a sort to extract the indices for
718 /* assume i is available */
719 #define CF_VARS hashbox *hb = mymalloc(sizeof(hashbox) * 512); \
725 static long *gdists; /* qsort is annoying */
726 /* int might be smaller than long, so we need to do a real compare
727 rather than a subtraction*/
728 static int distcomp(void const *a, void const *b) {
729 long ra = gdists[*(int const *)a];
730 long rb = gdists[*(int const *)b];
741 /* for each hashbox build a list of colours that are in the hb or is closer
743 This is pretty involved. The original gifquant generated the hashbox
744 as part of it's normal processing, but since the map generation is now
745 separated from the translation we need to do this on the spot.
746 Any optimizations, even if they don't produce perfect results would be
749 static void hbsetup(i_quantize *quant, hashbox *hb) {
750 long *dists, mind, maxd, cd;
751 int cr, cb, cg, hbnum, i;
754 int *indices = mymalloc(quant->mc_count * sizeof(int));
757 dists = mymalloc(quant->mc_count * sizeof(long));
758 for (cr = 0; cr < 8; ++cr) {
759 for (cg = 0; cg < 8; ++cg) {
760 for (cb = 0; cb < 8; ++cb) {
761 /* centre of the hashbox */
762 cenc.channel[0] = cr*pboxjump+pboxjump/2;
763 cenc.channel[1] = cg*pboxjump+pboxjump/2;
764 cenc.channel[2] = cb*pboxjump+pboxjump/2;
765 hbnum = pixbox(&cenc);
767 /* order indices in the order of distance from the hashbox */
768 for (i = 0; i < quant->mc_count; ++i) {
772 dists[i] = ceucl_d(&cenc, quant->mc_colors+i);
775 /* it should be possible to do this without a sort
776 but so far I'm too lazy */
778 qsort(indices, quant->mc_count, sizeof(int), distcomp);
779 /* any colors that can match are within mind+diagonal size of
781 mind = dists[indices[0]];
783 maxd = (sqrt(mind)+pboxjump)*(sqrt(mind)+pboxjump);
784 while (i < quant->mc_count && dists[indices[i]] < maxd) {
785 hb[hbnum].vec[hb[hbnum].cnt++] = indices[i++];
788 /* work out the minimum */
790 for (i = 0; i < quant->mc_count; ++i) {
791 if (dists[i] < mind) mind = dists[i];
793 /* transfer any colours that might be closest to a colour in
795 maxd = (sqrt(mind)+pboxjump)*(sqrt(mind)+pboxjump);
796 for (i = 0; i < quant->mc_count; ++i) {
798 hb[hbnum].vec[hb[hbnum].cnt++] = i;
809 #define CF_SETUP hbsetup(quant, hb)
812 currhb = pixbox(&val); \
814 for (i = 0; i < hb[currhb].cnt; ++i) { \
815 cd = ceucl_d(quant->mc_colors+hb[currhb].vec[i], &val); \
816 if (cd < ld) { ld = cd; bst_idx = hb[currhb].vec[i]; } \
819 #define CF_CLEANUP myfree(hb)
823 #ifdef IM_CFLINSEARCH
824 /* as simple as it gets */
825 #define CF_VARS long ld, cd
826 #define CF_SETUP /* none needed */
829 for (i = 0; i < quant->mc_count; ++i) { \
830 cd = ceucl_d(quant->mc_colors+i, &val); \
831 if (cd < ld) { ld = cd; bst_idx = i; } \
837 static int gsortchan;
838 static i_quantize *gquant;
839 static int chansort(void const *a, void const *b) {
840 return gquant->mc_colors[*(int const *)a].channel[gsortchan] -
841 gquant->mc_colors[*(int const *)b].channel[gsortchan];
843 #define CF_VARS int *indices, sortchan, diff; \
845 int vindex[256] /* where to find value i of chan */
847 static void chansetup(i_img *img, i_quantize *quant, int *csortchan,
848 int *vindex, int **cindices) {
849 int *indices, sortchan, chan, i, chval;
850 int chanmins[MAXCHANNELS], chanmaxs[MAXCHANNELS], maxrange;
852 /* find the channel with the maximum range */
853 /* the maximum stddev would probably be better */
854 for (chan = 0; chan < img->channels; ++chan) {
855 chanmins[chan] = 256; chanmaxs[chan] = 0;
856 for (i = 0; i < quant->mc_count; ++i) {
857 if (quant->mc_colors[i].channel[chan] < chanmins[chan])
858 chanmins[chan] = quant->mc_colors[i].channel[chan];
859 if (quant->mc_colors[i].channel[chan] > chanmaxs[chan])
860 chanmaxs[chan] = quant->mc_colors[i].channel[chan];
864 for (chan = 0; chan < img->channels; ++chan) {
865 if (chanmaxs[chan]-chanmins[chan] > maxrange) {
866 maxrange = chanmaxs[chan]-chanmins[chan];
870 indices = mymalloc(quant->mc_count * sizeof(int)) ;
871 for (i = 0; i < quant->mc_count; ++i) {
874 gsortchan = sortchan;
876 qsort(indices, quant->mc_count, sizeof(int), chansort) ;
877 /* now a lookup table to find entries faster */
878 for (chval=0, i=0; i < quant->mc_count; ++i) {
879 while (chval < 256 &&
880 chval < quant->mc_colors[indices[i]].channel[sortchan]) {
884 while (chval < 256) {
885 vindex[chval++] = quant->mc_count-1;
887 *csortchan = sortchan;
892 chansetup(img, quant, &sortchan, vindex, &indices)
894 int chanfind(i_color val, i_quantize *quant, int *indices, int *vindex,
896 int i, bst_idx, diff, maxdiff;
899 i = vindex[val.channel[sortchan]];
900 bst_idx = indices[i];
903 maxdiff = quant->mc_count;
904 while (diff < maxdiff) {
905 if (i+diff < quant->mc_count) {
906 cd = ceucl_d(&val, quant->mc_colors+indices[i+diff]);
908 bst_idx = indices[i+diff];
914 cd = ceucl_d(&val, quant->mc_colors+indices[i-diff]);
916 bst_idx = indices[i-diff];
928 bst_idx = chanfind(val, quant, indices, vindex, sortchan)
931 #define CF_CLEANUP myfree(indices)
935 #ifdef IM_CFRAND2DIST
937 /* This is based on a method described by Addi in the #imager channel
938 on the 28/2/2001. I was about 1am Sydney time at the time, so I
939 wasn't at my most cogent. Well, that's my excuse :)
941 <TonyC> what I have at the moment is: hashboxes, with optimum hash box
942 filling; simple linear search; and a lookup in the widest channel
943 (currently the channel with the maximum range)
944 <Addi> There is one more way that might be simple to implement.
945 <Addi> You want to hear?
947 <purl> somebody said that was not true
948 <Addi> For each of the colors in the palette start by creating a
949 sorted list of the form:
950 <Addi> [distance, color]
951 <Addi> Where they are sorted by distance.
952 <TonyC> distance to where?
953 <Addi> Where the elements in the lists are the distances and colors of
954 the other colors in the palette
956 <Addi> So if you are at color 0
957 <Addi> ok - now to search for the closest color when you are creating
958 the final image is done like this:
959 <Addi> a) pick a random color from the palette
960 <Addi> b) calculate the distance to it
961 <Addi> c) only check the vectors that are within double the distance
962 in the list of the color you picked from the palette.
963 <Addi> Does that seem logical?
964 <Addi> Lets imagine that we only have grayscale to make an example:
965 <Addi> Our palette has 1 4 10 20 as colors.
966 <Addi> And we want to quantize the color 11
967 <Addi> lets say we picked 10 randomly
968 <Addi> the double distance is 2
969 <Addi> since abs(10-11)*2 is 2
970 <Addi> And the list at vector 10 is this:
971 <Addi> [0, 10], [6 4], [9, 1], [10, 20]
972 <Addi> so we look at the first one (but not the second one since 6 is
973 at a greater distance than 2.
974 <Addi> Any of that make sense?
975 <TonyC> yes, though are you suggesting another random jump to one of
976 the colours with the possible choices? or an exhaustive search?
977 <Addi> TonyC: It's possible to come up with a recursive/iterative
978 enhancement but this is the 'basic' version.
979 <Addi> Which would do an iterative search.
980 <Addi> You can come up with conditions where it pays to switch to a new one.
981 <Addi> And the 'random' start can be switched over to a small tree.
982 <Addi> So you would have a little index at the start.
983 <Addi> to get you into the general direction
984 <Addi> Perhaps just an 8 split.
985 <Addi> that is - split each dimension in half.
987 <TonyC> I get the idea
988 <Addi> But this would seem to be a good approach in our case since we
989 usually have few codevectors.
990 <Addi> So we only need 256*256 entries in a table.
991 <Addi> We could even only index some of them that were deemed as good
993 <TonyC> I was considering adding paletted output support for PNG and
994 TIFF at some point, which support 16-bit palettes
1002 typedef struct i_dists {
1010 static int dists_sort(void const *a, void const *b) {
1011 return ((i_dists *)a)->dist - ((i_dists *)b)->dist;
1014 static void rand2dist_setup(i_quantize *quant, i_dists **cdists) {
1016 mymalloc(sizeof(i_dists)*quant->mc_count*quant->mc_count);
1019 for (i = 0; i < quant->mc_count; ++i) {
1020 i_dists *ldists = dists + quant->mc_count * i;
1021 i_color val = quant->mc_colors[i];
1022 for (j = 0; j < quant->mc_count; ++j) {
1023 ldists[j].index = j;
1024 ldists[j].dist = ceucl_d(&val, quant->mc_colors+j);
1026 qsort(ldists, quant->mc_count, sizeof(i_dists), dists_sort);
1032 bst_idx = rand() % quant->mc_count; \
1033 rand2dist_setup(quant, &dists)
1035 static int rand2dist_find(i_color val, i_quantize *quant, i_dists *dists, int index) {
1042 cdists = dists + index * quant->mc_count;
1044 maxld = 8 * ceucl_d(&val, quant->mc_colors+index);
1045 for (i = 0; i < quant->mc_count && cdists[i].dist <= maxld; ++i) {
1046 cd = ceucl_d(&val, quant->mc_colors+cdists[i].index);
1048 bst_idx = cdists[i].index;
1055 #define CF_FIND bst_idx = rand2dist_find(val, quant, dists, bst_idx)
1057 #define CF_CLEANUP myfree(dists)
1062 static void translate_addi(i_quantize *quant, i_img *img, i_palidx *out) {
1063 int x, y, i, k, bst_idx;
1065 int pixdev = quant->perturb;
1070 if (img->channels >= 3) {
1073 for(y=0;y<img->ysize;y++) for(x=0;x<img->xsize;x++) {
1074 i_gpix(img,x,y,&val);
1075 val.channel[0]=g_sat(val.channel[0]+(int)(pixdev*frandn()));
1076 val.channel[1]=g_sat(val.channel[1]+(int)(pixdev*frandn()));
1077 val.channel[2]=g_sat(val.channel[2]+(int)(pixdev*frandn()));
1083 for(y=0;y<img->ysize;y++) for(x=0;x<img->xsize;x++) {
1084 i_gpix(img,x,y,&val);
1093 for(y=0;y<img->ysize;y++) for(x=0;x<img->xsize;x++) {
1094 i_gpix(img,x,y,&val);
1095 val.channel[1] = val.channel[2] =
1096 val.channel[0]=g_sat(val.channel[0]+(int)(pixdev*frandn()));
1102 for(y=0;y<img->ysize;y++) for(x=0;x<img->xsize;x++) {
1103 i_gpix(img,x,y,&val);
1104 val.channel[1] = val.channel[2] = val.channel[0];
1113 static int floyd_map[] =
1119 static int jarvis_map[] =
1126 static int stucki_map[] =
1133 struct errdiff_map {
1135 int width, height, orig;
1138 static struct errdiff_map maps[] =
1140 { floyd_map, 3, 2, 1 },
1141 { jarvis_map, 5, 3, 2 },
1142 { stucki_map, 5, 3, 2 },
1145 typedef struct errdiff_tag {
1149 /* perform an error diffusion dither */
1152 translate_errdiff(i_quantize *quant, i_img *img, i_palidx *out) {
1154 int mapw, maph, mapo;
1160 int minr, maxr, ming, maxg, minb, maxb, cr, cg, cb;
1165 if ((quant->errdiff & ed_mask) == ed_custom) {
1166 map = quant->ed_map;
1167 mapw = quant->ed_width;
1168 maph = quant->ed_height;
1169 mapo = quant->ed_orig;
1172 int index = quant->errdiff & ed_mask;
1173 if (index >= ed_custom) index = ed_floyd;
1174 map = maps[index].map;
1175 mapw = maps[index].width;
1176 maph = maps[index].height;
1177 mapo = maps[index].orig;
1180 errw = img->xsize+mapw;
1181 err = mymalloc(sizeof(*err) * maph * errw);
1182 /*errp = err+mapo;*/
1183 memset(err, 0, sizeof(*err) * maph * errw);
1186 for (i = 0; i < maph * mapw; ++i)
1187 difftotal += map[i];
1189 for (dy = 0; dy < maph; ++dy) {
1190 for (dx = 0; dx < mapw; ++dx) {
1191 printf("%2d", map[dx+dy*mapw]);
1198 for (y = 0; y < img->ysize; ++y) {
1199 for (x = 0; x < img->xsize; ++x) {
1203 i_gpix(img, x, y, &val);
1204 if (img->channels < 3) {
1205 val.channel[1] = val.channel[2] = val.channel[0];
1208 perr.r = perr.r < 0 ? -((-perr.r)/difftotal) : perr.r/difftotal;
1209 perr.g = perr.g < 0 ? -((-perr.g)/difftotal) : perr.g/difftotal;
1210 perr.b = perr.b < 0 ? -((-perr.b)/difftotal) : perr.b/difftotal;
1211 /*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);*/
1212 val.channel[0] = g_sat(val.channel[0]-perr.r);
1213 val.channel[1] = g_sat(val.channel[1]-perr.g);
1214 val.channel[2] = g_sat(val.channel[2]-perr.b);
1217 perr.r = quant->mc_colors[bst_idx].channel[0] - val.channel[0];
1218 perr.g = quant->mc_colors[bst_idx].channel[1] - val.channel[1];
1219 perr.b = quant->mc_colors[bst_idx].channel[2] - val.channel[2];
1220 /*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);*/
1221 for (dx = 0; dx < mapw; ++dx) {
1222 for (dy = 0; dy < maph; ++dy) {
1223 err[x+dx+dy*errw].r += perr.r * map[dx+mapw*dy];
1224 err[x+dx+dy*errw].g += perr.g * map[dx+mapw*dy];
1225 err[x+dx+dy*errw].b += perr.b * map[dx+mapw*dy];
1230 /* shift up the error matrix */
1231 for (dy = 0; dy < maph-1; ++dy) {
1232 memcpy(err+dy*errw, err+(dy+1)*errw, sizeof(*err)*errw);
1234 memset(err+(maph-1)*errw, 0, sizeof(*err)*errw);
1239 /* Prescan finds the boxes in the image that have the highest number of colors
1240 and that result is used as the initial value for the vectores */
1243 static void prescan(i_img **imgs,int count, int cnum, cvec *clr, i_sample_t *line) {
1249 for(i=0;i<512;i++) {
1255 /* process each image */
1256 for (i = 0; i < count; ++i) {
1257 i_img *im = imgs[i];
1258 chans = im->channels >= 3 ? NULL : gray_samples;
1259 for(y=0;y<im->ysize;y++) {
1260 i_gsamp(im, 0, im->xsize, y, line, chans, 3);
1262 for(x=0;x<im->xsize;x++) {
1263 prebox[pixbox_ch(val)].pixcnt++;
1268 for(i=0;i<512;i++) prebox[i].pdc=prebox[i].pixcnt;
1269 qsort(prebox,512,sizeof(pbox),(cmpfunc)pboxcmp);
1271 for(i=0;i<cnum;i++) {
1272 /* printf("Color %d\n",i);
1273 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); }
1278 /* 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); } */
1284 /* printf("prebox[%d].cand=%d\n",k,prebox[k].cand); */
1285 if (clr[i].fixed) { i++; continue; } /* reserved go to next */
1286 if (j>=prebox[k].cand) { k++; j=1; } else {
1287 if (prebox[k].cand == 2) boxcenter(prebox[k].boxnum,&(clr[i]));
1288 else boxrand(prebox[k].boxnum,&(clr[i]));
1289 /* 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); */
1297 static void reorder(pbox prescan[512]) {
1305 c.pdc=c.pixcnt/(c.cand*c.cand);
1306 /* c.pdc=c.pixcnt/c.cand; */
1307 while(c.pdc < prescan[nidx+1].pdc && nidx < 511) {
1308 prescan[nidx]=prescan[nidx+1];
1315 pboxcmp(const pbox *a,const pbox *b) {
1316 if (a->pixcnt > b->pixcnt) return -1;
1317 if (a->pixcnt < b->pixcnt) return 1;
1322 boxcenter(int box,cvec *cv) {
1323 cv->r=15+((box&448)>>1);
1324 cv->g=15+((box&56)<<2);
1325 cv->b=15+((box&7)<<5);
1329 bbox(int box,int *r0,int *r1,int *g0,int *g1,int *b0,int *b1) {
1339 boxrand(int box,cvec *cv) {
1340 cv->r=6+(rand()%25)+((box&448)>>1);
1341 cv->g=6+(rand()%25)+((box&56)<<2);
1342 cv->b=6+(rand()%25)+((box&7)<<5);
1352 while (w >= 1 || w == 0) {
1353 u1 = 2 * frand() - 1;
1354 u2 = 2 * frand() - 1;
1358 w = sqrt((-2*log(w))/w);
1362 /* Create hash index */
1365 cr_hashindex(cvec clr[256],int cnum,hashbox hb[512]) {
1367 int bx,mind,cd,cumcnt,bst_idx,i;
1368 /* printf("indexing... \n");*/
1371 for(bx=0; bx<512; bx++) {
1373 for(i=0; i<cnum; i++) {
1374 cd = maxdist(bx,&clr[i]);
1375 if (cd < mind) { mind=cd; bst_idx=i; }
1379 for(i=0;i<cnum;i++) if (mindist(bx,&clr[i])<mind) hb[bx].vec[hb[bx].cnt++]=i;
1380 /*printf("box %d -> approx -> %d\n",bx,hb[bx].cnt); */
1381 /* statbox(bx,cnum,clr); */
1385 /* printf("Average search space: %d\n",cumcnt/512); */
1389 maxdist(int boxnum,cvec *cv) {
1390 int r0,r1,g0,g1,b0,b1;
1397 bbox(boxnum,&r0,&r1,&g0,&g1,&b0,&b1);
1399 mr=i_max(abs(b-b0),abs(b-b1));
1400 mg=i_max(abs(g-g0),abs(g-g1));
1401 mb=i_max(abs(r-r0),abs(r-r1));
1403 return PWR2(mr)+PWR2(mg)+PWR2(mb);
1407 mindist(int boxnum,cvec *cv) {
1408 int r0,r1,g0,g1,b0,b1;
1415 bbox(boxnum,&r0,&r1,&g0,&g1,&b0,&b1);
1417 /* printf("box %d, (%d,%d,%d)-(%d,%d,%d) vec (%d,%d,%d) ",boxnum,r0,g0,b0,r1,g1,b1,r,g,b); */
1419 if (r0<=r && r<=r1 && g0<=g && g<=g1 && b0<=b && b<=b1) return 0;
1421 mr=i_min(abs(b-b0),abs(b-b1));
1422 mg=i_min(abs(g-g0),abs(g-g1));
1423 mb=i_min(abs(r-r0),abs(r-r1));
1429 if (r0<=r && r<=r1 && g0<=g && g<=g1) return mb;
1430 if (r0<=r && r<=r1 && b0<=b && b<=b1) return mg;
1431 if (b0<=b && b<=b1 && g0<=g && g<=g1) return mr;
1433 if (r0<=r && r<=r1) return mg+mb;
1434 if (g0<=g && g<=g1) return mr+mb;
1435 if (b0<=b && b<=b1) return mg+mr;
1440 static void transparent_threshold(i_quantize *, i_palidx *, i_img *, i_palidx);
1441 static void transparent_errdiff(i_quantize *, i_palidx *, i_img *, i_palidx);
1442 static void transparent_ordered(i_quantize *, i_palidx *, i_img *, i_palidx);
1444 void quant_transparent(i_quantize *quant, i_palidx *data, i_img *img,
1445 i_palidx trans_index)
1447 switch (quant->transp) {
1452 quant->tr_threshold = 128;
1455 transparent_threshold(quant, data, img, trans_index);
1459 transparent_errdiff(quant, data, img, trans_index);
1463 transparent_ordered(quant, data, img, trans_index);
1469 transparent_threshold(i_quantize *quant, i_palidx *data, i_img *img,
1470 i_palidx trans_index)
1473 i_sample_t *line = mymalloc(img->xsize * sizeof(i_sample_t));
1474 int trans_chan = img->channels > 2 ? 3 : 1;
1476 for (y = 0; y < img->ysize; ++y) {
1477 i_gsamp(img, 0, img->xsize, y, line, &trans_chan, 1);
1478 for (x = 0; x < img->xsize; ++x) {
1479 if (line[x] < quant->tr_threshold)
1480 data[y*img->xsize+x] = trans_index;
1487 transparent_errdiff(i_quantize *quant, i_palidx *data, i_img *img,
1488 i_palidx trans_index)
1492 int mapw, maph, mapo;
1493 int errw, *err, *errp;
1494 int difftotal, out, error;
1495 int x, y, dx, dy, i;
1497 int trans_chan = img->channels > 2 ? 3 : 1;
1499 /* no custom map for transparency (yet) */
1500 index = quant->tr_errdiff & ed_mask;
1501 if (index >= ed_custom) index = ed_floyd;
1502 map = maps[index].map;
1503 mapw = maps[index].width;
1504 maph = maps[index].height;
1505 mapo = maps[index].orig;
1507 errw = img->xsize+mapw-1;
1508 err = mymalloc(sizeof(*err) * maph * errw);
1510 memset(err, 0, sizeof(*err) * maph * errw);
1512 line = mymalloc(img->xsize * sizeof(i_sample_t));
1514 for (i = 0; i < maph * mapw; ++i)
1515 difftotal += map[i];
1516 for (y = 0; y < img->ysize; ++y) {
1517 i_gsamp(img, 0, img->xsize, y, line, &trans_chan, 1);
1518 for (x = 0; x < img->xsize; ++x) {
1519 line[x] = g_sat(line[x]-errp[x]/difftotal);
1520 if (line[x] < 128) {
1522 data[y*img->xsize+x] = trans_index;
1527 error = out - line[x];
1528 for (dx = 0; dx < mapw; ++dx) {
1529 for (dy = 0; dy < maph; ++dy) {
1530 errp[x+dx-mapo+dy*errw] += error * map[dx+mapw*dy];
1534 /* shift up the error matrix */
1535 for (dy = 0; dy < maph-1; ++dy)
1536 memcpy(err+dy*errw, err+(dy+1)*errw, sizeof(*err)*errw);
1537 memset(err+(maph-1)*errw, 0, sizeof(*err)*errw);
1543 /* builtin ordered dither maps */
1544 static unsigned char
1545 orddith_maps[][64] =
1548 this is purely random - it's pretty awful
1550 48, 72, 196, 252, 180, 92, 108, 52,
1551 228, 176, 64, 8, 236, 40, 20, 164,
1552 120, 128, 84, 116, 24, 28, 172, 220,
1553 68, 0, 188, 124, 184, 224, 192, 104,
1554 132, 100, 240, 200, 152, 160, 244, 44,
1555 96, 204, 144, 16, 140, 56, 232, 216,
1556 208, 4, 76, 212, 136, 248, 80, 168,
1557 156, 88, 32, 112, 148, 12, 36, 60,
1561 perl spot.perl '($x-3.5)*($x-3.5)+($y-3.5)*($y-3.5)'
1563 240, 232, 200, 136, 140, 192, 228, 248,
1564 220, 148, 100, 76, 80, 104, 152, 212,
1565 180, 116, 56, 32, 36, 60, 120, 176,
1566 156, 64, 28, 0, 8, 44, 88, 160,
1567 128, 92, 24, 12, 4, 40, 68, 132,
1568 184, 96, 48, 20, 16, 52, 108, 188,
1569 216, 144, 112, 72, 84, 124, 164, 224,
1570 244, 236, 196, 168, 172, 204, 208, 252,
1574 'min(dist(1.5, 1.5),dist(5.5,1.5),dist(1.5,5.5),dist(5.5,5.5))'
1576 196, 72, 104, 220, 200, 80, 112, 224,
1577 76, 4, 24, 136, 84, 8, 32, 144,
1578 108, 28, 52, 168, 116, 36, 56, 176,
1579 216, 140, 172, 244, 228, 148, 180, 248,
1580 204, 92, 124, 236, 192, 68, 96, 208,
1581 88, 12, 44, 156, 64, 0, 16, 128,
1582 120, 40, 60, 188, 100, 20, 48, 160,
1583 232, 152, 184, 252, 212, 132, 164, 240,
1586 perl spot.perl '$y-3'
1588 160, 164, 168, 172, 176, 180, 184, 188,
1589 128, 132, 136, 140, 144, 148, 152, 156,
1590 32, 36, 40, 44, 48, 52, 56, 60,
1591 0, 4, 8, 12, 16, 20, 24, 28,
1592 64, 68, 72, 76, 80, 84, 88, 92,
1593 96, 100, 104, 108, 112, 116, 120, 124,
1594 192, 196, 200, 204, 208, 212, 216, 220,
1595 224, 228, 232, 236, 240, 244, 248, 252,
1598 perl spot.perl '$x-3'
1600 180, 100, 40, 12, 44, 104, 184, 232,
1601 204, 148, 60, 16, 64, 128, 208, 224,
1602 212, 144, 76, 8, 80, 132, 216, 244,
1603 160, 112, 68, 20, 84, 108, 172, 236,
1604 176, 96, 72, 28, 88, 152, 188, 228,
1605 200, 124, 92, 0, 32, 116, 164, 240,
1606 168, 120, 36, 24, 48, 136, 192, 248,
1607 196, 140, 52, 4, 56, 156, 220, 252,
1610 perl spot.perl '$y+$x-7'
1612 248, 232, 224, 192, 140, 92, 52, 28,
1613 240, 220, 196, 144, 108, 60, 12, 64,
1614 216, 180, 148, 116, 76, 20, 80, 128,
1615 204, 152, 104, 44, 16, 72, 100, 160,
1616 164, 96, 68, 24, 56, 112, 168, 176,
1617 124, 40, 8, 36, 88, 136, 184, 212,
1618 84, 4, 32, 120, 156, 188, 228, 236,
1619 0, 48, 132, 172, 200, 208, 244, 252,
1622 perl spot.perl '$y-$x'
1624 0, 32, 116, 172, 184, 216, 236, 252,
1625 56, 8, 72, 132, 136, 200, 228, 240,
1626 100, 36, 12, 40, 92, 144, 204, 220,
1627 168, 120, 60, 16, 44, 96, 156, 176,
1628 180, 164, 112, 48, 28, 52, 128, 148,
1629 208, 192, 152, 88, 84, 20, 64, 104,
1630 232, 224, 196, 140, 108, 68, 24, 76,
1631 248, 244, 212, 188, 160, 124, 80, 4,
1635 good for display, bad for print
1638 0, 128, 32, 192, 8, 136, 40, 200,
1639 224, 64, 160, 112, 232, 72, 168, 120,
1640 48, 144, 16, 208, 56, 152, 24, 216,
1641 176, 96, 240, 80, 184, 104, 248, 88,
1642 12, 140, 44, 204, 4, 132, 36, 196,
1643 236, 76, 172, 124, 228, 68, 164, 116,
1644 60, 156, 28, 220, 52, 148, 20, 212,
1645 188, 108, 252, 92, 180, 100, 244, 84,
1650 transparent_ordered(i_quantize *quant, i_palidx *data, i_img *img,
1651 i_palidx trans_index)
1653 unsigned char *spot;
1656 int trans_chan = img->channels > 2 ? 3 : 1;
1657 if (quant->tr_orddith == od_custom)
1658 spot = quant->tr_custom;
1660 spot = orddith_maps[quant->tr_orddith];
1662 line = mymalloc(img->xsize * sizeof(i_sample_t));
1663 for (y = 0; y < img->ysize; ++y) {
1664 i_gsamp(img, 0, img->xsize, y, line, &trans_chan, 1);
1665 for (x = 0; x < img->xsize; ++x) {
1666 if (line[x] < spot[(x&7)+(y&7)*8])
1667 data[x+y*img->xsize] = trans_index;