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-2018 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 #if STDC_HEADERS
50 # include <stdlib.h>
51 # include <stdio.h>
52 #endif  /* STDC_HEADERS */
53 #if HAVE_STRING_H
54 # include <string.h>
55 #endif  /* HAVE_STRING_H */
56 #if HAVE_MATH_H
57 #include <math.h>
58 #endif  /* HAVE_MATH_H */
59 #if HAVE_LIMITS_H
60 # include <limits.h>
61 #endif  /* HAVE_MATH_H */
62 #if HAVE_INTTYPES_H
63 # include <inttypes.h>
64 #endif  /* HAVE_MATH_H */
65 
66 #include "quant.h"
67 
68 #if HAVE_DEBUG
69 #define quant_trace fprintf
70 #else
quant_trace(FILE * f,...)71 static inline void quant_trace(FILE *f, ...) { (void) f; }
72 #endif
73 
74 /*****************************************************************************
75  *
76  * quantization
77  *
78  *****************************************************************************/
79 
80 typedef struct box* boxVector;
81 struct box {
82     unsigned int ind;
83     unsigned int colors;
84     unsigned int sum;
85 };
86 
87 typedef unsigned long sample;
88 typedef sample * tuple;
89 
90 struct tupleint {
91     /* An ordered pair of a tuple value and an integer, such as you
92        would find in a tuple table or tuple hash.
93        Note that this is a variable length structure.
94     */
95     unsigned int value;
96     sample tuple[1];
97     /* This is actually a variable size array -- its size is the
98        depth of the tuple in question.  Some compilers do not let us
99        declare a variable length array.
100     */
101 };
102 typedef struct tupleint ** tupletable;
103 
104 typedef struct {
105     unsigned int size;
106     tupletable table;
107 } tupletable2;
108 
109 static unsigned int compareplanePlane;
110     /* This is a parameter to compareplane().  We use this global variable
111        so that compareplane() can be called by qsort(), to compare two
112        tuples.  qsort() doesn't pass any arguments except the two tuples.
113     */
114 static int
compareplane(const void * const arg1,const void * const arg2)115 compareplane(const void * const arg1,
116              const void * const arg2)
117 {
118     int lhs, rhs;
119 
120     typedef const struct tupleint * const * const sortarg;
121     sortarg comparandPP  = (sortarg) arg1;
122     sortarg comparatorPP = (sortarg) arg2;
123     lhs = (int)(*comparandPP)->tuple[compareplanePlane];
124     rhs = (int)(*comparatorPP)->tuple[compareplanePlane];
125 
126     return lhs - rhs;
127 }
128 
129 
130 static int
sumcompare(const void * const b1,const void * const b2)131 sumcompare(const void * const b1, const void * const b2)
132 {
133     return (int)((boxVector)b2)->sum - (int)((boxVector)b1)->sum;
134 }
135 
136 
137 static SIXELSTATUS
alloctupletable(tupletable * result,unsigned int const depth,unsigned int const size,sixel_allocator_t * allocator)138 alloctupletable(
139     tupletable          /* out */ *result,
140     unsigned int const  /* in */  depth,
141     unsigned int const  /* in */  size,
142     sixel_allocator_t   /* in */  *allocator)
143 {
144     SIXELSTATUS status = SIXEL_FALSE;
145     enum { message_buffer_size = 256 };
146     char message[message_buffer_size];
147     int nwrite;
148     unsigned int mainTableSize;
149     unsigned int tupleIntSize;
150     unsigned int allocSize;
151     void * pool;
152     tupletable tbl;
153     unsigned int i;
154 
155     if (UINT_MAX / sizeof(struct tupleint) < size) {
156         nwrite = sprintf(message,
157                          "size %u is too big for arithmetic",
158                          size);
159         if (nwrite > 0) {
160             sixel_helper_set_additional_message(message);
161         }
162         status = SIXEL_RUNTIME_ERROR;
163         goto end;
164     }
165 
166     mainTableSize = size * sizeof(struct tupleint *);
167     tupleIntSize = sizeof(struct tupleint) - sizeof(sample)
168         + depth * sizeof(sample);
169 
170     /* To save the enormous amount of time it could take to allocate
171        each individual tuple, we do a trick here and allocate everything
172        as a single malloc block and suballocate internally.
173     */
174     if ((UINT_MAX - mainTableSize) / tupleIntSize < size) {
175         nwrite = sprintf(message,
176                          "size %u is too big for arithmetic",
177                          size);
178         if (nwrite > 0) {
179             sixel_helper_set_additional_message(message);
180         }
181         status = SIXEL_RUNTIME_ERROR;
182         goto end;
183     }
184 
185     allocSize = mainTableSize + size * tupleIntSize;
186 
187     pool = sixel_allocator_malloc(allocator, allocSize);
188     if (pool == NULL) {
189         sprintf(message,
190                 "unable to allocate %u bytes for a %u-entry "
191                 "tuple table",
192                  allocSize, size);
193         sixel_helper_set_additional_message(message);
194         status = SIXEL_BAD_ALLOCATION;
195         goto end;
196     }
197     tbl = (tupletable) pool;
198 
199     for (i = 0; i < size; ++i)
200         tbl[i] = (struct tupleint *)
201             ((char*)pool + mainTableSize + i * tupleIntSize);
202 
203     *result = tbl;
204 
205     status = SIXEL_OK;
206 
207 end:
208     return status;
209 }
210 
211 
212 /*
213 ** Here is the fun part, the median-cut colormap generator.  This is based
214 ** on Paul Heckbert's paper "Color Image Quantization for Frame Buffer
215 ** Display", SIGGRAPH '82 Proceedings, page 297.
216 */
217 
218 static tupletable2
newColorMap(unsigned int const newcolors,unsigned int const depth,sixel_allocator_t * allocator)219 newColorMap(unsigned int const newcolors, unsigned int const depth, sixel_allocator_t *allocator)
220 {
221     SIXELSTATUS status = SIXEL_FALSE;
222     tupletable2 colormap;
223     unsigned int i;
224 
225     colormap.size = 0;
226     status = alloctupletable(&colormap.table, depth, newcolors, allocator);
227     if (SIXEL_FAILED(status)) {
228         goto end;
229     }
230     if (colormap.table) {
231         for (i = 0; i < newcolors; ++i) {
232             unsigned int plane;
233             for (plane = 0; plane < depth; ++plane)
234                 colormap.table[i]->tuple[plane] = 0;
235         }
236         colormap.size = newcolors;
237     }
238 
239 end:
240     return colormap;
241 }
242 
243 
244 static boxVector
newBoxVector(unsigned int const colors,unsigned int const sum,unsigned int const newcolors,sixel_allocator_t * allocator)245 newBoxVector(
246     unsigned int const  /* in */ colors,
247     unsigned int const  /* in */ sum,
248     unsigned int const  /* in */ newcolors,
249     sixel_allocator_t   /* in */ *allocator)
250 {
251     boxVector bv;
252 
253     bv = (boxVector)sixel_allocator_malloc(allocator,
254                                            sizeof(struct box) * (size_t)newcolors);
255     if (bv == NULL) {
256         quant_trace(stderr, "out of memory allocating box vector table\n");
257         return NULL;
258     }
259 
260     /* Set up the initial box. */
261     bv[0].ind = 0;
262     bv[0].colors = colors;
263     bv[0].sum = sum;
264 
265     return bv;
266 }
267 
268 
269 static void
findBoxBoundaries(tupletable2 const colorfreqtable,unsigned int const depth,unsigned int const boxStart,unsigned int const boxSize,sample minval[],sample maxval[])270 findBoxBoundaries(tupletable2  const colorfreqtable,
271                   unsigned int const depth,
272                   unsigned int const boxStart,
273                   unsigned int const boxSize,
274                   sample             minval[],
275                   sample             maxval[])
276 {
277 /*----------------------------------------------------------------------------
278   Go through the box finding the minimum and maximum of each
279   component - the boundaries of the box.
280 -----------------------------------------------------------------------------*/
281     unsigned int plane;
282     unsigned int i;
283 
284     for (plane = 0; plane < depth; ++plane) {
285         minval[plane] = colorfreqtable.table[boxStart]->tuple[plane];
286         maxval[plane] = minval[plane];
287     }
288 
289     for (i = 1; i < boxSize; ++i) {
290         for (plane = 0; plane < depth; ++plane) {
291             sample const v = colorfreqtable.table[boxStart + i]->tuple[plane];
292             if (v < minval[plane]) minval[plane] = v;
293             if (v > maxval[plane]) maxval[plane] = v;
294         }
295     }
296 }
297 
298 
299 
300 static unsigned int
largestByNorm(sample minval[],sample maxval[],unsigned int const depth)301 largestByNorm(sample minval[], sample maxval[], unsigned int const depth)
302 {
303 
304     unsigned int largestDimension;
305     unsigned int plane;
306     sample largestSpreadSoFar;
307 
308     largestSpreadSoFar = 0;
309     largestDimension = 0;
310     for (plane = 0; plane < depth; ++plane) {
311         sample const spread = maxval[plane]-minval[plane];
312         if (spread > largestSpreadSoFar) {
313             largestDimension = plane;
314             largestSpreadSoFar = spread;
315         }
316     }
317     return largestDimension;
318 }
319 
320 
321 
322 static unsigned int
largestByLuminosity(sample minval[],sample maxval[],unsigned int const depth)323 largestByLuminosity(sample minval[], sample maxval[], unsigned int const depth)
324 {
325 /*----------------------------------------------------------------------------
326    This subroutine presumes that the tuple type is either
327    BLACKANDWHITE, GRAYSCALE, or RGB (which implies pamP->depth is 1 or 3).
328    To save time, we don't actually check it.
329 -----------------------------------------------------------------------------*/
330     unsigned int retval;
331 
332     double lumin_factor[3] = {0.2989, 0.5866, 0.1145};
333 
334     if (depth == 1) {
335         retval = 0;
336     } else {
337         /* An RGB tuple */
338         unsigned int largestDimension;
339         unsigned int plane;
340         double largestSpreadSoFar;
341 
342         largestSpreadSoFar = 0.0;
343         largestDimension = 0;
344 
345         for (plane = 0; plane < 3; ++plane) {
346             double const spread =
347                 lumin_factor[plane] * (maxval[plane]-minval[plane]);
348             if (spread > largestSpreadSoFar) {
349                 largestDimension = plane;
350                 largestSpreadSoFar = spread;
351             }
352         }
353         retval = largestDimension;
354     }
355     return retval;
356 }
357 
358 
359 
360 static void
centerBox(unsigned int const boxStart,unsigned int const boxSize,tupletable2 const colorfreqtable,unsigned int const depth,tuple const newTuple)361 centerBox(unsigned int const boxStart,
362           unsigned int const boxSize,
363           tupletable2  const colorfreqtable,
364           unsigned int const depth,
365           tuple        const newTuple)
366 {
367 
368     unsigned int plane;
369     sample minval, maxval;
370     unsigned int i;
371 
372     for (plane = 0; plane < depth; ++plane) {
373         minval = maxval = colorfreqtable.table[boxStart]->tuple[plane];
374 
375         for (i = 1; i < boxSize; ++i) {
376             sample v = colorfreqtable.table[boxStart + i]->tuple[plane];
377             minval = minval < v ? minval: v;
378             maxval = maxval > v ? maxval: v;
379         }
380         newTuple[plane] = (minval + maxval) / 2;
381     }
382 }
383 
384 
385 
386 static void
averageColors(unsigned int const boxStart,unsigned int const boxSize,tupletable2 const colorfreqtable,unsigned int const depth,tuple const newTuple)387 averageColors(unsigned int const boxStart,
388               unsigned int const boxSize,
389               tupletable2  const colorfreqtable,
390               unsigned int const depth,
391               tuple        const newTuple)
392 {
393     unsigned int plane;
394     sample sum;
395     unsigned int i;
396 
397     for (plane = 0; plane < depth; ++plane) {
398         sum = 0;
399 
400         for (i = 0; i < boxSize; ++i) {
401             sum += colorfreqtable.table[boxStart + i]->tuple[plane];
402         }
403 
404         newTuple[plane] = sum / boxSize;
405     }
406 }
407 
408 
409 
410 static void
averagePixels(unsigned int const boxStart,unsigned int const boxSize,tupletable2 const colorfreqtable,unsigned int const depth,tuple const newTuple)411 averagePixels(unsigned int const boxStart,
412               unsigned int const boxSize,
413               tupletable2 const colorfreqtable,
414               unsigned int const depth,
415               tuple const newTuple)
416 {
417 
418     unsigned int n;
419         /* Number of tuples represented by the box */
420     unsigned int plane;
421     unsigned int i;
422 
423     /* Count the tuples in question */
424     n = 0;  /* initial value */
425     for (i = 0; i < boxSize; ++i) {
426         n += (unsigned int)colorfreqtable.table[boxStart + i]->value;
427     }
428 
429     for (plane = 0; plane < depth; ++plane) {
430         sample sum;
431 
432         sum = 0;
433 
434         for (i = 0; i < boxSize; ++i) {
435             sum += colorfreqtable.table[boxStart + i]->tuple[plane]
436                 * (unsigned int)colorfreqtable.table[boxStart + i]->value;
437         }
438 
439         newTuple[plane] = sum / n;
440     }
441 }
442 
443 
444 
445 static tupletable2
colormapFromBv(unsigned int const newcolors,boxVector const bv,unsigned int const boxes,tupletable2 const colorfreqtable,unsigned int const depth,int const methodForRep,sixel_allocator_t * allocator)446 colormapFromBv(unsigned int const newcolors,
447                boxVector const bv,
448                unsigned int const boxes,
449                tupletable2 const colorfreqtable,
450                unsigned int const depth,
451                int const methodForRep,
452                sixel_allocator_t *allocator)
453 {
454     /*
455     ** Ok, we've got enough boxes.  Now choose a representative color for
456     ** each box.  There are a number of possible ways to make this choice.
457     ** One would be to choose the center of the box; this ignores any structure
458     ** within the boxes.  Another method would be to average all the colors in
459     ** the box - this is the method specified in Heckbert's paper.  A third
460     ** method is to average all the pixels in the box.
461     */
462     tupletable2 colormap;
463     unsigned int bi;
464 
465     colormap = newColorMap(newcolors, depth, allocator);
466     if (!colormap.size) {
467         return colormap;
468     }
469 
470     for (bi = 0; bi < boxes; ++bi) {
471         switch (methodForRep) {
472         case SIXEL_REP_CENTER_BOX:
473             centerBox(bv[bi].ind, bv[bi].colors,
474                       colorfreqtable, depth,
475                       colormap.table[bi]->tuple);
476             break;
477         case SIXEL_REP_AVERAGE_COLORS:
478             averageColors(bv[bi].ind, bv[bi].colors,
479                           colorfreqtable, depth,
480                           colormap.table[bi]->tuple);
481             break;
482         case SIXEL_REP_AVERAGE_PIXELS:
483             averagePixels(bv[bi].ind, bv[bi].colors,
484                           colorfreqtable, depth,
485                           colormap.table[bi]->tuple);
486             break;
487         default:
488             quant_trace(stderr, "Internal error: "
489                                 "invalid value of methodForRep: %d\n",
490                         methodForRep);
491         }
492     }
493     return colormap;
494 }
495 
496 
497 static SIXELSTATUS
splitBox(boxVector const bv,unsigned int * const boxesP,unsigned int const bi,tupletable2 const colorfreqtable,unsigned int const depth,int const methodForLargest)498 splitBox(boxVector const bv,
499          unsigned int *const boxesP,
500          unsigned int const bi,
501          tupletable2 const colorfreqtable,
502          unsigned int const depth,
503          int const methodForLargest)
504 {
505 /*----------------------------------------------------------------------------
506    Split Box 'bi' in the box vector bv (so that bv contains one more box
507    than it did as input).  Split it so that each new box represents about
508    half of the pixels in the distribution given by 'colorfreqtable' for
509    the colors in the original box, but with distinct colors in each of the
510    two new boxes.
511 
512    Assume the box contains at least two colors.
513 -----------------------------------------------------------------------------*/
514     SIXELSTATUS status = SIXEL_FALSE;
515     unsigned int const boxStart = bv[bi].ind;
516     unsigned int const boxSize  = bv[bi].colors;
517     unsigned int const sm       = bv[bi].sum;
518 
519     enum { max_depth= 16 };
520     sample minval[max_depth];
521     sample maxval[max_depth];
522 
523     /* assert(max_depth >= depth); */
524 
525     unsigned int largestDimension;
526         /* number of the plane with the largest spread */
527     unsigned int medianIndex;
528     unsigned int lowersum;
529         /* Number of pixels whose value is "less than" the median */
530 
531     findBoxBoundaries(colorfreqtable, depth, boxStart, boxSize,
532                       minval, maxval);
533 
534     /* Find the largest dimension, and sort by that component.  I have
535        included two methods for determining the "largest" dimension;
536        first by simply comparing the range in RGB space, and second by
537        transforming into luminosities before the comparison.
538     */
539     switch (methodForLargest) {
540     case SIXEL_LARGE_NORM:
541         largestDimension = largestByNorm(minval, maxval, depth);
542         break;
543     case SIXEL_LARGE_LUM:
544         largestDimension = largestByLuminosity(minval, maxval, depth);
545         break;
546     default:
547         sixel_helper_set_additional_message(
548             "Internal error: invalid value of methodForLargest.");
549         status = SIXEL_LOGIC_ERROR;
550         goto end;
551     }
552 
553     /* TODO: I think this sort should go after creating a box,
554        not before splitting.  Because you need the sort to use
555        the SIXEL_REP_CENTER_BOX method of choosing a color to
556        represent the final boxes
557     */
558 
559     /* Set the gross global variable 'compareplanePlane' as a
560        parameter to compareplane(), which is called by qsort().
561     */
562     compareplanePlane = largestDimension;
563     qsort((char*) &colorfreqtable.table[boxStart], boxSize,
564           sizeof(colorfreqtable.table[boxStart]),
565           compareplane);
566 
567     {
568         /* Now find the median based on the counts, so that about half
569            the pixels (not colors, pixels) are in each subdivision.  */
570 
571         unsigned int i;
572 
573         lowersum = colorfreqtable.table[boxStart]->value; /* initial value */
574         for (i = 1; i < boxSize - 1 && lowersum < sm / 2; ++i) {
575             lowersum += colorfreqtable.table[boxStart + i]->value;
576         }
577         medianIndex = i;
578     }
579     /* Split the box, and sort to bring the biggest boxes to the top.  */
580 
581     bv[bi].colors = medianIndex;
582     bv[bi].sum = lowersum;
583     bv[*boxesP].ind = boxStart + medianIndex;
584     bv[*boxesP].colors = boxSize - medianIndex;
585     bv[*boxesP].sum = sm - lowersum;
586     ++(*boxesP);
587     qsort((char*) bv, *boxesP, sizeof(struct box), sumcompare);
588 
589     status = SIXEL_OK;
590 
591 end:
592     return status;
593 }
594 
595 
596 
597 static SIXELSTATUS
mediancut(tupletable2 const colorfreqtable,unsigned int const depth,unsigned int const newcolors,int const methodForLargest,int const methodForRep,tupletable2 * const colormapP,sixel_allocator_t * allocator)598 mediancut(tupletable2 const colorfreqtable,
599           unsigned int const depth,
600           unsigned int const newcolors,
601           int const methodForLargest,
602           int const methodForRep,
603           tupletable2 *const colormapP,
604           sixel_allocator_t *allocator)
605 {
606 /*----------------------------------------------------------------------------
607    Compute a set of only 'newcolors' colors that best represent an
608    image whose pixels are summarized by the histogram
609    'colorfreqtable'.  Each tuple in that table has depth 'depth'.
610    colorfreqtable.table[i] tells the number of pixels in the subject image
611    have a particular color.
612 
613    As a side effect, sort 'colorfreqtable'.
614 -----------------------------------------------------------------------------*/
615     boxVector bv;
616     unsigned int bi;
617     unsigned int boxes;
618     int multicolorBoxesExist;
619     unsigned int i;
620     unsigned int sum;
621     SIXELSTATUS status = SIXEL_FALSE;
622 
623     sum = 0;
624 
625     for (i = 0; i < colorfreqtable.size; ++i) {
626         sum += colorfreqtable.table[i]->value;
627     }
628 
629     /* There is at least one box that contains at least 2 colors; ergo,
630        there is more splitting we can do.  */
631     bv = newBoxVector(colorfreqtable.size, sum, newcolors, allocator);
632     if (bv == NULL) {
633         goto end;
634     }
635     boxes = 1;
636     multicolorBoxesExist = (colorfreqtable.size > 1);
637 
638     /* Main loop: split boxes until we have enough. */
639     while (boxes < newcolors && multicolorBoxesExist) {
640         /* Find the first splittable box. */
641         for (bi = 0; bi < boxes && bv[bi].colors < 2; ++bi)
642             ;
643         if (bi >= boxes) {
644             multicolorBoxesExist = 0;
645         } else {
646             status = splitBox(bv, &boxes, bi,
647                               colorfreqtable, depth,
648                               methodForLargest);
649             if (SIXEL_FAILED(status)) {
650                 goto end;
651             }
652         }
653     }
654     *colormapP = colormapFromBv(newcolors, bv, boxes,
655                                 colorfreqtable, depth,
656                                 methodForRep, allocator);
657 
658     sixel_allocator_free(allocator, bv);
659 
660     status = SIXEL_OK;
661 
662 end:
663     return status;
664 }
665 
666 
667 static unsigned int
computeHash(unsigned char const * data,unsigned int const depth)668 computeHash(unsigned char const *data, unsigned int const depth)
669 {
670     unsigned int hash = 0;
671     unsigned int n;
672 
673     for (n = 0; n < depth; n++) {
674         hash |= (unsigned int)(data[depth - 1 - n] >> 3) << n * 5;
675     }
676 
677     return hash;
678 }
679 
680 
681 static SIXELSTATUS
computeHistogram(unsigned char const * data,unsigned int length,unsigned long const depth,tupletable2 * const colorfreqtableP,int const qualityMode,sixel_allocator_t * allocator)682 computeHistogram(unsigned char const    /* in */  *data,
683                  unsigned int           /* in */  length,
684                  unsigned long const    /* in */  depth,
685                  tupletable2 * const    /* out */ colorfreqtableP,
686                  int const              /* in */  qualityMode,
687                  sixel_allocator_t      /* in */  *allocator)
688 {
689     SIXELSTATUS status = SIXEL_FALSE;
690     typedef unsigned short unit_t;
691     unsigned int i, n;
692     unit_t *histogram = NULL;
693     unit_t *refmap = NULL;
694     unit_t *ref;
695     unit_t *it;
696     unsigned int bucket_index;
697     unsigned int step;
698     unsigned int max_sample;
699 
700     switch (qualityMode) {
701     case SIXEL_QUALITY_LOW:
702         max_sample = 18383;
703         step = length / depth / max_sample * depth;
704         break;
705     case SIXEL_QUALITY_HIGH:
706         max_sample = 18383;
707         step = length / depth / max_sample * depth;
708         break;
709     case SIXEL_QUALITY_FULL:
710     default:
711         max_sample = 4003079;
712         step = length / depth / max_sample * depth;
713         break;
714     }
715 
716     if (length < max_sample * depth) {
717         step = 6 * depth;
718     }
719 
720     if (step <= 0) {
721         step = depth;
722     }
723 
724     quant_trace(stderr, "making histogram...\n");
725 
726     histogram = (unit_t *)sixel_allocator_calloc(allocator,
727                                                  (size_t)(1 << depth * 5),
728                                                  sizeof(unit_t));
729     if (histogram == NULL) {
730         sixel_helper_set_additional_message(
731             "unable to allocate memory for histogram.");
732         status = SIXEL_BAD_ALLOCATION;
733         goto end;
734     }
735     it = ref = refmap
736         = (unsigned short *)sixel_allocator_malloc(allocator,
737                                                    (size_t)(1 << depth * 5) * sizeof(unit_t));
738     if (!it) {
739         sixel_helper_set_additional_message(
740             "unable to allocate memory for lookup table.");
741         status = SIXEL_BAD_ALLOCATION;
742         goto end;
743     }
744 
745     for (i = 0; i < length; i += step) {
746         bucket_index = computeHash(data + i, 3);
747         if (histogram[bucket_index] == 0) {
748             *ref++ = bucket_index;
749         }
750         if (histogram[bucket_index] < (unsigned int)(1 << sizeof(unsigned short) * 8) - 1) {
751             histogram[bucket_index]++;
752         }
753     }
754 
755     colorfreqtableP->size = (unsigned int)(ref - refmap);
756 
757     status = alloctupletable(&colorfreqtableP->table, depth, (unsigned int)(ref - refmap), allocator);
758     if (SIXEL_FAILED(status)) {
759         goto end;
760     }
761     for (i = 0; i < colorfreqtableP->size; ++i) {
762         if (histogram[refmap[i]] > 0) {
763             colorfreqtableP->table[i]->value = histogram[refmap[i]];
764             for (n = 0; n < depth; n++) {
765                 colorfreqtableP->table[i]->tuple[depth - 1 - n]
766                     = (sample)((*it >> n * 5 & 0x1f) << 3);
767             }
768         }
769         it++;
770     }
771 
772     quant_trace(stderr, "%u colors found\n", colorfreqtableP->size);
773 
774     status = SIXEL_OK;
775 
776 end:
777     sixel_allocator_free(allocator, refmap);
778     sixel_allocator_free(allocator, histogram);
779 
780     return status;
781 }
782 
783 
784 static int
computeColorMapFromInput(unsigned char const * data,unsigned int const length,unsigned int const depth,unsigned int const reqColors,int const methodForLargest,int const methodForRep,int const qualityMode,tupletable2 * const colormapP,unsigned int * origcolors,sixel_allocator_t * allocator)785 computeColorMapFromInput(unsigned char const *data,
786                          unsigned int const length,
787                          unsigned int const depth,
788                          unsigned int const reqColors,
789                          int const methodForLargest,
790                          int const methodForRep,
791                          int const qualityMode,
792                          tupletable2 * const colormapP,
793                          unsigned int *origcolors,
794                          sixel_allocator_t *allocator)
795 {
796 /*----------------------------------------------------------------------------
797    Produce a colormap containing the best colors to represent the
798    image stream in file 'ifP'.  Figure it out using the median cut
799    technique.
800 
801    The colormap will have 'reqcolors' or fewer colors in it, unless
802    'allcolors' is true, in which case it will have all the colors that
803    are in the input.
804 
805    The colormap has the same maxval as the input.
806 
807    Put the colormap in newly allocated storage as a tupletable2
808    and return its address as *colormapP.  Return the number of colors in
809    it as *colorsP and its maxval as *colormapMaxvalP.
810 
811    Return the characteristics of the input file as
812    *formatP and *freqPamP.  (This information is not really
813    relevant to our colormap mission; just a fringe benefit).
814 -----------------------------------------------------------------------------*/
815     SIXELSTATUS status = SIXEL_FALSE;
816     tupletable2 colorfreqtable = {0, NULL};
817     unsigned int i;
818     unsigned int n;
819 
820     status = computeHistogram(data, length, depth,
821                               &colorfreqtable, qualityMode, allocator);
822     if (SIXEL_FAILED(status)) {
823         goto end;
824     }
825     if (origcolors) {
826         *origcolors = colorfreqtable.size;
827     }
828 
829     if (colorfreqtable.size <= reqColors) {
830         quant_trace(stderr,
831                     "Image already has few enough colors (<=%d).  "
832                     "Keeping same colors.\n", reqColors);
833         /* *colormapP = colorfreqtable; */
834         colormapP->size = colorfreqtable.size;
835         status = alloctupletable(&colormapP->table, depth, colorfreqtable.size, allocator);
836         if (SIXEL_FAILED(status)) {
837             goto end;
838         }
839         for (i = 0; i < colorfreqtable.size; ++i) {
840             colormapP->table[i]->value = colorfreqtable.table[i]->value;
841             for (n = 0; n < depth; ++n) {
842                 colormapP->table[i]->tuple[n] = colorfreqtable.table[i]->tuple[n];
843             }
844         }
845     } else {
846         quant_trace(stderr, "choosing %d colors...\n", reqColors);
847         status = mediancut(colorfreqtable, depth, reqColors,
848                            methodForLargest, methodForRep, colormapP, allocator);
849         if (SIXEL_FAILED(status)) {
850             goto end;
851         }
852         quant_trace(stderr, "%d colors are choosed.\n", colorfreqtable.size);
853     }
854 
855     status = SIXEL_OK;
856 
857 end:
858     sixel_allocator_free(allocator, colorfreqtable.table);
859     return status;
860 }
861 
862 
863 /* diffuse error energy to surround pixels */
864 static void
error_diffuse(unsigned char * data,int pos,int depth,int error,int numerator,int denominator)865 error_diffuse(unsigned char /* in */    *data,      /* base address of pixel buffer */
866               int           /* in */    pos,        /* address of the destination pixel */
867               int           /* in */    depth,      /* color depth in bytes */
868               int           /* in */    error,      /* error energy */
869               int           /* in */    numerator,  /* numerator of diffusion coefficient */
870               int           /* in */    denominator /* denominator of diffusion coefficient */)
871 {
872     int c;
873 
874     data += pos * depth;
875 
876     c = *data + error * numerator / denominator;
877     if (c < 0) {
878         c = 0;
879     }
880     if (c >= 1 << 8) {
881         c = (1 << 8) - 1;
882     }
883     *data = (unsigned char)c;
884 }
885 
886 
887 static void
diffuse_none(unsigned char * data,int width,int height,int x,int y,int depth,int error)888 diffuse_none(unsigned char *data, int width, int height,
889              int x, int y, int depth, int error)
890 {
891     /* unused */ (void) data;
892     /* unused */ (void) width;
893     /* unused */ (void) height;
894     /* unused */ (void) x;
895     /* unused */ (void) y;
896     /* unused */ (void) depth;
897     /* unused */ (void) error;
898 }
899 
900 
901 static void
diffuse_fs(unsigned char * data,int width,int height,int x,int y,int depth,int error)902 diffuse_fs(unsigned char *data, int width, int height,
903            int x, int y, int depth, int error)
904 {
905     int pos;
906 
907     pos = y * width + x;
908 
909     /* Floyd Steinberg Method
910      *          curr    7/16
911      *  3/16    5/48    1/16
912      */
913     if (x < width - 1 && y < height - 1) {
914         /* add error to the right cell */
915         error_diffuse(data, pos + width * 0 + 1, depth, error, 7, 16);
916         /* add error to the left-bottom cell */
917         error_diffuse(data, pos + width * 1 - 1, depth, error, 3, 16);
918         /* add error to the bottom cell */
919         error_diffuse(data, pos + width * 1 + 0, depth, error, 5, 16);
920         /* add error to the right-bottom cell */
921         error_diffuse(data, pos + width * 1 + 1, depth, error, 1, 16);
922     }
923 }
924 
925 
926 static void
diffuse_atkinson(unsigned char * data,int width,int height,int x,int y,int depth,int error)927 diffuse_atkinson(unsigned char *data, int width, int height,
928                  int x, int y, int depth, int error)
929 {
930     int pos;
931 
932     pos = y * width + x;
933 
934     /* Atkinson's Method
935      *          curr    1/8    1/8
936      *   1/8     1/8    1/8
937      *           1/8
938      */
939     if (y < height - 2) {
940         /* add error to the right cell */
941         error_diffuse(data, pos + width * 0 + 1, depth, error, 1, 8);
942         /* add error to the 2th right cell */
943         error_diffuse(data, pos + width * 0 + 2, depth, error, 1, 8);
944         /* add error to the left-bottom cell */
945         error_diffuse(data, pos + width * 1 - 1, depth, error, 1, 8);
946         /* add error to the bottom cell */
947         error_diffuse(data, pos + width * 1 + 0, depth, error, 1, 8);
948         /* add error to the right-bottom cell */
949         error_diffuse(data, pos + width * 1 + 1, depth, error, 1, 8);
950         /* add error to the 2th bottom cell */
951         error_diffuse(data, pos + width * 2 + 0, depth, error, 1, 8);
952     }
953 }
954 
955 
956 static void
diffuse_jajuni(unsigned char * data,int width,int height,int x,int y,int depth,int error)957 diffuse_jajuni(unsigned char *data, int width, int height,
958                int x, int y, int depth, int error)
959 {
960     int pos;
961 
962     pos = y * width + x;
963 
964     /* Jarvis, Judice & Ninke Method
965      *                  curr    7/48    5/48
966      *  3/48    5/48    7/48    5/48    3/48
967      *  1/48    3/48    5/48    3/48    1/48
968      */
969     if (pos < (height - 2) * width - 2) {
970         error_diffuse(data, pos + width * 0 + 1, depth, error, 7, 48);
971         error_diffuse(data, pos + width * 0 + 2, depth, error, 5, 48);
972         error_diffuse(data, pos + width * 1 - 2, depth, error, 3, 48);
973         error_diffuse(data, pos + width * 1 - 1, depth, error, 5, 48);
974         error_diffuse(data, pos + width * 1 + 0, depth, error, 7, 48);
975         error_diffuse(data, pos + width * 1 + 1, depth, error, 5, 48);
976         error_diffuse(data, pos + width * 1 + 2, depth, error, 3, 48);
977         error_diffuse(data, pos + width * 2 - 2, depth, error, 1, 48);
978         error_diffuse(data, pos + width * 2 - 1, depth, error, 3, 48);
979         error_diffuse(data, pos + width * 2 + 0, depth, error, 5, 48);
980         error_diffuse(data, pos + width * 2 + 1, depth, error, 3, 48);
981         error_diffuse(data, pos + width * 2 + 2, depth, error, 1, 48);
982     }
983 }
984 
985 
986 static void
diffuse_stucki(unsigned char * data,int width,int height,int x,int y,int depth,int error)987 diffuse_stucki(unsigned char *data, int width, int height,
988                int x, int y, int depth, int error)
989 {
990     int pos;
991 
992     pos = y * width + x;
993 
994     /* Stucki's Method
995      *                  curr    8/48    4/48
996      *  2/48    4/48    8/48    4/48    2/48
997      *  1/48    2/48    4/48    2/48    1/48
998      */
999     if (pos < (height - 2) * width - 2) {
1000         error_diffuse(data, pos + width * 0 + 1, depth, error, 1, 6);
1001         error_diffuse(data, pos + width * 0 + 2, depth, error, 1, 12);
1002         error_diffuse(data, pos + width * 1 - 2, depth, error, 1, 24);
1003         error_diffuse(data, pos + width * 1 - 1, depth, error, 1, 12);
1004         error_diffuse(data, pos + width * 1 + 0, depth, error, 1, 6);
1005         error_diffuse(data, pos + width * 1 + 1, depth, error, 1, 12);
1006         error_diffuse(data, pos + width * 1 + 2, depth, error, 1, 24);
1007         error_diffuse(data, pos + width * 2 - 2, depth, error, 1, 48);
1008         error_diffuse(data, pos + width * 2 - 1, depth, error, 1, 24);
1009         error_diffuse(data, pos + width * 2 + 0, depth, error, 1, 12);
1010         error_diffuse(data, pos + width * 2 + 1, depth, error, 1, 24);
1011         error_diffuse(data, pos + width * 2 + 2, depth, error, 1, 48);
1012     }
1013 }
1014 
1015 
1016 static void
diffuse_burkes(unsigned char * data,int width,int height,int x,int y,int depth,int error)1017 diffuse_burkes(unsigned char *data, int width, int height,
1018                int x, int y, int depth, int error)
1019 {
1020     int pos;
1021 
1022     pos = y * width + x;
1023 
1024     /* Burkes' Method
1025      *                  curr    4/16    2/16
1026      *  1/16    2/16    4/16    2/16    1/16
1027      */
1028     if (pos < (height - 1) * width - 2) {
1029         error_diffuse(data, pos + width * 0 + 1, depth, error, 1, 4);
1030         error_diffuse(data, pos + width * 0 + 2, depth, error, 1, 8);
1031         error_diffuse(data, pos + width * 1 - 2, depth, error, 1, 16);
1032         error_diffuse(data, pos + width * 1 - 1, depth, error, 1, 8);
1033         error_diffuse(data, pos + width * 1 + 0, depth, error, 1, 4);
1034         error_diffuse(data, pos + width * 1 + 1, depth, error, 1, 8);
1035         error_diffuse(data, pos + width * 1 + 2, depth, error, 1, 16);
1036     }
1037 }
1038 
1039 static float
mask_a(int x,int y,int c)1040 mask_a (int x, int y, int c)
1041 {
1042     return ((((x + c * 67) + y * 236) * 119) & 255 ) / 128.0 - 1.0;
1043 }
1044 
1045 static float
mask_x(int x,int y,int c)1046 mask_x (int x, int y, int c)
1047 {
1048     return ((((x + c * 29) ^ y* 149) * 1234) & 511 ) / 256.0 - 1.0;
1049 }
1050 
1051 /* lookup closest color from palette with "normal" strategy */
1052 static int
lookup_normal(unsigned char const * const pixel,int const depth,unsigned char const * const palette,int const reqcolor,unsigned short * const cachetable,int const complexion)1053 lookup_normal(unsigned char const * const pixel,
1054               int const depth,
1055               unsigned char const * const palette,
1056               int const reqcolor,
1057               unsigned short * const cachetable,
1058               int const complexion)
1059 {
1060     int result;
1061     int diff;
1062     int r;
1063     int i;
1064     int n;
1065     int distant;
1066 
1067     result = (-1);
1068     diff = INT_MAX;
1069 
1070     /* don't use cachetable in 'normal' strategy */
1071     (void) cachetable;
1072 
1073     for (i = 0; i < reqcolor; i++) {
1074         distant = 0;
1075         r = pixel[0] - palette[i * depth + 0];
1076         distant += r * r * complexion;
1077         for (n = 1; n < depth; ++n) {
1078             r = pixel[n] - palette[i * depth + n];
1079             distant += r * r;
1080         }
1081         if (distant < diff) {
1082             diff = distant;
1083             result = i;
1084         }
1085     }
1086 
1087     return result;
1088 }
1089 
1090 
1091 /* lookup closest color from palette with "fast" strategy */
1092 static int
lookup_fast(unsigned char const * const pixel,int const depth,unsigned char const * const palette,int const reqcolor,unsigned short * const cachetable,int const complexion)1093 lookup_fast(unsigned char const * const pixel,
1094             int const depth,
1095             unsigned char const * const palette,
1096             int const reqcolor,
1097             unsigned short * const cachetable,
1098             int const complexion)
1099 {
1100     int result;
1101     unsigned int hash;
1102     int diff;
1103     int cache;
1104     int i;
1105     int distant;
1106 
1107     /* don't use depth in 'fast' strategy because it's always 3 */
1108     (void) depth;
1109 
1110     result = (-1);
1111     diff = INT_MAX;
1112     hash = computeHash(pixel, 3);
1113 
1114     cache = cachetable[hash];
1115     if (cache) {  /* fast lookup */
1116         return cache - 1;
1117     }
1118     /* collision */
1119     for (i = 0; i < reqcolor; i++) {
1120         distant = 0;
1121 #if 0
1122         for (n = 0; n < 3; ++n) {
1123             r = pixel[n] - palette[i * 3 + n];
1124             distant += r * r;
1125         }
1126 #elif 1  /* complexion correction */
1127         distant = (pixel[0] - palette[i * 3 + 0]) * (pixel[0] - palette[i * 3 + 0]) * complexion
1128                 + (pixel[1] - palette[i * 3 + 1]) * (pixel[1] - palette[i * 3 + 1])
1129                 + (pixel[2] - palette[i * 3 + 2]) * (pixel[2] - palette[i * 3 + 2])
1130                 ;
1131 #endif
1132         if (distant < diff) {
1133             diff = distant;
1134             result = i;
1135         }
1136     }
1137     cachetable[hash] = result + 1;
1138 
1139     return result;
1140 }
1141 
1142 
1143 static int
lookup_mono_darkbg(unsigned char const * const pixel,int const depth,unsigned char const * const palette,int const reqcolor,unsigned short * const cachetable,int const complexion)1144 lookup_mono_darkbg(unsigned char const * const pixel,
1145                    int const depth,
1146                    unsigned char const * const palette,
1147                    int const reqcolor,
1148                    unsigned short * const cachetable,
1149                    int const complexion)
1150 {
1151     int n;
1152     int distant;
1153 
1154     /* unused */ (void) palette;
1155     /* unused */ (void) cachetable;
1156     /* unused */ (void) complexion;
1157 
1158     distant = 0;
1159     for (n = 0; n < depth; ++n) {
1160         distant += pixel[n];
1161     }
1162     return distant >= 128 * reqcolor ? 1: 0;
1163 }
1164 
1165 
1166 static int
lookup_mono_lightbg(unsigned char const * const pixel,int const depth,unsigned char const * const palette,int const reqcolor,unsigned short * const cachetable,int const complexion)1167 lookup_mono_lightbg(unsigned char const * const pixel,
1168                     int const depth,
1169                     unsigned char const * const palette,
1170                     int const reqcolor,
1171                     unsigned short * const cachetable,
1172                     int const complexion)
1173 {
1174     int n;
1175     int distant;
1176 
1177     /* unused */ (void) palette;
1178     /* unused */ (void) cachetable;
1179     /* unused */ (void) complexion;
1180 
1181     distant = 0;
1182     for (n = 0; n < depth; ++n) {
1183         distant += pixel[n];
1184     }
1185     return distant < 128 * reqcolor ? 1: 0;
1186 }
1187 
1188 
1189 /* choose colors using median-cut method */
1190 SIXELSTATUS
sixel_quant_make_palette(unsigned char ** result,unsigned char const * data,unsigned int length,int pixelformat,unsigned int reqcolors,unsigned int * ncolors,unsigned int * origcolors,int methodForLargest,int methodForRep,int qualityMode,sixel_allocator_t * allocator)1191 sixel_quant_make_palette(
1192     unsigned char          /* out */ **result,
1193     unsigned char const    /* in */  *data,
1194     unsigned int           /* in */  length,
1195     int                    /* in */  pixelformat,
1196     unsigned int           /* in */  reqcolors,
1197     unsigned int           /* in */  *ncolors,
1198     unsigned int           /* in */  *origcolors,
1199     int                    /* in */  methodForLargest,
1200     int                    /* in */  methodForRep,
1201     int                    /* in */  qualityMode,
1202     sixel_allocator_t      /* in */  *allocator)
1203 {
1204     SIXELSTATUS status = SIXEL_FALSE;
1205     unsigned int i;
1206     unsigned int n;
1207     int ret;
1208     tupletable2 colormap;
1209     unsigned int depth;
1210     int result_depth;
1211 
1212     result_depth = sixel_helper_compute_depth(pixelformat);
1213     if (result_depth <= 0) {
1214         *result = NULL;
1215         goto end;
1216     }
1217 
1218     depth = (unsigned int)result_depth;
1219 
1220     ret = computeColorMapFromInput(data, length, depth,
1221                                    reqcolors, methodForLargest,
1222                                    methodForRep, qualityMode,
1223                                    &colormap, origcolors, allocator);
1224     if (ret != 0) {
1225         *result = NULL;
1226         goto end;
1227     }
1228     *ncolors = colormap.size;
1229     quant_trace(stderr, "tupletable size: %d\n", *ncolors);
1230     *result = (unsigned char *)sixel_allocator_malloc(allocator, *ncolors * depth);
1231     for (i = 0; i < *ncolors; i++) {
1232         for (n = 0; n < depth; ++n) {
1233             (*result)[i * depth + n] = colormap.table[i]->tuple[n];
1234         }
1235     }
1236 
1237     sixel_allocator_free(allocator, colormap.table);
1238 
1239     status = SIXEL_OK;
1240 
1241 end:
1242     return status;
1243 }
1244 
1245 
1246 /* apply color palette into specified pixel buffers */
1247 SIXELSTATUS
sixel_quant_apply_palette(sixel_index_t * result,unsigned char * data,int width,int height,int depth,unsigned char * palette,int reqcolor,int methodForDiffuse,int foptimize,int foptimize_palette,int complexion,unsigned short * cachetable,int * ncolors,sixel_allocator_t * allocator)1248 sixel_quant_apply_palette(
1249     sixel_index_t     /* out */ *result,
1250     unsigned char     /* in */  *data,
1251     int               /* in */  width,
1252     int               /* in */  height,
1253     int               /* in */  depth,
1254     unsigned char     /* in */  *palette,
1255     int               /* in */  reqcolor,
1256     int               /* in */  methodForDiffuse,
1257     int               /* in */  foptimize,
1258     int               /* in */  foptimize_palette,
1259     int               /* in */  complexion,
1260     unsigned short    /* in */  *cachetable,
1261     int               /* in */  *ncolors,
1262     sixel_allocator_t /* in */  *allocator)
1263 {
1264     typedef int component_t;
1265     enum { max_depth = 4 };
1266     SIXELSTATUS status = SIXEL_FALSE;
1267     int pos, n, x, y, sum1, sum2;
1268     component_t offset;
1269     int color_index;
1270     unsigned short *indextable;
1271     unsigned char new_palette[SIXEL_PALETTE_MAX * 4];
1272     unsigned short migration_map[SIXEL_PALETTE_MAX];
1273     float (*f_mask) (int x, int y, int c) = NULL;
1274     void (*f_diffuse)(unsigned char *data, int width, int height,
1275                       int x, int y, int depth, int offset);
1276     int (*f_lookup)(unsigned char const * const pixel,
1277                     int const depth,
1278                     unsigned char const * const palette,
1279                     int const reqcolor,
1280                     unsigned short * const cachetable,
1281                     int const complexion);
1282 
1283     /* check bad reqcolor */
1284     if (reqcolor < 1) {
1285         status = SIXEL_BAD_ARGUMENT;
1286         sixel_helper_set_additional_message(
1287             "sixel_quant_apply_palette: "
1288             "a bad argument is detected, reqcolor < 0.");
1289         goto end;
1290     }
1291 
1292     if (depth != 3) {
1293         f_diffuse = diffuse_none;
1294     } else {
1295         switch (methodForDiffuse) {
1296         case SIXEL_DIFFUSE_NONE:
1297             f_diffuse = diffuse_none;
1298             break;
1299         case SIXEL_DIFFUSE_ATKINSON:
1300             f_diffuse = diffuse_atkinson;
1301             break;
1302         case SIXEL_DIFFUSE_FS:
1303             f_diffuse = diffuse_fs;
1304             break;
1305         case SIXEL_DIFFUSE_JAJUNI:
1306             f_diffuse = diffuse_jajuni;
1307             break;
1308         case SIXEL_DIFFUSE_STUCKI:
1309             f_diffuse = diffuse_stucki;
1310             break;
1311         case SIXEL_DIFFUSE_BURKES:
1312             f_diffuse = diffuse_burkes;
1313             break;
1314         case SIXEL_DIFFUSE_A_DITHER:
1315             f_diffuse = diffuse_none;
1316             f_mask = mask_a;
1317             break;
1318         case SIXEL_DIFFUSE_X_DITHER:
1319             f_diffuse = diffuse_none;
1320             f_mask = mask_x;
1321             break;
1322         default:
1323             quant_trace(stderr, "Internal error: invalid value of"
1324                                 " methodForDiffuse: %d\n",
1325                         methodForDiffuse);
1326             f_diffuse = diffuse_none;
1327             break;
1328         }
1329     }
1330 
1331     f_lookup = NULL;
1332     if (reqcolor == 2) {
1333         sum1 = 0;
1334         sum2 = 0;
1335         for (n = 0; n < depth; ++n) {
1336             sum1 += palette[n];
1337         }
1338         for (n = depth; n < depth + depth; ++n) {
1339             sum2 += palette[n];
1340         }
1341         if (sum1 == 0 && sum2 == 255 * 3) {
1342             f_lookup = lookup_mono_darkbg;
1343         } else if (sum1 == 255 * 3 && sum2 == 0) {
1344             f_lookup = lookup_mono_lightbg;
1345         }
1346     }
1347     if (f_lookup == NULL) {
1348         if (foptimize && depth == 3) {
1349             f_lookup = lookup_fast;
1350         } else {
1351             f_lookup = lookup_normal;
1352         }
1353     }
1354 
1355     indextable = cachetable;
1356     if (cachetable == NULL && f_lookup == lookup_fast) {
1357         indextable = (unsigned short *)sixel_allocator_calloc(allocator,
1358                                                               (size_t)(1 << depth * 5),
1359                                                               sizeof(unsigned short));
1360         if (!indextable) {
1361             quant_trace(stderr, "Unable to allocate memory for indextable.\n");
1362             goto end;
1363         }
1364     }
1365 
1366     if (foptimize_palette) {
1367         *ncolors = 0;
1368 
1369         memset(new_palette, 0x00, sizeof(SIXEL_PALETTE_MAX * depth));
1370         memset(migration_map, 0x00, sizeof(migration_map));
1371 
1372         if (f_mask) {
1373             for (y = 0; y < height; ++y) {
1374                 for (x = 0; x < width; ++x) {
1375                     unsigned char copy[max_depth];
1376                     int d;
1377                     int val;
1378 
1379                     pos = y * width + x;
1380                     for (d = 0; d < depth; d ++) {
1381                         val = data[pos * depth + d] + f_mask(x, y, d) * 32;
1382                         copy[d] = val < 0 ? 0 : val > 255 ? 255 : val;
1383                     }
1384                     color_index = f_lookup(copy, depth,
1385                                            palette, reqcolor, indextable, complexion);
1386                     if (migration_map[color_index] == 0) {
1387                         result[pos] = *ncolors;
1388                         for (n = 0; n < depth; ++n) {
1389                             new_palette[*ncolors * depth + n] = palette[color_index * depth + n];
1390                         }
1391                         ++*ncolors;
1392                         migration_map[color_index] = *ncolors;
1393                     } else {
1394                         result[pos] = migration_map[color_index] - 1;
1395                     }
1396                 }
1397             }
1398             memcpy(palette, new_palette, (size_t)(*ncolors * depth));
1399         } else {
1400             for (y = 0; y < height; ++y) {
1401                 for (x = 0; x < width; ++x) {
1402                     pos = y * width + x;
1403                     color_index = f_lookup(data + (pos * depth), depth,
1404                                            palette, reqcolor, indextable, complexion);
1405                     if (migration_map[color_index] == 0) {
1406                         result[pos] = *ncolors;
1407                         for (n = 0; n < depth; ++n) {
1408                             new_palette[*ncolors * depth + n] = palette[color_index * depth + n];
1409                         }
1410                         ++*ncolors;
1411                         migration_map[color_index] = *ncolors;
1412                     } else {
1413                         result[pos] = migration_map[color_index] - 1;
1414                     }
1415                     for (n = 0; n < depth; ++n) {
1416                         offset = data[pos * depth + n] - palette[color_index * depth + n];
1417                         f_diffuse(data + n, width, height, x, y, depth, offset);
1418                     }
1419                 }
1420             }
1421             memcpy(palette, new_palette, (size_t)(*ncolors * depth));
1422         }
1423     } else {
1424         if (f_mask) {
1425             for (y = 0; y < height; ++y) {
1426                 for (x = 0; x < width; ++x) {
1427                     unsigned char copy[max_depth];
1428                     int d;
1429                     int val;
1430 
1431                     pos = y * width + x;
1432                     for (d = 0; d < depth; d ++) {
1433                         val = data[pos * depth + d] + f_mask(x, y, d) * 32;
1434                         copy[d] = val < 0 ? 0 : val > 255 ? 255 : val;
1435                     }
1436                     result[pos] = f_lookup(copy, depth,
1437                                            palette, reqcolor, indextable, complexion);
1438                 }
1439             }
1440         } else {
1441             for (y = 0; y < height; ++y) {
1442                 for (x = 0; x < width; ++x) {
1443                     pos = y * width + x;
1444                     color_index = f_lookup(data + (pos * depth), depth,
1445                                            palette, reqcolor, indextable, complexion);
1446                     result[pos] = color_index;
1447                     for (n = 0; n < depth; ++n) {
1448                         offset = data[pos * depth + n] - palette[color_index * depth + n];
1449                         f_diffuse(data + n, width, height, x, y, depth, offset);
1450                     }
1451                 }
1452             }
1453         }
1454         *ncolors = reqcolor;
1455     }
1456 
1457     if (cachetable == NULL) {
1458         sixel_allocator_free(allocator, indextable);
1459     }
1460 
1461     status = SIXEL_OK;
1462 
1463 end:
1464     return status;
1465 }
1466 
1467 
1468 void
sixel_quant_free_palette(unsigned char * data,sixel_allocator_t * allocator)1469 sixel_quant_free_palette(
1470     unsigned char       /* in */ *data,
1471     sixel_allocator_t   /* in */ *allocator)
1472 {
1473     sixel_allocator_free(allocator, data);
1474 }
1475 
1476 
1477 #if HAVE_TESTS
1478 static int
test1(void)1479 test1(void)
1480 {
1481     int nret = EXIT_FAILURE;
1482     sample minval[1] = { 1 };
1483     sample maxval[1] = { 2 };
1484     unsigned int retval;
1485 
1486     retval = largestByLuminosity(minval, maxval, 1);
1487     if (retval != 0) {
1488         goto error;
1489     }
1490     nret = EXIT_SUCCESS;
1491 
1492 error:
1493     return nret;
1494 }
1495 
1496 
1497 SIXELAPI int
sixel_quant_tests_main(void)1498 sixel_quant_tests_main(void)
1499 {
1500     int nret = EXIT_FAILURE;
1501     size_t i;
1502     typedef int (* testcase)(void);
1503 
1504     static testcase const testcases[] = {
1505         test1,
1506     };
1507 
1508     for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) {
1509         nret = testcases[i]();
1510         if (nret != EXIT_SUCCESS) {
1511             goto error;
1512         }
1513     }
1514 
1515     nret = EXIT_SUCCESS;
1516 
1517 error:
1518     return nret;
1519 }
1520 #endif  /* HAVE_TESTS */
1521 
1522 /* emacs Local Variables:      */
1523 /* emacs mode: c               */
1524 /* emacs tab-width: 4          */
1525 /* emacs indent-tabs-mode: nil */
1526 /* emacs c-basic-offset: 4     */
1527 /* emacs End:                  */
1528 /* vim: set expandtab ts=4 sts=4 sw=4 : */
1529 /* EOF */
1530