| line | stmt | bran | cond | sub | pod | time | code | 
| 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 | 
| 4 |  |  |  |  |  |  | */ | 
| 5 |  |  |  |  |  |  | #include "imager.h" | 
| 6 |  |  |  |  |  |  | #include "imageri.h" | 
| 7 |  |  |  |  |  |  |  | 
| 8 |  |  |  |  |  |  | static void makemap_webmap(i_quantize *); | 
| 9 |  |  |  |  |  |  | static void makemap_addi(i_quantize *, i_img **imgs, int count); | 
| 10 |  |  |  |  |  |  | static void makemap_mediancut(i_quantize *, i_img **imgs, int count); | 
| 11 |  |  |  |  |  |  | static void makemap_mono(i_quantize *); | 
| 12 |  |  |  |  |  |  | static void makemap_gray(i_quantize *, int step); | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | static int makemap_palette(i_quantize *, i_img **imgs, int count); | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | static | 
| 17 |  |  |  |  |  |  | void | 
| 18 | 1140 |  |  |  |  |  | setcol(i_color *cl,unsigned char r,unsigned char g,unsigned char b,unsigned char a) { | 
| 19 | 1140 |  |  |  |  |  | cl->rgba.r=r; | 
| 20 | 1140 |  |  |  |  |  | cl->rgba.g=g; | 
| 21 | 1140 |  |  |  |  |  | cl->rgba.b=b; | 
| 22 | 1140 |  |  |  |  |  | cl->rgba.a=a; | 
| 23 | 1140 |  |  |  |  |  | } | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | /* make a colour map overwrites mc_existing/mc_count in quant Note | 
| 28 |  |  |  |  |  |  | that i_makemap will be called once for each image if mc_perimage is | 
| 29 |  |  |  |  |  |  | set and the format support multiple colour maps per image. | 
| 30 |  |  |  |  |  |  |  | 
| 31 |  |  |  |  |  |  | This means we don't need any special processing at this level to | 
| 32 |  |  |  |  |  |  | handle multiple colour maps. | 
| 33 |  |  |  |  |  |  | */ | 
| 34 |  |  |  |  |  |  |  | 
| 35 |  |  |  |  |  |  | /* | 
| 36 |  |  |  |  |  |  | =item i_quant_makemap(C, C, C) | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | =category Image quantization | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | Analyzes the C images in C according to the rules in | 
| 41 |  |  |  |  |  |  | C to build a color map (optimal or not depending on | 
| 42 |  |  |  |  |  |  | C<< quant->make_colors >>). | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | =cut | 
| 45 |  |  |  |  |  |  | */ | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  | void | 
| 48 | 22 |  |  |  |  |  | i_quant_makemap(i_quantize *quant, i_img **imgs, int count) { | 
| 49 |  |  |  |  |  |  |  | 
| 50 | 22 | 50 |  |  |  |  | if (quant->translate == pt_giflib) { | 
| 51 |  |  |  |  |  |  | /* giflib does it's own color table generation */ | 
| 52 |  |  |  |  |  |  | /* previously we used giflib's quantizer, but it didn't handle multiple | 
| 53 |  |  |  |  |  |  | images, which made it hard to build a global color map | 
| 54 |  |  |  |  |  |  | We've implemented our own median cut code so we can ignore | 
| 55 |  |  |  |  |  |  | the giflib version */ | 
| 56 | 0 |  |  |  |  |  | makemap_mediancut(quant, imgs, count); | 
| 57 | 0 |  |  |  |  |  | return; | 
| 58 |  |  |  |  |  |  | } | 
| 59 |  |  |  |  |  |  |  | 
| 60 | 22 |  |  |  |  |  | switch (quant->make_colors & mc_mask) { | 
| 61 |  |  |  |  |  |  | case mc_none: | 
| 62 |  |  |  |  |  |  | /* use user's specified map */ | 
| 63 | 7 |  |  |  |  |  | break; | 
| 64 |  |  |  |  |  |  | case mc_web_map: | 
| 65 | 4 |  |  |  |  |  | makemap_webmap(quant); | 
| 66 | 4 |  |  |  |  |  | break; | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | case mc_median_cut: | 
| 69 | 5 |  |  |  |  |  | makemap_mediancut(quant, imgs, count); | 
| 70 | 5 |  |  |  |  |  | break; | 
| 71 |  |  |  |  |  |  |  | 
| 72 |  |  |  |  |  |  | case mc_mono: | 
| 73 | 3 |  |  |  |  |  | makemap_mono(quant); | 
| 74 | 3 |  |  |  |  |  | break; | 
| 75 |  |  |  |  |  |  |  | 
| 76 |  |  |  |  |  |  | case mc_gray: | 
| 77 | 1 |  |  |  |  |  | makemap_gray(quant, 1); | 
| 78 | 1 |  |  |  |  |  | break; | 
| 79 |  |  |  |  |  |  |  | 
| 80 |  |  |  |  |  |  | case mc_gray4: | 
| 81 | 1 |  |  |  |  |  | makemap_gray(quant, 85); | 
| 82 | 1 |  |  |  |  |  | break; | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | case mc_gray16: | 
| 85 | 1 |  |  |  |  |  | makemap_gray(quant, 17); | 
| 86 | 1 |  |  |  |  |  | break; | 
| 87 |  |  |  |  |  |  |  | 
| 88 |  |  |  |  |  |  | case mc_addi: | 
| 89 |  |  |  |  |  |  | default: | 
| 90 | 0 |  |  |  |  |  | makemap_addi(quant, imgs, count); | 
| 91 | 0 |  |  |  |  |  | break; | 
| 92 |  |  |  |  |  |  | } | 
| 93 |  |  |  |  |  |  | } | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  | static void translate_closest(i_quantize *, i_img *, i_palidx *); | 
| 96 |  |  |  |  |  |  | static int translate_errdiff(i_quantize *, i_img *, i_palidx *); | 
| 97 |  |  |  |  |  |  | static void translate_addi(i_quantize *, i_img *, i_palidx *); | 
| 98 |  |  |  |  |  |  |  | 
| 99 |  |  |  |  |  |  | /* | 
| 100 |  |  |  |  |  |  | =item i_quant_translate(C, C ![]() ) | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  | =category Image quantization | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | Quantize the image given the palette in C. | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | On success returns a pointer to a memory block of C<< img->xsize * | 
| 107 |  |  |  |  |  |  | img->ysize >> C entries. | 
| 108 |  |  |  |  |  |  |  | 
| 109 |  |  |  |  |  |  | On failure returns NULL. | 
| 110 |  |  |  |  |  |  |  | 
| 111 |  |  |  |  |  |  | You should call myfree() on the returned block when you're done with | 
| 112 |  |  |  |  |  |  | it. | 
| 113 |  |  |  |  |  |  |  | 
| 114 |  |  |  |  |  |  | This function will fail if the supplied palette contains no colors. | 
| 115 |  |  |  |  |  |  |  | 
| 116 |  |  |  |  |  |  | =cut | 
| 117 |  |  |  |  |  |  | */ | 
| 118 |  |  |  |  |  |  | i_palidx * | 
| 119 | 18 |  |  |  |  |  | i_quant_translate(i_quantize *quant, i_img *img) { | 
| 120 |  |  |  |  |  |  | i_palidx *result; | 
| 121 |  |  |  |  |  |  | size_t bytes; | 
| 122 |  |  |  |  |  |  |  | 
| 123 | 18 |  |  |  |  |  | mm_log((1, "quant_translate(quant %p, img %p)\n", quant, img)); | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | /* there must be at least one color in the paletted (though even that | 
| 126 |  |  |  |  |  |  | isn't very useful */ | 
| 127 | 18 | 100 |  |  |  |  | if (quant->mc_count == 0) { | 
| 128 | 1 |  |  |  |  |  | i_push_error(0, "no colors available for translation"); | 
| 129 | 1 |  |  |  |  |  | return NULL; | 
| 130 |  |  |  |  |  |  | } | 
| 131 |  |  |  |  |  |  |  | 
| 132 | 17 |  |  |  |  |  | bytes = img->xsize * img->ysize; | 
| 133 | 17 | 50 |  |  |  |  | if (bytes / img->ysize != img->xsize) { | 
| 134 | 0 |  |  |  |  |  | i_push_error(0, "integer overflow calculating memory allocation"); | 
| 135 | 0 |  |  |  |  |  | return NULL; | 
| 136 |  |  |  |  |  |  | } | 
| 137 | 17 |  |  |  |  |  | result = mymalloc(bytes); | 
| 138 |  |  |  |  |  |  |  | 
| 139 | 17 |  |  |  |  |  | switch (quant->translate) { | 
| 140 |  |  |  |  |  |  | case pt_closest: | 
| 141 |  |  |  |  |  |  | case pt_giflib: | 
| 142 | 12 |  |  |  |  |  | translate_closest(quant, img, result); | 
| 143 | 12 |  |  |  |  |  | break; | 
| 144 |  |  |  |  |  |  |  | 
| 145 |  |  |  |  |  |  | case pt_errdiff: | 
| 146 | 5 | 50 |  |  |  |  | if (!translate_errdiff(quant, img, result)) { | 
| 147 | 0 |  |  |  |  |  | myfree(result); | 
| 148 | 0 |  |  |  |  |  | return NULL; | 
| 149 |  |  |  |  |  |  | } | 
| 150 | 5 |  |  |  |  |  | break; | 
| 151 |  |  |  |  |  |  |  | 
| 152 |  |  |  |  |  |  | case pt_perturb: | 
| 153 |  |  |  |  |  |  | default: | 
| 154 | 0 |  |  |  |  |  | translate_addi(quant, img, result); | 
| 155 | 0 |  |  |  |  |  | break; | 
| 156 |  |  |  |  |  |  | } | 
| 157 |  |  |  |  |  |  |  | 
| 158 | 17 |  |  |  |  |  | return result; | 
| 159 |  |  |  |  |  |  | } | 
| 160 |  |  |  |  |  |  |  | 
| 161 | 12 |  |  |  |  |  | static void translate_closest(i_quantize *quant, i_img *img, i_palidx *out) { | 
| 162 | 12 |  |  |  |  |  | quant->perturb = 0; | 
| 163 | 12 |  |  |  |  |  | translate_addi(quant, img, out); | 
| 164 | 12 |  |  |  |  |  | } | 
| 165 |  |  |  |  |  |  |  | 
| 166 |  |  |  |  |  |  | #define PWR2(x) ((x)*(x)) | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  | typedef int (*cmpfunc)(const void*, const void*); | 
| 169 |  |  |  |  |  |  |  | 
| 170 |  |  |  |  |  |  | typedef struct { | 
| 171 |  |  |  |  |  |  | unsigned char r,g,b; | 
| 172 |  |  |  |  |  |  | char fixed; | 
| 173 |  |  |  |  |  |  | char used; | 
| 174 |  |  |  |  |  |  | int dr,dg,db; | 
| 175 |  |  |  |  |  |  | int cdist; | 
| 176 |  |  |  |  |  |  | int mcount; | 
| 177 |  |  |  |  |  |  | } cvec; | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | typedef struct { | 
| 180 |  |  |  |  |  |  | int cnt; | 
| 181 |  |  |  |  |  |  | int vec[256]; | 
| 182 |  |  |  |  |  |  | } hashbox; | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | typedef struct { | 
| 185 |  |  |  |  |  |  | int boxnum; | 
| 186 |  |  |  |  |  |  | int pixcnt; | 
| 187 |  |  |  |  |  |  | int cand; | 
| 188 |  |  |  |  |  |  | int pdc; | 
| 189 |  |  |  |  |  |  | } pbox; | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | static void prescan(i_img **im,int count, int cnum, cvec *clr, i_sample_t *line); | 
| 192 |  |  |  |  |  |  | static void reorder(pbox prescan[512]); | 
| 193 |  |  |  |  |  |  | static int pboxcmp(const pbox *a,const pbox *b); | 
| 194 |  |  |  |  |  |  | static void boxcenter(int box,cvec *cv); | 
| 195 |  |  |  |  |  |  | static float frandn(void); | 
| 196 |  |  |  |  |  |  | static void boxrand(int box,cvec *cv); | 
| 197 |  |  |  |  |  |  | static void bbox(int box,int *r0,int *r1,int *g0,int *g1,int *b0,int *b1); | 
| 198 |  |  |  |  |  |  | static void cr_hashindex(cvec clr[256],int cnum,hashbox hb[512]); | 
| 199 |  |  |  |  |  |  | static int mindist(int boxnum,cvec *cv); | 
| 200 |  |  |  |  |  |  | static int maxdist(int boxnum,cvec *cv); | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  | /* Some of the simpler functions are kept here to aid the compiler - | 
| 203 |  |  |  |  |  |  | maybe some of them will be inlined. */ | 
| 204 |  |  |  |  |  |  |  | 
| 205 |  |  |  |  |  |  | static int | 
| 206 | 593756 |  |  |  |  |  | pixbox(i_color *ic) { return ((ic->channel[0] & 224)<<1)+ ((ic->channel[1]&224)>>2) + ((ic->channel[2] &224) >> 5); } | 
| 207 |  |  |  |  |  |  |  | 
| 208 |  |  |  |  |  |  | static int | 
| 209 | 0 |  |  |  |  |  | pixbox_ch(i_sample_t *chans) { return ((chans[0] & 224)<<1)+ ((chans[1]&224)>>2) + ((chans[2] &224) >> 5); } | 
| 210 |  |  |  |  |  |  |  | 
| 211 |  |  |  |  |  |  | static unsigned char | 
| 212 | 270300 |  |  |  |  |  | g_sat(int in) { | 
| 213 | 270300 | 100 |  |  |  |  | if (in>255) { return 255; } | 
| 214 | 247920 | 100 |  |  |  |  | else if (in>0) return in; | 
| 215 | 204655 |  |  |  |  |  | return 0; | 
| 216 |  |  |  |  |  |  | } | 
| 217 |  |  |  |  |  |  |  | 
| 218 |  |  |  |  |  |  | static | 
| 219 |  |  |  |  |  |  | float | 
| 220 | 0 |  |  |  |  |  | frand(void) { | 
| 221 | 0 |  |  |  |  |  | return rand()/(RAND_MAX+1.0); | 
| 222 |  |  |  |  |  |  | } | 
| 223 |  |  |  |  |  |  |  | 
| 224 |  |  |  |  |  |  | #ifdef NOTEF | 
| 225 |  |  |  |  |  |  | static | 
| 226 |  |  |  |  |  |  | int | 
| 227 |  |  |  |  |  |  | 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]); } | 
| 228 |  |  |  |  |  |  | #endif | 
| 229 |  |  |  |  |  |  |  | 
| 230 |  |  |  |  |  |  | static | 
| 231 |  |  |  |  |  |  | int | 
| 232 | 0 |  |  |  |  |  | eucl_d_ch(cvec* cv,i_sample_t *chans) { | 
| 233 | 0 |  |  |  |  |  | return PWR2(cv->r - chans[0]) + PWR2(cv->g - chans[1]) | 
| 234 | 0 |  |  |  |  |  | + PWR2(cv->b - chans[2]); | 
| 235 |  |  |  |  |  |  | } | 
| 236 |  |  |  |  |  |  |  | 
| 237 |  |  |  |  |  |  | static int | 
| 238 | 2192846 |  |  |  |  |  | ceucl_d(i_color *c1, i_color *c2) { | 
| 239 | 2192846 |  |  |  |  |  | return PWR2(c1->channel[0]-c2->channel[0]) | 
| 240 | 2192846 |  |  |  |  |  | +PWR2(c1->channel[1]-c2->channel[1]) | 
| 241 | 2192846 |  |  |  |  |  | +PWR2(c1->channel[2]-c2->channel[2]); | 
| 242 |  |  |  |  |  |  | } | 
| 243 |  |  |  |  |  |  |  | 
| 244 |  |  |  |  |  |  | static const int | 
| 245 |  |  |  |  |  |  | gray_samples[] = { 0, 0, 0 }; | 
| 246 |  |  |  |  |  |  |  | 
| 247 |  |  |  |  |  |  | /* | 
| 248 |  |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  | This quantization algorithm and implementation routines are by Arnar | 
| 250 |  |  |  |  |  |  | M. Hrafnkelson. In case any new ideas are here they are mine since | 
| 251 |  |  |  |  |  |  | this was written from scratch. | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | The algorithm uses local means in the following way: | 
| 254 |  |  |  |  |  |  |  | 
| 255 |  |  |  |  |  |  | For each point in the colormap we find which image points | 
| 256 |  |  |  |  |  |  | have that point as it's closest point. We calculate the mean | 
| 257 |  |  |  |  |  |  | of those points and in the next iteration it will be the new | 
| 258 |  |  |  |  |  |  | entry in the colormap. | 
| 259 |  |  |  |  |  |  |  | 
| 260 |  |  |  |  |  |  | In order to speed this process up (i.e. nearest neighbor problem) We | 
| 261 |  |  |  |  |  |  | divied the r,g,b space up in equally large 512 boxes.  The boxes are | 
| 262 |  |  |  |  |  |  | numbered from 0 to 511. Their numbering is so that for a given vector | 
| 263 |  |  |  |  |  |  | it is known that it belongs to the box who is formed by concatenating the | 
| 264 |  |  |  |  |  |  | 3 most significant bits from each component of the RGB triplet. | 
| 265 |  |  |  |  |  |  |  | 
| 266 |  |  |  |  |  |  | For each box we find the list of points from the colormap who might be | 
| 267 |  |  |  |  |  |  | closest to any given point within the box.  The exact solution | 
| 268 |  |  |  |  |  |  | involves finding the Voronoi map (or the dual the Delauny | 
| 269 |  |  |  |  |  |  | triangulation) and has many issues including numerical stability. | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | So we use this approximation: | 
| 272 |  |  |  |  |  |  |  | 
| 273 |  |  |  |  |  |  | 1. Find which point has the shortest maximum distance to the box. | 
| 274 |  |  |  |  |  |  | 2. Find all points that have a shorter minimum distance than that to the box | 
| 275 |  |  |  |  |  |  |  | 
| 276 |  |  |  |  |  |  | This is a very simple task and is not computationally heavy if one | 
| 277 |  |  |  |  |  |  | takes into account that the minimum distances from a pixel to a box is | 
| 278 |  |  |  |  |  |  | always found by checking if it's inside the box or is closest to some | 
| 279 |  |  |  |  |  |  | side or a corner. Finding the maximum distance is also either a side | 
| 280 |  |  |  |  |  |  | or a corner. | 
| 281 |  |  |  |  |  |  |  | 
| 282 |  |  |  |  |  |  | This approach results 2-3 times more than the actual points needed but | 
| 283 |  |  |  |  |  |  | is still a good gain over the complete space.  Usually when one has a | 
| 284 |  |  |  |  |  |  | 256 Colorcolor map a search over 30 is often obtained. | 
| 285 |  |  |  |  |  |  |  | 
| 286 |  |  |  |  |  |  | A bit of an enhancement to this approach is to keep a seperate list | 
| 287 |  |  |  |  |  |  | for each side of the cube, but this will require even more memory. | 
| 288 |  |  |  |  |  |  |  | 
| 289 |  |  |  |  |  |  | Arnar M. Hrafnkelsson (addi@umich.edu); | 
| 290 |  |  |  |  |  |  |  | 
| 291 |  |  |  |  |  |  | */ | 
| 292 |  |  |  |  |  |  | /* | 
| 293 |  |  |  |  |  |  | Extracted from gifquant.c, removed dependencies on gif_lib, | 
| 294 |  |  |  |  |  |  | and added support for multiple images. | 
| 295 |  |  |  |  |  |  | starting from 1nov2000 by TonyC . | 
| 296 |  |  |  |  |  |  |  | 
| 297 |  |  |  |  |  |  | */ | 
| 298 |  |  |  |  |  |  |  | 
| 299 |  |  |  |  |  |  | static void | 
| 300 | 0 |  |  |  |  |  | makemap_addi(i_quantize *quant, i_img **imgs, int count) { | 
| 301 |  |  |  |  |  |  | cvec *clr; | 
| 302 | 0 |  |  |  |  |  | int cnum, i, bst_idx=0, ld, cd, iter, currhb, img_num; | 
| 303 |  |  |  |  |  |  | i_img_dim x, y; | 
| 304 |  |  |  |  |  |  | i_sample_t *val; | 
| 305 |  |  |  |  |  |  | float dlt, accerr; | 
| 306 |  |  |  |  |  |  | hashbox *hb; | 
| 307 |  |  |  |  |  |  | i_mempool mp; | 
| 308 | 0 |  |  |  |  |  | i_img_dim maxwidth = 0; | 
| 309 |  |  |  |  |  |  | i_sample_t *line; | 
| 310 |  |  |  |  |  |  | const int *sample_indices; | 
| 311 |  |  |  |  |  |  |  | 
| 312 | 0 |  |  |  |  |  | mm_log((1, "makemap_addi(quant %p { mc_count=%d, mc_colors=%p }, imgs %p, count %d)\n", | 
| 313 |  |  |  |  |  |  | quant, quant->mc_count, quant->mc_colors, imgs, count)); | 
| 314 |  |  |  |  |  |  |  | 
| 315 | 0 | 0 |  |  |  |  | if (makemap_palette(quant, imgs, count)) | 
| 316 | 0 |  |  |  |  |  | return; | 
| 317 |  |  |  |  |  |  |  | 
| 318 | 0 |  |  |  |  |  | i_mempool_init(&mp); | 
| 319 |  |  |  |  |  |  |  | 
| 320 | 0 |  |  |  |  |  | clr = i_mempool_alloc(&mp, sizeof(cvec) * quant->mc_size); | 
| 321 | 0 |  |  |  |  |  | hb = i_mempool_alloc(&mp, sizeof(hashbox) * 512); | 
| 322 | 0 | 0 |  |  |  |  | for (i=0; i < quant->mc_count; ++i) { | 
| 323 | 0 |  |  |  |  |  | clr[i].r = quant->mc_colors[i].rgb.r; | 
| 324 | 0 |  |  |  |  |  | clr[i].g = quant->mc_colors[i].rgb.g; | 
| 325 | 0 |  |  |  |  |  | clr[i].b = quant->mc_colors[i].rgb.b; | 
| 326 | 0 |  |  |  |  |  | clr[i].fixed = 1; | 
| 327 | 0 |  |  |  |  |  | clr[i].mcount = 0; | 
| 328 |  |  |  |  |  |  | } | 
| 329 |  |  |  |  |  |  | /* mymalloc doesn't clear memory, so I think we need this */ | 
| 330 | 0 | 0 |  |  |  |  | for (; i < quant->mc_size; ++i) { | 
| 331 |  |  |  |  |  |  | /*clr[i].r = clr[i].g = clr[i].b = 0;*/ | 
| 332 | 0 |  |  |  |  |  | clr[i].dr = 0; | 
| 333 | 0 |  |  |  |  |  | clr[i].dg = 0; | 
| 334 | 0 |  |  |  |  |  | clr[i].db = 0; | 
| 335 | 0 |  |  |  |  |  | clr[i].fixed = 0; | 
| 336 | 0 |  |  |  |  |  | clr[i].mcount = 0; | 
| 337 |  |  |  |  |  |  | } | 
| 338 | 0 |  |  |  |  |  | cnum = quant->mc_size; | 
| 339 | 0 |  |  |  |  |  | dlt = 1; | 
| 340 |  |  |  |  |  |  |  | 
| 341 | 0 | 0 |  |  |  |  | for (img_num = 0; img_num < count; ++img_num) { | 
| 342 | 0 | 0 |  |  |  |  | if (imgs[img_num]->xsize > maxwidth) | 
| 343 | 0 |  |  |  |  |  | maxwidth = imgs[img_num]->xsize; | 
| 344 |  |  |  |  |  |  | } | 
| 345 | 0 |  |  |  |  |  | line = i_mempool_alloc(&mp, 3 * maxwidth * sizeof(*line)); | 
| 346 |  |  |  |  |  |  |  | 
| 347 | 0 |  |  |  |  |  | prescan(imgs, count, cnum, clr, line); | 
| 348 | 0 |  |  |  |  |  | cr_hashindex(clr, cnum, hb); | 
| 349 |  |  |  |  |  |  |  | 
| 350 | 0 | 0 |  |  |  |  | for(iter=0;iter<3;iter++) { | 
| 351 | 0 |  |  |  |  |  | accerr=0.0; | 
| 352 |  |  |  |  |  |  |  | 
| 353 | 0 | 0 |  |  |  |  | for (img_num = 0; img_num < count; ++img_num) { | 
| 354 | 0 |  |  |  |  |  | i_img *im = imgs[img_num]; | 
| 355 | 0 | 0 |  |  |  |  | sample_indices = im->channels >= 3 ? NULL : gray_samples; | 
| 356 | 0 | 0 |  |  |  |  | for(y=0;yysize;y++) { | 
| 357 | 0 |  |  |  |  |  | i_gsamp(im, 0, im->xsize, y, line, sample_indices, 3); | 
| 358 | 0 |  |  |  |  |  | val = line; | 
| 359 | 0 | 0 |  |  |  |  | for(x=0;xxsize;x++) { | 
| 360 | 0 |  |  |  |  |  | ld=196608; | 
| 361 |  |  |  |  |  |  | /*i_gpix(im,x,y,&val);*/ | 
| 362 | 0 |  |  |  |  |  | currhb=pixbox_ch(val); | 
| 363 |  |  |  |  |  |  | /*      printf("box = %d \n",currhb); */ | 
| 364 | 0 | 0 |  |  |  |  | for(i=0;i | 
| 365 |  |  |  |  |  |  | /*	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); */ | 
| 366 |  |  |  |  |  |  |  | 
| 367 | 0 |  |  |  |  |  | cd=eucl_d_ch(&clr[hb[currhb].vec[i]],val); | 
| 368 | 0 | 0 |  |  |  |  | if (cd | 
| 369 | 0 |  |  |  |  |  | ld=cd;     /* shortest distance yet */ | 
| 370 | 0 |  |  |  |  |  | bst_idx=hb[currhb].vec[i]; /* index of closest vector  yet */ | 
| 371 |  |  |  |  |  |  | } | 
| 372 |  |  |  |  |  |  | } | 
| 373 |  |  |  |  |  |  |  | 
| 374 | 0 |  |  |  |  |  | clr[bst_idx].mcount++; | 
| 375 | 0 |  |  |  |  |  | accerr+=(ld); | 
| 376 | 0 |  |  |  |  |  | clr[bst_idx].dr+=val[0]; | 
| 377 | 0 |  |  |  |  |  | clr[bst_idx].dg+=val[1]; | 
| 378 | 0 |  |  |  |  |  | clr[bst_idx].db+=val[2]; | 
| 379 |  |  |  |  |  |  |  | 
| 380 | 0 |  |  |  |  |  | val += 3; /* next 3 samples (next pixel) */ | 
| 381 |  |  |  |  |  |  | } | 
| 382 |  |  |  |  |  |  | } | 
| 383 |  |  |  |  |  |  | } | 
| 384 |  |  |  |  |  |  |  | 
| 385 | 0 | 0 |  |  |  |  | for(i=0;i | 
| 386 | 0 | 0 |  |  |  |  | if (clr[i].mcount) { | 
| 387 | 0 |  |  |  |  |  | clr[i].dr/=clr[i].mcount; | 
| 388 | 0 |  |  |  |  |  | clr[i].dg/=clr[i].mcount; | 
| 389 | 0 |  |  |  |  |  | clr[i].db/=clr[i].mcount; | 
| 390 |  |  |  |  |  |  | } | 
| 391 |  |  |  |  |  |  |  | 
| 392 |  |  |  |  |  |  | /*    for(i=0;i | 
| 393 |  |  |  |  |  |  | i,clr[i].r,clr[i].g,clr[i].b,clr[i].dr,clr[i].dg,clr[i].db,clr[i].mcount); */ | 
| 394 |  |  |  |  |  |  |  | 
| 395 |  |  |  |  |  |  | /*    printf("total error: %.2f\n",sqrt(accerr)); */ | 
| 396 |  |  |  |  |  |  |  | 
| 397 | 0 | 0 |  |  |  |  | for(i=0;i | 
| 398 | 0 | 0 |  |  |  |  | if (clr[i].fixed) continue; /* skip reserved colors */ | 
| 399 |  |  |  |  |  |  |  | 
| 400 | 0 | 0 |  |  |  |  | if (clr[i].mcount) { | 
| 401 | 0 |  |  |  |  |  | clr[i].used = 1; | 
| 402 | 0 |  |  |  |  |  | clr[i].r=clr[i].r*(1-dlt)+dlt*clr[i].dr; | 
| 403 | 0 |  |  |  |  |  | clr[i].g=clr[i].g*(1-dlt)+dlt*clr[i].dg; | 
| 404 | 0 |  |  |  |  |  | clr[i].b=clr[i].b*(1-dlt)+dlt*clr[i].db; | 
| 405 |  |  |  |  |  |  | } else { | 
| 406 |  |  |  |  |  |  | /* let's try something else */ | 
| 407 | 0 |  |  |  |  |  | clr[i].used = 0; | 
| 408 | 0 |  |  |  |  |  | clr[i].r=rand(); | 
| 409 | 0 |  |  |  |  |  | clr[i].g=rand(); | 
| 410 | 0 |  |  |  |  |  | clr[i].b=rand(); | 
| 411 |  |  |  |  |  |  | } | 
| 412 |  |  |  |  |  |  |  | 
| 413 | 0 |  |  |  |  |  | clr[i].dr=0; | 
| 414 | 0 |  |  |  |  |  | clr[i].dg=0; | 
| 415 | 0 |  |  |  |  |  | clr[i].db=0; | 
| 416 | 0 |  |  |  |  |  | clr[i].mcount=0; | 
| 417 |  |  |  |  |  |  | } | 
| 418 | 0 |  |  |  |  |  | cr_hashindex(clr,cnum,hb); | 
| 419 |  |  |  |  |  |  | } | 
| 420 |  |  |  |  |  |  |  | 
| 421 |  |  |  |  |  |  |  | 
| 422 |  |  |  |  |  |  | #ifdef NOTEF | 
| 423 |  |  |  |  |  |  | for(i=0;i | 
| 424 |  |  |  |  |  |  | cd=eucl_d(&clr[i],&val); | 
| 425 |  |  |  |  |  |  | if (cd | 
| 426 |  |  |  |  |  |  | ld=cd; | 
| 427 |  |  |  |  |  |  | bst_idx=i; | 
| 428 |  |  |  |  |  |  | } | 
| 429 |  |  |  |  |  |  | } | 
| 430 |  |  |  |  |  |  | #endif | 
| 431 |  |  |  |  |  |  |  | 
| 432 |  |  |  |  |  |  | /* if defined, we only include colours with an mcount or that were | 
| 433 |  |  |  |  |  |  | supplied in the fixed palette, giving us a smaller output palette */ | 
| 434 |  |  |  |  |  |  | #define ONLY_USE_USED | 
| 435 |  |  |  |  |  |  | #ifdef ONLY_USE_USED | 
| 436 |  |  |  |  |  |  | /* transfer the colors back */ | 
| 437 | 0 |  |  |  |  |  | quant->mc_count = 0; | 
| 438 | 0 | 0 |  |  |  |  | for (i = 0; i < cnum; ++i) { | 
| 439 | 0 | 0 |  |  |  |  | if (clr[i].fixed || clr[i].used) { | 
|  |  | 0 |  |  |  |  |  | 
| 440 |  |  |  |  |  |  | /*printf("Adding %d (%d,%d,%d)\n", i, clr[i].r, clr[i].g, clr[i].b);*/ | 
| 441 | 0 |  |  |  |  |  | quant->mc_colors[quant->mc_count].rgb.r = clr[i].r; | 
| 442 | 0 |  |  |  |  |  | quant->mc_colors[quant->mc_count].rgb.g = clr[i].g; | 
| 443 | 0 |  |  |  |  |  | quant->mc_colors[quant->mc_count].rgb.b = clr[i].b; | 
| 444 | 0 |  |  |  |  |  | ++quant->mc_count; | 
| 445 |  |  |  |  |  |  | } | 
| 446 |  |  |  |  |  |  | } | 
| 447 |  |  |  |  |  |  | #else | 
| 448 |  |  |  |  |  |  | /* transfer the colors back */ | 
| 449 |  |  |  |  |  |  | for (i = 0; i < cnum; ++i) { | 
| 450 |  |  |  |  |  |  | quant->mc_colors[i].rgb.r = clr[i].r; | 
| 451 |  |  |  |  |  |  | quant->mc_colors[i].rgb.g = clr[i].g; | 
| 452 |  |  |  |  |  |  | quant->mc_colors[i].rgb.b = clr[i].b; | 
| 453 |  |  |  |  |  |  | } | 
| 454 |  |  |  |  |  |  | quant->mc_count = cnum; | 
| 455 |  |  |  |  |  |  | #endif | 
| 456 |  |  |  |  |  |  |  | 
| 457 |  |  |  |  |  |  | #if 0 | 
| 458 |  |  |  |  |  |  | mm_log((1, "makemap_addi returns - quant.mc_count = %d\n", quant->mc_count)); | 
| 459 |  |  |  |  |  |  | for (i = 0; i < quant->mc_count; ++i) | 
| 460 |  |  |  |  |  |  | mm_log((5, "  map entry %d: (%d, %d, %d)\n", i, clr[i].r, clr[i].g, clr[i].b)); | 
| 461 |  |  |  |  |  |  | #endif | 
| 462 |  |  |  |  |  |  |  | 
| 463 | 0 |  |  |  |  |  | i_mempool_destroy(&mp); | 
| 464 |  |  |  |  |  |  |  | 
| 465 | 0 |  |  |  |  |  | mm_log((1, "makemap_addi() - %d colors\n", quant->mc_count)); | 
| 466 |  |  |  |  |  |  | } | 
| 467 |  |  |  |  |  |  |  | 
| 468 |  |  |  |  |  |  | typedef struct { | 
| 469 |  |  |  |  |  |  | i_sample_t rgb[3]; | 
| 470 |  |  |  |  |  |  | i_img_dim count; | 
| 471 |  |  |  |  |  |  | } quant_color_entry; | 
| 472 |  |  |  |  |  |  |  | 
| 473 |  |  |  |  |  |  | #define MEDIAN_CUT_COLORS 32768 | 
| 474 |  |  |  |  |  |  |  | 
| 475 |  |  |  |  |  |  | #define MED_CUT_INDEX(c) ((((c).rgb.r & 0xF8) << 7) | \ | 
| 476 |  |  |  |  |  |  | (((c).rgb.g & 0xF8) << 2) | (((c).rgb.b & 0xF8) >> 3)) | 
| 477 |  |  |  |  |  |  |  | 
| 478 |  |  |  |  |  |  | #define MED_CUT_GRAY_INDEX(c) ((((c).rgb.r & 0xF8) << 7) | \ | 
| 479 |  |  |  |  |  |  | (((c).rgb.r & 0xF8) << 2) | (((c).rgb.r & 0xF8) >> 3)) | 
| 480 |  |  |  |  |  |  |  | 
| 481 |  |  |  |  |  |  | /* scale these to cover the whole range */ | 
| 482 |  |  |  |  |  |  | #define MED_CUT_RED(index) ((((index) & 0x7C00) >> 10) * 255 / 31) | 
| 483 |  |  |  |  |  |  | #define MED_CUT_GREEN(index) ((((index) & 0x3E0) >> 5) * 255 / 31) | 
| 484 |  |  |  |  |  |  | #define MED_CUT_BLUE(index) (((index) & 0x1F) * 255 / 31) | 
| 485 |  |  |  |  |  |  |  | 
| 486 |  |  |  |  |  |  | typedef struct { | 
| 487 |  |  |  |  |  |  | i_sample_t min[3]; /* minimum for each channel */ | 
| 488 |  |  |  |  |  |  | i_sample_t max[3]; /* maximum for each channel */ | 
| 489 |  |  |  |  |  |  | i_sample_t width[3]; /* width for each channel */ | 
| 490 |  |  |  |  |  |  | int start, size;   /* beginning and size of the partition */ | 
| 491 |  |  |  |  |  |  | i_img_dim pixels; /* number of pixels represented by this partition */ | 
| 492 |  |  |  |  |  |  | } medcut_partition; | 
| 493 |  |  |  |  |  |  |  | 
| 494 |  |  |  |  |  |  | /* | 
| 495 |  |  |  |  |  |  | =item calc_part(part, colors) | 
| 496 |  |  |  |  |  |  |  | 
| 497 |  |  |  |  |  |  | Calculates the new color limits for the given partition. | 
| 498 |  |  |  |  |  |  |  | 
| 499 |  |  |  |  |  |  | Giflib assumes that the limits for the non-split channels stay the | 
| 500 |  |  |  |  |  |  | same, but this strikes me as incorrect, especially if the colors tend | 
| 501 |  |  |  |  |  |  | to be color ramps. | 
| 502 |  |  |  |  |  |  |  | 
| 503 |  |  |  |  |  |  | Of course this could be optimized by not recalculating the channel we | 
| 504 |  |  |  |  |  |  | just sorted on, but it's not worth the effort right now. | 
| 505 |  |  |  |  |  |  |  | 
| 506 |  |  |  |  |  |  | =cut | 
| 507 |  |  |  |  |  |  | */ | 
| 508 | 0 |  |  |  |  |  | static void calc_part(medcut_partition *part, quant_color_entry *colors) { | 
| 509 |  |  |  |  |  |  | int i, ch; | 
| 510 |  |  |  |  |  |  |  | 
| 511 | 0 | 0 |  |  |  |  | for (ch = 0; ch < 3; ++ch) { | 
| 512 | 0 |  |  |  |  |  | part->min[ch] = 255; | 
| 513 | 0 |  |  |  |  |  | part->max[ch] = 0; | 
| 514 |  |  |  |  |  |  | } | 
| 515 | 0 | 0 |  |  |  |  | for (i = part->start; i < part->start + part->size; ++i) { | 
| 516 | 0 | 0 |  |  |  |  | for (ch = 0; ch < 3; ++ch) { | 
| 517 | 0 | 0 |  |  |  |  | if (part->min[ch] > colors[i].rgb[ch]) | 
| 518 | 0 |  |  |  |  |  | part->min[ch] = colors[i].rgb[ch]; | 
| 519 | 0 | 0 |  |  |  |  | if (part->max[ch] < colors[i].rgb[ch]) | 
| 520 | 0 |  |  |  |  |  | part->max[ch] = colors[i].rgb[ch]; | 
| 521 |  |  |  |  |  |  | } | 
| 522 |  |  |  |  |  |  | } | 
| 523 | 0 | 0 |  |  |  |  | for (ch = 0; ch < 3; ++ch) { | 
| 524 | 0 |  |  |  |  |  | part->width[ch] = part->max[ch] - part->min[ch]; | 
| 525 |  |  |  |  |  |  | } | 
| 526 | 0 |  |  |  |  |  | } | 
| 527 |  |  |  |  |  |  |  | 
| 528 |  |  |  |  |  |  | /* simple functions to sort by each channel - we could use a global, but | 
| 529 |  |  |  |  |  |  | that would be bad */ | 
| 530 |  |  |  |  |  |  |  | 
| 531 |  |  |  |  |  |  | static int | 
| 532 | 0 |  |  |  |  |  | color_sort_red(void const *left, void const *right) { | 
| 533 | 0 |  |  |  |  |  | return ((quant_color_entry *)left)->rgb[0] - ((quant_color_entry *)right)->rgb[0]; | 
| 534 |  |  |  |  |  |  | } | 
| 535 |  |  |  |  |  |  |  | 
| 536 |  |  |  |  |  |  | static int | 
| 537 | 0 |  |  |  |  |  | color_sort_green(void const *left, void const *right) { | 
| 538 | 0 |  |  |  |  |  | return ((quant_color_entry *)left)->rgb[1] - ((quant_color_entry *)right)->rgb[1]; | 
| 539 |  |  |  |  |  |  | } | 
| 540 |  |  |  |  |  |  |  | 
| 541 |  |  |  |  |  |  | static int | 
| 542 | 0 |  |  |  |  |  | color_sort_blue(void const *left, void const *right) { | 
| 543 | 0 |  |  |  |  |  | return ((quant_color_entry *)left)->rgb[2] - ((quant_color_entry *)right)->rgb[2]; | 
| 544 |  |  |  |  |  |  | } | 
| 545 |  |  |  |  |  |  |  | 
| 546 |  |  |  |  |  |  | static int (*sorters[])(void const *, void const *) = | 
| 547 |  |  |  |  |  |  | { | 
| 548 |  |  |  |  |  |  | color_sort_red, | 
| 549 |  |  |  |  |  |  | color_sort_green, | 
| 550 |  |  |  |  |  |  | color_sort_blue, | 
| 551 |  |  |  |  |  |  | }; | 
| 552 |  |  |  |  |  |  |  | 
| 553 |  |  |  |  |  |  | static void | 
| 554 | 5 |  |  |  |  |  | makemap_mediancut(i_quantize *quant, i_img **imgs, int count) { | 
| 555 |  |  |  |  |  |  | quant_color_entry *colors; | 
| 556 |  |  |  |  |  |  | i_mempool mp; | 
| 557 |  |  |  |  |  |  | int imgn, i, ch; | 
| 558 |  |  |  |  |  |  | i_img_dim x, y, max_width; | 
| 559 |  |  |  |  |  |  | i_color *line; | 
| 560 |  |  |  |  |  |  | int color_count; | 
| 561 |  |  |  |  |  |  | i_img_dim total_pixels; | 
| 562 |  |  |  |  |  |  | medcut_partition *parts; | 
| 563 |  |  |  |  |  |  | int part_num; | 
| 564 |  |  |  |  |  |  | int in, out; | 
| 565 |  |  |  |  |  |  | /* number of channels we search for the best channel to partition | 
| 566 |  |  |  |  |  |  | this isn't terribly efficient, but it should work */ | 
| 567 |  |  |  |  |  |  | int chan_count; | 
| 568 |  |  |  |  |  |  |  | 
| 569 | 5 |  |  |  |  |  | mm_log((1, "makemap_mediancut(quant %p { mc_count=%d, mc_colors=%p }, imgs %p, count %d)\n", | 
| 570 |  |  |  |  |  |  | quant, quant->mc_count, quant->mc_colors, imgs, count)); | 
| 571 |  |  |  |  |  |  |  | 
| 572 | 5 | 50 |  |  |  |  | if (makemap_palette(quant, imgs, count)) | 
| 573 | 0 |  |  |  |  |  | return; | 
| 574 |  |  |  |  |  |  |  | 
| 575 | 5 |  |  |  |  |  | i_mempool_init(&mp); | 
| 576 |  |  |  |  |  |  |  | 
| 577 | 5 |  |  |  |  |  | colors = i_mempool_alloc(&mp, sizeof(*colors) * MEDIAN_CUT_COLORS); | 
| 578 | 163845 | 100 |  |  |  |  | for (i = 0; i < MEDIAN_CUT_COLORS; ++i) { | 
| 579 | 163840 |  |  |  |  |  | colors[i].rgb[0] = MED_CUT_RED(i); | 
| 580 | 163840 |  |  |  |  |  | colors[i].rgb[1] = MED_CUT_GREEN(i); | 
| 581 | 163840 |  |  |  |  |  | colors[i].rgb[2] = MED_CUT_BLUE(i); | 
| 582 | 163840 |  |  |  |  |  | colors[i].count = 0; | 
| 583 |  |  |  |  |  |  | } | 
| 584 |  |  |  |  |  |  |  | 
| 585 | 5 |  |  |  |  |  | max_width = -1; | 
| 586 | 10 | 100 |  |  |  |  | for (imgn = 0; imgn < count; ++imgn) { | 
| 587 | 5 | 50 |  |  |  |  | if (imgs[imgn]->xsize > max_width) | 
| 588 | 5 |  |  |  |  |  | max_width = imgs[imgn]->xsize; | 
| 589 |  |  |  |  |  |  | } | 
| 590 | 5 |  |  |  |  |  | line = i_mempool_alloc(&mp, sizeof(i_color) * max_width); | 
| 591 |  |  |  |  |  |  |  | 
| 592 |  |  |  |  |  |  | /* build the stats */ | 
| 593 | 5 |  |  |  |  |  | total_pixels = 0; | 
| 594 | 5 |  |  |  |  |  | chan_count = 1; /* assume we just have grayscale */ | 
| 595 | 10 | 100 |  |  |  |  | for (imgn = 0; imgn < count; ++imgn) { | 
| 596 | 5 |  |  |  |  |  | total_pixels += imgs[imgn]->xsize * imgs[imgn]->ysize; | 
| 597 | 452 | 100 |  |  |  |  | for (y = 0; y < imgs[imgn]->ysize; ++y) { | 
| 598 | 447 |  |  |  |  |  | i_glin(imgs[imgn], 0, imgs[imgn]->xsize, y, line); | 
| 599 | 447 | 50 |  |  |  |  | if (imgs[imgn]->channels > 2) { | 
| 600 | 447 |  |  |  |  |  | chan_count = 3; | 
| 601 | 58121 | 100 |  |  |  |  | for (x = 0; x < imgs[imgn]->xsize; ++x) { | 
| 602 | 57674 |  |  |  |  |  | ++colors[MED_CUT_INDEX(line[x])].count; | 
| 603 |  |  |  |  |  |  | } | 
| 604 |  |  |  |  |  |  | } | 
| 605 |  |  |  |  |  |  | else { | 
| 606 |  |  |  |  |  |  | /* a gray-scale image, just use the first channel */ | 
| 607 | 0 | 0 |  |  |  |  | for (x = 0; x < imgs[imgn]->xsize; ++x) { | 
| 608 | 0 |  |  |  |  |  | ++colors[MED_CUT_GRAY_INDEX(line[x])].count; | 
| 609 |  |  |  |  |  |  | } | 
| 610 |  |  |  |  |  |  | } | 
| 611 |  |  |  |  |  |  | } | 
| 612 |  |  |  |  |  |  | } | 
| 613 |  |  |  |  |  |  |  | 
| 614 |  |  |  |  |  |  | /* eliminate the empty colors */ | 
| 615 | 5 |  |  |  |  |  | out = 0; | 
| 616 | 163845 | 100 |  |  |  |  | for (in = 0; in < MEDIAN_CUT_COLORS; ++in) { | 
| 617 | 163840 | 100 |  |  |  |  | if (colors[in].count) { | 
| 618 | 374 |  |  |  |  |  | colors[out++] = colors[in]; | 
| 619 |  |  |  |  |  |  | } | 
| 620 |  |  |  |  |  |  | } | 
| 621 |  |  |  |  |  |  | /*printf("out %d\n", out); | 
| 622 |  |  |  |  |  |  |  | 
| 623 |  |  |  |  |  |  | for (i = 0; i < out; ++i) { | 
| 624 |  |  |  |  |  |  | if (colors[i].count) { | 
| 625 |  |  |  |  |  |  | printf("%d: (%d,%d,%d) -> %d\n", i, colors[i].rgb[0], colors[i].rgb[1], | 
| 626 |  |  |  |  |  |  | colors[i].rgb[2], colors[i].count); | 
| 627 |  |  |  |  |  |  | } | 
| 628 |  |  |  |  |  |  | }*/ | 
| 629 |  |  |  |  |  |  |  | 
| 630 | 5 | 50 |  |  |  |  | if (out < quant->mc_size) { | 
| 631 |  |  |  |  |  |  | /* just copy them into the color table */ | 
| 632 | 379 | 100 |  |  |  |  | for (i = 0; i < out; ++i) { | 
| 633 | 1496 | 100 |  |  |  |  | for (ch = 0; ch < 3; ++ch) { | 
| 634 | 1122 |  |  |  |  |  | quant->mc_colors[i].channel[ch] = colors[i].rgb[ch]; | 
| 635 |  |  |  |  |  |  | } | 
| 636 | 374 |  |  |  |  |  | quant->mc_colors[i].rgba.a = 255; | 
| 637 |  |  |  |  |  |  | } | 
| 638 | 5 |  |  |  |  |  | quant->mc_count = out; | 
| 639 |  |  |  |  |  |  | } | 
| 640 |  |  |  |  |  |  | else { | 
| 641 |  |  |  |  |  |  | /* build the starting partition */ | 
| 642 | 0 |  |  |  |  |  | parts = i_mempool_alloc(&mp, sizeof(*parts) * quant->mc_size); | 
| 643 | 0 |  |  |  |  |  | parts[0].start = 0; | 
| 644 | 0 |  |  |  |  |  | parts[0].size = out; | 
| 645 | 0 |  |  |  |  |  | parts[0].pixels = total_pixels; | 
| 646 | 0 |  |  |  |  |  | calc_part(parts, colors); | 
| 647 | 0 |  |  |  |  |  | color_count = 1; | 
| 648 |  |  |  |  |  |  |  | 
| 649 | 0 | 0 |  |  |  |  | while (color_count < quant->mc_size) { | 
| 650 |  |  |  |  |  |  | /* initialized to avoid compiler warnings */ | 
| 651 | 0 |  |  |  |  |  | int max_index = 0, max_ch = 0; /* index/channel with biggest spread */ | 
| 652 |  |  |  |  |  |  | int max_size; | 
| 653 |  |  |  |  |  |  | medcut_partition *workpart; | 
| 654 |  |  |  |  |  |  | i_img_dim cum_total; | 
| 655 |  |  |  |  |  |  | i_img_dim half; | 
| 656 |  |  |  |  |  |  |  | 
| 657 |  |  |  |  |  |  | /* find the partition with the most biggest span with more than | 
| 658 |  |  |  |  |  |  | one color */ | 
| 659 | 0 |  |  |  |  |  | max_size = -1; | 
| 660 | 0 | 0 |  |  |  |  | for (i = 0; i < color_count; ++i) { | 
| 661 | 0 | 0 |  |  |  |  | for (ch = 0; ch < chan_count; ++ch) { | 
| 662 | 0 | 0 |  |  |  |  | if (parts[i].width[ch] > max_size | 
| 663 | 0 | 0 |  |  |  |  | && parts[i].size > 1) { | 
| 664 | 0 |  |  |  |  |  | max_index = i; | 
| 665 | 0 |  |  |  |  |  | max_ch = ch; | 
| 666 | 0 |  |  |  |  |  | max_size = parts[i].width[ch]; | 
| 667 |  |  |  |  |  |  | } | 
| 668 |  |  |  |  |  |  | } | 
| 669 |  |  |  |  |  |  | } | 
| 670 |  |  |  |  |  |  |  | 
| 671 |  |  |  |  |  |  | /* nothing else we can split */ | 
| 672 | 0 | 0 |  |  |  |  | if (max_size == -1) | 
| 673 | 0 |  |  |  |  |  | break; | 
| 674 |  |  |  |  |  |  |  | 
| 675 | 0 |  |  |  |  |  | workpart = parts+max_index; | 
| 676 |  |  |  |  |  |  | /*printf("splitting partition %d (pixels %ld, start %d, size %d)\n", max_index, workpart->pixels, workpart->start, workpart->size);*/ | 
| 677 | 0 |  |  |  |  |  | qsort(colors + workpart->start, workpart->size, sizeof(*colors), | 
| 678 |  |  |  |  |  |  | sorters[max_ch]); | 
| 679 |  |  |  |  |  |  |  | 
| 680 |  |  |  |  |  |  | /* find the median or something like it we need to make sure both | 
| 681 |  |  |  |  |  |  | sides of the split have at least one color in them, so we don't | 
| 682 |  |  |  |  |  |  | test at the first or last entry */ | 
| 683 | 0 |  |  |  |  |  | i = workpart->start; | 
| 684 | 0 |  |  |  |  |  | cum_total = colors[i].count; | 
| 685 | 0 |  |  |  |  |  | ++i; | 
| 686 | 0 |  |  |  |  |  | half = workpart->pixels / 2; | 
| 687 | 0 | 0 |  |  |  |  | while (i < workpart->start + workpart->size - 1 | 
| 688 | 0 | 0 |  |  |  |  | && cum_total < half) { | 
| 689 | 0 |  |  |  |  |  | cum_total += colors[i++].count; | 
| 690 |  |  |  |  |  |  | } | 
| 691 |  |  |  |  |  |  | /*printf("Split at %d to make %d (half %ld, cumtotal %ld)\n", i, color_count, half, cum_total);*/ | 
| 692 |  |  |  |  |  |  |  | 
| 693 |  |  |  |  |  |  | /* found the spot to split */ | 
| 694 | 0 |  |  |  |  |  | parts[color_count].start = i; | 
| 695 | 0 |  |  |  |  |  | parts[color_count].size = workpart->start + workpart->size - i; | 
| 696 | 0 |  |  |  |  |  | workpart->size = i - workpart->start; | 
| 697 | 0 |  |  |  |  |  | parts[color_count].pixels = workpart->pixels - cum_total; | 
| 698 | 0 |  |  |  |  |  | workpart->pixels = cum_total; | 
| 699 |  |  |  |  |  |  |  | 
| 700 |  |  |  |  |  |  | /* recalculate the limits */ | 
| 701 | 0 |  |  |  |  |  | calc_part(workpart, colors); | 
| 702 | 0 |  |  |  |  |  | calc_part(parts+color_count, colors); | 
| 703 | 0 |  |  |  |  |  | ++color_count; | 
| 704 |  |  |  |  |  |  | } | 
| 705 |  |  |  |  |  |  |  | 
| 706 |  |  |  |  |  |  | /* fill in the color table - since we could still have partitions | 
| 707 |  |  |  |  |  |  | that have more than one color, we need to average the colors */ | 
| 708 | 0 | 0 |  |  |  |  | for (part_num = 0; part_num < color_count; ++part_num) { | 
| 709 |  |  |  |  |  |  | double sums[3]; | 
| 710 |  |  |  |  |  |  | medcut_partition *workpart; | 
| 711 |  |  |  |  |  |  |  | 
| 712 | 0 |  |  |  |  |  | workpart = parts+part_num; | 
| 713 | 0 | 0 |  |  |  |  | for (ch = 0; ch < 3; ++ch) | 
| 714 | 0 |  |  |  |  |  | sums[ch] = 0; | 
| 715 |  |  |  |  |  |  |  | 
| 716 | 0 | 0 |  |  |  |  | for (i = workpart->start; i < workpart->start + workpart->size; ++i) { | 
| 717 | 0 | 0 |  |  |  |  | for (ch = 0; ch < 3; ++ch) { | 
| 718 | 0 |  |  |  |  |  | sums[ch] += (int)(colors[i].rgb[ch]) * colors[i].count; | 
| 719 |  |  |  |  |  |  | } | 
| 720 |  |  |  |  |  |  | } | 
| 721 | 0 | 0 |  |  |  |  | for (ch = 0; ch < 3; ++ch) { | 
| 722 | 0 |  |  |  |  |  | quant->mc_colors[part_num].channel[ch] = sums[ch] / workpart->pixels; | 
| 723 |  |  |  |  |  |  | } | 
| 724 | 0 |  |  |  |  |  | quant->mc_colors[part_num].rgba.a = 255; | 
| 725 |  |  |  |  |  |  | } | 
| 726 | 0 |  |  |  |  |  | quant->mc_count = color_count; | 
| 727 |  |  |  |  |  |  | } | 
| 728 |  |  |  |  |  |  | /*printf("out %d colors\n", quant->mc_count);*/ | 
| 729 | 5 |  |  |  |  |  | i_mempool_destroy(&mp); | 
| 730 |  |  |  |  |  |  |  | 
| 731 | 5 |  |  |  |  |  | mm_log((1, "makemap_mediancut() - %d colors\n", quant->mc_count)); | 
| 732 |  |  |  |  |  |  | } | 
| 733 |  |  |  |  |  |  |  | 
| 734 |  |  |  |  |  |  | static void | 
| 735 | 3 |  |  |  |  |  | makemap_mono(i_quantize *quant) { | 
| 736 | 3 |  |  |  |  |  | quant->mc_colors[0].rgba.r = 0; | 
| 737 | 3 |  |  |  |  |  | quant->mc_colors[0].rgba.g = 0; | 
| 738 | 3 |  |  |  |  |  | quant->mc_colors[0].rgba.b = 0; | 
| 739 | 3 |  |  |  |  |  | quant->mc_colors[0].rgba.a = 255; | 
| 740 | 3 |  |  |  |  |  | quant->mc_colors[1].rgba.r = 255; | 
| 741 | 3 |  |  |  |  |  | quant->mc_colors[1].rgba.g = 255; | 
| 742 | 3 |  |  |  |  |  | quant->mc_colors[1].rgba.b = 255; | 
| 743 | 3 |  |  |  |  |  | quant->mc_colors[1].rgba.a = 255; | 
| 744 | 3 |  |  |  |  |  | quant->mc_count = 2; | 
| 745 | 3 |  |  |  |  |  | } | 
| 746 |  |  |  |  |  |  |  | 
| 747 |  |  |  |  |  |  | static void | 
| 748 | 3 |  |  |  |  |  | makemap_gray(i_quantize *quant, int step) { | 
| 749 | 3 |  |  |  |  |  | int gray = 0; | 
| 750 | 3 |  |  |  |  |  | int i = 0; | 
| 751 |  |  |  |  |  |  |  | 
| 752 | 279 | 100 |  |  |  |  | while (gray < 256) { | 
| 753 | 276 |  |  |  |  |  | setcol(quant->mc_colors+i, gray, gray, gray, 255); | 
| 754 | 276 |  |  |  |  |  | ++i; | 
| 755 | 276 |  |  |  |  |  | gray += step; | 
| 756 |  |  |  |  |  |  | } | 
| 757 | 3 |  |  |  |  |  | quant->mc_count = i; | 
| 758 | 3 |  |  |  |  |  | } | 
| 759 |  |  |  |  |  |  |  | 
| 760 |  |  |  |  |  |  | static void | 
| 761 | 4 |  |  |  |  |  | makemap_webmap(i_quantize *quant) { | 
| 762 |  |  |  |  |  |  | int r, g, b; | 
| 763 |  |  |  |  |  |  |  | 
| 764 | 4 |  |  |  |  |  | int i = 0; | 
| 765 | 28 | 100 |  |  |  |  | for (r = 0; r < 256; r+=0x33) | 
| 766 | 168 | 100 |  |  |  |  | for (g = 0; g < 256; g+=0x33) | 
| 767 | 1008 | 100 |  |  |  |  | for (b = 0; b < 256; b += 0x33) | 
| 768 | 864 |  |  |  |  |  | setcol(quant->mc_colors+i++, r, g, b, 255); | 
| 769 | 4 |  |  |  |  |  | quant->mc_count = i; | 
| 770 | 4 |  |  |  |  |  | } | 
| 771 |  |  |  |  |  |  |  | 
| 772 |  |  |  |  |  |  | static int | 
| 773 | 0 |  |  |  |  |  | in_palette(i_color *c, i_quantize *quant, int size) { | 
| 774 |  |  |  |  |  |  | int i; | 
| 775 |  |  |  |  |  |  |  | 
| 776 | 0 | 0 |  |  |  |  | for (i = 0; i < size; ++i) { | 
| 777 | 0 | 0 |  |  |  |  | if (c->channel[0] == quant->mc_colors[i].channel[0] | 
| 778 | 0 | 0 |  |  |  |  | && c->channel[1] == quant->mc_colors[i].channel[1] | 
| 779 | 0 | 0 |  |  |  |  | && c->channel[2] == quant->mc_colors[i].channel[2]) { | 
| 780 | 0 |  |  |  |  |  | return i; | 
| 781 |  |  |  |  |  |  | } | 
| 782 |  |  |  |  |  |  | } | 
| 783 |  |  |  |  |  |  |  | 
| 784 | 0 |  |  |  |  |  | return -1; | 
| 785 |  |  |  |  |  |  | } | 
| 786 |  |  |  |  |  |  |  | 
| 787 |  |  |  |  |  |  | /* | 
| 788 |  |  |  |  |  |  | =item makemap_palette(quant, imgs, count) | 
| 789 |  |  |  |  |  |  |  | 
| 790 |  |  |  |  |  |  | Tests if all the given images are paletted and have a common palette, | 
| 791 |  |  |  |  |  |  | if they do it builds that palette. | 
| 792 |  |  |  |  |  |  |  | 
| 793 |  |  |  |  |  |  | A possible improvement might be to eliminate unused colors in the | 
| 794 |  |  |  |  |  |  | images palettes. | 
| 795 |  |  |  |  |  |  |  | 
| 796 |  |  |  |  |  |  | =cut | 
| 797 |  |  |  |  |  |  | */ | 
| 798 |  |  |  |  |  |  |  | 
| 799 |  |  |  |  |  |  | static int | 
| 800 | 5 |  |  |  |  |  | makemap_palette(i_quantize *quant, i_img **imgs, int count) { | 
| 801 | 5 |  |  |  |  |  | int size = quant->mc_count; | 
| 802 |  |  |  |  |  |  | int i; | 
| 803 |  |  |  |  |  |  | int imgn; | 
| 804 |  |  |  |  |  |  | char used[256]; | 
| 805 |  |  |  |  |  |  | int col_count; | 
| 806 |  |  |  |  |  |  |  | 
| 807 | 5 |  |  |  |  |  | mm_log((1, "makemap_palette(quant %p { mc_count=%d, mc_colors=%p }, imgs %p, count %d)\n", | 
| 808 |  |  |  |  |  |  | quant, quant->mc_count, quant->mc_colors, imgs, count)); | 
| 809 |  |  |  |  |  |  | /* we try to build a common palette here, if we can manage that, then | 
| 810 |  |  |  |  |  |  | that's the palette we use */ | 
| 811 | 5 | 50 |  |  |  |  | for (imgn = 0; imgn < count; ++imgn) { | 
| 812 |  |  |  |  |  |  | int eliminate_unused; | 
| 813 | 5 | 50 |  |  |  |  | if (imgs[imgn]->type != i_palette_type) { | 
| 814 | 5 |  |  |  |  |  | mm_log((1, "makemap_palette() -> 0 (non-palette image)\n")); | 
| 815 | 5 |  |  |  |  |  | return 0; | 
| 816 |  |  |  |  |  |  | } | 
| 817 |  |  |  |  |  |  |  | 
| 818 | 0 | 0 |  |  |  |  | if (!i_tags_get_int(&imgs[imgn]->tags, "gif_eliminate_unused", 0, | 
| 819 |  |  |  |  |  |  | &eliminate_unused)) { | 
| 820 | 0 |  |  |  |  |  | eliminate_unused = 1; | 
| 821 |  |  |  |  |  |  | } | 
| 822 |  |  |  |  |  |  |  | 
| 823 | 0 | 0 |  |  |  |  | if (eliminate_unused) { | 
| 824 | 0 |  |  |  |  |  | i_palidx *line = mymalloc(sizeof(i_palidx) * imgs[imgn]->xsize); | 
| 825 |  |  |  |  |  |  | i_img_dim x, y; | 
| 826 | 0 |  |  |  |  |  | memset(used, 0, sizeof(used)); | 
| 827 |  |  |  |  |  |  |  | 
| 828 | 0 | 0 |  |  |  |  | for (y = 0; y < imgs[imgn]->ysize; ++y) { | 
| 829 | 0 | 0 |  |  |  |  | i_gpal(imgs[imgn], 0, imgs[imgn]->xsize, y, line); | 
| 830 | 0 | 0 |  |  |  |  | for (x = 0; x < imgs[imgn]->xsize; ++x) | 
| 831 | 0 |  |  |  |  |  | used[line[x]] = 1; | 
| 832 |  |  |  |  |  |  | } | 
| 833 |  |  |  |  |  |  |  | 
| 834 | 0 |  |  |  |  |  | myfree(line); | 
| 835 |  |  |  |  |  |  | } | 
| 836 |  |  |  |  |  |  | else { | 
| 837 |  |  |  |  |  |  | /* assume all are in use */ | 
| 838 | 0 |  |  |  |  |  | memset(used, 1, sizeof(used)); | 
| 839 |  |  |  |  |  |  | } | 
| 840 |  |  |  |  |  |  |  | 
| 841 | 0 | 0 |  |  |  |  | col_count = i_colorcount(imgs[imgn]); | 
| 842 | 0 | 0 |  |  |  |  | for (i = 0; i < col_count; ++i) { | 
| 843 |  |  |  |  |  |  | i_color c; | 
| 844 |  |  |  |  |  |  |  | 
| 845 | 0 | 0 |  |  |  |  | i_getcolors(imgs[imgn], i, &c, 1); | 
| 846 | 0 | 0 |  |  |  |  | if (used[i]) { | 
| 847 | 0 | 0 |  |  |  |  | if (in_palette(&c, quant, size) < 0) { | 
| 848 | 0 | 0 |  |  |  |  | if (size < quant->mc_size) { | 
| 849 | 0 |  |  |  |  |  | quant->mc_colors[size++] = c; | 
| 850 |  |  |  |  |  |  | } | 
| 851 |  |  |  |  |  |  | else { | 
| 852 | 0 |  |  |  |  |  | mm_log((1, "makemap_palette() -> 0 (too many colors)\n")); | 
| 853 | 0 |  |  |  |  |  | return 0; | 
| 854 |  |  |  |  |  |  | } | 
| 855 |  |  |  |  |  |  | } | 
| 856 |  |  |  |  |  |  | } | 
| 857 |  |  |  |  |  |  | } | 
| 858 |  |  |  |  |  |  | } | 
| 859 |  |  |  |  |  |  |  | 
| 860 | 0 |  |  |  |  |  | mm_log((1, "makemap_palette() -> 1 (%d total colors)\n", size)); | 
| 861 | 0 |  |  |  |  |  | quant->mc_count = size; | 
| 862 |  |  |  |  |  |  |  | 
| 863 | 5 |  |  |  |  |  | return 1; | 
| 864 |  |  |  |  |  |  | } | 
| 865 |  |  |  |  |  |  |  | 
| 866 |  |  |  |  |  |  | #define pboxjump 32 | 
| 867 |  |  |  |  |  |  |  | 
| 868 |  |  |  |  |  |  | /* Define one of the following 4 symbols to choose a colour search method | 
| 869 |  |  |  |  |  |  | The idea is to try these out, including benchmarking, to see which | 
| 870 |  |  |  |  |  |  | is fastest in a good spread of circumstances. | 
| 871 |  |  |  |  |  |  | I'd expect IM_CFLINSEARCH to be fastest for very small palettes, and | 
| 872 |  |  |  |  |  |  | IM_CFHASHBOX for large images with large palettes. | 
| 873 |  |  |  |  |  |  |  | 
| 874 |  |  |  |  |  |  | Some other possibilities include: | 
| 875 |  |  |  |  |  |  | - search over entries sorted by luminance | 
| 876 |  |  |  |  |  |  |  | 
| 877 |  |  |  |  |  |  | Initially I was planning on testing using the macros and then | 
| 878 |  |  |  |  |  |  | integrating the code directly into each function, but this means if | 
| 879 |  |  |  |  |  |  | we find a bug at a late stage we will need to update N copies of | 
| 880 |  |  |  |  |  |  | the same code.  Also, keeping the code in the macros means that the | 
| 881 |  |  |  |  |  |  | code in the translation functions is much more to the point, | 
| 882 |  |  |  |  |  |  | there's no distracting colour search code to remove attention from | 
| 883 |  |  |  |  |  |  | what makes _this_ translation function different.  It may be | 
| 884 |  |  |  |  |  |  | advisable to move the setup code into functions at some point, but | 
| 885 |  |  |  |  |  |  | it should be possible to do this fairly transparently. | 
| 886 |  |  |  |  |  |  |  | 
| 887 |  |  |  |  |  |  | If IM_CF_COPTS is defined then CFLAGS must have an appropriate | 
| 888 |  |  |  |  |  |  | definition. | 
| 889 |  |  |  |  |  |  |  | 
| 890 |  |  |  |  |  |  | Each option needs to define 4 macros: | 
| 891 |  |  |  |  |  |  | CF_VARS - variables to define in the function | 
| 892 |  |  |  |  |  |  | CF_SETUP - code to setup for the colour search, eg. allocating and | 
| 893 |  |  |  |  |  |  | initializing lookup tables | 
| 894 |  |  |  |  |  |  | CF_FIND - code that looks for the color in val and puts the best | 
| 895 |  |  |  |  |  |  | matching index in bst_idx | 
| 896 |  |  |  |  |  |  | CF_CLEANUP - code to clean up, eg. releasing memory | 
| 897 |  |  |  |  |  |  | */ | 
| 898 |  |  |  |  |  |  | #ifndef IM_CF_COPTS | 
| 899 |  |  |  |  |  |  | /*#define IM_CFLINSEARCH*/ | 
| 900 |  |  |  |  |  |  | #define IM_CFHASHBOX | 
| 901 |  |  |  |  |  |  | /*#define IM_CFSORTCHAN*/ | 
| 902 |  |  |  |  |  |  | /*#define IM_CFRAND2DIST*/ | 
| 903 |  |  |  |  |  |  | #endif | 
| 904 |  |  |  |  |  |  |  | 
| 905 |  |  |  |  |  |  | /* return true if the color map contains only grays */ | 
| 906 |  |  |  |  |  |  | static int | 
| 907 | 5 |  |  |  |  |  | is_gray_map(const i_quantize *quant) { | 
| 908 |  |  |  |  |  |  | int i; | 
| 909 |  |  |  |  |  |  |  | 
| 910 | 11 | 100 |  |  |  |  | for (i = 0; i < quant->mc_count; ++i) { | 
| 911 | 10 | 100 |  |  |  |  | if (quant->mc_colors[i].rgb.r != quant->mc_colors[i].rgb.g | 
| 912 | 8 | 100 |  |  |  |  | || quant->mc_colors[i].rgb.r != quant->mc_colors[i].rgb.b) { | 
| 913 | 4 |  |  |  |  |  | mm_log((1, "  not a gray map\n")); | 
| 914 | 4 |  |  |  |  |  | return 0; | 
| 915 |  |  |  |  |  |  | } | 
| 916 |  |  |  |  |  |  | } | 
| 917 |  |  |  |  |  |  |  | 
| 918 | 1 |  |  |  |  |  | mm_log((1, "  is a gray map\n")); | 
| 919 | 1 |  |  |  |  |  | return 1; | 
| 920 |  |  |  |  |  |  | } | 
| 921 |  |  |  |  |  |  |  | 
| 922 |  |  |  |  |  |  | #ifdef IM_CFHASHBOX | 
| 923 |  |  |  |  |  |  |  | 
| 924 |  |  |  |  |  |  | /* The original version I wrote for this used the sort. | 
| 925 |  |  |  |  |  |  | If this is defined then we use a sort to extract the indices for | 
| 926 |  |  |  |  |  |  | the hashbox */ | 
| 927 |  |  |  |  |  |  | #define HB_SORT | 
| 928 |  |  |  |  |  |  |  | 
| 929 |  |  |  |  |  |  | /* assume i is available */ | 
| 930 |  |  |  |  |  |  | #define CF_VARS hashbox *hb = mymalloc(sizeof(hashbox) * 512); \ | 
| 931 |  |  |  |  |  |  | int currhb;  \ | 
| 932 |  |  |  |  |  |  | long ld, cd | 
| 933 |  |  |  |  |  |  |  | 
| 934 |  |  |  |  |  |  | #ifdef HB_SORT | 
| 935 |  |  |  |  |  |  |  | 
| 936 |  |  |  |  |  |  | static long *gdists; /* qsort is annoying */ | 
| 937 |  |  |  |  |  |  | /* int might be smaller than long, so we need to do a real compare | 
| 938 |  |  |  |  |  |  | rather than a subtraction*/ | 
| 939 | 3787328 |  |  |  |  |  | static int distcomp(void const *a, void const *b) { | 
| 940 | 3787328 |  |  |  |  |  | long ra = gdists[*(int const *)a]; | 
| 941 | 3787328 |  |  |  |  |  | long rb = gdists[*(int const *)b]; | 
| 942 | 3787328 | 100 |  |  |  |  | if (ra < rb) | 
| 943 | 1740270 |  |  |  |  |  | return -1; | 
| 944 | 2047058 | 100 |  |  |  |  | else if (ra > rb) | 
| 945 | 1944402 |  |  |  |  |  | return 1; | 
| 946 |  |  |  |  |  |  | else | 
| 947 | 102656 |  |  |  |  |  | return 0; | 
| 948 |  |  |  |  |  |  | } | 
| 949 |  |  |  |  |  |  |  | 
| 950 |  |  |  |  |  |  | #endif | 
| 951 |  |  |  |  |  |  |  | 
| 952 |  |  |  |  |  |  | /* for each hashbox build a list of colours that are in the hb or is closer | 
| 953 |  |  |  |  |  |  | than other colours | 
| 954 |  |  |  |  |  |  | This is pretty involved.  The original gifquant generated the hashbox | 
| 955 |  |  |  |  |  |  | as part of it's normal processing, but since the map generation is now | 
| 956 |  |  |  |  |  |  | separated from the translation we need to do this on the spot. | 
| 957 |  |  |  |  |  |  | Any optimizations, even if they don't produce perfect results would be | 
| 958 |  |  |  |  |  |  | welcome. | 
| 959 |  |  |  |  |  |  | */ | 
| 960 | 17 |  |  |  |  |  | static void hbsetup(i_quantize *quant, hashbox *hb) { | 
| 961 |  |  |  |  |  |  | long *dists, mind, maxd; | 
| 962 |  |  |  |  |  |  | int cr, cb, cg, hbnum, i; | 
| 963 |  |  |  |  |  |  | i_color cenc; | 
| 964 |  |  |  |  |  |  | #ifdef HB_SORT | 
| 965 | 17 |  |  |  |  |  | int *indices = mymalloc(quant->mc_count * sizeof(int)); | 
| 966 |  |  |  |  |  |  | #endif | 
| 967 |  |  |  |  |  |  |  | 
| 968 | 17 |  |  |  |  |  | dists = mymalloc(quant->mc_count * sizeof(long)); | 
| 969 | 153 | 100 |  |  |  |  | for (cr = 0; cr < 8; ++cr) { | 
| 970 | 1224 | 100 |  |  |  |  | for (cg = 0; cg < 8; ++cg) { | 
| 971 | 9792 | 100 |  |  |  |  | for (cb = 0; cb < 8; ++cb) { | 
| 972 |  |  |  |  |  |  | /* centre of the hashbox */ | 
| 973 | 8704 |  |  |  |  |  | cenc.channel[0] = cr*pboxjump+pboxjump/2; | 
| 974 | 8704 |  |  |  |  |  | cenc.channel[1] = cg*pboxjump+pboxjump/2; | 
| 975 | 8704 |  |  |  |  |  | cenc.channel[2] = cb*pboxjump+pboxjump/2; | 
| 976 | 8704 |  |  |  |  |  | hbnum = pixbox(&cenc); | 
| 977 | 8704 |  |  |  |  |  | hb[hbnum].cnt = 0; | 
| 978 |  |  |  |  |  |  | /* order indices in the order of distance from the hashbox */ | 
| 979 | 662528 | 100 |  |  |  |  | for (i = 0; i < quant->mc_count; ++i) { | 
| 980 |  |  |  |  |  |  | #ifdef HB_SORT | 
| 981 | 653824 |  |  |  |  |  | indices[i] = i; | 
| 982 |  |  |  |  |  |  | #endif | 
| 983 | 653824 |  |  |  |  |  | dists[i] = ceucl_d(&cenc, quant->mc_colors+i); | 
| 984 |  |  |  |  |  |  | } | 
| 985 |  |  |  |  |  |  | #ifdef HB_SORT | 
| 986 |  |  |  |  |  |  | /* it should be possible to do this without a sort | 
| 987 |  |  |  |  |  |  | but so far I'm too lazy */ | 
| 988 | 8704 |  |  |  |  |  | gdists = dists; | 
| 989 | 8704 |  |  |  |  |  | qsort(indices, quant->mc_count, sizeof(int), distcomp); | 
| 990 |  |  |  |  |  |  | /* any colors that can match are within mind+diagonal size of | 
| 991 |  |  |  |  |  |  | a hashbox */ | 
| 992 | 8704 |  |  |  |  |  | mind = dists[indices[0]]; | 
| 993 | 8704 |  |  |  |  |  | i = 0; | 
| 994 | 8704 |  |  |  |  |  | maxd = (sqrt(mind)+pboxjump)*(sqrt(mind)+pboxjump); | 
| 995 | 50213 | 100 |  |  |  |  | while (i < quant->mc_count && dists[indices[i]] < maxd) { | 
|  |  | 100 |  |  |  |  |  | 
| 996 | 41509 |  |  |  |  |  | hb[hbnum].vec[hb[hbnum].cnt++] = indices[i++]; | 
| 997 |  |  |  |  |  |  | } | 
| 998 |  |  |  |  |  |  | #else | 
| 999 |  |  |  |  |  |  | /* work out the minimum */ | 
| 1000 |  |  |  |  |  |  | mind = 256*256*3; | 
| 1001 |  |  |  |  |  |  | for (i = 0; i < quant->mc_count; ++i) { | 
| 1002 |  |  |  |  |  |  | if (dists[i] < mind) mind = dists[i]; | 
| 1003 |  |  |  |  |  |  | } | 
| 1004 |  |  |  |  |  |  | /* transfer any colours that might be closest to a colour in | 
| 1005 |  |  |  |  |  |  | this hashbox */ | 
| 1006 |  |  |  |  |  |  | maxd = (sqrt(mind)+pboxjump)*(sqrt(mind)+pboxjump); | 
| 1007 |  |  |  |  |  |  | for (i = 0; i < quant->mc_count; ++i) { | 
| 1008 |  |  |  |  |  |  | if (dists[i] < maxd) | 
| 1009 |  |  |  |  |  |  | hb[hbnum].vec[hb[hbnum].cnt++] = i; | 
| 1010 |  |  |  |  |  |  | } | 
| 1011 |  |  |  |  |  |  | #endif | 
| 1012 |  |  |  |  |  |  | } | 
| 1013 |  |  |  |  |  |  | } | 
| 1014 |  |  |  |  |  |  | } | 
| 1015 |  |  |  |  |  |  | #ifdef HB_SORT | 
| 1016 | 17 |  |  |  |  |  | myfree(indices); | 
| 1017 |  |  |  |  |  |  | #endif | 
| 1018 | 17 |  |  |  |  |  | myfree(dists) ; | 
| 1019 | 17 |  |  |  |  |  | } | 
| 1020 |  |  |  |  |  |  | #define CF_SETUP hbsetup(quant, hb) | 
| 1021 |  |  |  |  |  |  |  | 
| 1022 |  |  |  |  |  |  | #define CF_FIND \ | 
| 1023 |  |  |  |  |  |  | currhb = pixbox(&val); \ | 
| 1024 |  |  |  |  |  |  | ld = 196608; \ | 
| 1025 |  |  |  |  |  |  | for (i = 0; i < hb[currhb].cnt; ++i) { \ | 
| 1026 |  |  |  |  |  |  | cd = ceucl_d(quant->mc_colors+hb[currhb].vec[i], &val); \ | 
| 1027 |  |  |  |  |  |  | if (cd < ld) { ld = cd; bst_idx = hb[currhb].vec[i]; } \ | 
| 1028 |  |  |  |  |  |  | } | 
| 1029 |  |  |  |  |  |  |  | 
| 1030 |  |  |  |  |  |  | #define CF_CLEANUP myfree(hb) | 
| 1031 |  |  |  |  |  |  |  | 
| 1032 |  |  |  |  |  |  | #endif | 
| 1033 |  |  |  |  |  |  |  | 
| 1034 |  |  |  |  |  |  | #ifdef IM_CFLINSEARCH | 
| 1035 |  |  |  |  |  |  | /* as simple as it gets */ | 
| 1036 |  |  |  |  |  |  | #define CF_VARS long ld, cd | 
| 1037 |  |  |  |  |  |  | #define CF_SETUP /* none needed */ | 
| 1038 |  |  |  |  |  |  | #define CF_FIND \ | 
| 1039 |  |  |  |  |  |  | ld = 196608; \ | 
| 1040 |  |  |  |  |  |  | for (i = 0; i < quant->mc_count; ++i) { \ | 
| 1041 |  |  |  |  |  |  | cd = ceucl_d(quant->mc_colors+i, &val); \ | 
| 1042 |  |  |  |  |  |  | if (cd < ld) { ld = cd; bst_idx = i; } \ | 
| 1043 |  |  |  |  |  |  | } | 
| 1044 |  |  |  |  |  |  | #define CF_CLEANUP | 
| 1045 |  |  |  |  |  |  | #endif | 
| 1046 |  |  |  |  |  |  |  | 
| 1047 |  |  |  |  |  |  | #ifdef IM_CFSORTCHAN | 
| 1048 |  |  |  |  |  |  | static int gsortchan; | 
| 1049 |  |  |  |  |  |  | static i_quantize *gquant; | 
| 1050 |  |  |  |  |  |  | static int chansort(void const *a, void const *b) { | 
| 1051 |  |  |  |  |  |  | return gquant->mc_colors[*(int const *)a].channel[gsortchan] - | 
| 1052 |  |  |  |  |  |  | gquant->mc_colors[*(int const *)b].channel[gsortchan]; | 
| 1053 |  |  |  |  |  |  | } | 
| 1054 |  |  |  |  |  |  | #define CF_VARS int *indices, sortchan, diff; \ | 
| 1055 |  |  |  |  |  |  | long ld, cd; \ | 
| 1056 |  |  |  |  |  |  | int vindex[256] /* where to find value i of chan */ | 
| 1057 |  |  |  |  |  |  |  | 
| 1058 |  |  |  |  |  |  | static void chansetup(i_img *img, i_quantize *quant, int *csortchan, | 
| 1059 |  |  |  |  |  |  | int *vindex, int **cindices) { | 
| 1060 |  |  |  |  |  |  | int *indices, sortchan, chan, i, chval; | 
| 1061 |  |  |  |  |  |  | int chanmins[MAXCHANNELS], chanmaxs[MAXCHANNELS], maxrange; | 
| 1062 |  |  |  |  |  |  |  | 
| 1063 |  |  |  |  |  |  | /* find the channel with the maximum range */ | 
| 1064 |  |  |  |  |  |  | /* the maximum stddev would probably be better */ | 
| 1065 |  |  |  |  |  |  | for (chan = 0; chan < img->channels; ++chan) { | 
| 1066 |  |  |  |  |  |  | chanmins[chan] = 256; chanmaxs[chan] = 0; | 
| 1067 |  |  |  |  |  |  | for (i = 0; i < quant->mc_count; ++i) { | 
| 1068 |  |  |  |  |  |  | if (quant->mc_colors[i].channel[chan] < chanmins[chan]) | 
| 1069 |  |  |  |  |  |  | chanmins[chan] = quant->mc_colors[i].channel[chan]; | 
| 1070 |  |  |  |  |  |  | if (quant->mc_colors[i].channel[chan] > chanmaxs[chan]) | 
| 1071 |  |  |  |  |  |  | chanmaxs[chan] = quant->mc_colors[i].channel[chan]; | 
| 1072 |  |  |  |  |  |  | } | 
| 1073 |  |  |  |  |  |  | } | 
| 1074 |  |  |  |  |  |  | maxrange = -1; | 
| 1075 |  |  |  |  |  |  | for (chan = 0; chan < img->channels; ++chan) { | 
| 1076 |  |  |  |  |  |  | if (chanmaxs[chan]-chanmins[chan] > maxrange) { | 
| 1077 |  |  |  |  |  |  | maxrange = chanmaxs[chan]-chanmins[chan]; | 
| 1078 |  |  |  |  |  |  | sortchan = chan; | 
| 1079 |  |  |  |  |  |  | } | 
| 1080 |  |  |  |  |  |  | } | 
| 1081 |  |  |  |  |  |  | indices = mymalloc(quant->mc_count * sizeof(int)) ; | 
| 1082 |  |  |  |  |  |  | for (i = 0; i < quant->mc_count; ++i) { | 
| 1083 |  |  |  |  |  |  | indices[i] = i; | 
| 1084 |  |  |  |  |  |  | } | 
| 1085 |  |  |  |  |  |  | gsortchan = sortchan; | 
| 1086 |  |  |  |  |  |  | gquant = quant; | 
| 1087 |  |  |  |  |  |  | qsort(indices, quant->mc_count, sizeof(int), chansort) ; | 
| 1088 |  |  |  |  |  |  | /* now a lookup table to find entries faster */ | 
| 1089 |  |  |  |  |  |  | for (chval=0, i=0; i < quant->mc_count; ++i) { | 
| 1090 |  |  |  |  |  |  | while (chval < 256 && | 
| 1091 |  |  |  |  |  |  | chval < quant->mc_colors[indices[i]].channel[sortchan]) { | 
| 1092 |  |  |  |  |  |  | vindex[chval++] = i; | 
| 1093 |  |  |  |  |  |  | } | 
| 1094 |  |  |  |  |  |  | } | 
| 1095 |  |  |  |  |  |  | while (chval < 256) { | 
| 1096 |  |  |  |  |  |  | vindex[chval++] = quant->mc_count-1; | 
| 1097 |  |  |  |  |  |  | } | 
| 1098 |  |  |  |  |  |  | *csortchan = sortchan; | 
| 1099 |  |  |  |  |  |  | *cindices = indices; | 
| 1100 |  |  |  |  |  |  | } | 
| 1101 |  |  |  |  |  |  |  | 
| 1102 |  |  |  |  |  |  | #define CF_SETUP \ | 
| 1103 |  |  |  |  |  |  | chansetup(img, quant, &sortchan, vindex, &indices) | 
| 1104 |  |  |  |  |  |  |  | 
| 1105 |  |  |  |  |  |  | int chanfind(i_color val, i_quantize *quant, int *indices, int *vindex, | 
| 1106 |  |  |  |  |  |  | int sortchan) { | 
| 1107 |  |  |  |  |  |  | int i, bst_idx, diff, maxdiff; | 
| 1108 |  |  |  |  |  |  | long ld, cd; | 
| 1109 |  |  |  |  |  |  |  | 
| 1110 |  |  |  |  |  |  | i = vindex[val.channel[sortchan]]; | 
| 1111 |  |  |  |  |  |  | bst_idx = indices[i]; | 
| 1112 |  |  |  |  |  |  | ld = 196608; | 
| 1113 |  |  |  |  |  |  | diff = 0; | 
| 1114 |  |  |  |  |  |  | maxdiff = quant->mc_count; | 
| 1115 |  |  |  |  |  |  | while (diff < maxdiff) { | 
| 1116 |  |  |  |  |  |  | if (i+diff < quant->mc_count) { | 
| 1117 |  |  |  |  |  |  | cd = ceucl_d(&val, quant->mc_colors+indices[i+diff]); | 
| 1118 |  |  |  |  |  |  | if (cd < ld) { | 
| 1119 |  |  |  |  |  |  | bst_idx = indices[i+diff]; | 
| 1120 |  |  |  |  |  |  | ld = cd; | 
| 1121 |  |  |  |  |  |  | maxdiff = sqrt(ld); | 
| 1122 |  |  |  |  |  |  | } | 
| 1123 |  |  |  |  |  |  | } | 
| 1124 |  |  |  |  |  |  | if (i-diff >= 0) { | 
| 1125 |  |  |  |  |  |  | cd = ceucl_d(&val, quant->mc_colors+indices[i-diff]); | 
| 1126 |  |  |  |  |  |  | if (cd < ld) { | 
| 1127 |  |  |  |  |  |  | bst_idx = indices[i-diff]; | 
| 1128 |  |  |  |  |  |  | ld = cd; | 
| 1129 |  |  |  |  |  |  | maxdiff = sqrt(ld); | 
| 1130 |  |  |  |  |  |  | } | 
| 1131 |  |  |  |  |  |  | } | 
| 1132 |  |  |  |  |  |  | ++diff; | 
| 1133 |  |  |  |  |  |  | } | 
| 1134 |  |  |  |  |  |  |  | 
| 1135 |  |  |  |  |  |  | return bst_idx; | 
| 1136 |  |  |  |  |  |  | } | 
| 1137 |  |  |  |  |  |  |  | 
| 1138 |  |  |  |  |  |  | #define CF_FIND \ | 
| 1139 |  |  |  |  |  |  | bst_idx = chanfind(val, quant, indices, vindex, sortchan) | 
| 1140 |  |  |  |  |  |  |  | 
| 1141 |  |  |  |  |  |  |  | 
| 1142 |  |  |  |  |  |  | #define CF_CLEANUP myfree(indices) | 
| 1143 |  |  |  |  |  |  |  | 
| 1144 |  |  |  |  |  |  | #endif | 
| 1145 |  |  |  |  |  |  |  | 
| 1146 |  |  |  |  |  |  | #ifdef IM_CFRAND2DIST | 
| 1147 |  |  |  |  |  |  |  | 
| 1148 |  |  |  |  |  |  | /* This is based on a method described by Addi in the #imager channel | 
| 1149 |  |  |  |  |  |  | on the 28/2/2001.  I was about 1am Sydney time at the time, so I | 
| 1150 |  |  |  |  |  |  | wasn't at my most cogent.  Well, that's my excuse :) | 
| 1151 |  |  |  |  |  |  |  | 
| 1152 |  |  |  |  |  |  | what I have at the moment is: hashboxes, with optimum hash box | 
| 1153 |  |  |  |  |  |  | filling; simple linear search; and a lookup in the widest channel | 
| 1154 |  |  |  |  |  |  | (currently the channel with the maximum range) | 
| 1155 |  |  |  |  |  |  | There is one more way that might be simple to implement. | 
| 1156 |  |  |  |  |  |  | You want to hear? | 
| 1157 |  |  |  |  |  |  | what's that? | 
| 1158 |  |  |  |  |  |  | somebody said that was not true | 
| 1159 |  |  |  |  |  |  | For each of the colors in the palette start by creating a | 
| 1160 |  |  |  |  |  |  | sorted list of the form: | 
| 1161 |  |  |  |  |  |  | [distance, color] | 
| 1162 |  |  |  |  |  |  | Where they are sorted by distance. | 
| 1163 |  |  |  |  |  |  | distance to where? | 
| 1164 |  |  |  |  |  |  | Where the elements in the lists are the distances and colors of | 
| 1165 |  |  |  |  |  |  | the other colors in the palette | 
| 1166 |  |  |  |  |  |  | ok | 
| 1167 |  |  |  |  |  |  | So if you are at color 0 | 
| 1168 |  |  |  |  |  |  | ok - now to search for the closest color when you are creating | 
| 1169 |  |  |  |  |  |  | the final image is done like this: | 
| 1170 |  |  |  |  |  |  | a) pick a random color from the palette | 
| 1171 |  |  |  |  |  |  | b) calculate the distance to it | 
| 1172 |  |  |  |  |  |  | c) only check the vectors that are within double the distance | 
| 1173 |  |  |  |  |  |  | in the list of the color you picked from the palette. | 
| 1174 |  |  |  |  |  |  | Does that seem logical? | 
| 1175 |  |  |  |  |  |  | Lets imagine that we only have grayscale to make an example: | 
| 1176 |  |  |  |  |  |  | Our palette has 1 4 10 20 as colors. | 
| 1177 |  |  |  |  |  |  | And we want to quantize the color 11 | 
| 1178 |  |  |  |  |  |  | lets say we picked 10 randomly | 
| 1179 |  |  |  |  |  |  | the double distance is 2 | 
| 1180 |  |  |  |  |  |  | since abs(10-11)*2 is 2 | 
| 1181 |  |  |  |  |  |  | And the list at vector 10 is this: | 
| 1182 |  |  |  |  |  |  | [0, 10], [6 4], [9, 1], [10, 20] | 
| 1183 |  |  |  |  |  |  | so we look at the first one (but not the second one since 6 is | 
| 1184 |  |  |  |  |  |  | at a greater distance than 2. | 
| 1185 |  |  |  |  |  |  | Any of that make sense? | 
| 1186 |  |  |  |  |  |  | yes, though are you suggesting another random jump to one of | 
| 1187 |  |  |  |  |  |  | the colours with the possible choices? or an exhaustive search? | 
| 1188 |  |  |  |  |  |  | TonyC: It's possible to come up with a recursive/iterative | 
| 1189 |  |  |  |  |  |  | enhancement but this is the 'basic' version. | 
| 1190 |  |  |  |  |  |  | Which would do an iterative search. | 
| 1191 |  |  |  |  |  |  | You can come up with conditions where it pays to switch to a new one. | 
| 1192 |  |  |  |  |  |  | And the 'random' start can be switched over to a small tree. | 
| 1193 |  |  |  |  |  |  | So you would have a little index at the start. | 
| 1194 |  |  |  |  |  |  | to get you into the general direction | 
| 1195 |  |  |  |  |  |  | Perhaps just an 8 split. | 
| 1196 |  |  |  |  |  |  | that is - split each dimension in half. | 
| 1197 |  |  |  |  |  |  | yep | 
| 1198 |  |  |  |  |  |  | I get the idea | 
| 1199 |  |  |  |  |  |  | But this would seem to be a good approach in our case since we | 
| 1200 |  |  |  |  |  |  | usually have few codevectors. | 
| 1201 |  |  |  |  |  |  | So we only need 256*256 entries in a table. | 
| 1202 |  |  |  |  |  |  | We could even only index some of them that were deemed as good | 
| 1203 |  |  |  |  |  |  | candidates. | 
| 1204 |  |  |  |  |  |  | I was considering adding paletted output support for PNG and | 
| 1205 |  |  |  |  |  |  | TIFF at some point, which support 16-bit palettes | 
| 1206 |  |  |  |  |  |  | ohh. | 
| 1207 |  |  |  |  |  |  | 'darn' ;) | 
| 1208 |  |  |  |  |  |  |  | 
| 1209 |  |  |  |  |  |  |  | 
| 1210 |  |  |  |  |  |  | */ | 
| 1211 |  |  |  |  |  |  |  | 
| 1212 |  |  |  |  |  |  |  | 
| 1213 |  |  |  |  |  |  | typedef struct i_dists { | 
| 1214 |  |  |  |  |  |  | int index; | 
| 1215 |  |  |  |  |  |  | long dist; | 
| 1216 |  |  |  |  |  |  | } i_dists; | 
| 1217 |  |  |  |  |  |  |  | 
| 1218 |  |  |  |  |  |  | #define CF_VARS \ | 
| 1219 |  |  |  |  |  |  | i_dists *dists; | 
| 1220 |  |  |  |  |  |  |  | 
| 1221 |  |  |  |  |  |  | static int dists_sort(void const *a, void const *b) { | 
| 1222 |  |  |  |  |  |  | return ((i_dists *)a)->dist - ((i_dists *)b)->dist; | 
| 1223 |  |  |  |  |  |  | } | 
| 1224 |  |  |  |  |  |  |  | 
| 1225 |  |  |  |  |  |  | static void rand2dist_setup(i_quantize *quant, i_dists **cdists) { | 
| 1226 |  |  |  |  |  |  | i_dists *dists = | 
| 1227 |  |  |  |  |  |  | mymalloc(sizeof(i_dists)*quant->mc_count*quant->mc_count); | 
| 1228 |  |  |  |  |  |  | int i, j; | 
| 1229 |  |  |  |  |  |  | long cd; | 
| 1230 |  |  |  |  |  |  | for (i = 0; i < quant->mc_count; ++i) { | 
| 1231 |  |  |  |  |  |  | i_dists *ldists = dists + quant->mc_count * i; | 
| 1232 |  |  |  |  |  |  | i_color val = quant->mc_colors[i]; | 
| 1233 |  |  |  |  |  |  | for (j = 0; j < quant->mc_count; ++j) { | 
| 1234 |  |  |  |  |  |  | ldists[j].index = j; | 
| 1235 |  |  |  |  |  |  | ldists[j].dist = ceucl_d(&val, quant->mc_colors+j); | 
| 1236 |  |  |  |  |  |  | } | 
| 1237 |  |  |  |  |  |  | qsort(ldists, quant->mc_count, sizeof(i_dists), dists_sort); | 
| 1238 |  |  |  |  |  |  | } | 
| 1239 |  |  |  |  |  |  | *cdists = dists; | 
| 1240 |  |  |  |  |  |  | } | 
| 1241 |  |  |  |  |  |  |  | 
| 1242 |  |  |  |  |  |  | #define CF_SETUP \ | 
| 1243 |  |  |  |  |  |  | bst_idx = rand() % quant->mc_count; \ | 
| 1244 |  |  |  |  |  |  | rand2dist_setup(quant, &dists) | 
| 1245 |  |  |  |  |  |  |  | 
| 1246 |  |  |  |  |  |  | static int rand2dist_find(i_color val, i_quantize *quant, i_dists *dists, int index) { | 
| 1247 |  |  |  |  |  |  | i_dists *cdists; | 
| 1248 |  |  |  |  |  |  | long cd, ld; | 
| 1249 |  |  |  |  |  |  | long maxld; | 
| 1250 |  |  |  |  |  |  | int i; | 
| 1251 |  |  |  |  |  |  | int bst_idx; | 
| 1252 |  |  |  |  |  |  |  | 
| 1253 |  |  |  |  |  |  | cdists = dists + index * quant->mc_count; | 
| 1254 |  |  |  |  |  |  | ld = 3 * 256 * 256; | 
| 1255 |  |  |  |  |  |  | maxld = 8 * ceucl_d(&val, quant->mc_colors+index); | 
| 1256 |  |  |  |  |  |  | for (i = 0; i < quant->mc_count && cdists[i].dist <= maxld; ++i) { | 
| 1257 |  |  |  |  |  |  | cd = ceucl_d(&val, quant->mc_colors+cdists[i].index); | 
| 1258 |  |  |  |  |  |  | if (cd < ld) { | 
| 1259 |  |  |  |  |  |  | bst_idx = cdists[i].index; | 
| 1260 |  |  |  |  |  |  | ld = cd; | 
| 1261 |  |  |  |  |  |  | } | 
| 1262 |  |  |  |  |  |  | } | 
| 1263 |  |  |  |  |  |  | return bst_idx; | 
| 1264 |  |  |  |  |  |  | } | 
| 1265 |  |  |  |  |  |  |  | 
| 1266 |  |  |  |  |  |  | #define CF_FIND bst_idx = rand2dist_find(val, quant, dists, bst_idx) | 
| 1267 |  |  |  |  |  |  |  | 
| 1268 |  |  |  |  |  |  | #define CF_CLEANUP myfree(dists) | 
| 1269 |  |  |  |  |  |  |  | 
| 1270 |  |  |  |  |  |  |  | 
| 1271 |  |  |  |  |  |  | #endif | 
| 1272 |  |  |  |  |  |  |  | 
| 1273 | 12 |  |  |  |  |  | static void translate_addi(i_quantize *quant, i_img *img, i_palidx *out) { | 
| 1274 |  |  |  |  |  |  | i_img_dim x, y, k; | 
| 1275 | 12 |  |  |  |  |  | int i, bst_idx = 0; | 
| 1276 |  |  |  |  |  |  | i_color val; | 
| 1277 | 12 |  |  |  |  |  | int pixdev = quant->perturb; | 
| 1278 | 12 |  |  |  |  |  | CF_VARS; | 
| 1279 |  |  |  |  |  |  |  | 
| 1280 | 12 |  |  |  |  |  | CF_SETUP; | 
| 1281 |  |  |  |  |  |  |  | 
| 1282 | 12 | 50 |  |  |  |  | if (img->channels >= 3) { | 
| 1283 | 12 | 50 |  |  |  |  | if (pixdev) { | 
| 1284 | 0 |  |  |  |  |  | k=0; | 
| 1285 | 0 | 0 |  |  |  |  | for(y=0;yysize;y++) for(x=0;xxsize;x++) { | 
|  |  | 0 |  |  |  |  |  | 
| 1286 | 0 |  |  |  |  |  | i_gpix(img,x,y,&val); | 
| 1287 | 0 |  |  |  |  |  | val.channel[0]=g_sat(val.channel[0]+(int)(pixdev*frandn())); | 
| 1288 | 0 |  |  |  |  |  | val.channel[1]=g_sat(val.channel[1]+(int)(pixdev*frandn())); | 
| 1289 | 0 |  |  |  |  |  | val.channel[2]=g_sat(val.channel[2]+(int)(pixdev*frandn())); | 
| 1290 | 0 | 0 |  |  |  |  | CF_FIND; | 
|  |  | 0 |  |  |  |  |  | 
| 1291 | 0 |  |  |  |  |  | out[k++]=bst_idx; | 
| 1292 |  |  |  |  |  |  | } | 
| 1293 |  |  |  |  |  |  | } else { | 
| 1294 | 12 |  |  |  |  |  | k=0; | 
| 1295 | 199445 | 100 |  |  |  |  | for(y=0;yysize;y++) for(x=0;xxsize;x++) { | 
|  |  | 100 |  |  |  |  |  | 
| 1296 | 198074 |  |  |  |  |  | i_gpix(img,x,y,&val); | 
| 1297 | 1357746 | 100 |  |  |  |  | CF_FIND; | 
|  |  | 100 |  |  |  |  |  | 
| 1298 | 198074 |  |  |  |  |  | out[k++]=bst_idx; | 
| 1299 |  |  |  |  |  |  | } | 
| 1300 |  |  |  |  |  |  | } | 
| 1301 |  |  |  |  |  |  | } | 
| 1302 |  |  |  |  |  |  | else { | 
| 1303 | 0 | 0 |  |  |  |  | if (pixdev) { | 
| 1304 | 0 |  |  |  |  |  | k=0; | 
| 1305 | 0 | 0 |  |  |  |  | for(y=0;yysize;y++) for(x=0;xxsize;x++) { | 
|  |  | 0 |  |  |  |  |  | 
| 1306 | 0 |  |  |  |  |  | i_gpix(img,x,y,&val); | 
| 1307 | 0 |  |  |  |  |  | val.channel[1] = val.channel[2] = | 
| 1308 | 0 |  |  |  |  |  | val.channel[0]=g_sat(val.channel[0]+(int)(pixdev*frandn())); | 
| 1309 | 0 | 0 |  |  |  |  | CF_FIND; | 
|  |  | 0 |  |  |  |  |  | 
| 1310 | 0 |  |  |  |  |  | out[k++]=bst_idx; | 
| 1311 |  |  |  |  |  |  | } | 
| 1312 |  |  |  |  |  |  | } else { | 
| 1313 | 0 |  |  |  |  |  | k=0; | 
| 1314 | 0 | 0 |  |  |  |  | for(y=0;yysize;y++) for(x=0;xxsize;x++) { | 
|  |  | 0 |  |  |  |  |  | 
| 1315 | 0 |  |  |  |  |  | i_gpix(img,x,y,&val); | 
| 1316 | 0 |  |  |  |  |  | val.channel[1] = val.channel[2] = val.channel[0]; | 
| 1317 | 0 | 0 |  |  |  |  | CF_FIND; | 
|  |  | 0 |  |  |  |  |  | 
| 1318 | 0 |  |  |  |  |  | out[k++]=bst_idx; | 
| 1319 |  |  |  |  |  |  | } | 
| 1320 |  |  |  |  |  |  | } | 
| 1321 |  |  |  |  |  |  | } | 
| 1322 | 12 |  |  |  |  |  | CF_CLEANUP; | 
| 1323 | 12 |  |  |  |  |  | } | 
| 1324 |  |  |  |  |  |  |  | 
| 1325 |  |  |  |  |  |  | static int floyd_map[] = | 
| 1326 |  |  |  |  |  |  | { | 
| 1327 |  |  |  |  |  |  | 0, 0, 7, | 
| 1328 |  |  |  |  |  |  | 3, 5, 1 | 
| 1329 |  |  |  |  |  |  | }; | 
| 1330 |  |  |  |  |  |  |  | 
| 1331 |  |  |  |  |  |  | static int jarvis_map[] = | 
| 1332 |  |  |  |  |  |  | { | 
| 1333 |  |  |  |  |  |  | 0, 0, 0, 7, 5, | 
| 1334 |  |  |  |  |  |  | 3, 5, 7, 5, 3, | 
| 1335 |  |  |  |  |  |  | 1, 3, 5, 3, 1 | 
| 1336 |  |  |  |  |  |  | }; | 
| 1337 |  |  |  |  |  |  |  | 
| 1338 |  |  |  |  |  |  | static int stucki_map[] = | 
| 1339 |  |  |  |  |  |  | { | 
| 1340 |  |  |  |  |  |  | 0, 0, 0, 8, 4, | 
| 1341 |  |  |  |  |  |  | 2, 4, 8, 4, 2, | 
| 1342 |  |  |  |  |  |  | 1, 2, 4, 2, 1 | 
| 1343 |  |  |  |  |  |  | }; | 
| 1344 |  |  |  |  |  |  |  | 
| 1345 |  |  |  |  |  |  | struct errdiff_map { | 
| 1346 |  |  |  |  |  |  | int *map; | 
| 1347 |  |  |  |  |  |  | int width, height, orig; | 
| 1348 |  |  |  |  |  |  | }; | 
| 1349 |  |  |  |  |  |  |  | 
| 1350 |  |  |  |  |  |  | static struct errdiff_map maps[] = | 
| 1351 |  |  |  |  |  |  | { | 
| 1352 |  |  |  |  |  |  | { floyd_map, 3, 2, 1 }, | 
| 1353 |  |  |  |  |  |  | { jarvis_map, 5, 3, 2 }, | 
| 1354 |  |  |  |  |  |  | { stucki_map, 5, 3, 2 }, | 
| 1355 |  |  |  |  |  |  | }; | 
| 1356 |  |  |  |  |  |  |  | 
| 1357 |  |  |  |  |  |  | typedef struct errdiff_tag { | 
| 1358 |  |  |  |  |  |  | int r, g, b; | 
| 1359 |  |  |  |  |  |  | } errdiff_t; | 
| 1360 |  |  |  |  |  |  |  | 
| 1361 |  |  |  |  |  |  | /* perform an error diffusion dither */ | 
| 1362 |  |  |  |  |  |  | static int | 
| 1363 | 5 |  |  |  |  |  | translate_errdiff(i_quantize *quant, i_img *img, i_palidx *out) { | 
| 1364 |  |  |  |  |  |  | int *map; | 
| 1365 |  |  |  |  |  |  | int mapw, maph, mapo; | 
| 1366 |  |  |  |  |  |  | int i; | 
| 1367 |  |  |  |  |  |  | errdiff_t *err; | 
| 1368 |  |  |  |  |  |  | i_img_dim errw; | 
| 1369 |  |  |  |  |  |  | int difftotal; | 
| 1370 |  |  |  |  |  |  | i_img_dim x, y, dx, dy; | 
| 1371 | 5 |  |  |  |  |  | int bst_idx = 0; | 
| 1372 | 5 |  |  |  |  |  | int is_gray = is_gray_map(quant); | 
| 1373 | 5 |  |  |  |  |  | CF_VARS; | 
| 1374 |  |  |  |  |  |  |  | 
| 1375 | 5 | 50 |  |  |  |  | if ((quant->errdiff & ed_mask) == ed_custom) { | 
| 1376 | 0 |  |  |  |  |  | map = quant->ed_map; | 
| 1377 | 0 |  |  |  |  |  | mapw = quant->ed_width; | 
| 1378 | 0 |  |  |  |  |  | maph = quant->ed_height; | 
| 1379 | 0 |  |  |  |  |  | mapo = quant->ed_orig; | 
| 1380 |  |  |  |  |  |  | } | 
| 1381 |  |  |  |  |  |  | else { | 
| 1382 | 5 |  |  |  |  |  | int index = quant->errdiff & ed_mask; | 
| 1383 | 5 | 50 |  |  |  |  | if (index >= ed_custom) index = ed_floyd; | 
| 1384 | 5 |  |  |  |  |  | map = maps[index].map; | 
| 1385 | 5 |  |  |  |  |  | mapw = maps[index].width; | 
| 1386 | 5 |  |  |  |  |  | maph = maps[index].height; | 
| 1387 | 5 |  |  |  |  |  | mapo = maps[index].orig; | 
| 1388 |  |  |  |  |  |  | } | 
| 1389 |  |  |  |  |  |  |  | 
| 1390 | 5 |  |  |  |  |  | difftotal = 0; | 
| 1391 | 35 | 100 |  |  |  |  | for (i = 0; i < maph * mapw; ++i) { | 
| 1392 | 30 | 50 |  |  |  |  | if (map[i] < 0) { | 
| 1393 | 0 |  |  |  |  |  | i_push_errorf(0, "errdiff_map values must be non-negative, errdiff[%d] is negative", i); | 
| 1394 | 0 |  |  |  |  |  | goto fail; | 
| 1395 |  |  |  |  |  |  | } | 
| 1396 | 30 |  |  |  |  |  | difftotal += map[i]; | 
| 1397 |  |  |  |  |  |  | } | 
| 1398 |  |  |  |  |  |  |  | 
| 1399 | 5 | 50 |  |  |  |  | if (!difftotal) { | 
| 1400 | 0 |  |  |  |  |  | i_push_error(0, "error diffusion map must contain some non-zero values"); | 
| 1401 | 0 |  |  |  |  |  | goto fail; | 
| 1402 |  |  |  |  |  |  | } | 
| 1403 |  |  |  |  |  |  |  | 
| 1404 | 5 |  |  |  |  |  | errw = img->xsize+mapw; | 
| 1405 | 5 |  |  |  |  |  | err = mymalloc(sizeof(*err) * maph * errw); | 
| 1406 |  |  |  |  |  |  | /*errp = err+mapo;*/ | 
| 1407 | 5 |  |  |  |  |  | memset(err, 0, sizeof(*err) * maph * errw); | 
| 1408 |  |  |  |  |  |  |  | 
| 1409 |  |  |  |  |  |  | /*printf("map:\n"); | 
| 1410 |  |  |  |  |  |  | for (dy = 0; dy < maph; ++dy) { | 
| 1411 |  |  |  |  |  |  | for (dx = 0; dx < mapw; ++dx) { | 
| 1412 |  |  |  |  |  |  | printf("%2d", map[dx+dy*mapw]); | 
| 1413 |  |  |  |  |  |  | } | 
| 1414 |  |  |  |  |  |  | putchar('\n'); | 
| 1415 |  |  |  |  |  |  | }*/ | 
| 1416 |  |  |  |  |  |  |  | 
| 1417 | 5 |  |  |  |  |  | CF_SETUP; | 
| 1418 |  |  |  |  |  |  |  | 
| 1419 | 615 | 100 |  |  |  |  | for (y = 0; y < img->ysize; ++y) { | 
| 1420 | 90710 | 100 |  |  |  |  | for (x = 0; x < img->xsize; ++x) { | 
| 1421 |  |  |  |  |  |  | i_color val; | 
| 1422 |  |  |  |  |  |  | errdiff_t perr; | 
| 1423 | 90100 |  |  |  |  |  | i_gpix(img, x, y, &val); | 
| 1424 | 90100 | 50 |  |  |  |  | if (img->channels < 3) { | 
| 1425 | 0 |  |  |  |  |  | val.channel[1] = val.channel[2] = val.channel[0]; | 
| 1426 |  |  |  |  |  |  | } | 
| 1427 | 90100 | 100 |  |  |  |  | else if (is_gray) { | 
| 1428 | 100 |  |  |  |  |  | int gray = 0.5 + color_to_grey(&val); | 
| 1429 | 100 |  |  |  |  |  | val.channel[0] = val.channel[1] = val.channel[2] = gray; | 
| 1430 |  |  |  |  |  |  | } | 
| 1431 | 90100 |  |  |  |  |  | perr = err[x+mapo]; | 
| 1432 | 90100 | 100 |  |  |  |  | perr.r = perr.r < 0 ? -((-perr.r)/difftotal) : perr.r/difftotal; | 
| 1433 | 90100 | 100 |  |  |  |  | perr.g = perr.g < 0 ? -((-perr.g)/difftotal) : perr.g/difftotal; | 
| 1434 | 90100 | 100 |  |  |  |  | perr.b = perr.b < 0 ? -((-perr.b)/difftotal) : perr.b/difftotal; | 
| 1435 |  |  |  |  |  |  | /*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);*/ | 
| 1436 | 90100 |  |  |  |  |  | val.channel[0] = g_sat(val.channel[0]-perr.r); | 
| 1437 | 90100 |  |  |  |  |  | val.channel[1] = g_sat(val.channel[1]-perr.g); | 
| 1438 | 90100 |  |  |  |  |  | val.channel[2] = g_sat(val.channel[2]-perr.b); | 
| 1439 | 469450 | 100 |  |  |  |  | CF_FIND; | 
|  |  | 100 |  |  |  |  |  | 
| 1440 |  |  |  |  |  |  | /* save error */ | 
| 1441 | 90100 |  |  |  |  |  | perr.r = quant->mc_colors[bst_idx].channel[0] - val.channel[0]; | 
| 1442 | 90100 |  |  |  |  |  | perr.g = quant->mc_colors[bst_idx].channel[1] - val.channel[1]; | 
| 1443 | 90100 |  |  |  |  |  | perr.b = quant->mc_colors[bst_idx].channel[2] - val.channel[2]; | 
| 1444 |  |  |  |  |  |  | /*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);*/ | 
| 1445 | 360400 | 100 |  |  |  |  | for (dx = 0; dx < mapw; ++dx) { | 
| 1446 | 810900 | 100 |  |  |  |  | for (dy = 0; dy < maph; ++dy) { | 
| 1447 | 540600 |  |  |  |  |  | err[x+dx+dy*errw].r += perr.r * map[dx+mapw*dy]; | 
| 1448 | 540600 |  |  |  |  |  | err[x+dx+dy*errw].g += perr.g * map[dx+mapw*dy]; | 
| 1449 | 540600 |  |  |  |  |  | err[x+dx+dy*errw].b += perr.b * map[dx+mapw*dy]; | 
| 1450 |  |  |  |  |  |  | } | 
| 1451 |  |  |  |  |  |  | } | 
| 1452 | 90100 |  |  |  |  |  | *out++ = bst_idx; | 
| 1453 |  |  |  |  |  |  | } | 
| 1454 |  |  |  |  |  |  | /* shift up the error matrix */ | 
| 1455 | 1220 | 100 |  |  |  |  | for (dy = 0; dy < maph-1; ++dy) { | 
| 1456 | 610 |  |  |  |  |  | memcpy(err+dy*errw, err+(dy+1)*errw, sizeof(*err)*errw); | 
| 1457 |  |  |  |  |  |  | } | 
| 1458 | 610 |  |  |  |  |  | memset(err+(maph-1)*errw, 0, sizeof(*err)*errw); | 
| 1459 |  |  |  |  |  |  | } | 
| 1460 | 5 |  |  |  |  |  | CF_CLEANUP; | 
| 1461 | 5 |  |  |  |  |  | myfree(err); | 
| 1462 |  |  |  |  |  |  |  | 
| 1463 | 5 |  |  |  |  |  | return 1; | 
| 1464 |  |  |  |  |  |  |  | 
| 1465 |  |  |  |  |  |  | fail: | 
| 1466 | 0 |  |  |  |  |  | CF_CLEANUP; | 
| 1467 |  |  |  |  |  |  |  | 
| 1468 | 0 |  |  |  |  |  | return 0; | 
| 1469 |  |  |  |  |  |  | } | 
| 1470 |  |  |  |  |  |  | /* Prescan finds the boxes in the image that have the highest number of colors | 
| 1471 |  |  |  |  |  |  | and that result is used as the initial value for the vectores */ | 
| 1472 |  |  |  |  |  |  |  | 
| 1473 |  |  |  |  |  |  |  | 
| 1474 | 0 |  |  |  |  |  | static void prescan(i_img **imgs,int count, int cnum, cvec *clr, i_sample_t *line) { | 
| 1475 |  |  |  |  |  |  | int i,k,j; | 
| 1476 |  |  |  |  |  |  | i_img_dim x,y; | 
| 1477 |  |  |  |  |  |  | i_sample_t *val; | 
| 1478 |  |  |  |  |  |  | const int *chans; | 
| 1479 |  |  |  |  |  |  |  | 
| 1480 |  |  |  |  |  |  | pbox prebox[512]; | 
| 1481 | 0 | 0 |  |  |  |  | for(i=0;i<512;i++) { | 
| 1482 | 0 |  |  |  |  |  | prebox[i].boxnum=i; | 
| 1483 | 0 |  |  |  |  |  | prebox[i].pixcnt=0; | 
| 1484 | 0 |  |  |  |  |  | prebox[i].cand=1; | 
| 1485 |  |  |  |  |  |  | } | 
| 1486 |  |  |  |  |  |  |  | 
| 1487 |  |  |  |  |  |  | /* process each image */ | 
| 1488 | 0 | 0 |  |  |  |  | for (i = 0; i < count; ++i) { | 
| 1489 | 0 |  |  |  |  |  | i_img *im = imgs[i]; | 
| 1490 | 0 | 0 |  |  |  |  | chans = im->channels >= 3 ? NULL : gray_samples; | 
| 1491 | 0 | 0 |  |  |  |  | for(y=0;yysize;y++) { | 
| 1492 | 0 |  |  |  |  |  | i_gsamp(im, 0, im->xsize, y, line, chans, 3); | 
| 1493 | 0 |  |  |  |  |  | val = line; | 
| 1494 | 0 | 0 |  |  |  |  | for(x=0;xxsize;x++) { | 
| 1495 | 0 |  |  |  |  |  | prebox[pixbox_ch(val)].pixcnt++; | 
| 1496 |  |  |  |  |  |  | } | 
| 1497 |  |  |  |  |  |  | } | 
| 1498 |  |  |  |  |  |  | } | 
| 1499 |  |  |  |  |  |  |  | 
| 1500 | 0 | 0 |  |  |  |  | for(i=0;i<512;i++) prebox[i].pdc=prebox[i].pixcnt; | 
| 1501 | 0 |  |  |  |  |  | qsort(prebox,512,sizeof(pbox),(cmpfunc)pboxcmp); | 
| 1502 |  |  |  |  |  |  |  | 
| 1503 | 0 | 0 |  |  |  |  | for(i=0;i | 
| 1504 |  |  |  |  |  |  | /*      printf("Color %d\n",i); | 
| 1505 |  |  |  |  |  |  | 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); } | 
| 1506 |  |  |  |  |  |  | printf("\n\n"); */ | 
| 1507 | 0 |  |  |  |  |  | reorder(prebox); | 
| 1508 |  |  |  |  |  |  | } | 
| 1509 |  |  |  |  |  |  |  | 
| 1510 |  |  |  |  |  |  | /*    for(k=0;k | 
| 1511 |  |  |  |  |  |  |  | 
| 1512 | 0 |  |  |  |  |  | k=0; | 
| 1513 | 0 |  |  |  |  |  | j=1; | 
| 1514 | 0 |  |  |  |  |  | i=0; | 
| 1515 | 0 | 0 |  |  |  |  | while(i | 
| 1516 |  |  |  |  |  |  | /*    printf("prebox[%d].cand=%d\n",k,prebox[k].cand); */ | 
| 1517 | 0 | 0 |  |  |  |  | if (clr[i].fixed) { i++; continue; } /* reserved go to next */ | 
| 1518 | 0 | 0 |  |  |  |  | if (j>=prebox[k].cand) { k++; j=1; } else { | 
| 1519 | 0 | 0 |  |  |  |  | if (prebox[k].cand == 2) boxcenter(prebox[k].boxnum,&(clr[i])); | 
| 1520 | 0 |  |  |  |  |  | else boxrand(prebox[k].boxnum,&(clr[i])); | 
| 1521 |  |  |  |  |  |  | /*      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); */ | 
| 1522 | 0 |  |  |  |  |  | j++; | 
| 1523 | 0 |  |  |  |  |  | i++; | 
| 1524 |  |  |  |  |  |  | } | 
| 1525 |  |  |  |  |  |  | } | 
| 1526 | 0 |  |  |  |  |  | } | 
| 1527 |  |  |  |  |  |  |  | 
| 1528 |  |  |  |  |  |  |  | 
| 1529 | 0 |  |  |  |  |  | static void reorder(pbox prescan[512]) { | 
| 1530 |  |  |  |  |  |  | int nidx; | 
| 1531 |  |  |  |  |  |  | pbox c; | 
| 1532 |  |  |  |  |  |  |  | 
| 1533 | 0 |  |  |  |  |  | nidx=0; | 
| 1534 | 0 |  |  |  |  |  | c=prescan[0]; | 
| 1535 |  |  |  |  |  |  |  | 
| 1536 | 0 |  |  |  |  |  | c.cand++; | 
| 1537 | 0 |  |  |  |  |  | c.pdc=c.pixcnt/(c.cand*c.cand); | 
| 1538 |  |  |  |  |  |  | /*  c.pdc=c.pixcnt/c.cand; */ | 
| 1539 | 0 | 0 |  |  |  |  | while(nidx < 511 && c.pdc < prescan[nidx+1].pdc) { | 
|  |  | 0 |  |  |  |  |  | 
| 1540 | 0 |  |  |  |  |  | prescan[nidx]=prescan[nidx+1]; | 
| 1541 | 0 |  |  |  |  |  | nidx++; | 
| 1542 |  |  |  |  |  |  | } | 
| 1543 | 0 |  |  |  |  |  | prescan[nidx]=c; | 
| 1544 | 0 |  |  |  |  |  | } | 
| 1545 |  |  |  |  |  |  |  | 
| 1546 |  |  |  |  |  |  | static int | 
| 1547 | 0 |  |  |  |  |  | pboxcmp(const pbox *a,const pbox *b) { | 
| 1548 | 0 | 0 |  |  |  |  | if (a->pixcnt > b->pixcnt) return -1; | 
| 1549 | 0 | 0 |  |  |  |  | if (a->pixcnt < b->pixcnt) return 1; | 
| 1550 | 0 |  |  |  |  |  | return 0; | 
| 1551 |  |  |  |  |  |  | } | 
| 1552 |  |  |  |  |  |  |  | 
| 1553 |  |  |  |  |  |  | static void | 
| 1554 | 0 |  |  |  |  |  | boxcenter(int box,cvec *cv) { | 
| 1555 | 0 |  |  |  |  |  | cv->r=15+((box&448)>>1); | 
| 1556 | 0 |  |  |  |  |  | cv->g=15+((box&56)<<2); | 
| 1557 | 0 |  |  |  |  |  | cv->b=15+((box&7)<<5); | 
| 1558 | 0 |  |  |  |  |  | } | 
| 1559 |  |  |  |  |  |  |  | 
| 1560 |  |  |  |  |  |  | static void | 
| 1561 | 0 |  |  |  |  |  | bbox(int box,int *r0,int *r1,int *g0,int *g1,int *b0,int *b1) { | 
| 1562 | 0 |  |  |  |  |  | *r0=(box&448)>>1; | 
| 1563 | 0 |  |  |  |  |  | *r1=(*r0)|31; | 
| 1564 | 0 |  |  |  |  |  | *g0=(box&56)<<2; | 
| 1565 | 0 |  |  |  |  |  | *g1=(*g0)|31; | 
| 1566 | 0 |  |  |  |  |  | *b0=(box&7)<<5; | 
| 1567 | 0 |  |  |  |  |  | *b1=(*b0)|31; | 
| 1568 | 0 |  |  |  |  |  | } | 
| 1569 |  |  |  |  |  |  |  | 
| 1570 |  |  |  |  |  |  | static void | 
| 1571 | 0 |  |  |  |  |  | boxrand(int box,cvec *cv) { | 
| 1572 | 0 |  |  |  |  |  | cv->r=6+(rand()%25)+((box&448)>>1); | 
| 1573 | 0 |  |  |  |  |  | cv->g=6+(rand()%25)+((box&56)<<2); | 
| 1574 | 0 |  |  |  |  |  | cv->b=6+(rand()%25)+((box&7)<<5); | 
| 1575 | 0 |  |  |  |  |  | } | 
| 1576 |  |  |  |  |  |  |  | 
| 1577 |  |  |  |  |  |  | static float | 
| 1578 | 0 |  |  |  |  |  | frandn(void) { | 
| 1579 |  |  |  |  |  |  |  | 
| 1580 |  |  |  |  |  |  | float u1,u2,w; | 
| 1581 |  |  |  |  |  |  |  | 
| 1582 | 0 |  |  |  |  |  | w=1; | 
| 1583 |  |  |  |  |  |  |  | 
| 1584 | 0 | 0 |  |  |  |  | while (w >= 1 || w == 0) { | 
|  |  | 0 |  |  |  |  |  | 
| 1585 | 0 |  |  |  |  |  | u1 = 2 * frand() - 1; | 
| 1586 | 0 |  |  |  |  |  | u2 = 2 * frand() - 1; | 
| 1587 | 0 |  |  |  |  |  | w = u1*u1 + u2*u2; | 
| 1588 |  |  |  |  |  |  | } | 
| 1589 |  |  |  |  |  |  |  | 
| 1590 | 0 |  |  |  |  |  | w = sqrt((-2*log(w))/w); | 
| 1591 | 0 |  |  |  |  |  | return u1*w; | 
| 1592 |  |  |  |  |  |  | } | 
| 1593 |  |  |  |  |  |  |  | 
| 1594 |  |  |  |  |  |  | /* Create hash index */ | 
| 1595 |  |  |  |  |  |  | static | 
| 1596 |  |  |  |  |  |  | void | 
| 1597 | 0 |  |  |  |  |  | cr_hashindex(cvec clr[256],int cnum,hashbox hb[512]) { | 
| 1598 |  |  |  |  |  |  |  | 
| 1599 |  |  |  |  |  |  | int bx,mind,cd,cumcnt,i; | 
| 1600 |  |  |  |  |  |  | /*  printf("indexing... \n");*/ | 
| 1601 |  |  |  |  |  |  |  | 
| 1602 | 0 |  |  |  |  |  | cumcnt=0; | 
| 1603 | 0 | 0 |  |  |  |  | for(bx=0; bx<512; bx++) { | 
| 1604 | 0 |  |  |  |  |  | mind=196608; | 
| 1605 | 0 | 0 |  |  |  |  | for(i=0; i | 
| 1606 | 0 |  |  |  |  |  | cd = maxdist(bx,&clr[i]); | 
| 1607 | 0 | 0 |  |  |  |  | if (cd < mind) { mind=cd; } | 
| 1608 |  |  |  |  |  |  | } | 
| 1609 |  |  |  |  |  |  |  | 
| 1610 | 0 |  |  |  |  |  | hb[bx].cnt=0; | 
| 1611 | 0 | 0 |  |  |  |  | for(i=0;i | 
|  |  | 0 |  |  |  |  |  | 
| 1612 |  |  |  |  |  |  | /*printf("box %d -> approx -> %d\n",bx,hb[bx].cnt); */ | 
| 1613 |  |  |  |  |  |  | /*	statbox(bx,cnum,clr); */ | 
| 1614 | 0 |  |  |  |  |  | cumcnt+=hb[bx].cnt; | 
| 1615 |  |  |  |  |  |  | } | 
| 1616 |  |  |  |  |  |  |  | 
| 1617 |  |  |  |  |  |  | /*  printf("Average search space: %d\n",cumcnt/512); */ | 
| 1618 | 0 |  |  |  |  |  | } | 
| 1619 |  |  |  |  |  |  |  | 
| 1620 |  |  |  |  |  |  | static int | 
| 1621 | 0 |  |  |  |  |  | maxdist(int boxnum,cvec *cv) { | 
| 1622 |  |  |  |  |  |  | int r0,r1,g0,g1,b0,b1; | 
| 1623 |  |  |  |  |  |  | int r,g,b,mr,mg,mb; | 
| 1624 |  |  |  |  |  |  |  | 
| 1625 | 0 |  |  |  |  |  | r=cv->r; | 
| 1626 | 0 |  |  |  |  |  | g=cv->g; | 
| 1627 | 0 |  |  |  |  |  | b=cv->b; | 
| 1628 |  |  |  |  |  |  |  | 
| 1629 | 0 |  |  |  |  |  | bbox(boxnum,&r0,&r1,&g0,&g1,&b0,&b1); | 
| 1630 |  |  |  |  |  |  |  | 
| 1631 | 0 |  |  |  |  |  | mr=i_max(abs(b-b0),abs(b-b1)); | 
| 1632 | 0 |  |  |  |  |  | mg=i_max(abs(g-g0),abs(g-g1)); | 
| 1633 | 0 |  |  |  |  |  | mb=i_max(abs(r-r0),abs(r-r1)); | 
| 1634 |  |  |  |  |  |  |  | 
| 1635 | 0 |  |  |  |  |  | return PWR2(mr)+PWR2(mg)+PWR2(mb); | 
| 1636 |  |  |  |  |  |  | } | 
| 1637 |  |  |  |  |  |  |  | 
| 1638 |  |  |  |  |  |  | static int | 
| 1639 | 0 |  |  |  |  |  | mindist(int boxnum,cvec *cv) { | 
| 1640 |  |  |  |  |  |  | int r0,r1,g0,g1,b0,b1; | 
| 1641 |  |  |  |  |  |  | int r,g,b,mr,mg,mb; | 
| 1642 |  |  |  |  |  |  |  | 
| 1643 | 0 |  |  |  |  |  | r=cv->r; | 
| 1644 | 0 |  |  |  |  |  | g=cv->g; | 
| 1645 | 0 |  |  |  |  |  | b=cv->b; | 
| 1646 |  |  |  |  |  |  |  | 
| 1647 | 0 |  |  |  |  |  | bbox(boxnum,&r0,&r1,&g0,&g1,&b0,&b1); | 
| 1648 |  |  |  |  |  |  |  | 
| 1649 |  |  |  |  |  |  | /*  printf("box %d, (%d,%d,%d)-(%d,%d,%d) vec (%d,%d,%d) ",boxnum,r0,g0,b0,r1,g1,b1,r,g,b); */ | 
| 1650 |  |  |  |  |  |  |  | 
| 1651 | 0 | 0 |  |  |  |  | if (r0<=r && r<=r1 && g0<=g && g<=g1 && b0<=b && b<=b1) return 0; | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 1652 |  |  |  |  |  |  |  | 
| 1653 | 0 |  |  |  |  |  | mr=i_min(abs(b-b0),abs(b-b1)); | 
| 1654 | 0 |  |  |  |  |  | mg=i_min(abs(g-g0),abs(g-g1)); | 
| 1655 | 0 |  |  |  |  |  | mb=i_min(abs(r-r0),abs(r-r1)); | 
| 1656 |  |  |  |  |  |  |  | 
| 1657 | 0 |  |  |  |  |  | mr=PWR2(mr); | 
| 1658 | 0 |  |  |  |  |  | mg=PWR2(mg); | 
| 1659 | 0 |  |  |  |  |  | mb=PWR2(mb); | 
| 1660 |  |  |  |  |  |  |  | 
| 1661 | 0 | 0 |  |  |  |  | if (r0<=r && r<=r1 && g0<=g && g<=g1) return mb; | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 1662 | 0 | 0 |  |  |  |  | if (r0<=r && r<=r1 && b0<=b && b<=b1) return mg; | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 1663 | 0 | 0 |  |  |  |  | if (b0<=b && b<=b1 && g0<=g && g<=g1) return mr; | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 1664 |  |  |  |  |  |  |  | 
| 1665 | 0 | 0 |  |  |  |  | if (r0<=r && r<=r1) return mg+mb; | 
|  |  | 0 |  |  |  |  |  | 
| 1666 | 0 | 0 |  |  |  |  | if (g0<=g && g<=g1) return mr+mb; | 
|  |  | 0 |  |  |  |  |  | 
| 1667 | 0 | 0 |  |  |  |  | if (b0<=b && b<=b1) return mg+mr; | 
|  |  | 0 |  |  |  |  |  | 
| 1668 |  |  |  |  |  |  |  | 
| 1669 | 0 |  |  |  |  |  | return mr+mg+mb; | 
| 1670 |  |  |  |  |  |  | } | 
| 1671 |  |  |  |  |  |  |  | 
| 1672 |  |  |  |  |  |  | static void transparent_threshold(i_quantize *, i_palidx *, i_img *, i_palidx); | 
| 1673 |  |  |  |  |  |  | static void transparent_errdiff(i_quantize *, i_palidx *, i_img *, i_palidx); | 
| 1674 |  |  |  |  |  |  | static void transparent_ordered(i_quantize *, i_palidx *, i_img *, i_palidx); | 
| 1675 |  |  |  |  |  |  |  | 
| 1676 |  |  |  |  |  |  | /* | 
| 1677 |  |  |  |  |  |  | =item i_quant_transparent(C, C, C ![]() , C) | 
| 1678 |  |  |  |  |  |  |  | 
| 1679 |  |  |  |  |  |  | =category Image quantization | 
| 1680 |  |  |  |  |  |  |  | 
| 1681 |  |  |  |  |  |  | Dither the alpha channel on C ![]() into the palette indexes in | 
| 1682 |  |  |  |  |  |  | C.  Pixels to be transparent are replaced with C. | 
| 1683 |  |  |  |  |  |  |  | 
| 1684 |  |  |  |  |  |  | The method used depends on the tr_* members of C. | 
| 1685 |  |  |  |  |  |  |  | 
| 1686 |  |  |  |  |  |  | =cut | 
| 1687 |  |  |  |  |  |  | */ | 
| 1688 |  |  |  |  |  |  |  | 
| 1689 |  |  |  |  |  |  | void | 
| 1690 | 0 |  |  |  |  |  | i_quant_transparent(i_quantize *quant, i_palidx *data, i_img *img, | 
| 1691 |  |  |  |  |  |  | i_palidx trans_index) | 
| 1692 |  |  |  |  |  |  | { | 
| 1693 | 0 |  |  |  |  |  | switch (quant->transp) { | 
| 1694 |  |  |  |  |  |  | case tr_none: | 
| 1695 | 0 |  |  |  |  |  | break; | 
| 1696 |  |  |  |  |  |  |  | 
| 1697 |  |  |  |  |  |  | default: | 
| 1698 | 0 |  |  |  |  |  | quant->tr_threshold = 128; | 
| 1699 |  |  |  |  |  |  | /* fall through */ | 
| 1700 |  |  |  |  |  |  | case tr_threshold: | 
| 1701 | 0 |  |  |  |  |  | transparent_threshold(quant, data, img, trans_index); | 
| 1702 | 0 |  |  |  |  |  | break; | 
| 1703 |  |  |  |  |  |  |  | 
| 1704 |  |  |  |  |  |  | case tr_errdiff: | 
| 1705 | 0 |  |  |  |  |  | transparent_errdiff(quant, data, img, trans_index); | 
| 1706 | 0 |  |  |  |  |  | break; | 
| 1707 |  |  |  |  |  |  |  | 
| 1708 |  |  |  |  |  |  | case tr_ordered: | 
| 1709 | 0 |  |  |  |  |  | transparent_ordered(quant, data, img, trans_index); | 
| 1710 | 0 |  |  |  |  |  | break; | 
| 1711 |  |  |  |  |  |  | } | 
| 1712 | 0 |  |  |  |  |  | } | 
| 1713 |  |  |  |  |  |  |  | 
| 1714 |  |  |  |  |  |  | static void | 
| 1715 | 0 |  |  |  |  |  | transparent_threshold(i_quantize *quant, i_palidx *data, i_img *img, | 
| 1716 |  |  |  |  |  |  | i_palidx trans_index) | 
| 1717 |  |  |  |  |  |  | { | 
| 1718 |  |  |  |  |  |  | i_img_dim x, y; | 
| 1719 | 0 |  |  |  |  |  | i_sample_t *line = mymalloc(img->xsize * sizeof(i_sample_t)); | 
| 1720 | 0 | 0 |  |  |  |  | int trans_chan = img->channels > 2 ? 3 : 1; | 
| 1721 |  |  |  |  |  |  |  | 
| 1722 | 0 | 0 |  |  |  |  | for (y = 0; y < img->ysize; ++y) { | 
| 1723 | 0 |  |  |  |  |  | i_gsamp(img, 0, img->xsize, y, line, &trans_chan, 1); | 
| 1724 | 0 | 0 |  |  |  |  | for (x = 0; x < img->xsize; ++x) { | 
| 1725 | 0 | 0 |  |  |  |  | if (line[x] < quant->tr_threshold) | 
| 1726 | 0 |  |  |  |  |  | data[y*img->xsize+x] = trans_index; | 
| 1727 |  |  |  |  |  |  | } | 
| 1728 |  |  |  |  |  |  | } | 
| 1729 | 0 |  |  |  |  |  | myfree(line); | 
| 1730 | 0 |  |  |  |  |  | } | 
| 1731 |  |  |  |  |  |  |  | 
| 1732 |  |  |  |  |  |  | static void | 
| 1733 | 0 |  |  |  |  |  | transparent_errdiff(i_quantize *quant, i_palidx *data, i_img *img, | 
| 1734 |  |  |  |  |  |  | i_palidx trans_index) | 
| 1735 |  |  |  |  |  |  | { | 
| 1736 |  |  |  |  |  |  | int *map; | 
| 1737 |  |  |  |  |  |  | int index; | 
| 1738 |  |  |  |  |  |  | int mapw, maph, mapo; | 
| 1739 |  |  |  |  |  |  | int errw, *err, *errp; | 
| 1740 |  |  |  |  |  |  | int difftotal, out, error; | 
| 1741 |  |  |  |  |  |  | i_img_dim x, y, dx, dy; | 
| 1742 |  |  |  |  |  |  | int i; | 
| 1743 |  |  |  |  |  |  | i_sample_t *line; | 
| 1744 | 0 | 0 |  |  |  |  | int trans_chan = img->channels > 2 ? 3 : 1; | 
| 1745 |  |  |  |  |  |  |  | 
| 1746 |  |  |  |  |  |  | /* no custom map for transparency (yet) */ | 
| 1747 | 0 |  |  |  |  |  | index = quant->tr_errdiff & ed_mask; | 
| 1748 | 0 | 0 |  |  |  |  | if (index >= ed_custom) index = ed_floyd; | 
| 1749 | 0 |  |  |  |  |  | map = maps[index].map; | 
| 1750 | 0 |  |  |  |  |  | mapw = maps[index].width; | 
| 1751 | 0 |  |  |  |  |  | maph = maps[index].height; | 
| 1752 | 0 |  |  |  |  |  | mapo = maps[index].orig; | 
| 1753 |  |  |  |  |  |  |  | 
| 1754 | 0 |  |  |  |  |  | errw = img->xsize+mapw-1; | 
| 1755 | 0 |  |  |  |  |  | err = mymalloc(sizeof(*err) * maph * errw); | 
| 1756 | 0 |  |  |  |  |  | errp = err+mapo; | 
| 1757 | 0 |  |  |  |  |  | memset(err, 0, sizeof(*err) * maph * errw); | 
| 1758 |  |  |  |  |  |  |  | 
| 1759 | 0 |  |  |  |  |  | line = mymalloc(img->xsize * sizeof(i_sample_t)); | 
| 1760 | 0 |  |  |  |  |  | difftotal = 0; | 
| 1761 | 0 | 0 |  |  |  |  | for (i = 0; i < maph * mapw; ++i) | 
| 1762 | 0 |  |  |  |  |  | difftotal += map[i]; | 
| 1763 | 0 | 0 |  |  |  |  | for (y = 0; y < img->ysize; ++y) { | 
| 1764 | 0 |  |  |  |  |  | i_gsamp(img, 0, img->xsize, y, line, &trans_chan, 1); | 
| 1765 | 0 | 0 |  |  |  |  | for (x = 0; x < img->xsize; ++x) { | 
| 1766 | 0 |  |  |  |  |  | line[x] = g_sat(line[x]-errp[x]/difftotal); | 
| 1767 | 0 | 0 |  |  |  |  | if (line[x] < 128) { | 
| 1768 | 0 |  |  |  |  |  | out = 0; | 
| 1769 | 0 |  |  |  |  |  | data[y*img->xsize+x] = trans_index; | 
| 1770 |  |  |  |  |  |  | } | 
| 1771 |  |  |  |  |  |  | else { | 
| 1772 | 0 |  |  |  |  |  | out = 255; | 
| 1773 |  |  |  |  |  |  | } | 
| 1774 | 0 |  |  |  |  |  | error = out - line[x]; | 
| 1775 | 0 | 0 |  |  |  |  | for (dx = 0; dx < mapw; ++dx) { | 
| 1776 | 0 | 0 |  |  |  |  | for (dy = 0; dy < maph; ++dy) { | 
| 1777 | 0 |  |  |  |  |  | errp[x+dx-mapo+dy*errw] += error * map[dx+mapw*dy]; | 
| 1778 |  |  |  |  |  |  | } | 
| 1779 |  |  |  |  |  |  | } | 
| 1780 |  |  |  |  |  |  | } | 
| 1781 |  |  |  |  |  |  | /* shift up the error matrix */ | 
| 1782 | 0 | 0 |  |  |  |  | for (dy = 0; dy < maph-1; ++dy) | 
| 1783 | 0 |  |  |  |  |  | memcpy(err+dy*errw, err+(dy+1)*errw, sizeof(*err)*errw); | 
| 1784 | 0 |  |  |  |  |  | memset(err+(maph-1)*errw, 0, sizeof(*err)*errw); | 
| 1785 |  |  |  |  |  |  | } | 
| 1786 | 0 |  |  |  |  |  | myfree(err); | 
| 1787 | 0 |  |  |  |  |  | myfree(line); | 
| 1788 | 0 |  |  |  |  |  | } | 
| 1789 |  |  |  |  |  |  |  | 
| 1790 |  |  |  |  |  |  | /* builtin ordered dither maps */ | 
| 1791 |  |  |  |  |  |  | static unsigned char | 
| 1792 |  |  |  |  |  |  | orddith_maps[][64] = | 
| 1793 |  |  |  |  |  |  | { | 
| 1794 |  |  |  |  |  |  | { /* random | 
| 1795 |  |  |  |  |  |  | this is purely random - it's pretty awful | 
| 1796 |  |  |  |  |  |  | */ | 
| 1797 |  |  |  |  |  |  | 48,  72, 196, 252, 180,  92, 108,  52, | 
| 1798 |  |  |  |  |  |  | 228, 176,  64,   8, 236,  40,  20, 164, | 
| 1799 |  |  |  |  |  |  | 120, 128,  84, 116,  24,  28, 172, 220, | 
| 1800 |  |  |  |  |  |  | 68,   0, 188, 124, 184, 224, 192, 104, | 
| 1801 |  |  |  |  |  |  | 132, 100, 240, 200, 152, 160, 244,  44, | 
| 1802 |  |  |  |  |  |  | 96, 204, 144,  16, 140,  56, 232, 216, | 
| 1803 |  |  |  |  |  |  | 208,   4,  76, 212, 136, 248,  80, 168, | 
| 1804 |  |  |  |  |  |  | 156,  88,  32, 112, 148,  12,  36,  60, | 
| 1805 |  |  |  |  |  |  | }, | 
| 1806 |  |  |  |  |  |  | { | 
| 1807 |  |  |  |  |  |  | /* dot8 | 
| 1808 |  |  |  |  |  |  | perl spot.perl '($x-3.5)*($x-3.5)+($y-3.5)*($y-3.5)' | 
| 1809 |  |  |  |  |  |  | */ | 
| 1810 |  |  |  |  |  |  | 240, 232, 200, 136, 140, 192, 228, 248, | 
| 1811 |  |  |  |  |  |  | 220, 148, 100,  76,  80, 104, 152, 212, | 
| 1812 |  |  |  |  |  |  | 180, 116,  56,  32,  36,  60, 120, 176, | 
| 1813 |  |  |  |  |  |  | 156,  64,  28,   0,   8,  44,  88, 160, | 
| 1814 |  |  |  |  |  |  | 128,  92,  24,  12,   4,  40,  68, 132, | 
| 1815 |  |  |  |  |  |  | 184,  96,  48,  20,  16,  52, 108, 188, | 
| 1816 |  |  |  |  |  |  | 216, 144, 112,  72,  84, 124, 164, 224, | 
| 1817 |  |  |  |  |  |  | 244, 236, 196, 168, 172, 204, 208, 252, | 
| 1818 |  |  |  |  |  |  | }, | 
| 1819 |  |  |  |  |  |  | { /* dot4 | 
| 1820 |  |  |  |  |  |  | perl spot.perl \ | 
| 1821 |  |  |  |  |  |  | 'min(dist(1.5, 1.5),dist(5.5,1.5),dist(1.5,5.5),dist(5.5,5.5))' | 
| 1822 |  |  |  |  |  |  | */ | 
| 1823 |  |  |  |  |  |  | 196,  72, 104, 220, 200,  80, 112, 224, | 
| 1824 |  |  |  |  |  |  | 76,   4,  24, 136,  84,   8,  32, 144, | 
| 1825 |  |  |  |  |  |  | 108,  28,  52, 168, 116,  36,  56, 176, | 
| 1826 |  |  |  |  |  |  | 216, 140, 172, 244, 228, 148, 180, 248, | 
| 1827 |  |  |  |  |  |  | 204,  92, 124, 236, 192,  68,  96, 208, | 
| 1828 |  |  |  |  |  |  | 88,  12,  44, 156,  64,   0,  16, 128, | 
| 1829 |  |  |  |  |  |  | 120,  40,  60, 188, 100,  20,  48, 160, | 
| 1830 |  |  |  |  |  |  | 232, 152, 184, 252, 212, 132, 164, 240, | 
| 1831 |  |  |  |  |  |  | }, | 
| 1832 |  |  |  |  |  |  | { /* hline | 
| 1833 |  |  |  |  |  |  | perl spot.perl '$y-3' | 
| 1834 |  |  |  |  |  |  | */ | 
| 1835 |  |  |  |  |  |  | 160, 164, 168, 172, 176, 180, 184, 188, | 
| 1836 |  |  |  |  |  |  | 128, 132, 136, 140, 144, 148, 152, 156, | 
| 1837 |  |  |  |  |  |  | 32,  36,  40,  44,  48,  52,  56,  60, | 
| 1838 |  |  |  |  |  |  | 0,   4,   8,  12,  16,  20,  24,  28, | 
| 1839 |  |  |  |  |  |  | 64,  68,  72,  76,  80,  84,  88,  92, | 
| 1840 |  |  |  |  |  |  | 96, 100, 104, 108, 112, 116, 120, 124, | 
| 1841 |  |  |  |  |  |  | 192, 196, 200, 204, 208, 212, 216, 220, | 
| 1842 |  |  |  |  |  |  | 224, 228, 232, 236, 240, 244, 248, 252, | 
| 1843 |  |  |  |  |  |  | }, | 
| 1844 |  |  |  |  |  |  | { /* vline | 
| 1845 |  |  |  |  |  |  | perl spot.perl '$x-3' | 
| 1846 |  |  |  |  |  |  | */ | 
| 1847 |  |  |  |  |  |  | 180, 100,  40,  12,  44, 104, 184, 232, | 
| 1848 |  |  |  |  |  |  | 204, 148,  60,  16,  64, 128, 208, 224, | 
| 1849 |  |  |  |  |  |  | 212, 144,  76,   8,  80, 132, 216, 244, | 
| 1850 |  |  |  |  |  |  | 160, 112,  68,  20,  84, 108, 172, 236, | 
| 1851 |  |  |  |  |  |  | 176,  96,  72,  28,  88, 152, 188, 228, | 
| 1852 |  |  |  |  |  |  | 200, 124,  92,   0,  32, 116, 164, 240, | 
| 1853 |  |  |  |  |  |  | 168, 120,  36,  24,  48, 136, 192, 248, | 
| 1854 |  |  |  |  |  |  | 196, 140,  52,   4,  56, 156, 220, 252, | 
| 1855 |  |  |  |  |  |  | }, | 
| 1856 |  |  |  |  |  |  | { /* slashline | 
| 1857 |  |  |  |  |  |  | perl spot.perl '$y+$x-7' | 
| 1858 |  |  |  |  |  |  | */ | 
| 1859 |  |  |  |  |  |  | 248, 232, 224, 192, 140,  92,  52,  28, | 
| 1860 |  |  |  |  |  |  | 240, 220, 196, 144, 108,  60,  12,  64, | 
| 1861 |  |  |  |  |  |  | 216, 180, 148, 116,  76,  20,  80, 128, | 
| 1862 |  |  |  |  |  |  | 204, 152, 104,  44,  16,  72, 100, 160, | 
| 1863 |  |  |  |  |  |  | 164,  96,  68,  24,  56, 112, 168, 176, | 
| 1864 |  |  |  |  |  |  | 124,  40,   8,  36,  88, 136, 184, 212, | 
| 1865 |  |  |  |  |  |  | 84,   4,  32, 120, 156, 188, 228, 236, | 
| 1866 |  |  |  |  |  |  | 0,  48, 132, 172, 200, 208, 244, 252, | 
| 1867 |  |  |  |  |  |  | }, | 
| 1868 |  |  |  |  |  |  | { /* backline | 
| 1869 |  |  |  |  |  |  | perl spot.perl '$y-$x' | 
| 1870 |  |  |  |  |  |  | */ | 
| 1871 |  |  |  |  |  |  | 0,  32, 116, 172, 184, 216, 236, 252, | 
| 1872 |  |  |  |  |  |  | 56,   8,  72, 132, 136, 200, 228, 240, | 
| 1873 |  |  |  |  |  |  | 100,  36,  12,  40,  92, 144, 204, 220, | 
| 1874 |  |  |  |  |  |  | 168, 120,  60,  16,  44,  96, 156, 176, | 
| 1875 |  |  |  |  |  |  | 180, 164, 112,  48,  28,  52, 128, 148, | 
| 1876 |  |  |  |  |  |  | 208, 192, 152,  88,  84,  20,  64, 104, | 
| 1877 |  |  |  |  |  |  | 232, 224, 196, 140, 108,  68,  24,  76, | 
| 1878 |  |  |  |  |  |  | 248, 244, 212, 188, 160, 124,  80,   4, | 
| 1879 |  |  |  |  |  |  | }, | 
| 1880 |  |  |  |  |  |  | { | 
| 1881 |  |  |  |  |  |  | /* tiny | 
| 1882 |  |  |  |  |  |  | good for display, bad for print | 
| 1883 |  |  |  |  |  |  | hand generated | 
| 1884 |  |  |  |  |  |  | */ | 
| 1885 |  |  |  |  |  |  | 0, 128,  32, 192,   8, 136,  40, 200, | 
| 1886 |  |  |  |  |  |  | 224,  64, 160, 112, 232,  72, 168, 120, | 
| 1887 |  |  |  |  |  |  | 48, 144,  16, 208,  56, 152,  24, 216, | 
| 1888 |  |  |  |  |  |  | 176,  96, 240,  80, 184, 104, 248,  88, | 
| 1889 |  |  |  |  |  |  | 12, 140,  44, 204,   4, 132,  36, 196, | 
| 1890 |  |  |  |  |  |  | 236,  76, 172, 124, 228,  68, 164, 116, | 
| 1891 |  |  |  |  |  |  | 60, 156,  28, 220,  52, 148,  20, 212, | 
| 1892 |  |  |  |  |  |  | 188, 108, 252,  92, 180, 100, 244,  84, | 
| 1893 |  |  |  |  |  |  | }, | 
| 1894 |  |  |  |  |  |  | }; | 
| 1895 |  |  |  |  |  |  |  | 
| 1896 |  |  |  |  |  |  | static void | 
| 1897 | 0 |  |  |  |  |  | transparent_ordered(i_quantize *quant, i_palidx *data, i_img *img, | 
| 1898 |  |  |  |  |  |  | i_palidx trans_index) | 
| 1899 |  |  |  |  |  |  | { | 
| 1900 |  |  |  |  |  |  | unsigned char *spot; | 
| 1901 |  |  |  |  |  |  | i_img_dim x, y; | 
| 1902 |  |  |  |  |  |  | i_sample_t *line; | 
| 1903 | 0 | 0 |  |  |  |  | int trans_chan = img->channels > 2 ? 3 : 1; | 
| 1904 | 0 | 0 |  |  |  |  | if (quant->tr_orddith == od_custom) | 
| 1905 | 0 |  |  |  |  |  | spot = quant->tr_custom; | 
| 1906 |  |  |  |  |  |  | else | 
| 1907 | 0 |  |  |  |  |  | spot = orddith_maps[quant->tr_orddith]; | 
| 1908 |  |  |  |  |  |  |  | 
| 1909 | 0 |  |  |  |  |  | line = mymalloc(img->xsize * sizeof(i_sample_t)); | 
| 1910 | 0 | 0 |  |  |  |  | for (y = 0; y < img->ysize; ++y) { | 
| 1911 | 0 |  |  |  |  |  | i_gsamp(img, 0, img->xsize, y, line, &trans_chan, 1); | 
| 1912 | 0 | 0 |  |  |  |  | for (x = 0; x < img->xsize; ++x) { | 
| 1913 | 0 | 0 |  |  |  |  | if (line[x] < spot[(x&7)+(y&7)*8]) | 
| 1914 | 0 |  |  |  |  |  | data[x+y*img->xsize] = trans_index; | 
| 1915 |  |  |  |  |  |  | } | 
| 1916 |  |  |  |  |  |  | } | 
| 1917 | 0 |  |  |  |  |  | myfree(line); | 
| 1918 | 0 |  |  |  |  |  | } | 
| 1919 |  |  |  |  |  |  |  |