1 /*
2 *
3 * mediancut algorithm implementation is imported from pnmcolormap.c
4 * in netpbm library.
5 * http://netpbm.sourceforge.net/
6 *
7 * *******************************************************************************
8 * original license block of pnmcolormap.c
9 * *******************************************************************************
10 *
11 * Derived from ppmquant, originally by Jef Poskanzer.
12 *
13 * Copyright (C) 1989, 1991 by Jef Poskanzer.
14 * Copyright (C) 2001 by Bryan Henderson.
15 *
16 * Permission to use, copy, modify, and distribute this software and its
17 * documentation for any purpose and without fee is hereby granted, provided
18 * that the above copyright notice appear in all copies and that both that
19 * copyright notice and this permission notice appear in supporting
20 * documentation. This software is provided "as is" without express or
21 * implied warranty.
22 *
23 * ******************************************************************************
24 *
25 * Copyright (c) 2014 Hayaki Saito
26 *
27 * Permission is hereby granted, free of charge, to any person obtaining a copy of
28 * this software and associated documentation files (the "Software"), to deal in
29 * the Software without restriction, including without limitation the rights to
30 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
31 * the Software, and to permit persons to whom the Software is furnished to do so,
32 * subject to the following conditions:
33 *
34 * The above copyright notice and this permission notice shall be included in all
35 * copies or substantial portions of the Software.
36 *
37 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
38 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
39 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
40 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
41 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
42 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
43 *
44 *
45 */
46
47 #include "config.h"
48
49 #include <string.h>
50 #include <stdlib.h>
51 #include <stdio.h>
52 #include <math.h>
53 #include <limits.h>
54
55 #if defined(HAVE_INTTYPES_H)
56 # include <inttypes.h>
57 #endif
58
59 #include "quant.h"
60 #include "sixel.h"
61
62 #if 0
63 #define quant_trace fprintf
64 #else
quant_trace(FILE * f,...)65 static inline void quant_trace(FILE *f, ...) {}
66 #endif
67
68 /*****************************************************************************
69 *
70 * quantization
71 *
72 *****************************************************************************/
73
74 typedef struct box* boxVector;
75 struct box {
76 int ind;
77 int colors;
78 int sum;
79 };
80
81 typedef unsigned long sample;
82 typedef sample * tuple;
83
84 struct tupleint {
85 /* An ordered pair of a tuple value and an integer, such as you
86 would find in a tuple table or tuple hash.
87 Note that this is a variable length structure.
88 */
89 int value;
90 sample tuple[1];
91 /* This is actually a variable size array -- its size is the
92 depth of the tuple in question. Some compilers do not let us
93 declare a variable length array.
94 */
95 };
96 typedef struct tupleint ** tupletable;
97
98 typedef struct {
99 unsigned int size;
100 tupletable table;
101 } tupletable2;
102
103 static unsigned int compareplanePlane;
104 /* This is a parameter to compareplane(). We use this global variable
105 so that compareplane() can be called by qsort(), to compare two
106 tuples. qsort() doesn't pass any arguments except the two tuples.
107 */
108 static int
compareplane(const void * const arg1,const void * const arg2)109 compareplane(const void * const arg1,
110 const void * const arg2)
111 {
112
113 const struct tupleint * const * const comparandPP = arg1;
114 const struct tupleint * const * const comparatorPP = arg2;
115
116 return (*comparandPP)->tuple[compareplanePlane] -
117 (*comparatorPP)->tuple[compareplanePlane];
118 }
119
120
121 static int
sumcompare(const void * const b1,const void * const b2)122 sumcompare(const void * const b1, const void * const b2)
123 {
124 return(((boxVector)b2)->sum - ((boxVector)b1)->sum);
125 }
126
127
128 static tupletable const
alloctupletable(unsigned int const depth,unsigned int const size)129 alloctupletable(unsigned int const depth, unsigned int const size)
130 {
131 if (UINT_MAX / sizeof(struct tupleint) < size) {
132 quant_trace(stderr, "size %u is too big for arithmetic\n", size);
133 return NULL;
134 }
135
136 unsigned int const mainTableSize = size * sizeof(struct tupleint *);
137 unsigned int const tupleIntSize =
138 sizeof(struct tupleint) - sizeof(sample)
139 + depth * sizeof(sample);
140
141 /* To save the enormous amount of time it could take to allocate
142 each individual tuple, we do a trick here and allocate everything
143 as a single malloc block and suballocate internally.
144 */
145 if ((UINT_MAX - mainTableSize) / tupleIntSize < size) {
146 quant_trace(stderr, "size %u is too big for arithmetic\n", size);
147 return NULL;
148 }
149
150 unsigned int const allocSize = mainTableSize + size * tupleIntSize;
151 void * pool;
152
153 pool = malloc(allocSize);
154
155 if (!pool) {
156 quant_trace(stderr, "Unable to allocate %u bytes for a %u-entry "
157 "tuple table\n", allocSize, size);
158 return NULL;
159 }
160 tupletable const tbl = (tupletable) pool;
161
162 unsigned int i;
163
164 for (i = 0; i < size; ++i)
165 tbl[i] = (struct tupleint *)
166 ((char*)pool + mainTableSize + i * tupleIntSize);
167
168 return tbl;
169 }
170
171
172
173 /*
174 ** Here is the fun part, the median-cut colormap generator. This is based
175 ** on Paul Heckbert's paper "Color Image Quantization for Frame Buffer
176 ** Display", SIGGRAPH '82 Proceedings, page 297.
177 */
178
179 static tupletable2
newColorMap(unsigned int const newcolors,unsigned int const depth)180 newColorMap(unsigned int const newcolors, unsigned int const depth)
181 {
182 tupletable2 colormap;
183 unsigned int i;
184 tupletable table;
185
186 colormap.size = 0;
187 colormap.table = alloctupletable(depth, newcolors);
188 if (colormap.table) {
189 for (i = 0; i < newcolors; ++i) {
190 unsigned int plane;
191 for (plane = 0; plane < depth; ++plane)
192 colormap.table[i]->tuple[plane] = 0;
193 }
194 colormap.size = newcolors;
195 }
196
197 return colormap;
198 }
199
200
201 static boxVector
newBoxVector(int const colors,int const sum,int const newcolors)202 newBoxVector(int const colors, int const sum, int const newcolors)
203 {
204 boxVector bv;
205
206 bv = (boxVector)malloc(sizeof(struct box) * newcolors);
207 if (bv == NULL) {
208 quant_trace(stderr, "out of memory allocating box vector table\n");
209 return NULL;
210 }
211
212 /* Set up the initial box. */
213 bv[0].ind = 0;
214 bv[0].colors = colors;
215 bv[0].sum = sum;
216
217 return bv;
218 }
219
220
221 static void
findBoxBoundaries(tupletable2 const colorfreqtable,unsigned int const depth,unsigned int const boxStart,unsigned int const boxSize,sample minval[],sample maxval[])222 findBoxBoundaries(tupletable2 const colorfreqtable,
223 unsigned int const depth,
224 unsigned int const boxStart,
225 unsigned int const boxSize,
226 sample minval[],
227 sample maxval[])
228 {
229 /*----------------------------------------------------------------------------
230 Go through the box finding the minimum and maximum of each
231 component - the boundaries of the box.
232 -----------------------------------------------------------------------------*/
233 unsigned int plane;
234 unsigned int i;
235
236 for (plane = 0; plane < depth; ++plane) {
237 minval[plane] = colorfreqtable.table[boxStart]->tuple[plane];
238 maxval[plane] = minval[plane];
239 }
240
241 for (i = 1; i < boxSize; ++i) {
242 unsigned int plane;
243 for (plane = 0; plane < depth; ++plane) {
244 sample const v = colorfreqtable.table[boxStart + i]->tuple[plane];
245 if (v < minval[plane]) minval[plane] = v;
246 if (v > maxval[plane]) maxval[plane] = v;
247 }
248 }
249 }
250
251
252
253 static unsigned int
largestByNorm(sample minval[],sample maxval[],unsigned int const depth)254 largestByNorm(sample minval[], sample maxval[], unsigned int const depth)
255 {
256
257 unsigned int largestDimension;
258 unsigned int plane;
259 sample largestSpreadSoFar;
260
261 largestSpreadSoFar = 0;
262 largestDimension = 0;
263 for (plane = 0; plane < depth; ++plane) {
264 sample const spread = maxval[plane]-minval[plane];
265 if (spread > largestSpreadSoFar) {
266 largestDimension = plane;
267 largestSpreadSoFar = spread;
268 }
269 }
270 return largestDimension;
271 }
272
273
274
275 static unsigned int
largestByLuminosity(sample minval[],sample maxval[],unsigned int const depth)276 largestByLuminosity(sample minval[], sample maxval[], unsigned int const depth)
277 {
278 /*----------------------------------------------------------------------------
279 This subroutine presumes that the tuple type is either
280 BLACKANDWHITE, GRAYSCALE, or RGB (which implies pamP->depth is 1 or 3).
281 To save time, we don't actually check it.
282 -----------------------------------------------------------------------------*/
283 unsigned int retval;
284
285 double lumin_factor[3] = {0.2989, 0.5866, 0.1145};
286
287 if (depth == 1) {
288 retval = 0;
289 } else {
290 /* An RGB tuple */
291 unsigned int largestDimension;
292 unsigned int plane;
293 double largestSpreadSoFar;
294
295 largestSpreadSoFar = 0.0;
296
297 for (plane = 0; plane < 3; ++plane) {
298 double const spread =
299 lumin_factor[plane] * (maxval[plane]-minval[plane]);
300 if (spread > largestSpreadSoFar) {
301 largestDimension = plane;
302 largestSpreadSoFar = spread;
303 }
304 }
305 retval = largestDimension;
306 }
307 return retval;
308 }
309
310
311
312 static void
centerBox(int const boxStart,int const boxSize,tupletable2 const colorfreqtable,unsigned int const depth,tuple const newTuple)313 centerBox(int const boxStart,
314 int const boxSize,
315 tupletable2 const colorfreqtable,
316 unsigned int const depth,
317 tuple const newTuple)
318 {
319
320 unsigned int plane;
321
322 for (plane = 0; plane < depth; ++plane) {
323 int minval, maxval;
324 unsigned int i;
325
326 minval = maxval = colorfreqtable.table[boxStart]->tuple[plane];
327
328 for (i = 1; i < boxSize; ++i) {
329 int const v = colorfreqtable.table[boxStart + i]->tuple[plane];
330 minval = minval < v ? minval: v;
331 maxval = maxval > v ? maxval: v;
332 }
333 newTuple[plane] = (minval + maxval) / 2;
334 }
335 }
336
337
338
339 static void
averageColors(int const boxStart,int const boxSize,tupletable2 const colorfreqtable,unsigned int const depth,tuple const newTuple)340 averageColors(int const boxStart,
341 int const boxSize,
342 tupletable2 const colorfreqtable,
343 unsigned int const depth,
344 tuple const newTuple)
345 {
346 unsigned int plane;
347
348 for (plane = 0; plane < depth; ++plane) {
349 sample sum;
350 int i;
351
352 sum = 0;
353
354 for (i = 0; i < boxSize; ++i)
355 sum += colorfreqtable.table[boxStart+i]->tuple[plane];
356
357 newTuple[plane] = sum / boxSize;
358 }
359 }
360
361
362
363 static void
averagePixels(int const boxStart,int const boxSize,tupletable2 const colorfreqtable,unsigned int const depth,tuple const newTuple)364 averagePixels(int const boxStart,
365 int const boxSize,
366 tupletable2 const colorfreqtable,
367 unsigned int const depth,
368 tuple const newTuple)
369 {
370
371 unsigned int n;
372 /* Number of tuples represented by the box */
373 unsigned int plane;
374 unsigned int i;
375
376 /* Count the tuples in question */
377 n = 0; /* initial value */
378 for (i = 0; i < boxSize; ++i)
379 n += colorfreqtable.table[boxStart + i]->value;
380
381 for (plane = 0; plane < depth; ++plane) {
382 sample sum;
383 int i;
384
385 sum = 0;
386
387 for (i = 0; i < boxSize; ++i)
388 sum += colorfreqtable.table[boxStart+i]->tuple[plane]
389 * colorfreqtable.table[boxStart+i]->value;
390
391 newTuple[plane] = sum / n;
392 }
393 }
394
395
396
397 static tupletable2
colormapFromBv(unsigned int const newcolors,boxVector const bv,unsigned int const boxes,tupletable2 const colorfreqtable,unsigned int const depth,int const methodForRep)398 colormapFromBv(unsigned int const newcolors,
399 boxVector const bv,
400 unsigned int const boxes,
401 tupletable2 const colorfreqtable,
402 unsigned int const depth,
403 int const methodForRep)
404 {
405 /*
406 ** Ok, we've got enough boxes. Now choose a representative color for
407 ** each box. There are a number of possible ways to make this choice.
408 ** One would be to choose the center of the box; this ignores any structure
409 ** within the boxes. Another method would be to average all the colors in
410 ** the box - this is the method specified in Heckbert's paper. A third
411 ** method is to average all the pixels in the box.
412 */
413 tupletable2 colormap;
414 unsigned int bi;
415
416 colormap = newColorMap(newcolors, depth);
417 if (!colormap.size) {
418 return colormap;
419 }
420
421 for (bi = 0; bi < boxes; ++bi) {
422 switch (methodForRep) {
423 case REP_CENTER_BOX:
424 centerBox(bv[bi].ind, bv[bi].colors,
425 colorfreqtable, depth,
426 colormap.table[bi]->tuple);
427 break;
428 case REP_AVERAGE_COLORS:
429 averageColors(bv[bi].ind, bv[bi].colors,
430 colorfreqtable, depth,
431 colormap.table[bi]->tuple);
432 break;
433 case REP_AVERAGE_PIXELS:
434 averagePixels(bv[bi].ind, bv[bi].colors,
435 colorfreqtable, depth,
436 colormap.table[bi]->tuple);
437 break;
438 default:
439 quant_trace(stderr, "Internal error: "
440 "invalid value of methodForRep: %d\n",
441 methodForRep);
442 }
443 }
444 return colormap;
445 }
446
447
448 static int
splitBox(boxVector const bv,unsigned int * const boxesP,unsigned int const bi,tupletable2 const colorfreqtable,unsigned int const depth,int const methodForLargest)449 splitBox(boxVector const bv,
450 unsigned int *const boxesP,
451 unsigned int const bi,
452 tupletable2 const colorfreqtable,
453 unsigned int const depth,
454 int const methodForLargest)
455 {
456 /*----------------------------------------------------------------------------
457 Split Box 'bi' in the box vector bv (so that bv contains one more box
458 than it did as input). Split it so that each new box represents about
459 half of the pixels in the distribution given by 'colorfreqtable' for
460 the colors in the original box, but with distinct colors in each of the
461 two new boxes.
462
463 Assume the box contains at least two colors.
464 -----------------------------------------------------------------------------*/
465 unsigned int const boxStart = bv[bi].ind;
466 unsigned int const boxSize = bv[bi].colors;
467 unsigned int const sm = bv[bi].sum;
468
469 unsigned int const max_depth = 16;
470 sample minval[max_depth];
471 sample maxval[max_depth];
472
473 /* assert(max_depth >= depth); */
474
475 unsigned int largestDimension;
476 /* number of the plane with the largest spread */
477 unsigned int medianIndex;
478 int lowersum;
479 /* Number of pixels whose value is "less than" the median */
480
481 findBoxBoundaries(colorfreqtable, depth, boxStart, boxSize,
482 minval, maxval);
483
484 /* Find the largest dimension, and sort by that component. I have
485 included two methods for determining the "largest" dimension;
486 first by simply comparing the range in RGB space, and second by
487 transforming into luminosities before the comparison.
488 */
489 switch (methodForLargest) {
490 case LARGE_NORM:
491 largestDimension = largestByNorm(minval, maxval, depth);
492 break;
493 case LARGE_LUM:
494 largestDimension = largestByLuminosity(minval, maxval, depth);
495 break;
496 default:
497 quant_trace(stderr, "Internal error: invalid value of methodForLargest: %d\n",
498 methodForLargest);
499 return -1;
500 }
501
502 /* TODO: I think this sort should go after creating a box,
503 not before splitting. Because you need the sort to use
504 the REP_CENTER_BOX method of choosing a color to
505 represent the final boxes
506 */
507
508 /* Set the gross global variable 'compareplanePlane' as a
509 parameter to compareplane(), which is called by qsort().
510 */
511 compareplanePlane = largestDimension;
512 qsort((char*) &colorfreqtable.table[boxStart], boxSize,
513 sizeof(colorfreqtable.table[boxStart]),
514 compareplane);
515
516 {
517 /* Now find the median based on the counts, so that about half
518 the pixels (not colors, pixels) are in each subdivision. */
519
520 unsigned int i;
521
522 lowersum = colorfreqtable.table[boxStart]->value; /* initial value */
523 for (i = 1; i < boxSize - 1 && lowersum < sm/2; ++i) {
524 lowersum += colorfreqtable.table[boxStart + i]->value;
525 }
526 medianIndex = i;
527 }
528 /* Split the box, and sort to bring the biggest boxes to the top. */
529
530 bv[bi].colors = medianIndex;
531 bv[bi].sum = lowersum;
532 bv[*boxesP].ind = boxStart + medianIndex;
533 bv[*boxesP].colors = boxSize - medianIndex;
534 bv[*boxesP].sum = sm - lowersum;
535 ++(*boxesP);
536 qsort((char*) bv, *boxesP, sizeof(struct box), sumcompare);
537 return 0;
538 }
539
540
541
542 static int
mediancut(tupletable2 const colorfreqtable,unsigned int const depth,int const newcolors,int const methodForLargest,int const methodForRep,tupletable2 * const colormapP)543 mediancut(tupletable2 const colorfreqtable,
544 unsigned int const depth,
545 int const newcolors,
546 int const methodForLargest,
547 int const methodForRep,
548 tupletable2 *const colormapP)
549 {
550 /*----------------------------------------------------------------------------
551 Compute a set of only 'newcolors' colors that best represent an
552 image whose pixels are summarized by the histogram
553 'colorfreqtable'. Each tuple in that table has depth 'depth'.
554 colorfreqtable.table[i] tells the number of pixels in the subject image
555 have a particular color.
556
557 As a side effect, sort 'colorfreqtable'.
558 -----------------------------------------------------------------------------*/
559 boxVector bv;
560 unsigned int bi;
561 unsigned int boxes;
562 int multicolorBoxesExist;
563 unsigned int i;
564 unsigned int sum;
565
566 sum = 0;
567
568 for (i = 0; i < colorfreqtable.size; ++i)
569 sum += colorfreqtable.table[i]->value;
570
571 /* There is at least one box that contains at least 2 colors; ergo,
572 there is more splitting we can do.
573 */
574
575 bv = newBoxVector(colorfreqtable.size, sum, newcolors);
576 if (!bv) {
577 return (-1);
578 }
579 boxes = 1;
580 multicolorBoxesExist = (colorfreqtable.size > 1);
581
582 /* Main loop: split boxes until we have enough. */
583 while (boxes < newcolors && multicolorBoxesExist) {
584 /* Find the first splittable box. */
585 for (bi = 0; bi < boxes && bv[bi].colors < 2; ++bi);
586 if (bi >= boxes)
587 multicolorBoxesExist = 0;
588 else
589 splitBox(bv, &boxes, bi, colorfreqtable, depth, methodForLargest);
590 }
591 *colormapP = colormapFromBv(newcolors, bv, boxes,
592 colorfreqtable, depth,
593 methodForRep);
594
595 free(bv);
596 return 0;
597 }
598
599
600 static int
computeHistogram(unsigned char * data,unsigned int length,unsigned long const depth,tupletable2 * const colorfreqtableP,enum qualityMode const qualityMode)601 computeHistogram(unsigned char *data,
602 unsigned int length,
603 unsigned long const depth,
604 tupletable2 * const colorfreqtableP,
605 enum qualityMode const qualityMode)
606 {
607 typedef unsigned short unit_t;
608 unsigned int i, n;
609 unit_t *histgram;
610 unit_t *refmap;
611 unit_t *ref;
612 unit_t *it;
613 struct tupleint *t;
614 unsigned int index;
615 unsigned int step;
616 unsigned int max_sample;
617
618 if (qualityMode == QUALITY_HIGH) {
619 max_sample = 1118383;
620 } else { /* if (qualityMode == QUALITY_LOW) */
621 max_sample = 18383;
622 }
623
624 quant_trace(stderr, "making histogram...\n");
625
626 histgram = malloc((1 << depth * 5) * sizeof(unit_t));
627 if (!histgram) {
628 quant_trace(stderr, "Unable to allocate memory for histgram.");
629 return (-1);
630 }
631 memset(histgram, 0, (1 << depth * 5) * sizeof(unit_t));
632 it = ref = refmap = (unsigned short *)malloc(max_sample * sizeof(unit_t));
633 if (!it) {
634 quant_trace(stderr, "Unable to allocate memory for lookup table.");
635 return (-1);
636 }
637
638 if (length > max_sample * depth) {
639 step = length / depth / max_sample;
640 } else {
641 step = depth;
642 }
643
644 for (i = 0; i < length; i += step) {
645 index = 0;
646 for (n = 0; n < depth; n++) {
647 index |= data[i + depth - 1 - n] >> 3 << n * 5;
648 }
649 if (histgram[index] == 0) {
650 *ref++ = index;
651 }
652 if (histgram[index] < (1 << sizeof(unsigned short) * 8) - 1) {
653 histgram[index]++;
654 }
655 }
656
657 colorfreqtableP->size = ref - refmap;
658 colorfreqtableP->table = alloctupletable(depth, ref - refmap);
659 for (i = 0; i < colorfreqtableP->size; ++i) {
660 if (histgram[refmap[i]] > 0) {
661 colorfreqtableP->table[i]->value = histgram[refmap[i]];
662 for (n = 0; n < depth; n++) {
663 colorfreqtableP->table[i]->tuple[depth - 1 - n]
664 = (*it >> n * 5 & 0x1f) << 3;
665 }
666 }
667 it++;
668 }
669
670 free(refmap);
671 free(histgram);
672
673 quant_trace(stderr, "%u colors found\n", colorfreqtableP->size);
674 return 0;
675 }
676
677
678 static int
computeColorMapFromInput(unsigned char * data,size_t length,unsigned int const depth,int const reqColors,enum methodForLargest const methodForLargest,enum methodForRep const methodForRep,enum qualityMode const qualityMode,tupletable2 * const colormapP,int * origcolors)679 computeColorMapFromInput(unsigned char *data,
680 size_t length,
681 unsigned int const depth,
682 int const reqColors,
683 enum methodForLargest const methodForLargest,
684 enum methodForRep const methodForRep,
685 enum qualityMode const qualityMode,
686 tupletable2 * const colormapP,
687 int *origcolors)
688 {
689 /*----------------------------------------------------------------------------
690 Produce a colormap containing the best colors to represent the
691 image stream in file 'ifP'. Figure it out using the median cut
692 technique.
693
694 The colormap will have 'reqcolors' or fewer colors in it, unless
695 'allcolors' is true, in which case it will have all the colors that
696 are in the input.
697
698 The colormap has the same maxval as the input.
699
700 Put the colormap in newly allocated storage as a tupletable2
701 and return its address as *colormapP. Return the number of colors in
702 it as *colorsP and its maxval as *colormapMaxvalP.
703
704 Return the characteristics of the input file as
705 *formatP and *freqPamP. (This information is not really
706 relevant to our colormap mission; just a fringe benefit).
707 -----------------------------------------------------------------------------*/
708 tupletable2 colorfreqtable;
709 int i, n;
710 int ret;
711
712 ret = computeHistogram(data, length, depth, &colorfreqtable, qualityMode);
713 if (ret != 0) {
714 return (-1);
715 }
716 if (origcolors) {
717 *origcolors = colorfreqtable.size;
718 }
719
720 if (colorfreqtable.size <= reqColors) {
721 quant_trace(stderr, "Image already has few enough colors (<=%d). "
722 "Keeping same colors.\n", reqColors);
723 /* *colormapP = colorfreqtable; */
724 colormapP->size = colorfreqtable.size;
725 colormapP->table = alloctupletable(depth, colorfreqtable.size);
726 for (i = 0; i < colorfreqtable.size; ++i) {
727 colormapP->table[i]->value = colorfreqtable.table[i]->value;
728 for (n = 0; n < depth; ++n) {
729 colormapP->table[i]->tuple[n] = colorfreqtable.table[i]->tuple[n];
730 }
731 }
732 } else {
733 quant_trace(stderr, "choosing %d colors...\n", reqColors);
734 ret = mediancut(colorfreqtable, depth, reqColors,
735 methodForLargest, methodForRep, colormapP);
736 if (ret != 0) {
737 return (-1);
738 }
739 quant_trace(stderr, "%d colors are choosed.\n", colorfreqtable.size);
740 }
741
742 free(colorfreqtable.table);
743 return 0;
744 }
745
746
747 static void
error_diffuse(unsigned char * data,int pos,int depth,int offset,int mul,int div)748 error_diffuse(unsigned char *data, int pos, int depth,
749 int offset, int mul, int div)
750 {
751 int c;
752
753 data += pos * depth;
754
755 c = *data + offset * mul / div;
756 if (c < 0) {
757 c = 0;
758 }
759 if (c >= 1 << 8) {
760 c = (1 << 8) - 1;
761 }
762 *data = (unsigned char)c;
763 }
764
765
766 static void
diffuse_none(unsigned char * data,int width,int height,int x,int y,int depth,int offset)767 diffuse_none(unsigned char *data, int width, int height,
768 int x, int y, int depth, int offset)
769 {
770 }
771
772
773 static void
diffuse_atkinson(unsigned char * data,int width,int height,int x,int y,int depth,int offset)774 diffuse_atkinson(unsigned char *data, int width, int height,
775 int x, int y, int depth, int offset)
776 {
777 int pos, n;
778
779 pos = y * width + x;
780
781 if (x < width - 2 && y < height - 2) {
782 /* add offset to the right cell */
783 error_diffuse(data, pos + width * 0 + 1, depth, offset, 1, 8);
784 /* add offset to the 2th right cell */
785 error_diffuse(data, pos + width * 0 + 2, depth, offset, 1, 8);
786 /* add offset to the left-bottom cell */
787 error_diffuse(data, pos + width * 1 - 1, depth, offset, 1, 8);
788 /* add offset to the bottom cell */
789 error_diffuse(data, pos + width * 1 + 0, depth, offset, 1, 8);
790 /* add offset to the right-bottom cell */
791 error_diffuse(data, pos + width * 1 + 1, depth, offset, 1, 8);
792 /* add offset to the 2th bottom cell */
793 error_diffuse(data, pos + width * 2 + 0, depth, offset, 1, 8);
794 }
795 }
796
797
798 static void
diffuse_fs(unsigned char * data,int width,int height,int x,int y,int depth,int offset)799 diffuse_fs(unsigned char *data, int width, int height,
800 int x, int y, int depth, int offset)
801 {
802 int n;
803 int pos;
804
805 pos = y * width + x;
806
807 /* Floyd Steinberg Method
808 * curr 7/16
809 * 3/16 5/48 1/16
810 */
811 if (x > 1 && x < width - 1 && y < height - 1) {
812 /* add offset to the right cell */
813 error_diffuse(data, pos + width * 0 + 1, depth, offset, 7, 16);
814 /* add offset to the left-bottom cell */
815 error_diffuse(data, pos + width * 1 - 1, depth, offset, 3, 16);
816 /* add offset to the bottom cell */
817 error_diffuse(data, pos + width * 1 + 0, depth, offset, 5, 16);
818 /* add offset to the right-bottom cell */
819 error_diffuse(data, pos + width * 1 + 1, depth, offset, 1, 16);
820 }
821 }
822
823
824 static void
diffuse_jajuni(unsigned char * data,int width,int height,int x,int y,int depth,int offset)825 diffuse_jajuni(unsigned char *data, int width, int height,
826 int x, int y, int depth, int offset)
827 {
828 int n;
829 int pos;
830
831 pos = y * width + x;
832
833 /* Jarvis, Judice & Ninke Method
834 * curr 7/48 5/48
835 * 3/48 5/48 7/48 5/48 3/48
836 * 1/48 3/48 5/48 3/48 1/48
837 */
838 if (x > 2 && x < width - 2 && y < height - 2) {
839 error_diffuse(data, pos + width * 0 + 1, depth, offset, 7, 48);
840 error_diffuse(data, pos + width * 0 + 2, depth, offset, 5, 48);
841 error_diffuse(data, pos + width * 1 - 2, depth, offset, 3, 48);
842 error_diffuse(data, pos + width * 1 - 1, depth, offset, 5, 48);
843 error_diffuse(data, pos + width * 1 + 0, depth, offset, 7, 48);
844 error_diffuse(data, pos + width * 1 + 1, depth, offset, 5, 48);
845 error_diffuse(data, pos + width * 1 + 2, depth, offset, 3, 48);
846 error_diffuse(data, pos + width * 2 - 2, depth, offset, 1, 48);
847 error_diffuse(data, pos + width * 2 - 1, depth, offset, 3, 48);
848 error_diffuse(data, pos + width * 2 + 0, depth, offset, 5, 48);
849 error_diffuse(data, pos + width * 2 + 1, depth, offset, 3, 48);
850 error_diffuse(data, pos + width * 2 + 2, depth, offset, 1, 48);
851 }
852 }
853
854
855 static void
diffuse_stucki(unsigned char * data,int width,int height,int x,int y,int depth,int offset)856 diffuse_stucki(unsigned char *data, int width, int height,
857 int x, int y, int depth, int offset)
858 {
859 int n;
860 int pos;
861
862 pos = y * width + x;
863
864 /* Stucki's Method
865 * curr 8/48 4/48
866 * 2/48 4/48 8/48 4/48 2/48
867 * 1/48 2/48 4/48 2/48 1/48
868 */
869 if (x > 2 && x < width - 2 && y < height - 2) {
870 error_diffuse(data, pos + width * 0 + 1, depth, offset, 1, 6);
871 error_diffuse(data, pos + width * 0 + 2, depth, offset, 1, 12);
872 error_diffuse(data, pos + width * 1 - 2, depth, offset, 1, 24);
873 error_diffuse(data, pos + width * 1 - 1, depth, offset, 1, 12);
874 error_diffuse(data, pos + width * 1 + 0, depth, offset, 1, 6);
875 error_diffuse(data, pos + width * 1 + 1, depth, offset, 1, 12);
876 error_diffuse(data, pos + width * 1 + 2, depth, offset, 1, 24);
877 error_diffuse(data, pos + width * 2 - 2, depth, offset, 1, 48);
878 error_diffuse(data, pos + width * 2 - 1, depth, offset, 1, 24);
879 error_diffuse(data, pos + width * 2 + 0, depth, offset, 1, 12);
880 error_diffuse(data, pos + width * 2 + 1, depth, offset, 1, 24);
881 error_diffuse(data, pos + width * 2 + 2, depth, offset, 1, 48);
882 }
883 }
884
885
886 static void
diffuse_burkes(unsigned char * data,int width,int height,int x,int y,int depth,int offset)887 diffuse_burkes(unsigned char *data, int width, int height,
888 int x, int y, int depth, int offset)
889 {
890 int n;
891 int pos;
892
893 pos = y * width + x;
894
895 /* Burkes' Method
896 * curr 4/16 2/16
897 * 1/16 2/16 4/16 2/16 1/16
898 */
899 if (x > 2 && x < width - 2 && y < height - 2) {
900 error_diffuse(data, pos + width * 0 + 1, depth, offset, 1, 4);
901 error_diffuse(data, pos + width * 0 + 2, depth, offset, 1, 8);
902 error_diffuse(data, pos + width * 1 - 2, depth, offset, 1, 16);
903 error_diffuse(data, pos + width * 1 - 1, depth, offset, 1, 8);
904 error_diffuse(data, pos + width * 1 + 0, depth, offset, 1, 4);
905 error_diffuse(data, pos + width * 1 + 1, depth, offset, 1, 8);
906 error_diffuse(data, pos + width * 1 + 2, depth, offset, 1, 16);
907 }
908 }
909
910
911 static int
lookup_normal(unsigned char const * const pixel,int const depth,unsigned char const * const palette,int const ncolor,unsigned short * const cachetable)912 lookup_normal(unsigned char const * const pixel,
913 int const depth,
914 unsigned char const * const palette,
915 int const ncolor,
916 unsigned short * const cachetable)
917 {
918 int index;
919 int diff;
920 int r;
921 int i;
922 int n;
923 int distant;
924
925 index = -1;
926 diff = INT_MAX;
927
928 for (i = 0; i < ncolor; i++) {
929 distant = 0;
930 for (n = 0; n < depth; ++n) {
931 r = pixel[n] - palette[i * depth + n];
932 distant += r * r;
933 }
934 if (distant < diff) {
935 diff = distant;
936 index = i;
937 }
938 }
939
940 return index;
941 }
942
943
944 static int
lookup_fast(unsigned char const * const pixel,int const depth,unsigned char const * const palette,int const ncolor,unsigned short * const cachetable)945 lookup_fast(unsigned char const * const pixel,
946 int const depth,
947 unsigned char const * const palette,
948 int const ncolor,
949 unsigned short * const cachetable)
950 {
951 int hash;
952 int index;
953 int diff;
954 int cache;
955 int r;
956 int i;
957 int n;
958 int distant;
959
960 index = -1;
961 diff = INT_MAX;
962 hash = 0;
963
964 for (n = 0; n < 3; ++n) {
965 hash |= *(pixel + n) >> 3 << ((3 - 1 - n) * 5);
966 }
967
968 cache = cachetable[hash];
969 if (cache) { /* fast lookup */
970 return cache - 1;
971 }
972 /* collision */
973 for (i = 0; i < ncolor; i++) {
974 distant = 0;
975 for (n = 0; n < 3; ++n) {
976 r = pixel[n] - palette[i * 3 + n];
977 distant += r * r;
978 }
979 if (distant < diff) {
980 diff = distant;
981 index = i;
982 }
983 }
984 cachetable[hash] = index + 1;
985
986 return index;
987 }
988
989
990 static int
lookup_mono_darkbg(unsigned char const * const pixel,int const depth,unsigned char const * const palette,int const ncolor,unsigned short * const cachetable)991 lookup_mono_darkbg(unsigned char const * const pixel,
992 int const depth,
993 unsigned char const * const palette,
994 int const ncolor,
995 unsigned short * const cachetable)
996 {
997 int n;
998 int distant;
999
1000 distant = 0;
1001 for (n = 0; n < depth; ++n) {
1002 distant += pixel[n];
1003 }
1004 return distant >= 128 * ncolor ? 1: 0;
1005 }
1006
1007
1008 static int
lookup_mono_lightbg(unsigned char const * const pixel,int const depth,unsigned char const * const palette,int const ncolor,unsigned short * const cachetable)1009 lookup_mono_lightbg(unsigned char const * const pixel,
1010 int const depth,
1011 unsigned char const * const palette,
1012 int const ncolor,
1013 unsigned short * const cachetable)
1014 {
1015 int n;
1016 int distant;
1017
1018 distant = 0;
1019 for (n = 0; n < depth; ++n) {
1020 distant += pixel[n];
1021 }
1022 return distant < 128 * ncolor ? 1: 0;
1023 }
1024
1025
1026 unsigned char *
LSQ_MakePalette(unsigned char * data,int x,int y,int depth,int reqcolors,int * ncolors,int * origcolors,int methodForLargest,int methodForRep,int qualityMode)1027 LSQ_MakePalette(unsigned char *data, int x, int y, int depth,
1028 int reqcolors, int *ncolors, int *origcolors,
1029 int methodForLargest,
1030 int methodForRep,
1031 int qualityMode)
1032 {
1033 int i, n;
1034 int ret;
1035 unsigned char *palette;
1036 tupletable2 colormap;
1037
1038 ret = computeColorMapFromInput(data, x * y * depth, depth,
1039 reqcolors, methodForLargest,
1040 methodForRep, qualityMode,
1041 &colormap, origcolors);
1042 if (ret != 0) {
1043 return NULL;
1044 }
1045 *ncolors = colormap.size;
1046 quant_trace(stderr, "tupletable size: %d", *ncolors);
1047 palette = malloc(*ncolors * depth);
1048 for (i = 0; i < *ncolors; i++) {
1049 for (n = 0; n < depth; ++n) {
1050 palette[i * depth + n] = colormap.table[i]->tuple[n];
1051 }
1052 }
1053
1054 free(colormap.table);
1055 return palette;
1056 }
1057
1058 int
LSQ_ApplyPalette(unsigned char * data,int width,int height,int depth,unsigned char * palette,int ncolor,int methodForDiffuse,int foptimize,unsigned short * cachetable,unsigned char * result)1059 LSQ_ApplyPalette(unsigned char *data,
1060 int width,
1061 int height,
1062 int depth,
1063 unsigned char *palette,
1064 int ncolor,
1065 int methodForDiffuse,
1066 int foptimize,
1067 unsigned short *cachetable,
1068 unsigned char *result)
1069 {
1070 typedef int component_t;
1071 int pos, j, n, x, y, sum1, sum2;
1072 component_t offset;
1073 int diff;
1074 int index;
1075 unsigned short *indextable;
1076 void (*f_diffuse)(unsigned char *data, int width, int height,
1077 int x, int y, int depth, int offset);
1078 int (*f_lookup)(unsigned char const * const pixel,
1079 int const depth,
1080 unsigned char const * const palette,
1081 int const ncolor,
1082 unsigned short * const cachetable);
1083
1084 if (depth != 3) {
1085 f_diffuse = diffuse_none;
1086 } else {
1087 switch (methodForDiffuse) {
1088 case DIFFUSE_NONE:
1089 f_diffuse = diffuse_none;
1090 break;
1091 case DIFFUSE_ATKINSON:
1092 f_diffuse = diffuse_atkinson;
1093 break;
1094 case DIFFUSE_FS:
1095 f_diffuse = diffuse_fs;
1096 break;
1097 case DIFFUSE_JAJUNI:
1098 f_diffuse = diffuse_jajuni;
1099 break;
1100 case DIFFUSE_STUCKI:
1101 f_diffuse = diffuse_stucki;
1102 break;
1103 case DIFFUSE_BURKES:
1104 f_diffuse = diffuse_burkes;
1105 break;
1106 default:
1107 quant_trace(stderr, "Internal error: invalid value of"
1108 " methodForDiffuse: %d\n",
1109 methodForDiffuse);
1110 f_diffuse = diffuse_none;
1111 break;
1112 }
1113 }
1114
1115 f_lookup = NULL;
1116 if (ncolor == 2) {
1117 sum1 = 0;
1118 sum2 = 0;
1119 for (n = 0; n < depth; ++n) {
1120 sum1 += palette[n];
1121 }
1122 for (n = depth; n < depth + depth; ++n) {
1123 sum2 += palette[n];
1124 }
1125 if (sum1 == 0 && sum2 == 255 * 3) {
1126 f_lookup = lookup_mono_darkbg;
1127 } else if (sum1 == 255 * 3 && sum2 == 0) {
1128 f_lookup = lookup_mono_lightbg;
1129 }
1130 }
1131 if (f_lookup == NULL) {
1132 if (foptimize && depth == 3) {
1133 f_lookup = lookup_fast;
1134 } else {
1135 f_lookup = lookup_normal;
1136 }
1137 }
1138
1139 indextable = cachetable;
1140 if (cachetable == NULL && f_lookup == lookup_fast) {
1141 indextable = malloc((1 << depth * 5) * sizeof(unsigned short));
1142 if (!indextable) {
1143 quant_trace(stderr, "Unable to allocate memory for indextable.");
1144 return (-1);
1145 }
1146 memset(indextable, 0x00, (1 << depth * 5) * sizeof(unsigned short));
1147 }
1148
1149 for (y = 0; y < height; ++y) {
1150 for (x = 0; x < width; ++x) {
1151 pos = y * width + x;
1152 index = f_lookup(data + (pos * depth), depth,
1153 palette, ncolor, indextable);
1154 result[pos] = index;
1155 for (n = 0; n < depth; ++n) {
1156 offset = data[pos * depth + n] - palette[index * depth + n];
1157 f_diffuse(data + n, width, height, x, y, depth, offset);
1158 }
1159 }
1160 }
1161
1162 if (cachetable == NULL) {
1163 free(indextable);
1164 }
1165
1166 return 0;
1167 }
1168
1169
1170 void
LSQ_FreePalette(unsigned char * data)1171 LSQ_FreePalette(unsigned char * data)
1172 {
1173 free(data);
1174 }
1175
1176 /* emacs, -*- Mode: C; tab-width: 4; indent-tabs-mode: nil -*- */
1177 /* vim: set expandtab ts=4 : */
1178 /* EOF */
1179