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 =item i_quant_makemap(quant, imgs, count)
32 =category Image quantization
34 Analyzes the I<count> images in I<imgs> according to the rules in
35 I<quant> to build a color map (optimal or not depending on
42 i_quant_makemap(i_quantize *quant, i_img **imgs, int count) {
44 if (quant->translate == pt_giflib) {
45 /* giflib does it's own color table generation */
46 /* previously we used giflib's quantizer, but it didn't handle multiple
47 images, which made it hard to build a global color map
48 We've implemented our own median cut code so we can ignore
50 makemap_mediancut(quant, imgs, count);
54 switch (quant->make_colors & mc_mask) {
56 /* use user's specified map */
62 for (r = 0; r < 256; r+=0x33)
63 for (g = 0; g < 256; g+=0x33)
64 for (b = 0; b < 256; b += 0x33)
65 setcol(quant->mc_colors+i++, r, g, b, 255);
71 makemap_mediancut(quant, imgs, count);
76 makemap_addi(quant, imgs, count);
81 static void translate_closest(i_quantize *, i_img *, i_palidx *);
82 static void translate_errdiff(i_quantize *, i_img *, i_palidx *);
83 static void translate_addi(i_quantize *, i_img *, i_palidx *);
86 =item i_quant_translate(quant, img)
88 =category Image quantization
90 Quantize the image given the palette in quant.
92 On success returns a pointer to a memory block of img->xsize *
93 img->ysize i_palidx entries.
95 On failure returns NULL.
97 You should call myfree() on the returned block when you're done with
100 This function will fail if the supplied palette contains no colors.
105 i_quant_translate(i_quantize *quant, i_img *img) {
109 mm_log((1, "quant_translate(quant %p, img %p)\n", quant, img));
111 /* there must be at least one color in the paletted (though even that
113 if (quant->mc_count == 0) {
114 i_push_error(0, "no colors available for translation");
118 bytes = img->xsize * img->ysize;
119 if (bytes / img->ysize != img->xsize) {
120 i_push_error(0, "integer overflow calculating memory allocation");
123 result = mymalloc(bytes);
125 switch (quant->translate) {
128 translate_closest(quant, img, result);
132 translate_errdiff(quant, img, result);
137 translate_addi(quant, img, result);
144 static void translate_closest(i_quantize *quant, i_img *img, i_palidx *out) {
146 translate_addi(quant, img, out);
149 #define PWR2(x) ((x)*(x))
151 typedef int (*cmpfunc)(const void*, const void*);
174 static void prescan(i_img **im,int count, int cnum, cvec *clr, i_sample_t *line);
175 static void reorder(pbox prescan[512]);
176 static int pboxcmp(const pbox *a,const pbox *b);
177 static void boxcenter(int box,cvec *cv);
178 static float frandn(void);
179 static void boxrand(int box,cvec *cv);
180 static void bbox(int box,int *r0,int *r1,int *g0,int *g1,int *b0,int *b1);
181 static void cr_hashindex(cvec clr[256],int cnum,hashbox hb[512]);
182 static int mindist(int boxnum,cvec *cv);
183 static int maxdist(int boxnum,cvec *cv);
185 /* Some of the simpler functions are kept here to aid the compiler -
186 maybe some of them will be inlined. */
189 pixbox(i_color *ic) { return ((ic->channel[0] & 224)<<1)+ ((ic->channel[1]&224)>>2) + ((ic->channel[2] &224) >> 5); }
192 pixbox_ch(i_sample_t *chans) { return ((chans[0] & 224)<<1)+ ((chans[1]&224)>>2) + ((chans[2] &224) >> 5); }
196 if (in>255) { return 255; }
197 else if (in>0) return in;
204 return rand()/(RAND_MAX+1.0);
210 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]); }
215 eucl_d_ch(cvec* cv,i_sample_t *chans) {
216 return PWR2(cv->r - chans[0]) + PWR2(cv->g - chans[1])
217 + PWR2(cv->b - chans[2]);
222 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]); }
225 gray_samples[] = { 0, 0, 0 };
229 This quantization algorithm and implementation routines are by Arnar
230 M. Hrafnkelson. In case any new ideas are here they are mine since
231 this was written from scratch.
233 The algorithm uses local means in the following way:
235 For each point in the colormap we find which image points
236 have that point as it's closest point. We calculate the mean
237 of those points and in the next iteration it will be the new
238 entry in the colormap.
240 In order to speed this process up (i.e. nearest neighbor problem) We
241 divied the r,g,b space up in equally large 512 boxes. The boxes are
242 numbered from 0 to 511. Their numbering is so that for a given vector
243 it is known that it belongs to the box who is formed by concatenating the
244 3 most significant bits from each component of the RGB triplet.
246 For each box we find the list of points from the colormap who might be
247 closest to any given point within the box. The exact solution
248 involves finding the Voronoi map (or the dual the Delauny
249 triangulation) and has many issues including numerical stability.
251 So we use this approximation:
253 1. Find which point has the shortest maximum distance to the box.
254 2. Find all points that have a shorter minimum distance than that to the box
256 This is a very simple task and is not computationally heavy if one
257 takes into account that the minimum distances from a pixel to a box is
258 always found by checking if it's inside the box or is closest to some
259 side or a corner. Finding the maximum distance is also either a side
262 This approach results 2-3 times more than the actual points needed but
263 is still a good gain over the complete space. Usually when one has a
264 256 Colorcolor map a search over 30 is often obtained.
266 A bit of an enhancement to this approach is to keep a seperate list
267 for each side of the cube, but this will require even more memory.
269 Arnar M. Hrafnkelsson (addi@umich.edu);
273 Extracted from gifquant.c, removed dependencies on gif_lib,
274 and added support for multiple images.
275 starting from 1nov2000 by TonyC <tony@develop-help.com>.
280 makemap_addi(i_quantize *quant, i_img **imgs, int count) {
282 int cnum, i, x, y, bst_idx=0, ld, cd, iter, currhb, img_num;
289 const int *sample_indices;
291 mm_log((1, "makemap_addi(quant %p { mc_count=%d, mc_colors=%p }, imgs %p, count %d)\n",
292 quant, quant->mc_count, quant->mc_colors, imgs, count));
296 clr = i_mempool_alloc(&mp, sizeof(cvec) * quant->mc_size);
297 hb = i_mempool_alloc(&mp, sizeof(hashbox) * 512);
298 for (i=0; i < quant->mc_count; ++i) {
299 clr[i].r = quant->mc_colors[i].rgb.r;
300 clr[i].g = quant->mc_colors[i].rgb.g;
301 clr[i].b = quant->mc_colors[i].rgb.b;
305 /* mymalloc doesn't clear memory, so I think we need this */
306 for (; i < quant->mc_size; ++i) {
307 /*clr[i].r = clr[i].g = clr[i].b = 0;*/
314 cnum = quant->mc_size;
317 for (img_num = 0; img_num < count; ++img_num) {
318 if (imgs[img_num]->xsize > maxwidth)
319 maxwidth = imgs[img_num]->xsize;
321 line = i_mempool_alloc(&mp, 3 * maxwidth * sizeof(*line));
323 prescan(imgs, count, cnum, clr, line);
324 cr_hashindex(clr, cnum, hb);
326 for(iter=0;iter<3;iter++) {
329 for (img_num = 0; img_num < count; ++img_num) {
330 i_img *im = imgs[img_num];
331 sample_indices = im->channels >= 3 ? NULL : gray_samples;
332 for(y=0;y<im->ysize;y++) {
333 i_gsamp(im, 0, im->xsize, y, line, sample_indices, 3);
335 for(x=0;x<im->xsize;x++) {
337 /*i_gpix(im,x,y,&val);*/
338 currhb=pixbox_ch(val);
339 /* printf("box = %d \n",currhb); */
340 for(i=0;i<hb[currhb].cnt;i++) {
341 /* 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); */
343 cd=eucl_d_ch(&clr[hb[currhb].vec[i]],val);
345 ld=cd; /* shortest distance yet */
346 bst_idx=hb[currhb].vec[i]; /* index of closest vector yet */
350 clr[bst_idx].mcount++;
352 clr[bst_idx].dr+=val[0];
353 clr[bst_idx].dg+=val[1];
354 clr[bst_idx].db+=val[2];
356 val += 3; /* next 3 samples (next pixel) */
363 clr[i].dr/=clr[i].mcount;
364 clr[i].dg/=clr[i].mcount;
365 clr[i].db/=clr[i].mcount;
368 /* for(i=0;i<cnum;i++) printf("vec(%d)=(%d,%d,%d) dest=(%d,%d,%d) matchcount=%d\n",
369 i,clr[i].r,clr[i].g,clr[i].b,clr[i].dr,clr[i].dg,clr[i].db,clr[i].mcount); */
371 /* printf("total error: %.2f\n",sqrt(accerr)); */
373 for(i=0;i<cnum;i++) {
374 if (clr[i].fixed) continue; /* skip reserved colors */
378 clr[i].r=clr[i].r*(1-dlt)+dlt*clr[i].dr;
379 clr[i].g=clr[i].g*(1-dlt)+dlt*clr[i].dg;
380 clr[i].b=clr[i].b*(1-dlt)+dlt*clr[i].db;
382 /* let's try something else */
394 cr_hashindex(clr,cnum,hb);
399 for(i=0;i<cnum;i++) {
400 cd=eucl_d(&clr[i],&val);
408 /* if defined, we only include colours with an mcount or that were
409 supplied in the fixed palette, giving us a smaller output palette */
410 #define ONLY_USE_USED
412 /* transfer the colors back */
414 for (i = 0; i < cnum; ++i) {
415 if (clr[i].fixed || clr[i].used) {
416 /*printf("Adding %d (%d,%d,%d)\n", i, clr[i].r, clr[i].g, clr[i].b);*/
417 quant->mc_colors[quant->mc_count].rgb.r = clr[i].r;
418 quant->mc_colors[quant->mc_count].rgb.g = clr[i].g;
419 quant->mc_colors[quant->mc_count].rgb.b = clr[i].b;
424 /* transfer the colors back */
425 for (i = 0; i < cnum; ++i) {
426 quant->mc_colors[i].rgb.r = clr[i].r;
427 quant->mc_colors[i].rgb.g = clr[i].g;
428 quant->mc_colors[i].rgb.b = clr[i].b;
430 quant->mc_count = cnum;
434 mm_log((1, "makemap_addi returns - quant.mc_count = %d\n", quant->mc_count));
435 for (i = 0; i < quant->mc_count; ++i)
436 mm_log((5, " map entry %d: (%d, %d, %d)\n", i, clr[i].r, clr[i].g, clr[i].b));
439 i_mempool_destroy(&mp);
447 #define MEDIAN_CUT_COLORS 32768
449 #define MED_CUT_INDEX(c) ((((c).rgb.r & 0xF8) << 7) | \
450 (((c).rgb.g & 0xF8) << 2) | (((c).rgb.b & 0xF8) >> 3))
452 #define MED_CUT_GRAY_INDEX(c) ((((c).rgb.r & 0xF8) << 7) | \
453 (((c).rgb.r & 0xF8) << 2) | (((c).rgb.r & 0xF8) >> 3))
455 /* scale these to cover the whole range */
456 #define MED_CUT_RED(index) ((((index) & 0x7C00) >> 10) * 255 / 31)
457 #define MED_CUT_GREEN(index) ((((index) & 0x3E0) >> 5) * 255 / 31)
458 #define MED_CUT_BLUE(index) (((index) & 0x1F) * 255 / 31)
461 i_sample_t min[3]; /* minimum for each channel */
462 i_sample_t max[3]; /* maximum for each channel */
463 i_sample_t width[3]; /* width for each channel */
464 int start, size; /* beginning and size of the partition */
465 int pixels; /* number of pixels represented by this partition */
469 =item calc_part(part, colors)
471 Calculates the new color limits for the given partition.
473 Giflib assumes that the limits for the non-split channels stay the
474 same, but this strikes me as incorrect, especially if the colors tend
477 Of course this could be optimized by not recalculating the channel we
478 just sorted on, but it's not worth the effort right now.
482 static void calc_part(medcut_partition *part, quant_color_entry *colors) {
485 for (ch = 0; ch < 3; ++ch) {
489 for (i = part->start; i < part->start + part->size; ++i) {
490 for (ch = 0; ch < 3; ++ch) {
491 if (part->min[ch] > colors[i].rgb[ch])
492 part->min[ch] = colors[i].rgb[ch];
493 if (part->max[ch] < colors[i].rgb[ch])
494 part->max[ch] = colors[i].rgb[ch];
497 for (ch = 0; ch < 3; ++ch) {
498 part->width[ch] = part->max[ch] - part->min[ch];
502 /* simple functions to sort by each channel - we could use a global, but
506 color_sort_red(void const *left, void const *right) {
507 return ((quant_color_entry *)left)->rgb[0] - ((quant_color_entry *)right)->rgb[0];
511 color_sort_green(void const *left, void const *right) {
512 return ((quant_color_entry *)left)->rgb[1] - ((quant_color_entry *)right)->rgb[1];
516 color_sort_blue(void const *left, void const *right) {
517 return ((quant_color_entry *)left)->rgb[2] - ((quant_color_entry *)right)->rgb[2];
520 static int (*sorters[])(void const *, void const *) =
528 makemap_mediancut(i_quantize *quant, i_img **imgs, int count) {
529 quant_color_entry *colors;
531 int imgn, x, y, i, ch;
536 medcut_partition *parts;
539 /* number of channels we search for the best channel to partition
540 this isn't terribly efficient, but it should work */
543 /*printf("images %d pal size %d\n", count, quant->mc_size);*/
547 colors = i_mempool_alloc(&mp, sizeof(*colors) * MEDIAN_CUT_COLORS);
548 for (i = 0; i < MEDIAN_CUT_COLORS; ++i) {
549 colors[i].rgb[0] = MED_CUT_RED(i);
550 colors[i].rgb[1] = MED_CUT_GREEN(i);
551 colors[i].rgb[2] = MED_CUT_BLUE(i);
556 for (imgn = 0; imgn < count; ++imgn) {
557 if (imgs[imgn]->xsize > max_width)
558 max_width = imgs[imgn]->xsize;
560 line = i_mempool_alloc(&mp, sizeof(i_color) * max_width);
562 /* build the stats */
564 chan_count = 1; /* assume we just have grayscale */
565 for (imgn = 0; imgn < count; ++imgn) {
566 total_pixels += imgs[imgn]->xsize * imgs[imgn]->ysize;
567 for (y = 0; y < imgs[imgn]->ysize; ++y) {
568 i_glin(imgs[imgn], 0, imgs[imgn]->xsize, y, line);
569 if (imgs[imgn]->channels > 2) {
571 for (x = 0; x < imgs[imgn]->xsize; ++x) {
572 ++colors[MED_CUT_INDEX(line[x])].count;
576 /* a gray-scale image, just use the first channel */
577 for (x = 0; x < imgs[imgn]->xsize; ++x) {
578 ++colors[MED_CUT_GRAY_INDEX(line[x])].count;
584 /* eliminate the empty colors */
586 for (in = 0; in < MEDIAN_CUT_COLORS; ++in) {
587 if (colors[in].count) {
588 colors[out++] = colors[in];
591 /*printf("out %d\n", out);
593 for (i = 0; i < out; ++i) {
594 if (colors[i].count) {
595 printf("%d: (%d,%d,%d) -> %d\n", i, colors[i].rgb[0], colors[i].rgb[1],
596 colors[i].rgb[2], colors[i].count);
600 if (out < quant->mc_size) {
601 /* just copy them into the color table */
602 for (i = 0; i < out; ++i) {
603 for (ch = 0; ch < 3; ++ch) {
604 quant->mc_colors[i].channel[ch] = colors[i].rgb[ch];
607 quant->mc_count = out;
610 /* build the starting partition */
611 parts = i_mempool_alloc(&mp, sizeof(*parts) * quant->mc_size);
614 parts[0].pixels = total_pixels;
615 calc_part(parts, colors);
618 while (color_count < quant->mc_size) {
619 /* initialized to avoid compiler warnings */
620 int max_index = 0, max_ch = 0; /* index/channel with biggest spread */
622 medcut_partition *workpart;
626 /* find the partition with the most biggest span with more than
629 for (i = 0; i < color_count; ++i) {
630 for (ch = 0; ch < chan_count; ++ch) {
631 if (parts[i].width[ch] > max_size
632 && parts[i].size > 1) {
635 max_size = parts[i].width[ch];
640 /* nothing else we can split */
644 workpart = parts+max_index;
645 /*printf("splitting partition %d (pixels %ld, start %d, size %d)\n", max_index, workpart->pixels, workpart->start, workpart->size);*/
646 qsort(colors + workpart->start, workpart->size, sizeof(*colors),
649 /* find the median or something like it we need to make sure both
650 sides of the split have at least one color in them, so we don't
651 test at the first or last entry */
653 cum_total = colors[i].count;
655 half = workpart->pixels / 2;
656 while (i < workpart->start + workpart->size - 1
657 && cum_total < half) {
658 cum_total += colors[i++].count;
660 /*printf("Split at %d to make %d (half %ld, cumtotal %ld)\n", i, color_count, half, cum_total);*/
662 /* found the spot to split */
663 parts[color_count].start = i;
664 parts[color_count].size = workpart->start + workpart->size - i;
665 workpart->size = i - workpart->start;
666 parts[color_count].pixels = workpart->pixels - cum_total;
667 workpart->pixels = cum_total;
669 /* recalculate the limits */
670 calc_part(workpart, colors);
671 calc_part(parts+color_count, colors);
675 /* fill in the color table - since we could still have partitions
676 that have more than one color, we need to average the colors */
677 for (part_num = 0; part_num < color_count; ++part_num) {
679 medcut_partition *workpart;
681 workpart = parts+part_num;
682 for (ch = 0; ch < 3; ++ch)
685 for (i = workpart->start; i < workpart->start + workpart->size; ++i) {
686 for (ch = 0; ch < 3; ++ch) {
687 sums[ch] += colors[i].rgb[ch] * colors[i].count;
690 for (ch = 0; ch < 3; ++ch) {
691 quant->mc_colors[part_num].channel[ch] = sums[ch] / workpart->pixels;
694 quant->mc_count = color_count;
696 /*printf("out %d colors\n", quant->mc_count);*/
697 i_mempool_destroy(&mp);
702 /* Define one of the following 4 symbols to choose a colour search method
703 The idea is to try these out, including benchmarking, to see which
704 is fastest in a good spread of circumstances.
705 I'd expect IM_CFLINSEARCH to be fastest for very small palettes, and
706 IM_CFHASHBOX for large images with large palettes.
708 Some other possibilities include:
709 - search over entries sorted by luminance
711 Initially I was planning on testing using the macros and then
712 integrating the code directly into each function, but this means if
713 we find a bug at a late stage we will need to update N copies of
714 the same code. Also, keeping the code in the macros means that the
715 code in the translation functions is much more to the point,
716 there's no distracting colour search code to remove attention from
717 what makes _this_ translation function different. It may be
718 advisable to move the setup code into functions at some point, but
719 it should be possible to do this fairly transparently.
721 If IM_CF_COPTS is defined then CFLAGS must have an appropriate
724 Each option needs to define 4 macros:
725 CF_VARS - variables to define in the function
726 CF_SETUP - code to setup for the colour search, eg. allocating and
727 initializing lookup tables
728 CF_FIND - code that looks for the color in val and puts the best
729 matching index in bst_idx
730 CF_CLEANUP - code to clean up, eg. releasing memory
733 /*#define IM_CFLINSEARCH*/
735 /*#define IM_CFSORTCHAN*/
736 /*#define IM_CFRAND2DIST*/
741 /* The original version I wrote for this used the sort.
742 If this is defined then we use a sort to extract the indices for
746 /* assume i is available */
747 #define CF_VARS hashbox *hb = mymalloc(sizeof(hashbox) * 512); \
753 static long *gdists; /* qsort is annoying */
754 /* int might be smaller than long, so we need to do a real compare
755 rather than a subtraction*/
756 static int distcomp(void const *a, void const *b) {
757 long ra = gdists[*(int const *)a];
758 long rb = gdists[*(int const *)b];
769 /* for each hashbox build a list of colours that are in the hb or is closer
771 This is pretty involved. The original gifquant generated the hashbox
772 as part of it's normal processing, but since the map generation is now
773 separated from the translation we need to do this on the spot.
774 Any optimizations, even if they don't produce perfect results would be
777 static void hbsetup(i_quantize *quant, hashbox *hb) {
778 long *dists, mind, maxd;
779 int cr, cb, cg, hbnum, i;
782 int *indices = mymalloc(quant->mc_count * sizeof(int));
785 dists = mymalloc(quant->mc_count * sizeof(long));
786 for (cr = 0; cr < 8; ++cr) {
787 for (cg = 0; cg < 8; ++cg) {
788 for (cb = 0; cb < 8; ++cb) {
789 /* centre of the hashbox */
790 cenc.channel[0] = cr*pboxjump+pboxjump/2;
791 cenc.channel[1] = cg*pboxjump+pboxjump/2;
792 cenc.channel[2] = cb*pboxjump+pboxjump/2;
793 hbnum = pixbox(&cenc);
795 /* order indices in the order of distance from the hashbox */
796 for (i = 0; i < quant->mc_count; ++i) {
800 dists[i] = ceucl_d(&cenc, quant->mc_colors+i);
803 /* it should be possible to do this without a sort
804 but so far I'm too lazy */
806 qsort(indices, quant->mc_count, sizeof(int), distcomp);
807 /* any colors that can match are within mind+diagonal size of
809 mind = dists[indices[0]];
811 maxd = (sqrt(mind)+pboxjump)*(sqrt(mind)+pboxjump);
812 while (i < quant->mc_count && dists[indices[i]] < maxd) {
813 hb[hbnum].vec[hb[hbnum].cnt++] = indices[i++];
816 /* work out the minimum */
818 for (i = 0; i < quant->mc_count; ++i) {
819 if (dists[i] < mind) mind = dists[i];
821 /* transfer any colours that might be closest to a colour in
823 maxd = (sqrt(mind)+pboxjump)*(sqrt(mind)+pboxjump);
824 for (i = 0; i < quant->mc_count; ++i) {
826 hb[hbnum].vec[hb[hbnum].cnt++] = i;
837 #define CF_SETUP hbsetup(quant, hb)
840 currhb = pixbox(&val); \
842 for (i = 0; i < hb[currhb].cnt; ++i) { \
843 cd = ceucl_d(quant->mc_colors+hb[currhb].vec[i], &val); \
844 if (cd < ld) { ld = cd; bst_idx = hb[currhb].vec[i]; } \
847 #define CF_CLEANUP myfree(hb)
851 #ifdef IM_CFLINSEARCH
852 /* as simple as it gets */
853 #define CF_VARS long ld, cd
854 #define CF_SETUP /* none needed */
857 for (i = 0; i < quant->mc_count; ++i) { \
858 cd = ceucl_d(quant->mc_colors+i, &val); \
859 if (cd < ld) { ld = cd; bst_idx = i; } \
865 static int gsortchan;
866 static i_quantize *gquant;
867 static int chansort(void const *a, void const *b) {
868 return gquant->mc_colors[*(int const *)a].channel[gsortchan] -
869 gquant->mc_colors[*(int const *)b].channel[gsortchan];
871 #define CF_VARS int *indices, sortchan, diff; \
873 int vindex[256] /* where to find value i of chan */
875 static void chansetup(i_img *img, i_quantize *quant, int *csortchan,
876 int *vindex, int **cindices) {
877 int *indices, sortchan, chan, i, chval;
878 int chanmins[MAXCHANNELS], chanmaxs[MAXCHANNELS], maxrange;
880 /* find the channel with the maximum range */
881 /* the maximum stddev would probably be better */
882 for (chan = 0; chan < img->channels; ++chan) {
883 chanmins[chan] = 256; chanmaxs[chan] = 0;
884 for (i = 0; i < quant->mc_count; ++i) {
885 if (quant->mc_colors[i].channel[chan] < chanmins[chan])
886 chanmins[chan] = quant->mc_colors[i].channel[chan];
887 if (quant->mc_colors[i].channel[chan] > chanmaxs[chan])
888 chanmaxs[chan] = quant->mc_colors[i].channel[chan];
892 for (chan = 0; chan < img->channels; ++chan) {
893 if (chanmaxs[chan]-chanmins[chan] > maxrange) {
894 maxrange = chanmaxs[chan]-chanmins[chan];
898 indices = mymalloc(quant->mc_count * sizeof(int)) ;
899 for (i = 0; i < quant->mc_count; ++i) {
902 gsortchan = sortchan;
904 qsort(indices, quant->mc_count, sizeof(int), chansort) ;
905 /* now a lookup table to find entries faster */
906 for (chval=0, i=0; i < quant->mc_count; ++i) {
907 while (chval < 256 &&
908 chval < quant->mc_colors[indices[i]].channel[sortchan]) {
912 while (chval < 256) {
913 vindex[chval++] = quant->mc_count-1;
915 *csortchan = sortchan;
920 chansetup(img, quant, &sortchan, vindex, &indices)
922 int chanfind(i_color val, i_quantize *quant, int *indices, int *vindex,
924 int i, bst_idx, diff, maxdiff;
927 i = vindex[val.channel[sortchan]];
928 bst_idx = indices[i];
931 maxdiff = quant->mc_count;
932 while (diff < maxdiff) {
933 if (i+diff < quant->mc_count) {
934 cd = ceucl_d(&val, quant->mc_colors+indices[i+diff]);
936 bst_idx = indices[i+diff];
942 cd = ceucl_d(&val, quant->mc_colors+indices[i-diff]);
944 bst_idx = indices[i-diff];
956 bst_idx = chanfind(val, quant, indices, vindex, sortchan)
959 #define CF_CLEANUP myfree(indices)
963 #ifdef IM_CFRAND2DIST
965 /* This is based on a method described by Addi in the #imager channel
966 on the 28/2/2001. I was about 1am Sydney time at the time, so I
967 wasn't at my most cogent. Well, that's my excuse :)
969 <TonyC> what I have at the moment is: hashboxes, with optimum hash box
970 filling; simple linear search; and a lookup in the widest channel
971 (currently the channel with the maximum range)
972 <Addi> There is one more way that might be simple to implement.
973 <Addi> You want to hear?
975 <purl> somebody said that was not true
976 <Addi> For each of the colors in the palette start by creating a
977 sorted list of the form:
978 <Addi> [distance, color]
979 <Addi> Where they are sorted by distance.
980 <TonyC> distance to where?
981 <Addi> Where the elements in the lists are the distances and colors of
982 the other colors in the palette
984 <Addi> So if you are at color 0
985 <Addi> ok - now to search for the closest color when you are creating
986 the final image is done like this:
987 <Addi> a) pick a random color from the palette
988 <Addi> b) calculate the distance to it
989 <Addi> c) only check the vectors that are within double the distance
990 in the list of the color you picked from the palette.
991 <Addi> Does that seem logical?
992 <Addi> Lets imagine that we only have grayscale to make an example:
993 <Addi> Our palette has 1 4 10 20 as colors.
994 <Addi> And we want to quantize the color 11
995 <Addi> lets say we picked 10 randomly
996 <Addi> the double distance is 2
997 <Addi> since abs(10-11)*2 is 2
998 <Addi> And the list at vector 10 is this:
999 <Addi> [0, 10], [6 4], [9, 1], [10, 20]
1000 <Addi> so we look at the first one (but not the second one since 6 is
1001 at a greater distance than 2.
1002 <Addi> Any of that make sense?
1003 <TonyC> yes, though are you suggesting another random jump to one of
1004 the colours with the possible choices? or an exhaustive search?
1005 <Addi> TonyC: It's possible to come up with a recursive/iterative
1006 enhancement but this is the 'basic' version.
1007 <Addi> Which would do an iterative search.
1008 <Addi> You can come up with conditions where it pays to switch to a new one.
1009 <Addi> And the 'random' start can be switched over to a small tree.
1010 <Addi> So you would have a little index at the start.
1011 <Addi> to get you into the general direction
1012 <Addi> Perhaps just an 8 split.
1013 <Addi> that is - split each dimension in half.
1015 <TonyC> I get the idea
1016 <Addi> But this would seem to be a good approach in our case since we
1017 usually have few codevectors.
1018 <Addi> So we only need 256*256 entries in a table.
1019 <Addi> We could even only index some of them that were deemed as good
1021 <TonyC> I was considering adding paletted output support for PNG and
1022 TIFF at some point, which support 16-bit palettes
1030 typedef struct i_dists {
1038 static int dists_sort(void const *a, void const *b) {
1039 return ((i_dists *)a)->dist - ((i_dists *)b)->dist;
1042 static void rand2dist_setup(i_quantize *quant, i_dists **cdists) {
1044 mymalloc(sizeof(i_dists)*quant->mc_count*quant->mc_count);
1047 for (i = 0; i < quant->mc_count; ++i) {
1048 i_dists *ldists = dists + quant->mc_count * i;
1049 i_color val = quant->mc_colors[i];
1050 for (j = 0; j < quant->mc_count; ++j) {
1051 ldists[j].index = j;
1052 ldists[j].dist = ceucl_d(&val, quant->mc_colors+j);
1054 qsort(ldists, quant->mc_count, sizeof(i_dists), dists_sort);
1060 bst_idx = rand() % quant->mc_count; \
1061 rand2dist_setup(quant, &dists)
1063 static int rand2dist_find(i_color val, i_quantize *quant, i_dists *dists, int index) {
1070 cdists = dists + index * quant->mc_count;
1072 maxld = 8 * ceucl_d(&val, quant->mc_colors+index);
1073 for (i = 0; i < quant->mc_count && cdists[i].dist <= maxld; ++i) {
1074 cd = ceucl_d(&val, quant->mc_colors+cdists[i].index);
1076 bst_idx = cdists[i].index;
1083 #define CF_FIND bst_idx = rand2dist_find(val, quant, dists, bst_idx)
1085 #define CF_CLEANUP myfree(dists)
1090 static void translate_addi(i_quantize *quant, i_img *img, i_palidx *out) {
1091 int x, y, i, k, bst_idx = 0;
1093 int pixdev = quant->perturb;
1098 if (img->channels >= 3) {
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[0]=g_sat(val.channel[0]+(int)(pixdev*frandn()));
1104 val.channel[1]=g_sat(val.channel[1]+(int)(pixdev*frandn()));
1105 val.channel[2]=g_sat(val.channel[2]+(int)(pixdev*frandn()));
1111 for(y=0;y<img->ysize;y++) for(x=0;x<img->xsize;x++) {
1112 i_gpix(img,x,y,&val);
1121 for(y=0;y<img->ysize;y++) for(x=0;x<img->xsize;x++) {
1122 i_gpix(img,x,y,&val);
1123 val.channel[1] = val.channel[2] =
1124 val.channel[0]=g_sat(val.channel[0]+(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);
1132 val.channel[1] = val.channel[2] = val.channel[0];
1141 static int floyd_map[] =
1147 static int jarvis_map[] =
1154 static int stucki_map[] =
1161 struct errdiff_map {
1163 int width, height, orig;
1166 static struct errdiff_map maps[] =
1168 { floyd_map, 3, 2, 1 },
1169 { jarvis_map, 5, 3, 2 },
1170 { stucki_map, 5, 3, 2 },
1173 typedef struct errdiff_tag {
1177 /* perform an error diffusion dither */
1180 translate_errdiff(i_quantize *quant, i_img *img, i_palidx *out) {
1182 int mapw, maph, mapo;
1191 if ((quant->errdiff & ed_mask) == ed_custom) {
1192 map = quant->ed_map;
1193 mapw = quant->ed_width;
1194 maph = quant->ed_height;
1195 mapo = quant->ed_orig;
1198 int index = quant->errdiff & ed_mask;
1199 if (index >= ed_custom) index = ed_floyd;
1200 map = maps[index].map;
1201 mapw = maps[index].width;
1202 maph = maps[index].height;
1203 mapo = maps[index].orig;
1206 errw = img->xsize+mapw;
1207 err = mymalloc(sizeof(*err) * maph * errw);
1208 /*errp = err+mapo;*/
1209 memset(err, 0, sizeof(*err) * maph * errw);
1212 for (i = 0; i < maph * mapw; ++i)
1213 difftotal += map[i];
1215 for (dy = 0; dy < maph; ++dy) {
1216 for (dx = 0; dx < mapw; ++dx) {
1217 printf("%2d", map[dx+dy*mapw]);
1224 for (y = 0; y < img->ysize; ++y) {
1225 for (x = 0; x < img->xsize; ++x) {
1229 i_gpix(img, x, y, &val);
1230 if (img->channels < 3) {
1231 val.channel[1] = val.channel[2] = val.channel[0];
1234 perr.r = perr.r < 0 ? -((-perr.r)/difftotal) : perr.r/difftotal;
1235 perr.g = perr.g < 0 ? -((-perr.g)/difftotal) : perr.g/difftotal;
1236 perr.b = perr.b < 0 ? -((-perr.b)/difftotal) : perr.b/difftotal;
1237 /*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);*/
1238 val.channel[0] = g_sat(val.channel[0]-perr.r);
1239 val.channel[1] = g_sat(val.channel[1]-perr.g);
1240 val.channel[2] = g_sat(val.channel[2]-perr.b);
1243 perr.r = quant->mc_colors[bst_idx].channel[0] - val.channel[0];
1244 perr.g = quant->mc_colors[bst_idx].channel[1] - val.channel[1];
1245 perr.b = quant->mc_colors[bst_idx].channel[2] - val.channel[2];
1246 /*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);*/
1247 for (dx = 0; dx < mapw; ++dx) {
1248 for (dy = 0; dy < maph; ++dy) {
1249 err[x+dx+dy*errw].r += perr.r * map[dx+mapw*dy];
1250 err[x+dx+dy*errw].g += perr.g * map[dx+mapw*dy];
1251 err[x+dx+dy*errw].b += perr.b * map[dx+mapw*dy];
1256 /* shift up the error matrix */
1257 for (dy = 0; dy < maph-1; ++dy) {
1258 memcpy(err+dy*errw, err+(dy+1)*errw, sizeof(*err)*errw);
1260 memset(err+(maph-1)*errw, 0, sizeof(*err)*errw);
1265 /* Prescan finds the boxes in the image that have the highest number of colors
1266 and that result is used as the initial value for the vectores */
1269 static void prescan(i_img **imgs,int count, int cnum, cvec *clr, i_sample_t *line) {
1275 for(i=0;i<512;i++) {
1281 /* process each image */
1282 for (i = 0; i < count; ++i) {
1283 i_img *im = imgs[i];
1284 chans = im->channels >= 3 ? NULL : gray_samples;
1285 for(y=0;y<im->ysize;y++) {
1286 i_gsamp(im, 0, im->xsize, y, line, chans, 3);
1288 for(x=0;x<im->xsize;x++) {
1289 prebox[pixbox_ch(val)].pixcnt++;
1294 for(i=0;i<512;i++) prebox[i].pdc=prebox[i].pixcnt;
1295 qsort(prebox,512,sizeof(pbox),(cmpfunc)pboxcmp);
1297 for(i=0;i<cnum;i++) {
1298 /* printf("Color %d\n",i);
1299 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); }
1304 /* 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); } */
1310 /* printf("prebox[%d].cand=%d\n",k,prebox[k].cand); */
1311 if (clr[i].fixed) { i++; continue; } /* reserved go to next */
1312 if (j>=prebox[k].cand) { k++; j=1; } else {
1313 if (prebox[k].cand == 2) boxcenter(prebox[k].boxnum,&(clr[i]));
1314 else boxrand(prebox[k].boxnum,&(clr[i]));
1315 /* 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); */
1323 static void reorder(pbox prescan[512]) {
1331 c.pdc=c.pixcnt/(c.cand*c.cand);
1332 /* c.pdc=c.pixcnt/c.cand; */
1333 while(c.pdc < prescan[nidx+1].pdc && nidx < 511) {
1334 prescan[nidx]=prescan[nidx+1];
1341 pboxcmp(const pbox *a,const pbox *b) {
1342 if (a->pixcnt > b->pixcnt) return -1;
1343 if (a->pixcnt < b->pixcnt) return 1;
1348 boxcenter(int box,cvec *cv) {
1349 cv->r=15+((box&448)>>1);
1350 cv->g=15+((box&56)<<2);
1351 cv->b=15+((box&7)<<5);
1355 bbox(int box,int *r0,int *r1,int *g0,int *g1,int *b0,int *b1) {
1365 boxrand(int box,cvec *cv) {
1366 cv->r=6+(rand()%25)+((box&448)>>1);
1367 cv->g=6+(rand()%25)+((box&56)<<2);
1368 cv->b=6+(rand()%25)+((box&7)<<5);
1378 while (w >= 1 || w == 0) {
1379 u1 = 2 * frand() - 1;
1380 u2 = 2 * frand() - 1;
1384 w = sqrt((-2*log(w))/w);
1388 /* Create hash index */
1391 cr_hashindex(cvec clr[256],int cnum,hashbox hb[512]) {
1393 int bx,mind,cd,cumcnt,bst_idx,i;
1394 /* printf("indexing... \n");*/
1397 for(bx=0; bx<512; bx++) {
1399 for(i=0; i<cnum; i++) {
1400 cd = maxdist(bx,&clr[i]);
1401 if (cd < mind) { mind=cd; bst_idx=i; }
1405 for(i=0;i<cnum;i++) if (mindist(bx,&clr[i])<mind) hb[bx].vec[hb[bx].cnt++]=i;
1406 /*printf("box %d -> approx -> %d\n",bx,hb[bx].cnt); */
1407 /* statbox(bx,cnum,clr); */
1411 /* printf("Average search space: %d\n",cumcnt/512); */
1415 maxdist(int boxnum,cvec *cv) {
1416 int r0,r1,g0,g1,b0,b1;
1423 bbox(boxnum,&r0,&r1,&g0,&g1,&b0,&b1);
1425 mr=i_max(abs(b-b0),abs(b-b1));
1426 mg=i_max(abs(g-g0),abs(g-g1));
1427 mb=i_max(abs(r-r0),abs(r-r1));
1429 return PWR2(mr)+PWR2(mg)+PWR2(mb);
1433 mindist(int boxnum,cvec *cv) {
1434 int r0,r1,g0,g1,b0,b1;
1441 bbox(boxnum,&r0,&r1,&g0,&g1,&b0,&b1);
1443 /* printf("box %d, (%d,%d,%d)-(%d,%d,%d) vec (%d,%d,%d) ",boxnum,r0,g0,b0,r1,g1,b1,r,g,b); */
1445 if (r0<=r && r<=r1 && g0<=g && g<=g1 && b0<=b && b<=b1) return 0;
1447 mr=i_min(abs(b-b0),abs(b-b1));
1448 mg=i_min(abs(g-g0),abs(g-g1));
1449 mb=i_min(abs(r-r0),abs(r-r1));
1455 if (r0<=r && r<=r1 && g0<=g && g<=g1) return mb;
1456 if (r0<=r && r<=r1 && b0<=b && b<=b1) return mg;
1457 if (b0<=b && b<=b1 && g0<=g && g<=g1) return mr;
1459 if (r0<=r && r<=r1) return mg+mb;
1460 if (g0<=g && g<=g1) return mr+mb;
1461 if (b0<=b && b<=b1) return mg+mr;
1466 static void transparent_threshold(i_quantize *, i_palidx *, i_img *, i_palidx);
1467 static void transparent_errdiff(i_quantize *, i_palidx *, i_img *, i_palidx);
1468 static void transparent_ordered(i_quantize *, i_palidx *, i_img *, i_palidx);
1471 =item i_quant_transparent(quant, data, img, trans_index)
1473 =category Image quantization
1475 Dither the alpha channel on I<img> into the palette indexes in
1476 I<data>. Pixels to be transparent are replaced with I<trans_pixel>.
1478 The method used depends on the tr_* members of quant.
1484 i_quant_transparent(i_quantize *quant, i_palidx *data, i_img *img,
1485 i_palidx trans_index)
1487 switch (quant->transp) {
1492 quant->tr_threshold = 128;
1495 transparent_threshold(quant, data, img, trans_index);
1499 transparent_errdiff(quant, data, img, trans_index);
1503 transparent_ordered(quant, data, img, trans_index);
1509 transparent_threshold(i_quantize *quant, i_palidx *data, i_img *img,
1510 i_palidx trans_index)
1513 i_sample_t *line = mymalloc(img->xsize * sizeof(i_sample_t));
1514 int trans_chan = img->channels > 2 ? 3 : 1;
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 if (line[x] < quant->tr_threshold)
1520 data[y*img->xsize+x] = trans_index;
1527 transparent_errdiff(i_quantize *quant, i_palidx *data, i_img *img,
1528 i_palidx trans_index)
1532 int mapw, maph, mapo;
1533 int errw, *err, *errp;
1534 int difftotal, out, error;
1535 int x, y, dx, dy, i;
1537 int trans_chan = img->channels > 2 ? 3 : 1;
1539 /* no custom map for transparency (yet) */
1540 index = quant->tr_errdiff & ed_mask;
1541 if (index >= ed_custom) index = ed_floyd;
1542 map = maps[index].map;
1543 mapw = maps[index].width;
1544 maph = maps[index].height;
1545 mapo = maps[index].orig;
1547 errw = img->xsize+mapw-1;
1548 err = mymalloc(sizeof(*err) * maph * errw);
1550 memset(err, 0, sizeof(*err) * maph * errw);
1552 line = mymalloc(img->xsize * sizeof(i_sample_t));
1554 for (i = 0; i < maph * mapw; ++i)
1555 difftotal += map[i];
1556 for (y = 0; y < img->ysize; ++y) {
1557 i_gsamp(img, 0, img->xsize, y, line, &trans_chan, 1);
1558 for (x = 0; x < img->xsize; ++x) {
1559 line[x] = g_sat(line[x]-errp[x]/difftotal);
1560 if (line[x] < 128) {
1562 data[y*img->xsize+x] = trans_index;
1567 error = out - line[x];
1568 for (dx = 0; dx < mapw; ++dx) {
1569 for (dy = 0; dy < maph; ++dy) {
1570 errp[x+dx-mapo+dy*errw] += error * map[dx+mapw*dy];
1574 /* shift up the error matrix */
1575 for (dy = 0; dy < maph-1; ++dy)
1576 memcpy(err+dy*errw, err+(dy+1)*errw, sizeof(*err)*errw);
1577 memset(err+(maph-1)*errw, 0, sizeof(*err)*errw);
1583 /* builtin ordered dither maps */
1584 static unsigned char
1585 orddith_maps[][64] =
1588 this is purely random - it's pretty awful
1590 48, 72, 196, 252, 180, 92, 108, 52,
1591 228, 176, 64, 8, 236, 40, 20, 164,
1592 120, 128, 84, 116, 24, 28, 172, 220,
1593 68, 0, 188, 124, 184, 224, 192, 104,
1594 132, 100, 240, 200, 152, 160, 244, 44,
1595 96, 204, 144, 16, 140, 56, 232, 216,
1596 208, 4, 76, 212, 136, 248, 80, 168,
1597 156, 88, 32, 112, 148, 12, 36, 60,
1601 perl spot.perl '($x-3.5)*($x-3.5)+($y-3.5)*($y-3.5)'
1603 240, 232, 200, 136, 140, 192, 228, 248,
1604 220, 148, 100, 76, 80, 104, 152, 212,
1605 180, 116, 56, 32, 36, 60, 120, 176,
1606 156, 64, 28, 0, 8, 44, 88, 160,
1607 128, 92, 24, 12, 4, 40, 68, 132,
1608 184, 96, 48, 20, 16, 52, 108, 188,
1609 216, 144, 112, 72, 84, 124, 164, 224,
1610 244, 236, 196, 168, 172, 204, 208, 252,
1614 'min(dist(1.5, 1.5),dist(5.5,1.5),dist(1.5,5.5),dist(5.5,5.5))'
1616 196, 72, 104, 220, 200, 80, 112, 224,
1617 76, 4, 24, 136, 84, 8, 32, 144,
1618 108, 28, 52, 168, 116, 36, 56, 176,
1619 216, 140, 172, 244, 228, 148, 180, 248,
1620 204, 92, 124, 236, 192, 68, 96, 208,
1621 88, 12, 44, 156, 64, 0, 16, 128,
1622 120, 40, 60, 188, 100, 20, 48, 160,
1623 232, 152, 184, 252, 212, 132, 164, 240,
1626 perl spot.perl '$y-3'
1628 160, 164, 168, 172, 176, 180, 184, 188,
1629 128, 132, 136, 140, 144, 148, 152, 156,
1630 32, 36, 40, 44, 48, 52, 56, 60,
1631 0, 4, 8, 12, 16, 20, 24, 28,
1632 64, 68, 72, 76, 80, 84, 88, 92,
1633 96, 100, 104, 108, 112, 116, 120, 124,
1634 192, 196, 200, 204, 208, 212, 216, 220,
1635 224, 228, 232, 236, 240, 244, 248, 252,
1638 perl spot.perl '$x-3'
1640 180, 100, 40, 12, 44, 104, 184, 232,
1641 204, 148, 60, 16, 64, 128, 208, 224,
1642 212, 144, 76, 8, 80, 132, 216, 244,
1643 160, 112, 68, 20, 84, 108, 172, 236,
1644 176, 96, 72, 28, 88, 152, 188, 228,
1645 200, 124, 92, 0, 32, 116, 164, 240,
1646 168, 120, 36, 24, 48, 136, 192, 248,
1647 196, 140, 52, 4, 56, 156, 220, 252,
1650 perl spot.perl '$y+$x-7'
1652 248, 232, 224, 192, 140, 92, 52, 28,
1653 240, 220, 196, 144, 108, 60, 12, 64,
1654 216, 180, 148, 116, 76, 20, 80, 128,
1655 204, 152, 104, 44, 16, 72, 100, 160,
1656 164, 96, 68, 24, 56, 112, 168, 176,
1657 124, 40, 8, 36, 88, 136, 184, 212,
1658 84, 4, 32, 120, 156, 188, 228, 236,
1659 0, 48, 132, 172, 200, 208, 244, 252,
1662 perl spot.perl '$y-$x'
1664 0, 32, 116, 172, 184, 216, 236, 252,
1665 56, 8, 72, 132, 136, 200, 228, 240,
1666 100, 36, 12, 40, 92, 144, 204, 220,
1667 168, 120, 60, 16, 44, 96, 156, 176,
1668 180, 164, 112, 48, 28, 52, 128, 148,
1669 208, 192, 152, 88, 84, 20, 64, 104,
1670 232, 224, 196, 140, 108, 68, 24, 76,
1671 248, 244, 212, 188, 160, 124, 80, 4,
1675 good for display, bad for print
1678 0, 128, 32, 192, 8, 136, 40, 200,
1679 224, 64, 160, 112, 232, 72, 168, 120,
1680 48, 144, 16, 208, 56, 152, 24, 216,
1681 176, 96, 240, 80, 184, 104, 248, 88,
1682 12, 140, 44, 204, 4, 132, 36, 196,
1683 236, 76, 172, 124, 228, 68, 164, 116,
1684 60, 156, 28, 220, 52, 148, 20, 212,
1685 188, 108, 252, 92, 180, 100, 244, 84,
1690 transparent_ordered(i_quantize *quant, i_palidx *data, i_img *img,
1691 i_palidx trans_index)
1693 unsigned char *spot;
1696 int trans_chan = img->channels > 2 ? 3 : 1;
1697 if (quant->tr_orddith == od_custom)
1698 spot = quant->tr_custom;
1700 spot = orddith_maps[quant->tr_orddith];
1702 line = mymalloc(img->xsize * sizeof(i_sample_t));
1703 for (y = 0; y < img->ysize; ++y) {
1704 i_gsamp(img, 0, img->xsize, y, line, &trans_chan, 1);
1705 for (x = 0; x < img->xsize; ++x) {
1706 if (line[x] < spot[(x&7)+(y&7)*8])
1707 data[x+y*img->xsize] = trans_index;