]>
Commit | Line | Data |
---|---|---|
8414a40c VZ |
1 | |
2 | /* | |
3 | * Apply median cut on an image. | |
4 | * | |
5 | * tiffmedian [-c n] [-f] input output | |
6 | * -C n - set colortable size. Default is 256. | |
7 | * -f - use Floyd-Steinberg dithering. | |
8 | * -c lzw - compress output with LZW | |
9 | * -c none - use no compression on output | |
10 | * -c packbits - use packbits compression on output | |
11 | * -r n - create output with n rows/strip of data | |
12 | * (by default the compression scheme and rows/strip are taken | |
13 | * from the input file) | |
14 | * | |
15 | * Notes: | |
16 | * | |
17 | * [1] Floyd-Steinberg dither: | |
18 | * I should point out that the actual fractions we used were, assuming | |
19 | * you are at X, moving left to right: | |
20 | * | |
21 | * X 7/16 | |
22 | * 3/16 5/16 1/16 | |
23 | * | |
24 | * Note that the error goes to four neighbors, not three. I think this | |
25 | * will probably do better (at least for black and white) than the | |
26 | * 3/8-3/8-1/4 distribution, at the cost of greater processing. I have | |
27 | * seen the 3/8-3/8-1/4 distribution described as "our" algorithm before, | |
28 | * but I have no idea who the credit really belongs to. | |
29 | ||
30 | * Also, I should add that if you do zig-zag scanning (see my immediately | |
31 | * previous message), it is sufficient (but not quite as good) to send | |
32 | * half the error one pixel ahead (e.g. to the right on lines you scan | |
33 | * left to right), and half one pixel straight down. Again, this is for | |
34 | * black and white; I've not tried it with color. | |
35 | * -- | |
36 | * Lou Steinberg | |
37 | * | |
38 | * [2] Color Image Quantization for Frame Buffer Display, Paul Heckbert, | |
39 | * Siggraph '82 proceedings, pp. 297-307 | |
40 | */ | |
41 | ||
42 | #include "tif_config.h" | |
43 | ||
44 | #include <stdio.h> | |
45 | #include <stdlib.h> | |
46 | #include <string.h> | |
47 | ||
48 | #ifdef HAVE_UNISTD_H | |
49 | # include <unistd.h> | |
50 | #endif | |
51 | ||
80ed523f VZ |
52 | #ifdef NEED_LIBPORT |
53 | # include "libport.h" | |
54 | #endif | |
55 | ||
8414a40c VZ |
56 | #include "tiffio.h" |
57 | ||
58 | #define MAX_CMAP_SIZE 256 | |
59 | ||
60 | #define streq(a,b) (strcmp(a,b) == 0) | |
61 | #define strneq(a,b,n) (strncmp(a,b,n) == 0) | |
62 | ||
63 | #define COLOR_DEPTH 8 | |
64 | #define MAX_COLOR 256 | |
65 | ||
66 | #define B_DEPTH 5 /* # bits/pixel to use */ | |
67 | #define B_LEN (1L<<B_DEPTH) | |
68 | ||
69 | #define C_DEPTH 2 | |
70 | #define C_LEN (1L<<C_DEPTH) /* # cells/color to use */ | |
71 | ||
72 | #define COLOR_SHIFT (COLOR_DEPTH-B_DEPTH) | |
73 | ||
74 | typedef struct colorbox { | |
75 | struct colorbox *next, *prev; | |
76 | int rmin, rmax; | |
77 | int gmin, gmax; | |
78 | int bmin, bmax; | |
79 | uint32 total; | |
80 | } Colorbox; | |
81 | ||
82 | typedef struct { | |
83 | int num_ents; | |
84 | int entries[MAX_CMAP_SIZE][2]; | |
85 | } C_cell; | |
86 | ||
87 | uint16 rm[MAX_CMAP_SIZE], gm[MAX_CMAP_SIZE], bm[MAX_CMAP_SIZE]; | |
88 | int num_colors; | |
89 | uint32 histogram[B_LEN][B_LEN][B_LEN]; | |
90 | Colorbox *freeboxes; | |
91 | Colorbox *usedboxes; | |
92 | C_cell **ColorCells; | |
93 | TIFF *in, *out; | |
94 | uint32 rowsperstrip = (uint32) -1; | |
95 | uint16 compression = (uint16) -1; | |
96 | uint16 bitspersample = 1; | |
97 | uint16 samplesperpixel; | |
98 | uint32 imagewidth; | |
99 | uint32 imagelength; | |
100 | uint16 predictor = 0; | |
101 | ||
102 | static void get_histogram(TIFF*, Colorbox*); | |
103 | static void splitbox(Colorbox*); | |
104 | static void shrinkbox(Colorbox*); | |
105 | static void map_colortable(void); | |
106 | static void quant(TIFF*, TIFF*); | |
107 | static void quant_fsdither(TIFF*, TIFF*); | |
108 | static Colorbox* largest_box(void); | |
109 | ||
110 | static void usage(void); | |
111 | static int processCompressOptions(char*); | |
112 | ||
113 | #define CopyField(tag, v) \ | |
114 | if (TIFFGetField(in, tag, &v)) TIFFSetField(out, tag, v) | |
115 | ||
116 | int | |
117 | main(int argc, char* argv[]) | |
118 | { | |
119 | int i, dither = 0; | |
120 | uint16 shortv, config, photometric; | |
121 | Colorbox *box_list, *ptr; | |
122 | float floatv; | |
123 | uint32 longv; | |
124 | int c; | |
125 | extern int optind; | |
126 | extern char* optarg; | |
127 | ||
128 | num_colors = MAX_CMAP_SIZE; | |
129 | while ((c = getopt(argc, argv, "c:C:r:f")) != -1) | |
130 | switch (c) { | |
131 | case 'c': /* compression scheme */ | |
132 | if (!processCompressOptions(optarg)) | |
133 | usage(); | |
134 | break; | |
135 | case 'C': /* set colormap size */ | |
136 | num_colors = atoi(optarg); | |
137 | if (num_colors > MAX_CMAP_SIZE) { | |
138 | fprintf(stderr, | |
139 | "-c: colormap too big, max %d\n", | |
140 | MAX_CMAP_SIZE); | |
141 | usage(); | |
142 | } | |
143 | break; | |
144 | case 'f': /* dither */ | |
145 | dither = 1; | |
146 | break; | |
147 | case 'r': /* rows/strip */ | |
148 | rowsperstrip = atoi(optarg); | |
149 | break; | |
150 | case '?': | |
151 | usage(); | |
152 | /*NOTREACHED*/ | |
153 | } | |
154 | if (argc - optind != 2) | |
155 | usage(); | |
156 | in = TIFFOpen(argv[optind], "r"); | |
157 | if (in == NULL) | |
158 | return (-1); | |
159 | TIFFGetField(in, TIFFTAG_IMAGEWIDTH, &imagewidth); | |
160 | TIFFGetField(in, TIFFTAG_IMAGELENGTH, &imagelength); | |
161 | TIFFGetField(in, TIFFTAG_BITSPERSAMPLE, &bitspersample); | |
162 | TIFFGetField(in, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel); | |
163 | if (bitspersample != 8 && bitspersample != 16) { | |
164 | fprintf(stderr, "%s: Image must have at least 8-bits/sample\n", | |
165 | argv[optind]); | |
166 | return (-3); | |
167 | } | |
168 | if (!TIFFGetField(in, TIFFTAG_PHOTOMETRIC, &photometric) || | |
169 | photometric != PHOTOMETRIC_RGB || samplesperpixel < 3) { | |
170 | fprintf(stderr, "%s: Image must have RGB data\n", argv[optind]); | |
171 | return (-4); | |
172 | } | |
173 | TIFFGetField(in, TIFFTAG_PLANARCONFIG, &config); | |
174 | if (config != PLANARCONFIG_CONTIG) { | |
175 | fprintf(stderr, "%s: Can only handle contiguous data packing\n", | |
176 | argv[optind]); | |
177 | return (-5); | |
178 | } | |
179 | ||
180 | /* | |
181 | * STEP 1: create empty boxes | |
182 | */ | |
183 | usedboxes = NULL; | |
184 | box_list = freeboxes = (Colorbox *)_TIFFmalloc(num_colors*sizeof (Colorbox)); | |
185 | freeboxes[0].next = &freeboxes[1]; | |
186 | freeboxes[0].prev = NULL; | |
187 | for (i = 1; i < num_colors-1; ++i) { | |
188 | freeboxes[i].next = &freeboxes[i+1]; | |
189 | freeboxes[i].prev = &freeboxes[i-1]; | |
190 | } | |
191 | freeboxes[num_colors-1].next = NULL; | |
192 | freeboxes[num_colors-1].prev = &freeboxes[num_colors-2]; | |
193 | ||
194 | /* | |
195 | * STEP 2: get histogram, initialize first box | |
196 | */ | |
197 | ptr = freeboxes; | |
198 | freeboxes = ptr->next; | |
199 | if (freeboxes) | |
200 | freeboxes->prev = NULL; | |
201 | ptr->next = usedboxes; | |
202 | usedboxes = ptr; | |
203 | if (ptr->next) | |
204 | ptr->next->prev = ptr; | |
205 | get_histogram(in, ptr); | |
206 | ||
207 | /* | |
208 | * STEP 3: continually subdivide boxes until no more free | |
209 | * boxes remain or until all colors assigned. | |
210 | */ | |
211 | while (freeboxes != NULL) { | |
212 | ptr = largest_box(); | |
213 | if (ptr != NULL) | |
214 | splitbox(ptr); | |
215 | else | |
216 | freeboxes = NULL; | |
217 | } | |
218 | ||
219 | /* | |
220 | * STEP 4: assign colors to all boxes | |
221 | */ | |
222 | for (i = 0, ptr = usedboxes; ptr != NULL; ++i, ptr = ptr->next) { | |
223 | rm[i] = ((ptr->rmin + ptr->rmax) << COLOR_SHIFT) / 2; | |
224 | gm[i] = ((ptr->gmin + ptr->gmax) << COLOR_SHIFT) / 2; | |
225 | bm[i] = ((ptr->bmin + ptr->bmax) << COLOR_SHIFT) / 2; | |
226 | } | |
227 | ||
228 | /* We're done with the boxes now */ | |
229 | _TIFFfree(box_list); | |
230 | freeboxes = usedboxes = NULL; | |
231 | ||
232 | /* | |
233 | * STEP 5: scan histogram and map all values to closest color | |
234 | */ | |
235 | /* 5a: create cell list as described in Heckbert[2] */ | |
236 | ColorCells = (C_cell **)_TIFFmalloc(C_LEN*C_LEN*C_LEN*sizeof (C_cell*)); | |
237 | _TIFFmemset(ColorCells, 0, C_LEN*C_LEN*C_LEN*sizeof (C_cell*)); | |
238 | /* 5b: create mapping from truncated pixel space to color | |
239 | table entries */ | |
240 | map_colortable(); | |
241 | ||
242 | /* | |
243 | * STEP 6: scan image, match input values to table entries | |
244 | */ | |
245 | out = TIFFOpen(argv[optind+1], "w"); | |
246 | if (out == NULL) | |
247 | return (-2); | |
248 | ||
249 | CopyField(TIFFTAG_SUBFILETYPE, longv); | |
250 | CopyField(TIFFTAG_IMAGEWIDTH, longv); | |
251 | TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, (short)COLOR_DEPTH); | |
252 | if (compression != (uint16)-1) { | |
253 | TIFFSetField(out, TIFFTAG_COMPRESSION, compression); | |
254 | switch (compression) { | |
255 | case COMPRESSION_LZW: | |
256 | case COMPRESSION_DEFLATE: | |
257 | if (predictor != 0) | |
258 | TIFFSetField(out, TIFFTAG_PREDICTOR, predictor); | |
259 | break; | |
260 | } | |
261 | } else | |
262 | CopyField(TIFFTAG_COMPRESSION, compression); | |
263 | TIFFSetField(out, TIFFTAG_PHOTOMETRIC, (short)PHOTOMETRIC_PALETTE); | |
264 | CopyField(TIFFTAG_ORIENTATION, shortv); | |
265 | TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, (short)1); | |
266 | CopyField(TIFFTAG_PLANARCONFIG, shortv); | |
267 | TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, | |
268 | TIFFDefaultStripSize(out, rowsperstrip)); | |
269 | CopyField(TIFFTAG_MINSAMPLEVALUE, shortv); | |
270 | CopyField(TIFFTAG_MAXSAMPLEVALUE, shortv); | |
271 | CopyField(TIFFTAG_RESOLUTIONUNIT, shortv); | |
272 | CopyField(TIFFTAG_XRESOLUTION, floatv); | |
273 | CopyField(TIFFTAG_YRESOLUTION, floatv); | |
274 | CopyField(TIFFTAG_XPOSITION, floatv); | |
275 | CopyField(TIFFTAG_YPOSITION, floatv); | |
276 | ||
277 | if (dither) | |
278 | quant_fsdither(in, out); | |
279 | else | |
280 | quant(in, out); | |
281 | /* | |
282 | * Scale colormap to TIFF-required 16-bit values. | |
283 | */ | |
284 | #define SCALE(x) (((x)*((1L<<16)-1))/255) | |
285 | for (i = 0; i < MAX_CMAP_SIZE; ++i) { | |
286 | rm[i] = SCALE(rm[i]); | |
287 | gm[i] = SCALE(gm[i]); | |
288 | bm[i] = SCALE(bm[i]); | |
289 | } | |
290 | TIFFSetField(out, TIFFTAG_COLORMAP, rm, gm, bm); | |
291 | (void) TIFFClose(out); | |
292 | return (0); | |
293 | } | |
294 | ||
295 | static int | |
296 | processCompressOptions(char* opt) | |
297 | { | |
298 | if (streq(opt, "none")) | |
299 | compression = COMPRESSION_NONE; | |
300 | else if (streq(opt, "packbits")) | |
301 | compression = COMPRESSION_PACKBITS; | |
302 | else if (strneq(opt, "lzw", 3)) { | |
303 | char* cp = strchr(opt, ':'); | |
304 | if (cp) | |
305 | predictor = atoi(cp+1); | |
306 | compression = COMPRESSION_LZW; | |
307 | } else if (strneq(opt, "zip", 3)) { | |
308 | char* cp = strchr(opt, ':'); | |
309 | if (cp) | |
310 | predictor = atoi(cp+1); | |
311 | compression = COMPRESSION_DEFLATE; | |
312 | } else | |
313 | return (0); | |
314 | return (1); | |
315 | } | |
316 | ||
317 | char* stuff[] = { | |
318 | "usage: tiffmedian [options] input.tif output.tif", | |
319 | "where options are:", | |
320 | " -r # make each strip have no more than # rows", | |
321 | " -C # create a colormap with # entries", | |
322 | " -f use Floyd-Steinberg dithering", | |
323 | " -c lzw[:opts] compress output with Lempel-Ziv & Welch encoding", | |
324 | " -c zip[:opts] compress output with deflate encoding", | |
325 | " -c packbits compress output with packbits encoding", | |
326 | " -c none use no compression algorithm on output", | |
327 | "", | |
328 | "LZW and deflate options:", | |
329 | " # set predictor value", | |
330 | "For example, -c lzw:2 to get LZW-encoded data with horizontal differencing", | |
331 | NULL | |
332 | }; | |
333 | ||
334 | static void | |
335 | usage(void) | |
336 | { | |
337 | char buf[BUFSIZ]; | |
338 | int i; | |
339 | ||
340 | setbuf(stderr, buf); | |
341 | fprintf(stderr, "%s\n\n", TIFFGetVersion()); | |
342 | for (i = 0; stuff[i] != NULL; i++) | |
343 | fprintf(stderr, "%s\n", stuff[i]); | |
344 | exit(-1); | |
345 | } | |
346 | ||
347 | static void | |
348 | get_histogram(TIFF* in, Colorbox* box) | |
349 | { | |
350 | register unsigned char *inptr; | |
351 | register int red, green, blue; | |
352 | register uint32 j, i; | |
353 | unsigned char *inputline; | |
354 | ||
355 | inputline = (unsigned char *)_TIFFmalloc(TIFFScanlineSize(in)); | |
356 | if (inputline == NULL) { | |
357 | fprintf(stderr, "No space for scanline buffer\n"); | |
358 | exit(-1); | |
359 | } | |
360 | box->rmin = box->gmin = box->bmin = 999; | |
361 | box->rmax = box->gmax = box->bmax = -1; | |
362 | box->total = imagewidth * imagelength; | |
363 | ||
364 | { register uint32 *ptr = &histogram[0][0][0]; | |
365 | for (i = B_LEN*B_LEN*B_LEN; i-- > 0;) | |
366 | *ptr++ = 0; | |
367 | } | |
368 | for (i = 0; i < imagelength; i++) { | |
369 | if (TIFFReadScanline(in, inputline, i, 0) <= 0) | |
370 | break; | |
371 | inptr = inputline; | |
372 | for (j = imagewidth; j-- > 0;) { | |
373 | red = *inptr++ >> COLOR_SHIFT; | |
374 | green = *inptr++ >> COLOR_SHIFT; | |
375 | blue = *inptr++ >> COLOR_SHIFT; | |
376 | if (red < box->rmin) | |
377 | box->rmin = red; | |
378 | if (red > box->rmax) | |
379 | box->rmax = red; | |
380 | if (green < box->gmin) | |
381 | box->gmin = green; | |
382 | if (green > box->gmax) | |
383 | box->gmax = green; | |
384 | if (blue < box->bmin) | |
385 | box->bmin = blue; | |
386 | if (blue > box->bmax) | |
387 | box->bmax = blue; | |
388 | histogram[red][green][blue]++; | |
389 | } | |
390 | } | |
391 | _TIFFfree(inputline); | |
392 | } | |
393 | ||
394 | static Colorbox * | |
395 | largest_box(void) | |
396 | { | |
397 | register Colorbox *p, *b; | |
398 | register uint32 size; | |
399 | ||
400 | b = NULL; | |
401 | size = 0; | |
402 | for (p = usedboxes; p != NULL; p = p->next) | |
403 | if ((p->rmax > p->rmin || p->gmax > p->gmin || | |
404 | p->bmax > p->bmin) && p->total > size) | |
405 | size = (b = p)->total; | |
406 | return (b); | |
407 | } | |
408 | ||
409 | static void | |
410 | splitbox(Colorbox* ptr) | |
411 | { | |
412 | uint32 hist2[B_LEN]; | |
413 | int first=0, last=0; | |
414 | register Colorbox *new; | |
415 | register uint32 *iptr, *histp; | |
416 | register int i, j; | |
417 | register int ir,ig,ib; | |
418 | register uint32 sum, sum1, sum2; | |
419 | enum { RED, GREEN, BLUE } axis; | |
420 | ||
421 | /* | |
422 | * See which axis is the largest, do a histogram along that | |
423 | * axis. Split at median point. Contract both new boxes to | |
424 | * fit points and return | |
425 | */ | |
426 | i = ptr->rmax - ptr->rmin; | |
427 | if (i >= ptr->gmax - ptr->gmin && i >= ptr->bmax - ptr->bmin) | |
428 | axis = RED; | |
429 | else if (ptr->gmax - ptr->gmin >= ptr->bmax - ptr->bmin) | |
430 | axis = GREEN; | |
431 | else | |
432 | axis = BLUE; | |
433 | /* get histogram along longest axis */ | |
434 | switch (axis) { | |
435 | case RED: | |
436 | histp = &hist2[ptr->rmin]; | |
437 | for (ir = ptr->rmin; ir <= ptr->rmax; ++ir) { | |
438 | *histp = 0; | |
439 | for (ig = ptr->gmin; ig <= ptr->gmax; ++ig) { | |
440 | iptr = &histogram[ir][ig][ptr->bmin]; | |
441 | for (ib = ptr->bmin; ib <= ptr->bmax; ++ib) | |
442 | *histp += *iptr++; | |
443 | } | |
444 | histp++; | |
445 | } | |
446 | first = ptr->rmin; | |
447 | last = ptr->rmax; | |
448 | break; | |
449 | case GREEN: | |
450 | histp = &hist2[ptr->gmin]; | |
451 | for (ig = ptr->gmin; ig <= ptr->gmax; ++ig) { | |
452 | *histp = 0; | |
453 | for (ir = ptr->rmin; ir <= ptr->rmax; ++ir) { | |
454 | iptr = &histogram[ir][ig][ptr->bmin]; | |
455 | for (ib = ptr->bmin; ib <= ptr->bmax; ++ib) | |
456 | *histp += *iptr++; | |
457 | } | |
458 | histp++; | |
459 | } | |
460 | first = ptr->gmin; | |
461 | last = ptr->gmax; | |
462 | break; | |
463 | case BLUE: | |
464 | histp = &hist2[ptr->bmin]; | |
465 | for (ib = ptr->bmin; ib <= ptr->bmax; ++ib) { | |
466 | *histp = 0; | |
467 | for (ir = ptr->rmin; ir <= ptr->rmax; ++ir) { | |
468 | iptr = &histogram[ir][ptr->gmin][ib]; | |
469 | for (ig = ptr->gmin; ig <= ptr->gmax; ++ig) { | |
470 | *histp += *iptr; | |
471 | iptr += B_LEN; | |
472 | } | |
473 | } | |
474 | histp++; | |
475 | } | |
476 | first = ptr->bmin; | |
477 | last = ptr->bmax; | |
478 | break; | |
479 | } | |
480 | /* find median point */ | |
481 | sum2 = ptr->total / 2; | |
482 | histp = &hist2[first]; | |
483 | sum = 0; | |
484 | for (i = first; i <= last && (sum += *histp++) < sum2; ++i) | |
485 | ; | |
486 | if (i == first) | |
487 | i++; | |
488 | ||
489 | /* Create new box, re-allocate points */ | |
490 | new = freeboxes; | |
491 | freeboxes = new->next; | |
492 | if (freeboxes) | |
493 | freeboxes->prev = NULL; | |
494 | if (usedboxes) | |
495 | usedboxes->prev = new; | |
496 | new->next = usedboxes; | |
497 | usedboxes = new; | |
498 | ||
499 | histp = &hist2[first]; | |
500 | for (sum1 = 0, j = first; j < i; j++) | |
501 | sum1 += *histp++; | |
502 | for (sum2 = 0, j = i; j <= last; j++) | |
503 | sum2 += *histp++; | |
504 | new->total = sum1; | |
505 | ptr->total = sum2; | |
506 | ||
507 | new->rmin = ptr->rmin; | |
508 | new->rmax = ptr->rmax; | |
509 | new->gmin = ptr->gmin; | |
510 | new->gmax = ptr->gmax; | |
511 | new->bmin = ptr->bmin; | |
512 | new->bmax = ptr->bmax; | |
513 | switch (axis) { | |
514 | case RED: | |
515 | new->rmax = i-1; | |
516 | ptr->rmin = i; | |
517 | break; | |
518 | case GREEN: | |
519 | new->gmax = i-1; | |
520 | ptr->gmin = i; | |
521 | break; | |
522 | case BLUE: | |
523 | new->bmax = i-1; | |
524 | ptr->bmin = i; | |
525 | break; | |
526 | } | |
527 | shrinkbox(new); | |
528 | shrinkbox(ptr); | |
529 | } | |
530 | ||
531 | static void | |
532 | shrinkbox(Colorbox* box) | |
533 | { | |
534 | register uint32 *histp; | |
535 | register int ir, ig, ib; | |
536 | ||
537 | if (box->rmax > box->rmin) { | |
538 | for (ir = box->rmin; ir <= box->rmax; ++ir) | |
539 | for (ig = box->gmin; ig <= box->gmax; ++ig) { | |
540 | histp = &histogram[ir][ig][box->bmin]; | |
541 | for (ib = box->bmin; ib <= box->bmax; ++ib) | |
542 | if (*histp++ != 0) { | |
543 | box->rmin = ir; | |
544 | goto have_rmin; | |
545 | } | |
546 | } | |
547 | have_rmin: | |
548 | if (box->rmax > box->rmin) | |
549 | for (ir = box->rmax; ir >= box->rmin; --ir) | |
550 | for (ig = box->gmin; ig <= box->gmax; ++ig) { | |
551 | histp = &histogram[ir][ig][box->bmin]; | |
552 | ib = box->bmin; | |
553 | for (; ib <= box->bmax; ++ib) | |
554 | if (*histp++ != 0) { | |
555 | box->rmax = ir; | |
556 | goto have_rmax; | |
557 | } | |
558 | } | |
559 | } | |
560 | have_rmax: | |
561 | if (box->gmax > box->gmin) { | |
562 | for (ig = box->gmin; ig <= box->gmax; ++ig) | |
563 | for (ir = box->rmin; ir <= box->rmax; ++ir) { | |
564 | histp = &histogram[ir][ig][box->bmin]; | |
565 | for (ib = box->bmin; ib <= box->bmax; ++ib) | |
566 | if (*histp++ != 0) { | |
567 | box->gmin = ig; | |
568 | goto have_gmin; | |
569 | } | |
570 | } | |
571 | have_gmin: | |
572 | if (box->gmax > box->gmin) | |
573 | for (ig = box->gmax; ig >= box->gmin; --ig) | |
574 | for (ir = box->rmin; ir <= box->rmax; ++ir) { | |
575 | histp = &histogram[ir][ig][box->bmin]; | |
576 | ib = box->bmin; | |
577 | for (; ib <= box->bmax; ++ib) | |
578 | if (*histp++ != 0) { | |
579 | box->gmax = ig; | |
580 | goto have_gmax; | |
581 | } | |
582 | } | |
583 | } | |
584 | have_gmax: | |
585 | if (box->bmax > box->bmin) { | |
586 | for (ib = box->bmin; ib <= box->bmax; ++ib) | |
587 | for (ir = box->rmin; ir <= box->rmax; ++ir) { | |
588 | histp = &histogram[ir][box->gmin][ib]; | |
589 | for (ig = box->gmin; ig <= box->gmax; ++ig) { | |
590 | if (*histp != 0) { | |
591 | box->bmin = ib; | |
592 | goto have_bmin; | |
593 | } | |
594 | histp += B_LEN; | |
595 | } | |
596 | } | |
597 | have_bmin: | |
598 | if (box->bmax > box->bmin) | |
599 | for (ib = box->bmax; ib >= box->bmin; --ib) | |
600 | for (ir = box->rmin; ir <= box->rmax; ++ir) { | |
601 | histp = &histogram[ir][box->gmin][ib]; | |
602 | ig = box->gmin; | |
603 | for (; ig <= box->gmax; ++ig) { | |
604 | if (*histp != 0) { | |
605 | box->bmax = ib; | |
606 | goto have_bmax; | |
607 | } | |
608 | histp += B_LEN; | |
609 | } | |
610 | } | |
611 | } | |
612 | have_bmax: | |
613 | ; | |
614 | } | |
615 | ||
616 | static C_cell * | |
617 | create_colorcell(int red, int green, int blue) | |
618 | { | |
619 | register int ir, ig, ib, i; | |
620 | register C_cell *ptr; | |
621 | int mindist, next_n; | |
622 | register int tmp, dist, n; | |
623 | ||
624 | ir = red >> (COLOR_DEPTH-C_DEPTH); | |
625 | ig = green >> (COLOR_DEPTH-C_DEPTH); | |
626 | ib = blue >> (COLOR_DEPTH-C_DEPTH); | |
627 | ptr = (C_cell *)_TIFFmalloc(sizeof (C_cell)); | |
628 | *(ColorCells + ir*C_LEN*C_LEN + ig*C_LEN + ib) = ptr; | |
629 | ptr->num_ents = 0; | |
630 | ||
631 | /* | |
632 | * Step 1: find all colors inside this cell, while we're at | |
633 | * it, find distance of centermost point to furthest corner | |
634 | */ | |
635 | mindist = 99999999; | |
636 | for (i = 0; i < num_colors; ++i) { | |
637 | if (rm[i]>>(COLOR_DEPTH-C_DEPTH) != ir || | |
638 | gm[i]>>(COLOR_DEPTH-C_DEPTH) != ig || | |
639 | bm[i]>>(COLOR_DEPTH-C_DEPTH) != ib) | |
640 | continue; | |
641 | ptr->entries[ptr->num_ents][0] = i; | |
642 | ptr->entries[ptr->num_ents][1] = 0; | |
643 | ++ptr->num_ents; | |
644 | tmp = rm[i] - red; | |
645 | if (tmp < (MAX_COLOR/C_LEN/2)) | |
646 | tmp = MAX_COLOR/C_LEN-1 - tmp; | |
647 | dist = tmp*tmp; | |
648 | tmp = gm[i] - green; | |
649 | if (tmp < (MAX_COLOR/C_LEN/2)) | |
650 | tmp = MAX_COLOR/C_LEN-1 - tmp; | |
651 | dist += tmp*tmp; | |
652 | tmp = bm[i] - blue; | |
653 | if (tmp < (MAX_COLOR/C_LEN/2)) | |
654 | tmp = MAX_COLOR/C_LEN-1 - tmp; | |
655 | dist += tmp*tmp; | |
656 | if (dist < mindist) | |
657 | mindist = dist; | |
658 | } | |
659 | ||
660 | /* | |
661 | * Step 3: find all points within that distance to cell. | |
662 | */ | |
663 | for (i = 0; i < num_colors; ++i) { | |
664 | if (rm[i] >> (COLOR_DEPTH-C_DEPTH) == ir && | |
665 | gm[i] >> (COLOR_DEPTH-C_DEPTH) == ig && | |
666 | bm[i] >> (COLOR_DEPTH-C_DEPTH) == ib) | |
667 | continue; | |
668 | dist = 0; | |
669 | if ((tmp = red - rm[i]) > 0 || | |
670 | (tmp = rm[i] - (red + MAX_COLOR/C_LEN-1)) > 0 ) | |
671 | dist += tmp*tmp; | |
672 | if ((tmp = green - gm[i]) > 0 || | |
673 | (tmp = gm[i] - (green + MAX_COLOR/C_LEN-1)) > 0 ) | |
674 | dist += tmp*tmp; | |
675 | if ((tmp = blue - bm[i]) > 0 || | |
676 | (tmp = bm[i] - (blue + MAX_COLOR/C_LEN-1)) > 0 ) | |
677 | dist += tmp*tmp; | |
678 | if (dist < mindist) { | |
679 | ptr->entries[ptr->num_ents][0] = i; | |
680 | ptr->entries[ptr->num_ents][1] = dist; | |
681 | ++ptr->num_ents; | |
682 | } | |
683 | } | |
684 | ||
685 | /* | |
686 | * Sort color cells by distance, use cheap exchange sort | |
687 | */ | |
688 | for (n = ptr->num_ents - 1; n > 0; n = next_n) { | |
689 | next_n = 0; | |
690 | for (i = 0; i < n; ++i) | |
691 | if (ptr->entries[i][1] > ptr->entries[i+1][1]) { | |
692 | tmp = ptr->entries[i][0]; | |
693 | ptr->entries[i][0] = ptr->entries[i+1][0]; | |
694 | ptr->entries[i+1][0] = tmp; | |
695 | tmp = ptr->entries[i][1]; | |
696 | ptr->entries[i][1] = ptr->entries[i+1][1]; | |
697 | ptr->entries[i+1][1] = tmp; | |
698 | next_n = i; | |
699 | } | |
700 | } | |
701 | return (ptr); | |
702 | } | |
703 | ||
704 | static void | |
705 | map_colortable(void) | |
706 | { | |
707 | register uint32 *histp = &histogram[0][0][0]; | |
708 | register C_cell *cell; | |
709 | register int j, tmp, d2, dist; | |
710 | int ir, ig, ib, i; | |
711 | ||
712 | for (ir = 0; ir < B_LEN; ++ir) | |
713 | for (ig = 0; ig < B_LEN; ++ig) | |
714 | for (ib = 0; ib < B_LEN; ++ib, histp++) { | |
715 | if (*histp == 0) { | |
716 | *histp = -1; | |
717 | continue; | |
718 | } | |
719 | cell = *(ColorCells + | |
720 | (((ir>>(B_DEPTH-C_DEPTH)) << C_DEPTH*2) + | |
721 | ((ig>>(B_DEPTH-C_DEPTH)) << C_DEPTH) + | |
722 | (ib>>(B_DEPTH-C_DEPTH)))); | |
723 | if (cell == NULL ) | |
724 | cell = create_colorcell( | |
725 | ir << COLOR_SHIFT, | |
726 | ig << COLOR_SHIFT, | |
727 | ib << COLOR_SHIFT); | |
728 | dist = 9999999; | |
729 | for (i = 0; i < cell->num_ents && | |
730 | dist > cell->entries[i][1]; ++i) { | |
731 | j = cell->entries[i][0]; | |
732 | d2 = rm[j] - (ir << COLOR_SHIFT); | |
733 | d2 *= d2; | |
734 | tmp = gm[j] - (ig << COLOR_SHIFT); | |
735 | d2 += tmp*tmp; | |
736 | tmp = bm[j] - (ib << COLOR_SHIFT); | |
737 | d2 += tmp*tmp; | |
738 | if (d2 < dist) { | |
739 | dist = d2; | |
740 | *histp = j; | |
741 | } | |
742 | } | |
743 | } | |
744 | } | |
745 | ||
746 | /* | |
747 | * straight quantization. Each pixel is mapped to the colors | |
748 | * closest to it. Color values are rounded to the nearest color | |
749 | * table entry. | |
750 | */ | |
751 | static void | |
752 | quant(TIFF* in, TIFF* out) | |
753 | { | |
754 | unsigned char *outline, *inputline; | |
755 | register unsigned char *outptr, *inptr; | |
756 | register uint32 i, j; | |
757 | register int red, green, blue; | |
758 | ||
759 | inputline = (unsigned char *)_TIFFmalloc(TIFFScanlineSize(in)); | |
760 | outline = (unsigned char *)_TIFFmalloc(imagewidth); | |
761 | for (i = 0; i < imagelength; i++) { | |
762 | if (TIFFReadScanline(in, inputline, i, 0) <= 0) | |
763 | break; | |
764 | inptr = inputline; | |
765 | outptr = outline; | |
766 | for (j = 0; j < imagewidth; j++) { | |
767 | red = *inptr++ >> COLOR_SHIFT; | |
768 | green = *inptr++ >> COLOR_SHIFT; | |
769 | blue = *inptr++ >> COLOR_SHIFT; | |
770 | *outptr++ = (unsigned char)histogram[red][green][blue]; | |
771 | } | |
772 | if (TIFFWriteScanline(out, outline, i, 0) < 0) | |
773 | break; | |
774 | } | |
775 | _TIFFfree(inputline); | |
776 | _TIFFfree(outline); | |
777 | } | |
778 | ||
779 | #define SWAP(type,a,b) { type p; p = a; a = b; b = p; } | |
780 | ||
781 | #define GetInputLine(tif, row, bad) \ | |
782 | if (TIFFReadScanline(tif, inputline, row, 0) <= 0) \ | |
783 | bad; \ | |
784 | inptr = inputline; \ | |
785 | nextptr = nextline; \ | |
786 | for (j = 0; j < imagewidth; ++j) { \ | |
787 | *nextptr++ = *inptr++; \ | |
788 | *nextptr++ = *inptr++; \ | |
789 | *nextptr++ = *inptr++; \ | |
790 | } | |
791 | #define GetComponent(raw, cshift, c) \ | |
792 | cshift = raw; \ | |
793 | if (cshift < 0) \ | |
794 | cshift = 0; \ | |
795 | else if (cshift >= MAX_COLOR) \ | |
796 | cshift = MAX_COLOR-1; \ | |
797 | c = cshift; \ | |
798 | cshift >>= COLOR_SHIFT; | |
799 | ||
800 | static void | |
801 | quant_fsdither(TIFF* in, TIFF* out) | |
802 | { | |
803 | unsigned char *outline, *inputline, *inptr; | |
804 | short *thisline, *nextline; | |
805 | register unsigned char *outptr; | |
806 | register short *thisptr, *nextptr; | |
807 | register uint32 i, j; | |
808 | uint32 imax, jmax; | |
809 | int lastline, lastpixel; | |
810 | ||
811 | imax = imagelength - 1; | |
812 | jmax = imagewidth - 1; | |
813 | inputline = (unsigned char *)_TIFFmalloc(TIFFScanlineSize(in)); | |
814 | thisline = (short *)_TIFFmalloc(imagewidth * 3 * sizeof (short)); | |
815 | nextline = (short *)_TIFFmalloc(imagewidth * 3 * sizeof (short)); | |
816 | outline = (unsigned char *) _TIFFmalloc(TIFFScanlineSize(out)); | |
817 | ||
818 | GetInputLine(in, 0, goto bad); /* get first line */ | |
819 | for (i = 1; i <= imagelength; ++i) { | |
820 | SWAP(short *, thisline, nextline); | |
821 | lastline = (i >= imax); | |
822 | if (i <= imax) | |
823 | GetInputLine(in, i, break); | |
824 | thisptr = thisline; | |
825 | nextptr = nextline; | |
826 | outptr = outline; | |
827 | for (j = 0; j < imagewidth; ++j) { | |
828 | int red, green, blue; | |
829 | register int oval, r2, g2, b2; | |
830 | ||
831 | lastpixel = (j == jmax); | |
832 | GetComponent(*thisptr++, r2, red); | |
833 | GetComponent(*thisptr++, g2, green); | |
834 | GetComponent(*thisptr++, b2, blue); | |
835 | oval = histogram[r2][g2][b2]; | |
836 | if (oval == -1) { | |
837 | int ci; | |
838 | register int cj, tmp, d2, dist; | |
839 | register C_cell *cell; | |
840 | ||
841 | cell = *(ColorCells + | |
842 | (((r2>>(B_DEPTH-C_DEPTH)) << C_DEPTH*2) + | |
843 | ((g2>>(B_DEPTH-C_DEPTH)) << C_DEPTH ) + | |
844 | (b2>>(B_DEPTH-C_DEPTH)))); | |
845 | if (cell == NULL) | |
846 | cell = create_colorcell(red, | |
847 | green, blue); | |
848 | dist = 9999999; | |
849 | for (ci = 0; ci < cell->num_ents && dist > cell->entries[ci][1]; ++ci) { | |
850 | cj = cell->entries[ci][0]; | |
851 | d2 = (rm[cj] >> COLOR_SHIFT) - r2; | |
852 | d2 *= d2; | |
853 | tmp = (gm[cj] >> COLOR_SHIFT) - g2; | |
854 | d2 += tmp*tmp; | |
855 | tmp = (bm[cj] >> COLOR_SHIFT) - b2; | |
856 | d2 += tmp*tmp; | |
857 | if (d2 < dist) { | |
858 | dist = d2; | |
859 | oval = cj; | |
860 | } | |
861 | } | |
862 | histogram[r2][g2][b2] = oval; | |
863 | } | |
864 | *outptr++ = oval; | |
865 | red -= rm[oval]; | |
866 | green -= gm[oval]; | |
867 | blue -= bm[oval]; | |
868 | if (!lastpixel) { | |
869 | thisptr[0] += blue * 7 / 16; | |
870 | thisptr[1] += green * 7 / 16; | |
871 | thisptr[2] += red * 7 / 16; | |
872 | } | |
873 | if (!lastline) { | |
874 | if (j != 0) { | |
875 | nextptr[-3] += blue * 3 / 16; | |
876 | nextptr[-2] += green * 3 / 16; | |
877 | nextptr[-1] += red * 3 / 16; | |
878 | } | |
879 | nextptr[0] += blue * 5 / 16; | |
880 | nextptr[1] += green * 5 / 16; | |
881 | nextptr[2] += red * 5 / 16; | |
882 | if (!lastpixel) { | |
883 | nextptr[3] += blue / 16; | |
884 | nextptr[4] += green / 16; | |
885 | nextptr[5] += red / 16; | |
886 | } | |
887 | nextptr += 3; | |
888 | } | |
889 | } | |
890 | if (TIFFWriteScanline(out, outline, i-1, 0) < 0) | |
891 | break; | |
892 | } | |
893 | bad: | |
894 | _TIFFfree(inputline); | |
895 | _TIFFfree(thisline); | |
896 | _TIFFfree(nextline); | |
897 | _TIFFfree(outline); | |
898 | } | |
80ed523f VZ |
899 | /* |
900 | * Local Variables: | |
901 | * mode: c | |
902 | * c-basic-offset: 8 | |
903 | * fill-column: 78 | |
904 | * End: | |
905 | */ |