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);
69 static void translate_closest(i_quantize *, i_img *, i_palidx *);
70 static void translate_errdiff(i_quantize *, i_img *, i_palidx *);
71 static void translate_addi(i_quantize *, i_img *, i_palidx *);
73 /* Quantize the image given the palette in quant.
75 The giflib quantizer ignores the palette.
77 i_palidx *quant_translate(i_quantize *quant, i_img *img) {
81 mm_log((1, "quant_translate(quant %p, img %p)\n", quant, img));
83 /* there must be at least one color in the paletted (though even that
85 if (quant->mc_count == 0) {
86 i_push_error(0, "no colors available for translation");
90 bytes = img->xsize * img->ysize;
91 if (bytes / img->ysize != img->xsize) {
92 i_push_error(0, "integer overflow calculating memory allocation");
95 result = mymalloc(bytes);
97 switch (quant->translate) {
100 translate_closest(quant, img, result);
104 translate_errdiff(quant, img, result);
109 translate_addi(quant, img, result);
116 static void translate_closest(i_quantize *quant, i_img *img, i_palidx *out) {
118 translate_addi(quant, img, out);
121 #define PWR2(x) ((x)*(x))
123 typedef int (*cmpfunc)(const void*, const void*);
146 static void prescan(i_img **im,int count, int cnum, cvec *clr, i_sample_t *line);
147 static void reorder(pbox prescan[512]);
148 static int pboxcmp(const pbox *a,const pbox *b);
149 static void boxcenter(int box,cvec *cv);
150 static float frandn(void);
151 static void boxrand(int box,cvec *cv);
152 static void bbox(int box,int *r0,int *r1,int *g0,int *g1,int *b0,int *b1);
153 static void cr_hashindex(cvec clr[256],int cnum,hashbox hb[512]);
154 static int mindist(int boxnum,cvec *cv);
155 static int maxdist(int boxnum,cvec *cv);
157 /* Some of the simpler functions are kept here to aid the compiler -
158 maybe some of them will be inlined. */
161 pixbox(i_color *ic) { return ((ic->channel[0] & 224)<<1)+ ((ic->channel[1]&224)>>2) + ((ic->channel[2] &224) >> 5); }
164 pixbox_ch(i_sample_t *chans) { return ((chans[0] & 224)<<1)+ ((chans[1]&224)>>2) + ((chans[2] &224) >> 5); }
168 if (in>255) { return 255; }
169 else if (in>0) return in;
176 return rand()/(RAND_MAX+1.0);
182 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]); }
187 eucl_d_ch(cvec* cv,i_sample_t *chans) {
188 return PWR2(cv->r - chans[0]) + PWR2(cv->g - chans[1])
189 + PWR2(cv->b - chans[2]);
194 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]); }
197 gray_samples[] = { 0, 0, 0 };
201 This quantization algorithm and implementation routines are by Arnar
202 M. Hrafnkelson. In case any new ideas are here they are mine since
203 this was written from scratch.
205 The algorithm uses local means in the following way:
207 For each point in the colormap we find which image points
208 have that point as it's closest point. We calculate the mean
209 of those points and in the next iteration it will be the new
210 entry in the colormap.
212 In order to speed this process up (i.e. nearest neighbor problem) We
213 divied the r,g,b space up in equally large 512 boxes. The boxes are
214 numbered from 0 to 511. Their numbering is so that for a given vector
215 it is known that it belongs to the box who is formed by concatenating the
216 3 most significant bits from each component of the RGB triplet.
218 For each box we find the list of points from the colormap who might be
219 closest to any given point within the box. The exact solution
220 involves finding the Voronoi map (or the dual the Delauny
221 triangulation) and has many issues including numerical stability.
223 So we use this approximation:
225 1. Find which point has the shortest maximum distance to the box.
226 2. Find all points that have a shorter minimum distance than that to the box
228 This is a very simple task and is not computationally heavy if one
229 takes into account that the minimum distances from a pixel to a box is
230 always found by checking if it's inside the box or is closest to some
231 side or a corner. Finding the maximum distance is also either a side
234 This approach results 2-3 times more than the actual points needed but
235 is still a good gain over the complete space. Usually when one has a
236 256 Colorcolor map a search over 30 is often obtained.
238 A bit of an enhancement to this approach is to keep a seperate list
239 for each side of the cube, but this will require even more memory.
241 Arnar M. Hrafnkelsson (addi@umich.edu);
245 Extracted from gifquant.c, removed dependencies on gif_lib,
246 and added support for multiple images.
247 starting from 1nov2000 by TonyC <tony@develop-help.com>.
252 makemap_addi(i_quantize *quant, i_img **imgs, int count) {
254 int cnum, i, x, y, bst_idx=0, ld, cd, iter, currhb, img_num;
261 const int *sample_indices;
263 mm_log((1, "makemap_addi(quant %p { mc_count=%d, mc_colors=%p }, imgs %p, count %d)\n",
264 quant, quant->mc_count, quant->mc_colors, imgs, count));
268 clr = i_mempool_alloc(&mp, sizeof(cvec) * quant->mc_size);
269 hb = i_mempool_alloc(&mp, sizeof(hashbox) * 512);
270 for (i=0; i < quant->mc_count; ++i) {
271 clr[i].r = quant->mc_colors[i].rgb.r;
272 clr[i].g = quant->mc_colors[i].rgb.g;
273 clr[i].b = quant->mc_colors[i].rgb.b;
277 /* mymalloc doesn't clear memory, so I think we need this */
278 for (; i < quant->mc_size; ++i) {
279 /*clr[i].r = clr[i].g = clr[i].b = 0;*/
286 cnum = quant->mc_size;
289 for (img_num = 0; img_num < count; ++img_num) {
290 if (imgs[img_num]->xsize > maxwidth)
291 maxwidth = imgs[img_num]->xsize;
293 line = i_mempool_alloc(&mp, 3 * maxwidth * sizeof(*line));
295 prescan(imgs, count, cnum, clr, line);
296 cr_hashindex(clr, cnum, hb);
298 for(iter=0;iter<3;iter++) {
301 for (img_num = 0; img_num < count; ++img_num) {
302 i_img *im = imgs[img_num];
303 sample_indices = im->channels >= 3 ? NULL : gray_samples;
304 for(y=0;y<im->ysize;y++) {
305 i_gsamp(im, 0, im->xsize, y, line, sample_indices, 3);
307 for(x=0;x<im->xsize;x++) {
309 /*i_gpix(im,x,y,&val);*/
310 currhb=pixbox_ch(val);
311 /* printf("box = %d \n",currhb); */
312 for(i=0;i<hb[currhb].cnt;i++) {
313 /* 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); */
315 cd=eucl_d_ch(&clr[hb[currhb].vec[i]],val);
317 ld=cd; /* shortest distance yet */
318 bst_idx=hb[currhb].vec[i]; /* index of closest vector yet */
322 clr[bst_idx].mcount++;
324 clr[bst_idx].dr+=val[0];
325 clr[bst_idx].dg+=val[1];
326 clr[bst_idx].db+=val[2];
328 val += 3; /* next 3 samples (next pixel) */
335 clr[i].dr/=clr[i].mcount;
336 clr[i].dg/=clr[i].mcount;
337 clr[i].db/=clr[i].mcount;
340 /* for(i=0;i<cnum;i++) printf("vec(%d)=(%d,%d,%d) dest=(%d,%d,%d) matchcount=%d\n",
341 i,clr[i].r,clr[i].g,clr[i].b,clr[i].dr,clr[i].dg,clr[i].db,clr[i].mcount); */
343 /* printf("total error: %.2f\n",sqrt(accerr)); */
345 for(i=0;i<cnum;i++) {
346 if (clr[i].fixed) continue; /* skip reserved colors */
350 clr[i].r=clr[i].r*(1-dlt)+dlt*clr[i].dr;
351 clr[i].g=clr[i].g*(1-dlt)+dlt*clr[i].dg;
352 clr[i].b=clr[i].b*(1-dlt)+dlt*clr[i].db;
354 /* let's try something else */
366 cr_hashindex(clr,cnum,hb);
371 for(i=0;i<cnum;i++) {
372 cd=eucl_d(&clr[i],&val);
380 /* if defined, we only include colours with an mcount or that were
381 supplied in the fixed palette, giving us a smaller output palette */
382 #define ONLY_USE_USED
384 /* transfer the colors back */
386 for (i = 0; i < cnum; ++i) {
387 if (clr[i].fixed || clr[i].used) {
388 /*printf("Adding %d (%d,%d,%d)\n", i, clr[i].r, clr[i].g, clr[i].b);*/
389 quant->mc_colors[quant->mc_count].rgb.r = clr[i].r;
390 quant->mc_colors[quant->mc_count].rgb.g = clr[i].g;
391 quant->mc_colors[quant->mc_count].rgb.b = clr[i].b;
396 /* transfer the colors back */
397 for (i = 0; i < cnum; ++i) {
398 quant->mc_colors[i].rgb.r = clr[i].r;
399 quant->mc_colors[i].rgb.g = clr[i].g;
400 quant->mc_colors[i].rgb.b = clr[i].b;
402 quant->mc_count = cnum;
406 mm_log((1, "makemap_addi returns - quant.mc_count = %d\n", quant->mc_count));
407 for (i = 0; i < quant->mc_count; ++i)
408 mm_log((5, " map entry %d: (%d, %d, %d)\n", i, clr[i].r, clr[i].g, clr[i].b));
411 i_mempool_destroy(&mp);
419 #define MEDIAN_CUT_COLORS 32768
421 #define MED_CUT_INDEX(c) ((((c).rgb.r & 0xF8) << 7) | \
422 (((c).rgb.g & 0xF8) << 2) | (((c).rgb.b & 0xF8) >> 3))
424 #define MED_CUT_GRAY_INDEX(c) ((((c).rgb.r & 0xF8) << 7) | \
425 (((c).rgb.r & 0xF8) << 2) | (((c).rgb.r & 0xF8) >> 3))
427 /* scale these to cover the whole range */
428 #define MED_CUT_RED(index) ((((index) & 0x7C00) >> 10) * 255 / 31)
429 #define MED_CUT_GREEN(index) ((((index) & 0x3E0) >> 5) * 255 / 31)
430 #define MED_CUT_BLUE(index) (((index) & 0x1F) * 255 / 31)
433 i_sample_t min[3]; /* minimum for each channel */
434 i_sample_t max[3]; /* maximum for each channel */
435 i_sample_t width[3]; /* width for each channel */
436 int start, size; /* beginning and size of the partition */
437 int pixels; /* number of pixels represented by this partition */
441 =item calc_part(part, colors)
443 Calculates the new color limits for the given partition.
445 Giflib assumes that the limits for the non-split channels stay the
446 same, but this strikes me as incorrect, especially if the colors tend
449 Of course this could be optimized by not recalculating the channel we
450 just sorted on, but it's not worth the effort right now.
454 static void calc_part(medcut_partition *part, quant_color_entry *colors) {
457 for (ch = 0; ch < 3; ++ch) {
461 for (i = part->start; i < part->start + part->size; ++i) {
462 for (ch = 0; ch < 3; ++ch) {
463 if (part->min[ch] > colors[i].rgb[ch])
464 part->min[ch] = colors[i].rgb[ch];
465 if (part->max[ch] < colors[i].rgb[ch])
466 part->max[ch] = colors[i].rgb[ch];
469 for (ch = 0; ch < 3; ++ch) {
470 part->width[ch] = part->max[ch] - part->min[ch];
474 /* simple functions to sort by each channel - we could use a global, but
478 color_sort_red(void const *left, void const *right) {
479 return ((quant_color_entry *)left)->rgb[0] - ((quant_color_entry *)right)->rgb[0];
483 color_sort_green(void const *left, void const *right) {
484 return ((quant_color_entry *)left)->rgb[1] - ((quant_color_entry *)right)->rgb[1];
488 color_sort_blue(void const *left, void const *right) {
489 return ((quant_color_entry *)left)->rgb[2] - ((quant_color_entry *)right)->rgb[2];
492 static int (*sorters[])(void const *, void const *) =
500 makemap_mediancut(i_quantize *quant, i_img **imgs, int count) {
501 quant_color_entry *colors;
503 int imgn, x, y, i, ch;
508 medcut_partition *parts;
511 /* number of channels we search for the best channel to partition
512 this isn't terribly efficient, but it should work */
515 /*printf("images %d pal size %d\n", count, quant->mc_size);*/
519 colors = i_mempool_alloc(&mp, sizeof(*colors) * MEDIAN_CUT_COLORS);
520 for (i = 0; i < MEDIAN_CUT_COLORS; ++i) {
521 colors[i].rgb[0] = MED_CUT_RED(i);
522 colors[i].rgb[1] = MED_CUT_GREEN(i);
523 colors[i].rgb[2] = MED_CUT_BLUE(i);
528 for (imgn = 0; imgn < count; ++imgn) {
529 if (imgs[imgn]->xsize > max_width)
530 max_width = imgs[imgn]->xsize;
532 line = i_mempool_alloc(&mp, sizeof(i_color) * max_width);
534 /* build the stats */
536 chan_count = 1; /* assume we just have grayscale */
537 for (imgn = 0; imgn < count; ++imgn) {
538 total_pixels += imgs[imgn]->xsize * imgs[imgn]->ysize;
539 for (y = 0; y < imgs[imgn]->ysize; ++y) {
540 i_glin(imgs[imgn], 0, imgs[imgn]->xsize, y, line);
541 if (imgs[imgn]->channels > 2) {
543 for (x = 0; x < imgs[imgn]->xsize; ++x) {
544 ++colors[MED_CUT_INDEX(line[x])].count;
548 /* a gray-scale image, just use the first channel */
549 for (x = 0; x < imgs[imgn]->xsize; ++x) {
550 ++colors[MED_CUT_GRAY_INDEX(line[x])].count;
556 /* eliminate the empty colors */
558 for (in = 0; in < MEDIAN_CUT_COLORS; ++in) {
559 if (colors[in].count) {
560 colors[out++] = colors[in];
563 /*printf("out %d\n", out);
565 for (i = 0; i < out; ++i) {
566 if (colors[i].count) {
567 printf("%d: (%d,%d,%d) -> %d\n", i, colors[i].rgb[0], colors[i].rgb[1],
568 colors[i].rgb[2], colors[i].count);
572 if (out < quant->mc_size) {
573 /* just copy them into the color table */
574 for (i = 0; i < out; ++i) {
575 for (ch = 0; ch < 3; ++ch) {
576 quant->mc_colors[i].channel[ch] = colors[i].rgb[ch];
579 quant->mc_count = out;
582 /* build the starting partition */
583 parts = i_mempool_alloc(&mp, sizeof(*parts) * quant->mc_size);
586 parts[0].pixels = total_pixels;
587 calc_part(parts, colors);
590 while (color_count < quant->mc_size) {
591 int max_index, max_ch; /* index/channel with biggest spread */
593 medcut_partition *workpart;
597 /* find the partition with the most biggest span with more than
600 for (i = 0; i < color_count; ++i) {
601 for (ch = 0; ch < chan_count; ++ch) {
602 if (parts[i].width[ch] > max_size
603 && parts[i].size > 1) {
606 max_size = parts[i].width[ch];
611 /* nothing else we can split */
615 workpart = parts+max_index;
616 /*printf("splitting partition %d (pixels %ld, start %d, size %d)\n", max_index, workpart->pixels, workpart->start, workpart->size);*/
617 qsort(colors + workpart->start, workpart->size, sizeof(*colors),
620 /* find the median or something like it we need to make sure both
621 sides of the split have at least one color in them, so we don't
622 test at the first or last entry */
624 cum_total = colors[i].count;
626 half = workpart->pixels / 2;
627 while (i < workpart->start + workpart->size - 1
628 && cum_total < half) {
629 cum_total += colors[i++].count;
631 /*printf("Split at %d to make %d (half %ld, cumtotal %ld)\n", i, color_count, half, cum_total);*/
633 /* found the spot to split */
634 parts[color_count].start = i;
635 parts[color_count].size = workpart->start + workpart->size - i;
636 workpart->size = i - workpart->start;
637 parts[color_count].pixels = workpart->pixels - cum_total;
638 workpart->pixels = cum_total;
640 /* recalculate the limits */
641 calc_part(workpart, colors);
642 calc_part(parts+color_count, colors);
646 /* fill in the color table - since we could still have partitions
647 that have more than one color, we need to average the colors */
648 for (part_num = 0; part_num < color_count; ++part_num) {
650 medcut_partition *workpart;
652 workpart = parts+part_num;
653 for (ch = 0; ch < 3; ++ch)
656 for (i = workpart->start; i < workpart->start + workpart->size; ++i) {
657 for (ch = 0; ch < 3; ++ch) {
658 sums[ch] += colors[i].rgb[ch] * colors[i].count;
661 for (ch = 0; ch < 3; ++ch) {
662 quant->mc_colors[part_num].channel[ch] = sums[ch] / workpart->pixels;
665 quant->mc_count = color_count;
667 /*printf("out %d colors\n", quant->mc_count);*/
668 i_mempool_destroy(&mp);
673 /* Define one of the following 4 symbols to choose a colour search method
674 The idea is to try these out, including benchmarking, to see which
675 is fastest in a good spread of circumstances.
676 I'd expect IM_CFLINSEARCH to be fastest for very small palettes, and
677 IM_CFHASHBOX for large images with large palettes.
679 Some other possibilities include:
680 - search over entries sorted by luminance
682 Initially I was planning on testing using the macros and then
683 integrating the code directly into each function, but this means if
684 we find a bug at a late stage we will need to update N copies of
685 the same code. Also, keeping the code in the macros means that the
686 code in the translation functions is much more to the point,
687 there's no distracting colour search code to remove attention from
688 what makes _this_ translation function different. It may be
689 advisable to move the setup code into functions at some point, but
690 it should be possible to do this fairly transparently.
692 If IM_CF_COPTS is defined then CFLAGS must have an appropriate
695 Each option needs to define 4 macros:
696 CF_VARS - variables to define in the function
697 CF_SETUP - code to setup for the colour search, eg. allocating and
698 initializing lookup tables
699 CF_FIND - code that looks for the color in val and puts the best
700 matching index in bst_idx
701 CF_CLEANUP - code to clean up, eg. releasing memory
704 /*#define IM_CFLINSEARCH*/
706 /*#define IM_CFSORTCHAN*/
707 /*#define IM_CFRAND2DIST*/
712 /* The original version I wrote for this used the sort.
713 If this is defined then we use a sort to extract the indices for
717 /* assume i is available */
718 #define CF_VARS hashbox *hb = mymalloc(sizeof(hashbox) * 512); \
724 static long *gdists; /* qsort is annoying */
725 /* int might be smaller than long, so we need to do a real compare
726 rather than a subtraction*/
727 static int distcomp(void const *a, void const *b) {
728 long ra = gdists[*(int const *)a];
729 long rb = gdists[*(int const *)b];
740 /* for each hashbox build a list of colours that are in the hb or is closer
742 This is pretty involved. The original gifquant generated the hashbox
743 as part of it's normal processing, but since the map generation is now
744 separated from the translation we need to do this on the spot.
745 Any optimizations, even if they don't produce perfect results would be
748 static void hbsetup(i_quantize *quant, hashbox *hb) {
749 long *dists, mind, maxd;
750 int cr, cb, cg, hbnum, i;
753 int *indices = mymalloc(quant->mc_count * sizeof(int));
756 dists = mymalloc(quant->mc_count * sizeof(long));
757 for (cr = 0; cr < 8; ++cr) {
758 for (cg = 0; cg < 8; ++cg) {
759 for (cb = 0; cb < 8; ++cb) {
760 /* centre of the hashbox */
761 cenc.channel[0] = cr*pboxjump+pboxjump/2;
762 cenc.channel[1] = cg*pboxjump+pboxjump/2;
763 cenc.channel[2] = cb*pboxjump+pboxjump/2;
764 hbnum = pixbox(&cenc);
766 /* order indices in the order of distance from the hashbox */
767 for (i = 0; i < quant->mc_count; ++i) {
771 dists[i] = ceucl_d(&cenc, quant->mc_colors+i);
774 /* it should be possible to do this without a sort
775 but so far I'm too lazy */
777 qsort(indices, quant->mc_count, sizeof(int), distcomp);
778 /* any colors that can match are within mind+diagonal size of
780 mind = dists[indices[0]];
782 maxd = (sqrt(mind)+pboxjump)*(sqrt(mind)+pboxjump);
783 while (i < quant->mc_count && dists[indices[i]] < maxd) {
784 hb[hbnum].vec[hb[hbnum].cnt++] = indices[i++];
787 /* work out the minimum */
789 for (i = 0; i < quant->mc_count; ++i) {
790 if (dists[i] < mind) mind = dists[i];
792 /* transfer any colours that might be closest to a colour in
794 maxd = (sqrt(mind)+pboxjump)*(sqrt(mind)+pboxjump);
795 for (i = 0; i < quant->mc_count; ++i) {
797 hb[hbnum].vec[hb[hbnum].cnt++] = i;
808 #define CF_SETUP hbsetup(quant, hb)
811 currhb = pixbox(&val); \
813 for (i = 0; i < hb[currhb].cnt; ++i) { \
814 cd = ceucl_d(quant->mc_colors+hb[currhb].vec[i], &val); \
815 if (cd < ld) { ld = cd; bst_idx = hb[currhb].vec[i]; } \
818 #define CF_CLEANUP myfree(hb)
822 #ifdef IM_CFLINSEARCH
823 /* as simple as it gets */
824 #define CF_VARS long ld, cd
825 #define CF_SETUP /* none needed */
828 for (i = 0; i < quant->mc_count; ++i) { \
829 cd = ceucl_d(quant->mc_colors+i, &val); \
830 if (cd < ld) { ld = cd; bst_idx = i; } \
836 static int gsortchan;
837 static i_quantize *gquant;
838 static int chansort(void const *a, void const *b) {
839 return gquant->mc_colors[*(int const *)a].channel[gsortchan] -
840 gquant->mc_colors[*(int const *)b].channel[gsortchan];
842 #define CF_VARS int *indices, sortchan, diff; \
844 int vindex[256] /* where to find value i of chan */
846 static void chansetup(i_img *img, i_quantize *quant, int *csortchan,
847 int *vindex, int **cindices) {
848 int *indices, sortchan, chan, i, chval;
849 int chanmins[MAXCHANNELS], chanmaxs[MAXCHANNELS], maxrange;
851 /* find the channel with the maximum range */
852 /* the maximum stddev would probably be better */
853 for (chan = 0; chan < img->channels; ++chan) {
854 chanmins[chan] = 256; chanmaxs[chan] = 0;
855 for (i = 0; i < quant->mc_count; ++i) {
856 if (quant->mc_colors[i].channel[chan] < chanmins[chan])
857 chanmins[chan] = quant->mc_colors[i].channel[chan];
858 if (quant->mc_colors[i].channel[chan] > chanmaxs[chan])
859 chanmaxs[chan] = quant->mc_colors[i].channel[chan];
863 for (chan = 0; chan < img->channels; ++chan) {
864 if (chanmaxs[chan]-chanmins[chan] > maxrange) {
865 maxrange = chanmaxs[chan]-chanmins[chan];
869 indices = mymalloc(quant->mc_count * sizeof(int)) ;
870 for (i = 0; i < quant->mc_count; ++i) {
873 gsortchan = sortchan;
875 qsort(indices, quant->mc_count, sizeof(int), chansort) ;
876 /* now a lookup table to find entries faster */
877 for (chval=0, i=0; i < quant->mc_count; ++i) {
878 while (chval < 256 &&
879 chval < quant->mc_colors[indices[i]].channel[sortchan]) {
883 while (chval < 256) {
884 vindex[chval++] = quant->mc_count-1;
886 *csortchan = sortchan;
891 chansetup(img, quant, &sortchan, vindex, &indices)
893 int chanfind(i_color val, i_quantize *quant, int *indices, int *vindex,
895 int i, bst_idx, diff, maxdiff;
898 i = vindex[val.channel[sortchan]];
899 bst_idx = indices[i];
902 maxdiff = quant->mc_count;
903 while (diff < maxdiff) {
904 if (i+diff < quant->mc_count) {
905 cd = ceucl_d(&val, quant->mc_colors+indices[i+diff]);
907 bst_idx = indices[i+diff];
913 cd = ceucl_d(&val, quant->mc_colors+indices[i-diff]);
915 bst_idx = indices[i-diff];
927 bst_idx = chanfind(val, quant, indices, vindex, sortchan)
930 #define CF_CLEANUP myfree(indices)
934 #ifdef IM_CFRAND2DIST
936 /* This is based on a method described by Addi in the #imager channel
937 on the 28/2/2001. I was about 1am Sydney time at the time, so I
938 wasn't at my most cogent. Well, that's my excuse :)
940 <TonyC> what I have at the moment is: hashboxes, with optimum hash box
941 filling; simple linear search; and a lookup in the widest channel
942 (currently the channel with the maximum range)
943 <Addi> There is one more way that might be simple to implement.
944 <Addi> You want to hear?
946 <purl> somebody said that was not true
947 <Addi> For each of the colors in the palette start by creating a
948 sorted list of the form:
949 <Addi> [distance, color]
950 <Addi> Where they are sorted by distance.
951 <TonyC> distance to where?
952 <Addi> Where the elements in the lists are the distances and colors of
953 the other colors in the palette
955 <Addi> So if you are at color 0
956 <Addi> ok - now to search for the closest color when you are creating
957 the final image is done like this:
958 <Addi> a) pick a random color from the palette
959 <Addi> b) calculate the distance to it
960 <Addi> c) only check the vectors that are within double the distance
961 in the list of the color you picked from the palette.
962 <Addi> Does that seem logical?
963 <Addi> Lets imagine that we only have grayscale to make an example:
964 <Addi> Our palette has 1 4 10 20 as colors.
965 <Addi> And we want to quantize the color 11
966 <Addi> lets say we picked 10 randomly
967 <Addi> the double distance is 2
968 <Addi> since abs(10-11)*2 is 2
969 <Addi> And the list at vector 10 is this:
970 <Addi> [0, 10], [6 4], [9, 1], [10, 20]
971 <Addi> so we look at the first one (but not the second one since 6 is
972 at a greater distance than 2.
973 <Addi> Any of that make sense?
974 <TonyC> yes, though are you suggesting another random jump to one of
975 the colours with the possible choices? or an exhaustive search?
976 <Addi> TonyC: It's possible to come up with a recursive/iterative
977 enhancement but this is the 'basic' version.
978 <Addi> Which would do an iterative search.
979 <Addi> You can come up with conditions where it pays to switch to a new one.
980 <Addi> And the 'random' start can be switched over to a small tree.
981 <Addi> So you would have a little index at the start.
982 <Addi> to get you into the general direction
983 <Addi> Perhaps just an 8 split.
984 <Addi> that is - split each dimension in half.
986 <TonyC> I get the idea
987 <Addi> But this would seem to be a good approach in our case since we
988 usually have few codevectors.
989 <Addi> So we only need 256*256 entries in a table.
990 <Addi> We could even only index some of them that were deemed as good
992 <TonyC> I was considering adding paletted output support for PNG and
993 TIFF at some point, which support 16-bit palettes
1001 typedef struct i_dists {
1009 static int dists_sort(void const *a, void const *b) {
1010 return ((i_dists *)a)->dist - ((i_dists *)b)->dist;
1013 static void rand2dist_setup(i_quantize *quant, i_dists **cdists) {
1015 mymalloc(sizeof(i_dists)*quant->mc_count*quant->mc_count);
1018 for (i = 0; i < quant->mc_count; ++i) {
1019 i_dists *ldists = dists + quant->mc_count * i;
1020 i_color val = quant->mc_colors[i];
1021 for (j = 0; j < quant->mc_count; ++j) {
1022 ldists[j].index = j;
1023 ldists[j].dist = ceucl_d(&val, quant->mc_colors+j);
1025 qsort(ldists, quant->mc_count, sizeof(i_dists), dists_sort);
1031 bst_idx = rand() % quant->mc_count; \
1032 rand2dist_setup(quant, &dists)
1034 static int rand2dist_find(i_color val, i_quantize *quant, i_dists *dists, int index) {
1041 cdists = dists + index * quant->mc_count;
1043 maxld = 8 * ceucl_d(&val, quant->mc_colors+index);
1044 for (i = 0; i < quant->mc_count && cdists[i].dist <= maxld; ++i) {
1045 cd = ceucl_d(&val, quant->mc_colors+cdists[i].index);
1047 bst_idx = cdists[i].index;
1054 #define CF_FIND bst_idx = rand2dist_find(val, quant, dists, bst_idx)
1056 #define CF_CLEANUP myfree(dists)
1061 static void translate_addi(i_quantize *quant, i_img *img, i_palidx *out) {
1062 int x, y, i, k, bst_idx;
1064 int pixdev = quant->perturb;
1069 if (img->channels >= 3) {
1072 for(y=0;y<img->ysize;y++) for(x=0;x<img->xsize;x++) {
1073 i_gpix(img,x,y,&val);
1074 val.channel[0]=g_sat(val.channel[0]+(int)(pixdev*frandn()));
1075 val.channel[1]=g_sat(val.channel[1]+(int)(pixdev*frandn()));
1076 val.channel[2]=g_sat(val.channel[2]+(int)(pixdev*frandn()));
1082 for(y=0;y<img->ysize;y++) for(x=0;x<img->xsize;x++) {
1083 i_gpix(img,x,y,&val);
1092 for(y=0;y<img->ysize;y++) for(x=0;x<img->xsize;x++) {
1093 i_gpix(img,x,y,&val);
1094 val.channel[1] = val.channel[2] =
1095 val.channel[0]=g_sat(val.channel[0]+(int)(pixdev*frandn()));
1101 for(y=0;y<img->ysize;y++) for(x=0;x<img->xsize;x++) {
1102 i_gpix(img,x,y,&val);
1103 val.channel[1] = val.channel[2] = val.channel[0];
1112 static int floyd_map[] =
1118 static int jarvis_map[] =
1125 static int stucki_map[] =
1132 struct errdiff_map {
1134 int width, height, orig;
1137 static struct errdiff_map maps[] =
1139 { floyd_map, 3, 2, 1 },
1140 { jarvis_map, 5, 3, 2 },
1141 { stucki_map, 5, 3, 2 },
1144 typedef struct errdiff_tag {
1148 /* perform an error diffusion dither */
1151 translate_errdiff(i_quantize *quant, i_img *img, i_palidx *out) {
1153 int mapw, maph, mapo;
1162 if ((quant->errdiff & ed_mask) == ed_custom) {
1163 map = quant->ed_map;
1164 mapw = quant->ed_width;
1165 maph = quant->ed_height;
1166 mapo = quant->ed_orig;
1169 int index = quant->errdiff & ed_mask;
1170 if (index >= ed_custom) index = ed_floyd;
1171 map = maps[index].map;
1172 mapw = maps[index].width;
1173 maph = maps[index].height;
1174 mapo = maps[index].orig;
1177 errw = img->xsize+mapw;
1178 err = mymalloc(sizeof(*err) * maph * errw);
1179 /*errp = err+mapo;*/
1180 memset(err, 0, sizeof(*err) * maph * errw);
1183 for (i = 0; i < maph * mapw; ++i)
1184 difftotal += map[i];
1186 for (dy = 0; dy < maph; ++dy) {
1187 for (dx = 0; dx < mapw; ++dx) {
1188 printf("%2d", map[dx+dy*mapw]);
1195 for (y = 0; y < img->ysize; ++y) {
1196 for (x = 0; x < img->xsize; ++x) {
1200 i_gpix(img, x, y, &val);
1201 if (img->channels < 3) {
1202 val.channel[1] = val.channel[2] = val.channel[0];
1205 perr.r = perr.r < 0 ? -((-perr.r)/difftotal) : perr.r/difftotal;
1206 perr.g = perr.g < 0 ? -((-perr.g)/difftotal) : perr.g/difftotal;
1207 perr.b = perr.b < 0 ? -((-perr.b)/difftotal) : perr.b/difftotal;
1208 /*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);*/
1209 val.channel[0] = g_sat(val.channel[0]-perr.r);
1210 val.channel[1] = g_sat(val.channel[1]-perr.g);
1211 val.channel[2] = g_sat(val.channel[2]-perr.b);
1214 perr.r = quant->mc_colors[bst_idx].channel[0] - val.channel[0];
1215 perr.g = quant->mc_colors[bst_idx].channel[1] - val.channel[1];
1216 perr.b = quant->mc_colors[bst_idx].channel[2] - val.channel[2];
1217 /*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);*/
1218 for (dx = 0; dx < mapw; ++dx) {
1219 for (dy = 0; dy < maph; ++dy) {
1220 err[x+dx+dy*errw].r += perr.r * map[dx+mapw*dy];
1221 err[x+dx+dy*errw].g += perr.g * map[dx+mapw*dy];
1222 err[x+dx+dy*errw].b += perr.b * map[dx+mapw*dy];
1227 /* shift up the error matrix */
1228 for (dy = 0; dy < maph-1; ++dy) {
1229 memcpy(err+dy*errw, err+(dy+1)*errw, sizeof(*err)*errw);
1231 memset(err+(maph-1)*errw, 0, sizeof(*err)*errw);
1236 /* Prescan finds the boxes in the image that have the highest number of colors
1237 and that result is used as the initial value for the vectores */
1240 static void prescan(i_img **imgs,int count, int cnum, cvec *clr, i_sample_t *line) {
1246 for(i=0;i<512;i++) {
1252 /* process each image */
1253 for (i = 0; i < count; ++i) {
1254 i_img *im = imgs[i];
1255 chans = im->channels >= 3 ? NULL : gray_samples;
1256 for(y=0;y<im->ysize;y++) {
1257 i_gsamp(im, 0, im->xsize, y, line, chans, 3);
1259 for(x=0;x<im->xsize;x++) {
1260 prebox[pixbox_ch(val)].pixcnt++;
1265 for(i=0;i<512;i++) prebox[i].pdc=prebox[i].pixcnt;
1266 qsort(prebox,512,sizeof(pbox),(cmpfunc)pboxcmp);
1268 for(i=0;i<cnum;i++) {
1269 /* printf("Color %d\n",i);
1270 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); }
1275 /* 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); } */
1281 /* printf("prebox[%d].cand=%d\n",k,prebox[k].cand); */
1282 if (clr[i].fixed) { i++; continue; } /* reserved go to next */
1283 if (j>=prebox[k].cand) { k++; j=1; } else {
1284 if (prebox[k].cand == 2) boxcenter(prebox[k].boxnum,&(clr[i]));
1285 else boxrand(prebox[k].boxnum,&(clr[i]));
1286 /* 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); */
1294 static void reorder(pbox prescan[512]) {
1302 c.pdc=c.pixcnt/(c.cand*c.cand);
1303 /* c.pdc=c.pixcnt/c.cand; */
1304 while(c.pdc < prescan[nidx+1].pdc && nidx < 511) {
1305 prescan[nidx]=prescan[nidx+1];
1312 pboxcmp(const pbox *a,const pbox *b) {
1313 if (a->pixcnt > b->pixcnt) return -1;
1314 if (a->pixcnt < b->pixcnt) return 1;
1319 boxcenter(int box,cvec *cv) {
1320 cv->r=15+((box&448)>>1);
1321 cv->g=15+((box&56)<<2);
1322 cv->b=15+((box&7)<<5);
1326 bbox(int box,int *r0,int *r1,int *g0,int *g1,int *b0,int *b1) {
1336 boxrand(int box,cvec *cv) {
1337 cv->r=6+(rand()%25)+((box&448)>>1);
1338 cv->g=6+(rand()%25)+((box&56)<<2);
1339 cv->b=6+(rand()%25)+((box&7)<<5);
1349 while (w >= 1 || w == 0) {
1350 u1 = 2 * frand() - 1;
1351 u2 = 2 * frand() - 1;
1355 w = sqrt((-2*log(w))/w);
1359 /* Create hash index */
1362 cr_hashindex(cvec clr[256],int cnum,hashbox hb[512]) {
1364 int bx,mind,cd,cumcnt,bst_idx,i;
1365 /* printf("indexing... \n");*/
1368 for(bx=0; bx<512; bx++) {
1370 for(i=0; i<cnum; i++) {
1371 cd = maxdist(bx,&clr[i]);
1372 if (cd < mind) { mind=cd; bst_idx=i; }
1376 for(i=0;i<cnum;i++) if (mindist(bx,&clr[i])<mind) hb[bx].vec[hb[bx].cnt++]=i;
1377 /*printf("box %d -> approx -> %d\n",bx,hb[bx].cnt); */
1378 /* statbox(bx,cnum,clr); */
1382 /* printf("Average search space: %d\n",cumcnt/512); */
1386 maxdist(int boxnum,cvec *cv) {
1387 int r0,r1,g0,g1,b0,b1;
1394 bbox(boxnum,&r0,&r1,&g0,&g1,&b0,&b1);
1396 mr=i_max(abs(b-b0),abs(b-b1));
1397 mg=i_max(abs(g-g0),abs(g-g1));
1398 mb=i_max(abs(r-r0),abs(r-r1));
1400 return PWR2(mr)+PWR2(mg)+PWR2(mb);
1404 mindist(int boxnum,cvec *cv) {
1405 int r0,r1,g0,g1,b0,b1;
1412 bbox(boxnum,&r0,&r1,&g0,&g1,&b0,&b1);
1414 /* printf("box %d, (%d,%d,%d)-(%d,%d,%d) vec (%d,%d,%d) ",boxnum,r0,g0,b0,r1,g1,b1,r,g,b); */
1416 if (r0<=r && r<=r1 && g0<=g && g<=g1 && b0<=b && b<=b1) return 0;
1418 mr=i_min(abs(b-b0),abs(b-b1));
1419 mg=i_min(abs(g-g0),abs(g-g1));
1420 mb=i_min(abs(r-r0),abs(r-r1));
1426 if (r0<=r && r<=r1 && g0<=g && g<=g1) return mb;
1427 if (r0<=r && r<=r1 && b0<=b && b<=b1) return mg;
1428 if (b0<=b && b<=b1 && g0<=g && g<=g1) return mr;
1430 if (r0<=r && r<=r1) return mg+mb;
1431 if (g0<=g && g<=g1) return mr+mb;
1432 if (b0<=b && b<=b1) return mg+mr;
1437 static void transparent_threshold(i_quantize *, i_palidx *, i_img *, i_palidx);
1438 static void transparent_errdiff(i_quantize *, i_palidx *, i_img *, i_palidx);
1439 static void transparent_ordered(i_quantize *, i_palidx *, i_img *, i_palidx);
1441 void quant_transparent(i_quantize *quant, i_palidx *data, i_img *img,
1442 i_palidx trans_index)
1444 switch (quant->transp) {
1449 quant->tr_threshold = 128;
1452 transparent_threshold(quant, data, img, trans_index);
1456 transparent_errdiff(quant, data, img, trans_index);
1460 transparent_ordered(quant, data, img, trans_index);
1466 transparent_threshold(i_quantize *quant, i_palidx *data, i_img *img,
1467 i_palidx trans_index)
1470 i_sample_t *line = mymalloc(img->xsize * sizeof(i_sample_t));
1471 int trans_chan = img->channels > 2 ? 3 : 1;
1473 for (y = 0; y < img->ysize; ++y) {
1474 i_gsamp(img, 0, img->xsize, y, line, &trans_chan, 1);
1475 for (x = 0; x < img->xsize; ++x) {
1476 if (line[x] < quant->tr_threshold)
1477 data[y*img->xsize+x] = trans_index;
1484 transparent_errdiff(i_quantize *quant, i_palidx *data, i_img *img,
1485 i_palidx trans_index)
1489 int mapw, maph, mapo;
1490 int errw, *err, *errp;
1491 int difftotal, out, error;
1492 int x, y, dx, dy, i;
1494 int trans_chan = img->channels > 2 ? 3 : 1;
1496 /* no custom map for transparency (yet) */
1497 index = quant->tr_errdiff & ed_mask;
1498 if (index >= ed_custom) index = ed_floyd;
1499 map = maps[index].map;
1500 mapw = maps[index].width;
1501 maph = maps[index].height;
1502 mapo = maps[index].orig;
1504 errw = img->xsize+mapw-1;
1505 err = mymalloc(sizeof(*err) * maph * errw);
1507 memset(err, 0, sizeof(*err) * maph * errw);
1509 line = mymalloc(img->xsize * sizeof(i_sample_t));
1511 for (i = 0; i < maph * mapw; ++i)
1512 difftotal += map[i];
1513 for (y = 0; y < img->ysize; ++y) {
1514 i_gsamp(img, 0, img->xsize, y, line, &trans_chan, 1);
1515 for (x = 0; x < img->xsize; ++x) {
1516 line[x] = g_sat(line[x]-errp[x]/difftotal);
1517 if (line[x] < 128) {
1519 data[y*img->xsize+x] = trans_index;
1524 error = out - line[x];
1525 for (dx = 0; dx < mapw; ++dx) {
1526 for (dy = 0; dy < maph; ++dy) {
1527 errp[x+dx-mapo+dy*errw] += error * map[dx+mapw*dy];
1531 /* shift up the error matrix */
1532 for (dy = 0; dy < maph-1; ++dy)
1533 memcpy(err+dy*errw, err+(dy+1)*errw, sizeof(*err)*errw);
1534 memset(err+(maph-1)*errw, 0, sizeof(*err)*errw);
1540 /* builtin ordered dither maps */
1541 static unsigned char
1542 orddith_maps[][64] =
1545 this is purely random - it's pretty awful
1547 48, 72, 196, 252, 180, 92, 108, 52,
1548 228, 176, 64, 8, 236, 40, 20, 164,
1549 120, 128, 84, 116, 24, 28, 172, 220,
1550 68, 0, 188, 124, 184, 224, 192, 104,
1551 132, 100, 240, 200, 152, 160, 244, 44,
1552 96, 204, 144, 16, 140, 56, 232, 216,
1553 208, 4, 76, 212, 136, 248, 80, 168,
1554 156, 88, 32, 112, 148, 12, 36, 60,
1558 perl spot.perl '($x-3.5)*($x-3.5)+($y-3.5)*($y-3.5)'
1560 240, 232, 200, 136, 140, 192, 228, 248,
1561 220, 148, 100, 76, 80, 104, 152, 212,
1562 180, 116, 56, 32, 36, 60, 120, 176,
1563 156, 64, 28, 0, 8, 44, 88, 160,
1564 128, 92, 24, 12, 4, 40, 68, 132,
1565 184, 96, 48, 20, 16, 52, 108, 188,
1566 216, 144, 112, 72, 84, 124, 164, 224,
1567 244, 236, 196, 168, 172, 204, 208, 252,
1571 'min(dist(1.5, 1.5),dist(5.5,1.5),dist(1.5,5.5),dist(5.5,5.5))'
1573 196, 72, 104, 220, 200, 80, 112, 224,
1574 76, 4, 24, 136, 84, 8, 32, 144,
1575 108, 28, 52, 168, 116, 36, 56, 176,
1576 216, 140, 172, 244, 228, 148, 180, 248,
1577 204, 92, 124, 236, 192, 68, 96, 208,
1578 88, 12, 44, 156, 64, 0, 16, 128,
1579 120, 40, 60, 188, 100, 20, 48, 160,
1580 232, 152, 184, 252, 212, 132, 164, 240,
1583 perl spot.perl '$y-3'
1585 160, 164, 168, 172, 176, 180, 184, 188,
1586 128, 132, 136, 140, 144, 148, 152, 156,
1587 32, 36, 40, 44, 48, 52, 56, 60,
1588 0, 4, 8, 12, 16, 20, 24, 28,
1589 64, 68, 72, 76, 80, 84, 88, 92,
1590 96, 100, 104, 108, 112, 116, 120, 124,
1591 192, 196, 200, 204, 208, 212, 216, 220,
1592 224, 228, 232, 236, 240, 244, 248, 252,
1595 perl spot.perl '$x-3'
1597 180, 100, 40, 12, 44, 104, 184, 232,
1598 204, 148, 60, 16, 64, 128, 208, 224,
1599 212, 144, 76, 8, 80, 132, 216, 244,
1600 160, 112, 68, 20, 84, 108, 172, 236,
1601 176, 96, 72, 28, 88, 152, 188, 228,
1602 200, 124, 92, 0, 32, 116, 164, 240,
1603 168, 120, 36, 24, 48, 136, 192, 248,
1604 196, 140, 52, 4, 56, 156, 220, 252,
1607 perl spot.perl '$y+$x-7'
1609 248, 232, 224, 192, 140, 92, 52, 28,
1610 240, 220, 196, 144, 108, 60, 12, 64,
1611 216, 180, 148, 116, 76, 20, 80, 128,
1612 204, 152, 104, 44, 16, 72, 100, 160,
1613 164, 96, 68, 24, 56, 112, 168, 176,
1614 124, 40, 8, 36, 88, 136, 184, 212,
1615 84, 4, 32, 120, 156, 188, 228, 236,
1616 0, 48, 132, 172, 200, 208, 244, 252,
1619 perl spot.perl '$y-$x'
1621 0, 32, 116, 172, 184, 216, 236, 252,
1622 56, 8, 72, 132, 136, 200, 228, 240,
1623 100, 36, 12, 40, 92, 144, 204, 220,
1624 168, 120, 60, 16, 44, 96, 156, 176,
1625 180, 164, 112, 48, 28, 52, 128, 148,
1626 208, 192, 152, 88, 84, 20, 64, 104,
1627 232, 224, 196, 140, 108, 68, 24, 76,
1628 248, 244, 212, 188, 160, 124, 80, 4,
1632 good for display, bad for print
1635 0, 128, 32, 192, 8, 136, 40, 200,
1636 224, 64, 160, 112, 232, 72, 168, 120,
1637 48, 144, 16, 208, 56, 152, 24, 216,
1638 176, 96, 240, 80, 184, 104, 248, 88,
1639 12, 140, 44, 204, 4, 132, 36, 196,
1640 236, 76, 172, 124, 228, 68, 164, 116,
1641 60, 156, 28, 220, 52, 148, 20, 212,
1642 188, 108, 252, 92, 180, 100, 244, 84,
1647 transparent_ordered(i_quantize *quant, i_palidx *data, i_img *img,
1648 i_palidx trans_index)
1650 unsigned char *spot;
1653 int trans_chan = img->channels > 2 ? 3 : 1;
1654 if (quant->tr_orddith == od_custom)
1655 spot = quant->tr_custom;
1657 spot = orddith_maps[quant->tr_orddith];
1659 line = mymalloc(img->xsize * sizeof(i_sample_t));
1660 for (y = 0; y < img->ysize; ++y) {
1661 i_gsamp(img, 0, img->xsize, y, line, &trans_chan, 1);
1662 for (x = 0; x < img->xsize; ++x) {
1663 if (line[x] < spot[(x&7)+(y&7)*8])
1664 data[x+y*img->xsize] = trans_index;