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