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