• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

saitoha / libsixel / 18800271060

25 Oct 2025 07:46AM UTC coverage: 52.018% (-0.02%) from 52.042%
18800271060

push

github

saitoha
quant: make img2sixel -p COLORS! generate an exact COLORS-color palette

5675 of 15880 branches covered (35.74%)

20 of 106 new or added lines in 3 files covered. (18.87%)

2 existing lines in 1 file now uncovered.

8261 of 15881 relevant lines covered (52.02%)

1208418.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

40.95
/src/quant.c
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
/* STDC_HEADERS */
50
#include <stdlib.h>
51
#include <stdio.h>
52
#include <time.h>
53

54
#if HAVE_STRING_H
55
# include <string.h>
56
#endif  /* HAVE_STRING_H */
57
#if HAVE_MATH_H
58
#include <math.h>
59
#endif  /* HAVE_MATH_H */
60
#if HAVE_LIMITS_H
61
# include <limits.h>
62
#endif  /* HAVE_MATH_H */
63
#if HAVE_STDINT_H
64
# include <stdint.h>
65
#else
66
# if HAVE_INTTYPES_H
67
#  include <inttypes.h>
68
# endif
69
#endif  /* HAVE_STDINT_H */
70

71
#if defined(SIXEL_USE_SSE2)
72
# include <emmintrin.h>
73
# include <xmmintrin.h>
74
#elif defined(SIXEL_USE_NEON)
75
# include <arm_neon.h>
76
#endif
77

78
#include "quant.h"
79

80
/*
81
 * Random threshold modulation described by Zhou & Fang (Table 2) assigns
82
 * jitter amplitudes to nine key tone distances (0, 44, 64, 85, 95, 102, 107,
83
 * 112, 127).  We reconstruct the full 0-127 table by linearly interpolating
84
 * between those key levels, mirroring the result to cover 0-255.  See
85
 * https://observablehq.com/@jobleonard/variable-coefficient-dithering for a
86
 * reconstruction reference.
87
 * The entries below store the percentage gain (0-100) used by T(n).
88
 */
89
static const int zhoufang_threshold_gain[128] = {
90
        0,    1,    2,    2,    3,    4,    5,    5,    6,    7,
91
        8,    8,    9,   10,   11,   12,   12,   13,   14,   15,
92
       15,   16,   17,   18,   19,   19,   20,   21,   22,   22,
93
       23,   24,   25,   26,   26,   27,   28,   29,   29,   30,
94
       31,   32,   32,   33,   34,   35,   36,   36,   37,   38,
95
       39,   40,   40,   41,   42,   43,   44,   44,   45,   46,
96
       47,   48,   48,   49,   50,   52,   55,   57,   60,   62,
97
       64,   67,   69,   71,   74,   76,   79,   81,   83,   86,
98
       88,   90,   93,   95,   98,  100,   92,   83,   75,   67,
99
       59,   50,   42,   34,   25,   17,   22,   26,   31,   36,
100
       41,   45,   50,   54,   58,   62,   66,   70,   72,   74,
101
       75,   77,   79,   80,   82,   83,   85,   86,   87,   89,
102
       90,   92,   93,   94,   96,   97,   99,  100,
103
};
104

105
static float mask_a(int x, int y, int c);
106
static float mask_x(int x, int y, int c);
107
static void diffuse_none(unsigned char *data, int width, int height,
108
                         int x, int y, int depth, int error, int direction);
109
static void diffuse_fs(unsigned char *data, int width, int height,
110
                       int x, int y, int depth, int error, int direction);
111
static void diffuse_atkinson(unsigned char *data, int width, int height,
112
                             int x, int y, int depth, int error,
113
                             int direction);
114
static void diffuse_jajuni(unsigned char *data, int width, int height,
115
                           int x, int y, int depth, int error,
116
                           int direction);
117
static void diffuse_stucki(unsigned char *data, int width, int height,
118
                           int x, int y, int depth, int error,
119
                           int direction);
120
static void diffuse_burkes(unsigned char *data, int width, int height,
121
                           int x, int y, int depth, int error,
122
                           int direction);
123
static void diffuse_lso1(unsigned char *data, int width, int height,
124
                         int x, int y, int depth, int error, int direction);
125
static void diffuse_none_carry(int32_t *carry_curr, int32_t *carry_next,
126
                               int32_t *carry_far, int width, int height,
127
                               int depth, int x, int y, int32_t error,
128
                               int direction, int channel);
129
static void diffuse_fs_carry(int32_t *carry_curr, int32_t *carry_next,
130
                             int32_t *carry_far, int width, int height,
131
                             int depth, int x, int y, int32_t error,
132
                             int direction, int channel);
133
static void diffuse_atkinson_carry(int32_t *carry_curr, int32_t *carry_next,
134
                                   int32_t *carry_far, int width,
135
                                   int height, int depth, int x, int y,
136
                                   int32_t error, int direction,
137
                                   int channel);
138
static void diffuse_jajuni_carry(int32_t *carry_curr, int32_t *carry_next,
139
                                 int32_t *carry_far, int width, int height,
140
                                 int depth, int x, int y, int32_t error,
141
                                 int direction, int channel);
142
static void diffuse_stucki_carry(int32_t *carry_curr, int32_t *carry_next,
143
                                 int32_t *carry_far, int width, int height,
144
                                 int depth, int x, int y, int32_t error,
145
                                 int direction, int channel);
146
static void diffuse_burkes_carry(int32_t *carry_curr, int32_t *carry_next,
147
                                 int32_t *carry_far, int width, int height,
148
                                 int depth, int x, int y, int32_t error,
149
                                 int direction, int channel);
150
static void diffuse_lso1_carry(int32_t *carry_curr, int32_t *carry_next,
151
                               int32_t *carry_far, int width, int height,
152
                               int depth, int x, int y, int32_t error,
153
                               int direction, int channel);
154

155
static const int (*
156
lso2_table(void))[7]
×
157
{
158
#include "lso2.h"
159
    return var_coefs;
160
}
161

162
static const int (*
163
lso3_table(void))[7]
×
164
{
165
/* Auto-generated by gen_varcoefs.awk */
166
#include "lso3.h"
167
    return var_coefs;
168
}
169

170

171
#define VARERR_SCALE_SHIFT 12
172
#define VARERR_SCALE       (1 << VARERR_SCALE_SHIFT)
173
#define VARERR_ROUND       (1 << (VARERR_SCALE_SHIFT - 1))
174
#define VARERR_MAX_VALUE   (255 * VARERR_SCALE)
175

176
#if HAVE_DEBUG
177
#define quant_trace fprintf
178
#else
179
static inline void quant_trace(FILE *f, ...) { (void) f; }
180
#endif
181

182
/*****************************************************************************
183
 *
184
 * quantization
185
 *
186
 *****************************************************************************/
187

188
typedef struct box* boxVector;
189
struct box {
190
    unsigned int ind;
191
    unsigned int colors;
192
    unsigned int sum;
193
};
194

195
typedef unsigned long sample;
196
typedef sample * tuple;
197

198
struct tupleint {
199
    /* An ordered pair of a tuple value and an integer, such as you
200
       would find in a tuple table or tuple hash.
201
       Note that this is a variable length structure.
202
    */
203
    unsigned int value;
204
    sample tuple[1];
205
    /* This is actually a variable size array -- its size is the
206
       depth of the tuple in question.  Some compilers do not let us
207
       declare a variable length array.
208
    */
209
};
210
typedef struct tupleint ** tupletable;
211

212
typedef struct {
213
    unsigned int size;
214
    tupletable table;
215
} tupletable2;
216

217
static unsigned int compareplanePlane;
218
static tupletable2 const *force_palette_source;
219
    /* This is a parameter to compareplane().  We use this global variable
220
       so that compareplane() can be called by qsort(), to compare two
221
       tuples.  qsort() doesn't pass any arguments except the two tuples.
222
    */
223
static int
224
compareplane(const void * const arg1,
6,571,383✔
225
             const void * const arg2)
226
{
227
    int lhs, rhs;
228

229
    typedef const struct tupleint * const * const sortarg;
230
    sortarg comparandPP  = (sortarg) arg1;
6,571,383✔
231
    sortarg comparatorPP = (sortarg) arg2;
6,571,383✔
232
    lhs = (int)(*comparandPP)->tuple[compareplanePlane];
6,571,383✔
233
    rhs = (int)(*comparatorPP)->tuple[compareplanePlane];
6,571,383✔
234

235
    return lhs - rhs;
6,571,383✔
236
}
237

238

239
static int
240
sumcompare(const void * const b1, const void * const b2)
4,522,638✔
241
{
242
    return (int)((boxVector)b2)->sum - (int)((boxVector)b1)->sum;
4,522,638✔
243
}
244

245

246
static SIXELSTATUS
247
alloctupletable(
470✔
248
    tupletable          /* out */ *result,
249
    unsigned int const  /* in */  depth,
250
    unsigned int const  /* in */  size,
251
    sixel_allocator_t   /* in */  *allocator)
252
{
253
    SIXELSTATUS status = SIXEL_FALSE;
470✔
254
    enum { message_buffer_size = 256 };
255
    char message[message_buffer_size];
256
    int nwrite;
257
    unsigned int mainTableSize;
258
    unsigned int tupleIntSize;
259
    unsigned int allocSize;
260
    void * pool;
261
    tupletable tbl;
262
    unsigned int i;
263

264
    if (UINT_MAX / sizeof(struct tupleint) < size) {
470!
265
        nwrite = sprintf(message,
×
266
                         "size %u is too big for arithmetic",
267
                         size);
268
        if (nwrite > 0) {
×
269
            sixel_helper_set_additional_message(message);
×
270
        }
271
        status = SIXEL_RUNTIME_ERROR;
×
272
        goto end;
×
273
    }
274

275
    mainTableSize = size * sizeof(struct tupleint *);
470✔
276
    tupleIntSize = sizeof(struct tupleint) - sizeof(sample)
470✔
277
        + depth * sizeof(sample);
218✔
278

279
    /* To save the enormous amount of time it could take to allocate
280
       each individual tuple, we do a trick here and allocate everything
281
       as a single malloc block and suballocate internally.
282
    */
283
    if ((UINT_MAX - mainTableSize) / tupleIntSize < size) {
470!
284
        nwrite = sprintf(message,
×
285
                         "size %u is too big for arithmetic",
286
                         size);
287
        if (nwrite > 0) {
×
288
            sixel_helper_set_additional_message(message);
×
289
        }
290
        status = SIXEL_RUNTIME_ERROR;
×
291
        goto end;
×
292
    }
293

294
    allocSize = mainTableSize + size * tupleIntSize;
470✔
295

296
    pool = sixel_allocator_malloc(allocator, allocSize);
470✔
297
    if (pool == NULL) {
470!
298
        sprintf(message,
×
299
                "unable to allocate %u bytes for a %u-entry "
300
                "tuple table",
301
                 allocSize, size);
302
        sixel_helper_set_additional_message(message);
×
303
        status = SIXEL_BAD_ALLOCATION;
×
304
        goto end;
×
305
    }
306
    tbl = (tupletable) pool;
470✔
307

308
    for (i = 0; i < size; ++i)
201,263✔
309
        tbl[i] = (struct tupleint *)
200,793✔
310
            ((char*)pool + mainTableSize + i * tupleIntSize);
200,793✔
311

312
    *result = tbl;
470✔
313

314
    status = SIXEL_OK;
470✔
315

316
end:
252✔
317
    return status;
470✔
318
}
319

320

321
/*
322
** Here is the fun part, the median-cut colormap generator.  This is based
323
** on Paul Heckbert's paper "Color Image Quantization for Frame Buffer
324
** Display", SIGGRAPH '82 Proceedings, page 297.
325
*/
326

327
static tupletable2
328
newColorMap(unsigned int const newcolors, unsigned int const depth, sixel_allocator_t *allocator)
45✔
329
{
330
    SIXELSTATUS status = SIXEL_FALSE;
45✔
331
    tupletable2 colormap;
332
    unsigned int i;
333

334
    colormap.size = 0;
45✔
335
    status = alloctupletable(&colormap.table, depth, newcolors, allocator);
45✔
336
    if (SIXEL_FAILED(status)) {
45!
337
        goto end;
×
338
    }
339
    if (colormap.table) {
60!
340
        for (i = 0; i < newcolors; ++i) {
7,665✔
341
            unsigned int plane;
342
            for (plane = 0; plane < depth; ++plane)
30,480✔
343
                colormap.table[i]->tuple[plane] = 0;
22,860✔
344
        }
2,540✔
345
        colormap.size = newcolors;
45✔
346
    }
15✔
347

348
end:
349
    return colormap;
45✔
350
}
351

352

353
static boxVector
354
newBoxVector(
45✔
355
    unsigned int const  /* in */ colors,
356
    unsigned int const  /* in */ sum,
357
    unsigned int const  /* in */ newcolors,
358
    sixel_allocator_t   /* in */ *allocator)
359
{
360
    boxVector bv;
361

362
    bv = (boxVector)sixel_allocator_malloc(allocator,
60✔
363
                                           sizeof(struct box) * (size_t)newcolors);
45✔
364
    if (bv == NULL) {
45!
365
        quant_trace(stderr, "out of memory allocating box vector table\n");
×
366
        return NULL;
×
367
    }
368

369
    /* Set up the initial box. */
370
    bv[0].ind = 0;
45✔
371
    bv[0].colors = colors;
45✔
372
    bv[0].sum = sum;
45✔
373

374
    return bv;
45✔
375
}
15✔
376

377

378
static void
379
findBoxBoundaries(tupletable2  const colorfreqtable,
7,575✔
380
                  unsigned int const depth,
381
                  unsigned int const boxStart,
382
                  unsigned int const boxSize,
383
                  sample             minval[],
384
                  sample             maxval[])
385
{
386
/*----------------------------------------------------------------------------
387
  Go through the box finding the minimum and maximum of each
388
  component - the boundaries of the box.
389
-----------------------------------------------------------------------------*/
390
    unsigned int plane;
391
    unsigned int i;
392

393
    for (plane = 0; plane < depth; ++plane) {
30,300✔
394
        minval[plane] = colorfreqtable.table[boxStart]->tuple[plane];
22,725✔
395
        maxval[plane] = minval[plane];
22,725✔
396
    }
7,575✔
397

398
    for (i = 1; i < boxSize; ++i) {
1,131,595✔
399
        for (plane = 0; plane < depth; ++plane) {
4,496,080✔
400
            sample const v = colorfreqtable.table[boxStart + i]->tuple[plane];
3,372,060✔
401
            if (v < minval[plane]) minval[plane] = v;
3,372,060✔
402
            if (v > maxval[plane]) maxval[plane] = v;
3,372,060✔
403
        }
1,124,790✔
404
    }
374,930✔
405
}
7,575✔
406

407

408

409
static unsigned int
410
largestByNorm(sample minval[], sample maxval[], unsigned int const depth)
6,810✔
411
{
412

413
    unsigned int largestDimension;
414
    unsigned int plane;
415
    sample largestSpreadSoFar;
416

417
    largestSpreadSoFar = 0;
6,810✔
418
    largestDimension = 0;
6,810✔
419
    for (plane = 0; plane < depth; ++plane) {
27,240✔
420
        sample const spread = maxval[plane]-minval[plane];
20,430✔
421
        if (spread > largestSpreadSoFar) {
20,430✔
422
            largestDimension = plane;
11,992✔
423
            largestSpreadSoFar = spread;
11,992✔
424
        }
3,972✔
425
    }
6,810✔
426
    return largestDimension;
6,810✔
427
}
428

429

430

431
static unsigned int
432
largestByLuminosity(sample minval[], sample maxval[], unsigned int const depth)
765✔
433
{
434
/*----------------------------------------------------------------------------
435
   This subroutine presumes that the tuple type is either
436
   BLACKANDWHITE, GRAYSCALE, or RGB (which implies pamP->depth is 1 or 3).
437
   To save time, we don't actually check it.
438
-----------------------------------------------------------------------------*/
439
    unsigned int retval;
440

441
    double lumin_factor[3] = {0.2989, 0.5866, 0.1145};
765✔
442

443
    if (depth == 1) {
765!
444
        retval = 0;
×
445
    } else {
446
        /* An RGB tuple */
447
        unsigned int largestDimension;
448
        unsigned int plane;
449
        double largestSpreadSoFar;
450

451
        largestSpreadSoFar = 0.0;
765✔
452
        largestDimension = 0;
765✔
453

454
        for (plane = 0; plane < 3; ++plane) {
3,060✔
455
            double const spread =
2,295✔
456
                lumin_factor[plane] * (maxval[plane]-minval[plane]);
2,295✔
457
            if (spread > largestSpreadSoFar) {
2,295✔
458
                largestDimension = plane;
1,331✔
459
                largestSpreadSoFar = spread;
1,331✔
460
            }
451✔
461
        }
765✔
462
        retval = largestDimension;
765✔
463
    }
464
    return retval;
765✔
465
}
466

467

468

469
static void
470
centerBox(unsigned int const boxStart,
6,084✔
471
          unsigned int const boxSize,
472
          tupletable2  const colorfreqtable,
473
          unsigned int const depth,
474
          tuple        const newTuple)
475
{
476

477
    unsigned int plane;
478
    sample minval, maxval;
479
    unsigned int i;
480

481
    for (plane = 0; plane < depth; ++plane) {
24,336✔
482
        minval = maxval = colorfreqtable.table[boxStart]->tuple[plane];
18,252✔
483

484
        for (i = 1; i < boxSize; ++i) {
482,172✔
485
            sample v = colorfreqtable.table[boxStart + i]->tuple[plane];
463,920✔
486
            minval = minval < v ? minval: v;
463,920✔
487
            maxval = maxval > v ? maxval: v;
463,920✔
488
        }
154,362✔
489
        newTuple[plane] = (minval + maxval) / 2;
18,252✔
490
    }
6,084✔
491
}
6,084✔
492

493

494

495
static void
496
averageColors(unsigned int const boxStart,
768✔
497
              unsigned int const boxSize,
498
              tupletable2  const colorfreqtable,
499
              unsigned int const depth,
500
              tuple        const newTuple)
501
{
502
    unsigned int plane;
503
    sample sum;
504
    unsigned int i;
505

506
    for (plane = 0; plane < depth; ++plane) {
3,072✔
507
        sum = 0;
2,304✔
508

509
        for (i = 0; i < boxSize; ++i) {
43,962✔
510
            sum += colorfreqtable.table[boxStart + i]->tuple[plane];
41,658✔
511
        }
13,878✔
512

513
        newTuple[plane] = sum / boxSize;
2,304✔
514
    }
768✔
515
}
768✔
516

517

518

519
static void
520
averagePixels(unsigned int const boxStart,
768✔
521
              unsigned int const boxSize,
522
              tupletable2 const colorfreqtable,
523
              unsigned int const depth,
524
              tuple const newTuple)
525
{
526

527
    unsigned int n;
528
        /* Number of tuples represented by the box */
529
    unsigned int plane;
530
    unsigned int i;
531

532
    /* Count the tuples in question */
533
    n = 0;  /* initial value */
768✔
534
    for (i = 0; i < boxSize; ++i) {
13,799✔
535
        n += (unsigned int)colorfreqtable.table[boxStart + i]->value;
13,031✔
536
    }
4,311✔
537

538
    for (plane = 0; plane < depth; ++plane) {
3,072✔
539
        sample sum;
540

541
        sum = 0;
2,304✔
542

543
        for (i = 0; i < boxSize; ++i) {
41,397✔
544
            sum += colorfreqtable.table[boxStart + i]->tuple[plane]
52,026✔
545
                * (unsigned int)colorfreqtable.table[boxStart + i]->value;
39,093✔
546
        }
12,933✔
547

548
        newTuple[plane] = sum / n;
2,304✔
549
    }
768✔
550
}
768✔
551

552

553

554
static tupletable2
555
colormapFromBv(unsigned int const newcolors,
45✔
556
               boxVector const bv,
557
               unsigned int const boxes,
558
               tupletable2 const colorfreqtable,
559
               unsigned int const depth,
560
               int const methodForRep,
561
               sixel_allocator_t *allocator)
562
{
563
    /*
564
    ** Ok, we've got enough boxes.  Now choose a representative color for
565
    ** each box.  There are a number of possible ways to make this choice.
566
    ** One would be to choose the center of the box; this ignores any structure
567
    ** within the boxes.  Another method would be to average all the colors in
568
    ** the box - this is the method specified in Heckbert's paper.  A third
569
    ** method is to average all the pixels in the box.
570
    */
571
    tupletable2 colormap;
572
    unsigned int bi;
573

574
    colormap = newColorMap(newcolors, depth, allocator);
45✔
575
    if (!colormap.size) {
45!
576
        return colormap;
×
577
    }
578

579
    for (bi = 0; bi < boxes; ++bi) {
7,665✔
580
        switch (methodForRep) {
7,620!
581
        case SIXEL_REP_CENTER_BOX:
4,056✔
582
            centerBox(bv[bi].ind, bv[bi].colors,
8,112✔
583
                      colorfreqtable, depth,
2,028✔
584
                      colormap.table[bi]->tuple);
6,084✔
585
            break;
6,084✔
586
        case SIXEL_REP_AVERAGE_COLORS:
512✔
587
            averageColors(bv[bi].ind, bv[bi].colors,
1,024✔
588
                          colorfreqtable, depth,
256✔
589
                          colormap.table[bi]->tuple);
768✔
590
            break;
768✔
591
        case SIXEL_REP_AVERAGE_PIXELS:
512✔
592
            averagePixels(bv[bi].ind, bv[bi].colors,
1,024✔
593
                          colorfreqtable, depth,
256✔
594
                          colormap.table[bi]->tuple);
768✔
595
            break;
768✔
596
        default:
597
            quant_trace(stderr, "Internal error: "
×
598
                                "invalid value of methodForRep: %d\n",
599
                        methodForRep);
600
        }
601
    }
2,540✔
602
    return colormap;
45✔
603
}
15✔
604

605

606
static int
NEW
607
force_palette_compare(const void *lhs, const void *rhs)
×
608
{
609
    unsigned int left;
610
    unsigned int right;
611
    unsigned int left_value;
612
    unsigned int right_value;
613

NEW
614
    left = *(const unsigned int *)lhs;
×
NEW
615
    right = *(const unsigned int *)rhs;
×
NEW
616
    left_value = force_palette_source->table[left]->value;
×
NEW
617
    right_value = force_palette_source->table[right]->value;
×
NEW
618
    if (left_value > right_value) {
×
NEW
619
        return -1;
×
620
    }
NEW
621
    if (left_value < right_value) {
×
NEW
622
        return 1;
×
623
    }
NEW
624
    if (left < right) {
×
NEW
625
        return -1;
×
626
    }
NEW
627
    if (left > right) {
×
NEW
628
        return 1;
×
629
    }
NEW
630
    return 0;
×
631
}
632

633

634
static SIXELSTATUS
NEW
635
force_palette_completion(tupletable2 *colormapP,
×
636
                         unsigned int depth,
637
                         unsigned int reqColors,
638
                         tupletable2 const colorfreqtable,
639
                         sixel_allocator_t *allocator)
640
{
641
    /*
642
     * We enqueue "losers" from the histogram so that we can revive them:
643
     *
644
     *   histogram --> sort by hit count --> append to palette tail
645
     *        ^                             |
646
     *        +-----------------------------+
647
     *
648
     * The ASCII loop shows how discarded colors walk back into the
649
     * palette when the user demands an exact size.
650
     */
NEW
651
    SIXELSTATUS status = SIXEL_FALSE;
×
NEW
652
    tupletable new_table = NULL;
×
NEW
653
    unsigned int *order = NULL;
×
654
    unsigned int current;
655
    unsigned int fill;
656
    unsigned int candidate;
657
    unsigned int plane;
658
    unsigned int source;
659

NEW
660
    current = colormapP->size;
×
NEW
661
    if (current >= reqColors) {
×
NEW
662
        return SIXEL_OK;
×
663
    }
664

NEW
665
    status = alloctupletable(&new_table, depth, reqColors, allocator);
×
NEW
666
    if (SIXEL_FAILED(status)) {
×
NEW
667
        goto end;
×
668
    }
669

NEW
670
    if (colorfreqtable.size > 0U) {
×
NEW
671
        order = (unsigned int *)sixel_allocator_malloc(
×
NEW
672
            allocator, colorfreqtable.size * sizeof(unsigned int));
×
NEW
673
        if (order == NULL) {
×
NEW
674
            status = SIXEL_BAD_ALLOCATION;
×
NEW
675
            goto end;
×
676
        }
NEW
677
        for (candidate = 0; candidate < colorfreqtable.size; ++candidate) {
×
NEW
678
            order[candidate] = candidate;
×
679
        }
NEW
680
        force_palette_source = &colorfreqtable;
×
NEW
681
        qsort(order, colorfreqtable.size, sizeof(unsigned int),
×
682
              force_palette_compare);
NEW
683
        force_palette_source = NULL;
×
684
    }
685

NEW
686
    for (fill = 0; fill < current; ++fill) {
×
NEW
687
        new_table[fill]->value = colormapP->table[fill]->value;
×
NEW
688
        for (plane = 0; plane < depth; ++plane) {
×
NEW
689
            new_table[fill]->tuple[plane] =
×
NEW
690
                colormapP->table[fill]->tuple[plane];
×
691
        }
692
    }
693

NEW
694
    candidate = 0U;
×
NEW
695
    fill = current;
×
NEW
696
    if (order != NULL) {
×
NEW
697
        while (fill < reqColors && candidate < colorfreqtable.size) {
×
698
            unsigned int index;
699

NEW
700
            index = order[candidate];
×
NEW
701
            new_table[fill]->value = colorfreqtable.table[index]->value;
×
NEW
702
            for (plane = 0; plane < depth; ++plane) {
×
NEW
703
                new_table[fill]->tuple[plane] =
×
NEW
704
                    colorfreqtable.table[index]->tuple[plane];
×
705
            }
NEW
706
            ++fill;
×
NEW
707
            ++candidate;
×
708
        }
709
    }
710

NEW
711
    if (fill < reqColors && fill == 0U) {
×
NEW
712
        new_table[fill]->value = 0U;
×
NEW
713
        for (plane = 0; plane < depth; ++plane) {
×
NEW
714
            new_table[fill]->tuple[plane] = 0U;
×
715
        }
NEW
716
        ++fill;
×
717
    }
718

NEW
719
    source = 0U;
×
NEW
720
    while (fill < reqColors) {
×
NEW
721
        new_table[fill]->value = new_table[source]->value;
×
NEW
722
        for (plane = 0; plane < depth; ++plane) {
×
NEW
723
            new_table[fill]->tuple[plane] = new_table[source]->tuple[plane];
×
724
        }
NEW
725
        ++fill;
×
NEW
726
        ++source;
×
NEW
727
        if (source >= fill) {
×
NEW
728
            source = 0U;
×
729
        }
730
    }
731

NEW
732
    sixel_allocator_free(allocator, colormapP->table);
×
NEW
733
    colormapP->table = new_table;
×
NEW
734
    colormapP->size = reqColors;
×
NEW
735
    status = SIXEL_OK;
×
736

737
end:
NEW
738
    if (status != SIXEL_OK && new_table != NULL) {
×
NEW
739
        sixel_allocator_free(allocator, new_table);
×
740
    }
NEW
741
    if (order != NULL) {
×
NEW
742
        sixel_allocator_free(allocator, order);
×
743
    }
NEW
744
    return status;
×
745
}
746

747

748
static SIXELSTATUS
749
splitBox(boxVector const bv,
7,575✔
750
         unsigned int *const boxesP,
751
         unsigned int const bi,
752
         tupletable2 const colorfreqtable,
753
         unsigned int const depth,
754
         int const methodForLargest)
755
{
756
/*----------------------------------------------------------------------------
757
   Split Box 'bi' in the box vector bv (so that bv contains one more box
758
   than it did as input).  Split it so that each new box represents about
759
   half of the pixels in the distribution given by 'colorfreqtable' for
760
   the colors in the original box, but with distinct colors in each of the
761
   two new boxes.
762

763
   Assume the box contains at least two colors.
764
-----------------------------------------------------------------------------*/
765
    SIXELSTATUS status = SIXEL_FALSE;
7,575✔
766
    unsigned int const boxStart = bv[bi].ind;
7,575✔
767
    unsigned int const boxSize  = bv[bi].colors;
7,575✔
768
    unsigned int const sm       = bv[bi].sum;
7,575✔
769

770
    enum { max_depth= 16 };
771
    sample minval[max_depth];
772
    sample maxval[max_depth];
773

774
    /* assert(max_depth >= depth); */
775

776
    unsigned int largestDimension;
777
        /* number of the plane with the largest spread */
778
    unsigned int medianIndex;
779
    unsigned int lowersum;
780
        /* Number of pixels whose value is "less than" the median */
781

782
    findBoxBoundaries(colorfreqtable, depth, boxStart, boxSize,
10,100✔
783
                      minval, maxval);
2,525✔
784

785
    /* Find the largest dimension, and sort by that component.  I have
786
       included two methods for determining the "largest" dimension;
787
       first by simply comparing the range in RGB space, and second by
788
       transforming into luminosities before the comparison.
789
    */
790
    switch (methodForLargest) {
7,575!
791
    case SIXEL_LARGE_NORM:
4,540✔
792
        largestDimension = largestByNorm(minval, maxval, depth);
6,810✔
793
        break;
6,810✔
794
    case SIXEL_LARGE_LUM:
510✔
795
        largestDimension = largestByLuminosity(minval, maxval, depth);
765✔
796
        break;
765✔
797
    default:
798
        sixel_helper_set_additional_message(
×
799
            "Internal error: invalid value of methodForLargest.");
800
        status = SIXEL_LOGIC_ERROR;
×
801
        goto end;
×
802
    }
803

804
    /* TODO: I think this sort should go after creating a box,
805
       not before splitting.  Because you need the sort to use
806
       the SIXEL_REP_CENTER_BOX method of choosing a color to
807
       represent the final boxes
808
    */
809

810
    /* Set the gross global variable 'compareplanePlane' as a
811
       parameter to compareplane(), which is called by qsort().
812
    */
813
    compareplanePlane = largestDimension;
7,575✔
814
    qsort((char*) &colorfreqtable.table[boxStart], boxSize,
7,575✔
815
          sizeof(colorfreqtable.table[boxStart]),
816
          compareplane);
817

818
    {
819
        /* Now find the median based on the counts, so that about half
820
           the pixels (not colors, pixels) are in each subdivision.  */
821

822
        unsigned int i;
823

824
        lowersum = colorfreqtable.table[boxStart]->value; /* initial value */
7,575✔
825
        for (i = 1; i < boxSize - 1 && lowersum < sm / 2; ++i) {
518,432✔
826
            lowersum += colorfreqtable.table[boxStart + i]->value;
510,857✔
827
        }
169,789✔
828
        medianIndex = i;
7,575✔
829
    }
830
    /* Split the box, and sort to bring the biggest boxes to the top.  */
831

832
    bv[bi].colors = medianIndex;
7,575✔
833
    bv[bi].sum = lowersum;
7,575✔
834
    bv[*boxesP].ind = boxStart + medianIndex;
7,575✔
835
    bv[*boxesP].colors = boxSize - medianIndex;
7,575✔
836
    bv[*boxesP].sum = sm - lowersum;
7,575✔
837
    ++(*boxesP);
7,575✔
838
    qsort((char*) bv, *boxesP, sizeof(struct box), sumcompare);
7,575✔
839

840
    status = SIXEL_OK;
7,575✔
841

842
end:
5,050✔
843
    return status;
7,575✔
844
}
845

846

847

848
static SIXELSTATUS
849
mediancut(tupletable2 const colorfreqtable,
45✔
850
          unsigned int const depth,
851
          unsigned int const newcolors,
852
          int const methodForLargest,
853
          int const methodForRep,
854
          tupletable2 *const colormapP,
855
          sixel_allocator_t *allocator)
856
{
857
/*----------------------------------------------------------------------------
858
   Compute a set of only 'newcolors' colors that best represent an
859
   image whose pixels are summarized by the histogram
860
   'colorfreqtable'.  Each tuple in that table has depth 'depth'.
861
   colorfreqtable.table[i] tells the number of pixels in the subject image
862
   have a particular color.
863

864
   As a side effect, sort 'colorfreqtable'.
865
-----------------------------------------------------------------------------*/
866
    boxVector bv;
867
    unsigned int bi;
868
    unsigned int boxes;
869
    int multicolorBoxesExist;
870
    unsigned int i;
871
    unsigned int sum;
872
    SIXELSTATUS status = SIXEL_FALSE;
45✔
873

874
    sum = 0;
45✔
875

876
    for (i = 0; i < colorfreqtable.size; ++i) {
187,686✔
877
        sum += colorfreqtable.table[i]->value;
187,641✔
878
    }
62,419✔
879

880
    /* There is at least one box that contains at least 2 colors; ergo,
881
       there is more splitting we can do.  */
882
    bv = newBoxVector(colorfreqtable.size, sum, newcolors, allocator);
45✔
883
    if (bv == NULL) {
45!
884
        goto end;
×
885
    }
886
    boxes = 1;
45✔
887
    multicolorBoxesExist = (colorfreqtable.size > 1);
45✔
888

889
    /* Main loop: split boxes until we have enough. */
890
    while (boxes < newcolors && multicolorBoxesExist) {
7,620!
891
        /* Find the first splittable box. */
892
        for (bi = 0; bi < boxes && bv[bi].colors < 2; ++bi)
40,306!
893
            ;
894
        if (bi >= boxes) {
7,575!
895
            multicolorBoxesExist = 0;
×
896
        } else {
897
            status = splitBox(bv, &boxes, bi,
10,100✔
898
                              colorfreqtable, depth,
2,525✔
899
                              methodForLargest);
2,525✔
900
            if (SIXEL_FAILED(status)) {
7,575!
901
                goto end;
×
902
            }
903
        }
904
    }
905
    *colormapP = colormapFromBv(newcolors, bv, boxes,
60✔
906
                                colorfreqtable, depth,
15✔
907
                                methodForRep, allocator);
15✔
908

909
    sixel_allocator_free(allocator, bv);
45✔
910

911
    status = SIXEL_OK;
45✔
912

913
end:
30✔
914
    return status;
45✔
915
}
916

917

918
static int histogram_lut_policy = SIXEL_LUT_POLICY_AUTO;
919

920
void
921
sixel_quant_set_lut_policy(int lut_policy)
492✔
922
{
923
    int normalized;
924

925
    normalized = SIXEL_LUT_POLICY_AUTO;
492✔
926
    if (lut_policy == SIXEL_LUT_POLICY_5BIT
706!
927
        || lut_policy == SIXEL_LUT_POLICY_6BIT
492!
928
        || lut_policy == SIXEL_LUT_POLICY_ROBINHOOD
492!
929
        || lut_policy == SIXEL_LUT_POLICY_HOPSCOTCH) {
492!
930
        normalized = lut_policy;
×
931
    }
932

933
    histogram_lut_policy = normalized;
492✔
934
}
492✔
935

936
struct histogram_control {
937
    unsigned int channel_shift;
938
    unsigned int channel_bits;
939
    unsigned int channel_mask;
940
};
941

942
static struct histogram_control
943
histogram_control_make(unsigned int depth);
944
static struct histogram_control
945
histogram_control_make_for_policy(unsigned int depth, int lut_policy);
946
static size_t histogram_dense_size(unsigned int depth,
947
                                   struct histogram_control const
948
                                       *control);
949
static unsigned int histogram_reconstruct(unsigned int quantized,
950
                                          struct histogram_control const
951
                                              *control);
952

953
static size_t
954
histogram_dense_size(unsigned int depth,
474✔
955
                     struct histogram_control const *control)
956
{
957
    size_t size;
958
    unsigned int exponent;
959
    unsigned int i;
960

961
    size = 1U;
474✔
962
    exponent = depth * control->channel_bits;
474✔
963
    for (i = 0U; i < exponent; ++i) {
9,006✔
964
        if (size > SIZE_MAX / 2U) {
8,532!
965
            size = SIZE_MAX;
×
966
            break;
×
967
        }
968
        size <<= 1U;
8,532✔
969
    }
3,744✔
970

971
    return size;
474✔
972
}
973

974
static struct histogram_control
975
histogram_control_make_for_policy(unsigned int depth, int lut_policy)
16,545,760✔
976
{
977
    struct histogram_control control;
978

979
    /*
980
     * The ASCII ladder below shows how each policy selects bucket width.
981
     *
982
     *   auto / 6bit RGB : |--6--|
983
     *   forced 5bit     : |---5---|
984
     *   robinhood       : |------8------|
985
     *   alpha fallback  : |---5---|  (avoids 2^(6*4) buckets)
986
     */
987
    control.channel_shift = 2U;
16,545,760✔
988
    if (depth > 3U) {
16,545,760!
989
        control.channel_shift = 3U;
×
990
    }
991
    if (lut_policy == SIXEL_LUT_POLICY_5BIT) {
16,545,760!
992
        control.channel_shift = 3U;
×
993
    } else if (lut_policy == SIXEL_LUT_POLICY_6BIT) {
16,545,760✔
994
        control.channel_shift = 2U;
239✔
995
        if (depth > 3U) {
239!
996
            control.channel_shift = 3U;
×
997
        }
998
    } else if (lut_policy == SIXEL_LUT_POLICY_ROBINHOOD
16,545,620!
999
               || lut_policy == SIXEL_LUT_POLICY_HOPSCOTCH) {
16,545,521!
1000
        control.channel_shift = 0U;
×
1001
    }
1002
    control.channel_bits = 8U - control.channel_shift;
16,545,760✔
1003
    control.channel_mask = (1U << control.channel_bits) - 1U;
16,545,760✔
1004

1005
    return control;
16,545,760✔
1006
}
1007

1008
static unsigned int
1009
histogram_reconstruct(unsigned int quantized,
571,221✔
1010
                      struct histogram_control const *control)
1011
{
1012
    unsigned int value;
1013

1014
    value = quantized << control->channel_shift;
571,221✔
1015
    if (quantized == control->channel_mask) {
571,221✔
1016
        value = 255U;
298✔
1017
    } else {
184✔
1018
        if (control->channel_shift > 0U) {
570,923!
1019
            value |= (1U << (control->channel_shift - 1U));
570,923✔
1020
        }
190,553✔
1021
    }
1022
    if (value > 255U) {
571,221!
1023
        value = 255U;
×
1024
    }
1025

1026
    return value;
571,221✔
1027
}
1028

1029
static unsigned int
1030
histogram_quantize(unsigned int sample8,
58,916,766✔
1031
                   struct histogram_control const *control)
1032
{
1033
    unsigned int quantized;
1034
    unsigned int shift;
1035
    unsigned int mask;
1036
    unsigned int rounding;
1037

1038
    /*
1039
     * We want each bucket to capture its center value instead of the lower
1040
     * edge.  The ASCII sketch below shows how rounding keeps the midpoint:
1041
     *
1042
     *   0---1---2---3        sample8 + round
1043
     *   |   |   |   |  ==>  ----------------  -> bucket index
1044
     *   0   1   2   3              2^shift
1045
     */
1046
    shift = control->channel_shift;
58,916,766✔
1047
    mask = control->channel_mask;
58,916,766✔
1048
    if (shift == 0U) {
58,916,766!
1049
        quantized = sample8;
×
1050
    } else {
1051
        rounding = 1U << (shift - 1U);
58,916,766✔
1052
        quantized = (sample8 + rounding) >> shift;
58,916,766✔
1053
        if (quantized > mask) {
58,916,766✔
1054
            quantized = mask;
968,660✔
1055
        }
334,068✔
1056
    }
1057

1058
    return quantized;
58,916,766✔
1059
}
1060

1061
static unsigned int
1062
computeHash(unsigned char const *data,
19,638,922✔
1063
            unsigned int const depth,
1064
            struct histogram_control const *control)
1065
{
1066
    unsigned int hash;
1067
    unsigned int n;
1068
    unsigned int sample8;
1069
    unsigned int bits;
1070

1071
    hash = 0;
19,638,922✔
1072
    bits = control->channel_bits;
19,638,922✔
1073
    for (n = 0; n < depth; n++) {
78,555,688✔
1074
#if 0
1075
        hash |= (unsigned int)(data[depth - 1 - n] >> 3) << n * 5;
1076
#else
1077
        sample8 = (unsigned int)data[depth - 1 - n];
58,916,766✔
1078
        hash |= histogram_quantize(sample8, control) << (n * bits);
58,916,766✔
1079
#endif
1080
    }
29,774,322✔
1081

1082
    return hash;
19,638,922✔
1083
}
1084

1085
#define CUCKOO_BUCKET_SIZE 4U
1086
#define CUCKOO_MAX_KICKS 128U
1087
#define CUCKOO_STASH_SIZE 32U
1088
#define CUCKOO_EMPTY_KEY 0xffffffffU
1089

1090
struct cuckoo_bucket32 {
1091
    uint32_t key[CUCKOO_BUCKET_SIZE];
1092
    uint32_t value[CUCKOO_BUCKET_SIZE];
1093
};
1094

1095
struct cuckoo_table32 {
1096
    struct cuckoo_bucket32 *buckets;
1097
    uint32_t stash_key[CUCKOO_STASH_SIZE];
1098
    uint32_t stash_value[CUCKOO_STASH_SIZE];
1099
    size_t bucket_count;
1100
    size_t bucket_mask;
1101
    size_t stash_count;
1102
    size_t entry_count;
1103
    sixel_allocator_t *allocator;
1104
};
1105

1106
static size_t cuckoo_round_buckets(size_t hint);
1107
static size_t cuckoo_hash_primary(uint32_t key, size_t mask);
1108
static size_t cuckoo_hash_secondary(uint32_t key, size_t mask);
1109
static size_t cuckoo_hash_alternate(uint32_t key,
1110
                                    size_t bucket,
1111
                                    size_t mask);
1112
static uint32_t *cuckoo_bucket_find(struct cuckoo_bucket32 *bucket,
1113
                                    uint32_t key);
1114
static int cuckoo_bucket_insert_direct(struct cuckoo_bucket32 *bucket,
1115
                                       uint32_t key,
1116
                                       uint32_t value);
1117
static SIXELSTATUS cuckoo_table32_init(struct cuckoo_table32 *table,
1118
                                       size_t expected,
1119
                                       sixel_allocator_t *allocator);
1120
static void cuckoo_table32_clear(struct cuckoo_table32 *table);
1121
static void cuckoo_table32_fini(struct cuckoo_table32 *table);
1122
static uint32_t *cuckoo_table32_lookup(struct cuckoo_table32 *table,
1123
                                       uint32_t key);
1124
static SIXELSTATUS cuckoo_table32_insert(struct cuckoo_table32 *table,
1125
                                         uint32_t key,
1126
                                         uint32_t value);
1127
static SIXELSTATUS cuckoo_table32_grow(struct cuckoo_table32 *table);
1128

1129
struct robinhood_slot32 {
1130
    uint32_t key;
1131
    uint32_t value;
1132
    uint16_t distance;
1133
    uint16_t pad;
1134
};
1135

1136
struct robinhood_table32 {
1137
    struct robinhood_slot32 *slots;
1138
    size_t capacity;
1139
    size_t count;
1140
    sixel_allocator_t *allocator;
1141
};
1142

1143
static size_t robinhood_round_capacity(size_t hint);
1144
static SIXELSTATUS robinhood_table32_init(struct robinhood_table32 *table,
1145
                                         size_t expected,
1146
                                         sixel_allocator_t *allocator);
1147
static void robinhood_table32_fini(struct robinhood_table32 *table);
1148
static struct robinhood_slot32 *
1149
robinhood_table32_lookup(struct robinhood_table32 *table, uint32_t key);
1150
static SIXELSTATUS robinhood_table32_insert(struct robinhood_table32 *table,
1151
                                           uint32_t key,
1152
                                           uint32_t value);
1153
static SIXELSTATUS robinhood_table32_grow(struct robinhood_table32 *table);
1154
static struct robinhood_slot32 *
1155
robinhood_table32_place(struct robinhood_table32 *table,
1156
                        struct robinhood_slot32 entry);
1157

1158
#define HOPSCOTCH_EMPTY_KEY 0xffffffffU
1159
#define HOPSCOTCH_DEFAULT_NEIGHBORHOOD 32U
1160
#define HOPSCOTCH_INSERT_RANGE 256U
1161

1162
struct hopscotch_slot32 {
1163
    uint32_t key;
1164
    uint32_t value;
1165
};
1166

1167
struct hopscotch_table32 {
1168
    struct hopscotch_slot32 *slots;
1169
    uint32_t *hopinfo;
1170
    size_t capacity;
1171
    size_t count;
1172
    size_t neighborhood;
1173
    sixel_allocator_t *allocator;
1174
};
1175

1176
static SIXELSTATUS hopscotch_table32_init(struct hopscotch_table32 *table,
1177
                                          size_t expected,
1178
                                          sixel_allocator_t *allocator);
1179
static void hopscotch_table32_fini(struct hopscotch_table32 *table);
1180
static struct hopscotch_slot32 *
1181
hopscotch_table32_lookup(struct hopscotch_table32 *table, uint32_t key);
1182
static SIXELSTATUS hopscotch_table32_insert(struct hopscotch_table32 *table,
1183
                                            uint32_t key,
1184
                                            uint32_t value);
1185
static SIXELSTATUS hopscotch_table32_grow(struct hopscotch_table32 *table);
1186

1187
static struct histogram_control
1188
histogram_control_make(unsigned int depth)
16,545,521✔
1189
{
1190
    struct histogram_control control;
1191

1192
    control = histogram_control_make_for_policy(depth,
24,865,112✔
1193
                                                histogram_lut_policy);
8,319,591✔
1194

1195
    return control;
16,545,521✔
1196
}
1197

1198
static size_t
1199
robinhood_round_capacity(size_t hint)
×
1200
{
1201
    size_t capacity;
1202

1203
    capacity = 16U;
×
1204
    if (hint < capacity) {
×
1205
        return capacity;
×
1206
    }
1207

1208
    capacity = hint - 1U;
×
1209
    capacity |= capacity >> 1;
×
1210
    capacity |= capacity >> 2;
×
1211
    capacity |= capacity >> 4;
×
1212
    capacity |= capacity >> 8;
×
1213
    capacity |= capacity >> 16;
×
1214
#if SIZE_MAX > UINT32_MAX
1215
    capacity |= capacity >> 32;
×
1216
#endif
1217
    if (capacity == SIZE_MAX) {
×
1218
        return SIZE_MAX;
×
1219
    }
1220
    capacity++;
×
1221
    if (capacity < 16U) {
×
1222
        capacity = 16U;
×
1223
    }
1224

1225
    return capacity;
×
1226
}
1227

1228
static SIXELSTATUS
1229
robinhood_table32_init(struct robinhood_table32 *table,
×
1230
                       size_t expected,
1231
                       sixel_allocator_t *allocator)
1232
{
1233
    size_t hint;
1234
    size_t capacity;
1235

1236
    table->slots = NULL;
×
1237
    table->capacity = 0U;
×
1238
    table->count = 0U;
×
1239
    table->allocator = allocator;
×
1240

1241
    if (expected < 16U) {
×
1242
        expected = 16U;
×
1243
    }
1244
    if (expected > SIZE_MAX / 2U) {
×
1245
        hint = SIZE_MAX / 2U;
×
1246
    } else {
1247
        hint = expected * 2U;
×
1248
    }
1249
    capacity = robinhood_round_capacity(hint);
×
1250
    if (capacity == SIZE_MAX && hint != SIZE_MAX) {
×
1251
        return SIXEL_BAD_ALLOCATION;
×
1252
    }
1253

1254
    table->slots = (struct robinhood_slot32 *)
×
1255
        sixel_allocator_calloc(allocator,
×
1256
                               capacity,
1257
                               sizeof(struct robinhood_slot32));
1258
    if (table->slots == NULL) {
×
1259
        table->capacity = 0U;
×
1260
        table->count = 0U;
×
1261
        return SIXEL_BAD_ALLOCATION;
×
1262
    }
1263
    table->capacity = capacity;
×
1264
    table->count = 0U;
×
1265

1266
    return SIXEL_OK;
×
1267
}
1268

1269
static void
1270
robinhood_table32_fini(struct robinhood_table32 *table)
×
1271
{
1272
    if (table->slots != NULL) {
×
1273
        sixel_allocator_free(table->allocator, table->slots);
×
1274
        table->slots = NULL;
×
1275
    }
1276
    table->capacity = 0U;
×
1277
    table->count = 0U;
×
1278
    table->allocator = NULL;
×
1279
}
×
1280

1281
static struct robinhood_slot32 *
1282
robinhood_table32_lookup(struct robinhood_table32 *table, uint32_t key)
×
1283
{
1284
    size_t mask;
1285
    size_t index;
1286
    uint16_t distance;
1287
    struct robinhood_slot32 *slot;
1288

1289
    if (table->capacity == 0U || table->slots == NULL) {
×
1290
        return NULL;
×
1291
    }
1292

1293
    mask = table->capacity - 1U;
×
1294
    index = (size_t)(key & mask);
×
1295
    distance = 0U;
×
1296

1297
    for (;;) {
1298
        slot = &table->slots[index];
×
1299
        if (slot->value == 0U) {
×
1300
            return NULL;
×
1301
        }
1302
        if (slot->key == key) {
×
1303
            return slot;
×
1304
        }
1305
        if (slot->distance < distance) {
×
1306
            return NULL;
×
1307
        }
1308
        index = (index + 1U) & mask;
×
1309
        distance++;
×
1310
    }
1311
}
1312

1313
static struct robinhood_slot32 *
1314
robinhood_table32_place(struct robinhood_table32 *table,
×
1315
                        struct robinhood_slot32 entry)
1316
{
1317
    size_t mask;
1318
    size_t index;
1319
    struct robinhood_slot32 *slot;
1320
    struct robinhood_slot32 tmp;
1321

1322
    mask = table->capacity - 1U;
×
1323
    index = (size_t)(entry.key & mask);
×
1324

1325
    for (;;) {
1326
        slot = &table->slots[index];
×
1327
        if (slot->value == 0U) {
×
1328
            *slot = entry;
×
1329
            table->count++;
×
1330
            return slot;
×
1331
        }
1332
        if (slot->key == entry.key) {
×
1333
            slot->value = entry.value;
×
1334
            return slot;
×
1335
        }
1336
        if (slot->distance < entry.distance) {
×
1337
            tmp = *slot;
×
1338
            *slot = entry;
×
1339
            entry = tmp;
×
1340
        }
1341
        index = (index + 1U) & mask;
×
1342
        entry.distance++;
×
1343
    }
1344
}
1345

1346
static SIXELSTATUS
1347
robinhood_table32_grow(struct robinhood_table32 *table)
×
1348
{
1349
    struct robinhood_slot32 *old_slots;
1350
    size_t old_capacity;
1351
    size_t new_capacity;
1352
    size_t i;
1353

1354
    if (table->allocator == NULL) {
×
1355
        return SIXEL_BAD_ALLOCATION;
×
1356
    }
1357
    if (table->capacity == 0U) {
×
1358
        new_capacity = 16U;
×
1359
    } else {
1360
        if (table->capacity > SIZE_MAX / 2U) {
×
1361
            return SIXEL_BAD_ALLOCATION;
×
1362
        }
1363
        new_capacity = table->capacity << 1U;
×
1364
    }
1365
    new_capacity = robinhood_round_capacity(new_capacity);
×
1366
    if (new_capacity <= table->capacity) {
×
1367
        return SIXEL_BAD_ALLOCATION;
×
1368
    }
1369

1370
    old_slots = table->slots;
×
1371
    old_capacity = table->capacity;
×
1372
    table->slots = (struct robinhood_slot32 *)
×
1373
        sixel_allocator_calloc(table->allocator,
×
1374
                               new_capacity,
1375
                               sizeof(struct robinhood_slot32));
1376
    if (table->slots == NULL) {
×
1377
        table->slots = old_slots;
×
1378
        table->capacity = old_capacity;
×
1379
        return SIXEL_BAD_ALLOCATION;
×
1380
    }
1381
    table->capacity = new_capacity;
×
1382
    table->count = 0U;
×
1383

1384
    for (i = 0U; i < old_capacity; ++i) {
×
1385
        struct robinhood_slot32 entry;
1386

1387
        if (old_slots[i].value == 0U) {
×
1388
            continue;
×
1389
        }
1390
        entry.key = old_slots[i].key;
×
1391
        entry.value = old_slots[i].value;
×
1392
        entry.distance = 0U;
×
1393
        (void)robinhood_table32_place(table, entry);
×
1394
    }
1395

1396
    sixel_allocator_free(table->allocator, old_slots);
×
1397

1398
    return SIXEL_OK;
×
1399
}
1400

1401
static SIXELSTATUS
1402
robinhood_table32_insert(struct robinhood_table32 *table,
×
1403
                         uint32_t key,
1404
                         uint32_t value)
1405
{
1406
    SIXELSTATUS status;
1407
    struct robinhood_slot32 entry;
1408

1409
    if (table->slots == NULL || table->capacity == 0U) {
×
1410
        return SIXEL_BAD_ARGUMENT;
×
1411
    }
1412
    if (table->count * 2U >= table->capacity) {
×
1413
        status = robinhood_table32_grow(table);
×
1414
        if (SIXEL_FAILED(status)) {
×
1415
            return status;
×
1416
        }
1417
    }
1418

1419
    entry.key = key;
×
1420
    entry.value = value;
×
1421
    entry.distance = 0U;
×
1422
    (void)robinhood_table32_place(table, entry);
×
1423

1424
    return SIXEL_OK;
×
1425
}
1426

1427
static SIXELSTATUS
1428
hopscotch_table32_init(struct hopscotch_table32 *table,
×
1429
                       size_t expected,
1430
                       sixel_allocator_t *allocator)
1431
{
1432
    size_t hint;
1433
    size_t capacity;
1434
    size_t i;
1435

1436
    if (table == NULL) {
×
1437
        return SIXEL_BAD_ARGUMENT;
×
1438
    }
1439

1440
    table->slots = NULL;
×
1441
    table->hopinfo = NULL;
×
1442
    table->capacity = 0U;
×
1443
    table->count = 0U;
×
1444
    table->neighborhood = HOPSCOTCH_DEFAULT_NEIGHBORHOOD;
×
1445
    table->allocator = allocator;
×
1446

1447
    if (expected < 16U) {
×
1448
        expected = 16U;
×
1449
    }
1450
    if (expected > SIZE_MAX / 2U) {
×
1451
        hint = SIZE_MAX / 2U;
×
1452
    } else {
1453
        hint = expected * 2U;
×
1454
    }
1455
    capacity = robinhood_round_capacity(hint);
×
1456
    if (capacity == SIZE_MAX && hint != SIZE_MAX) {
×
1457
        return SIXEL_BAD_ALLOCATION;
×
1458
    }
1459
    if (capacity < table->neighborhood) {
×
1460
        capacity = table->neighborhood;
×
1461
    }
1462

1463
    table->slots = (struct hopscotch_slot32 *)
×
1464
        sixel_allocator_malloc(allocator,
×
1465
                               capacity * sizeof(struct hopscotch_slot32));
1466
    if (table->slots == NULL) {
×
1467
        return SIXEL_BAD_ALLOCATION;
×
1468
    }
1469
    table->hopinfo = (uint32_t *)
×
1470
        sixel_allocator_calloc(allocator,
×
1471
                               capacity,
1472
                               sizeof(uint32_t));
1473
    if (table->hopinfo == NULL) {
×
1474
        sixel_allocator_free(allocator, table->slots);
×
1475
        table->slots = NULL;
×
1476
        return SIXEL_BAD_ALLOCATION;
×
1477
    }
1478

1479
    for (i = 0U; i < capacity; ++i) {
×
1480
        table->slots[i].key = HOPSCOTCH_EMPTY_KEY;
×
1481
        table->slots[i].value = 0U;
×
1482
    }
1483
    table->capacity = capacity;
×
1484
    table->count = 0U;
×
1485
    if (table->neighborhood > 32U) {
×
1486
        table->neighborhood = 32U;
×
1487
    }
1488
    if (table->neighborhood > table->capacity) {
×
1489
        table->neighborhood = table->capacity;
×
1490
    }
1491

1492
    return SIXEL_OK;
×
1493
}
1494

1495
static void
1496
hopscotch_table32_fini(struct hopscotch_table32 *table)
×
1497
{
1498
    sixel_allocator_t *allocator;
1499

1500
    if (table == NULL) {
×
1501
        return;
×
1502
    }
1503

1504
    allocator = table->allocator;
×
1505
    if (allocator != NULL) {
×
1506
        if (table->slots != NULL) {
×
1507
            sixel_allocator_free(allocator, table->slots);
×
1508
        }
1509
        if (table->hopinfo != NULL) {
×
1510
            sixel_allocator_free(allocator, table->hopinfo);
×
1511
        }
1512
    }
1513
    table->slots = NULL;
×
1514
    table->hopinfo = NULL;
×
1515
    table->capacity = 0U;
×
1516
    table->count = 0U;
×
1517
}
1518

1519
static struct hopscotch_slot32 *
1520
hopscotch_table32_lookup(struct hopscotch_table32 *table, uint32_t key)
×
1521
{
1522
    size_t index;
1523
    size_t bit;
1524
    size_t candidate;
1525
    uint32_t hop;
1526
    size_t mask;
1527
    size_t neighborhood;
1528

1529
    if (table == NULL || table->slots == NULL || table->hopinfo == NULL) {
×
1530
        return NULL;
×
1531
    }
1532
    if (table->capacity == 0U) {
×
1533
        return NULL;
×
1534
    }
1535

1536
    mask = table->capacity - 1U;
×
1537
    index = (size_t)key & mask;
×
1538
    hop = table->hopinfo[index];
×
1539
    neighborhood = table->neighborhood;
×
1540
    for (bit = 0U; bit < neighborhood; ++bit) {
×
1541
        if ((hop & (1U << bit)) == 0U) {
×
1542
            continue;
×
1543
        }
1544
        candidate = (index + bit) & mask;
×
1545
        if (table->slots[candidate].key == key) {
×
1546
            return &table->slots[candidate];
×
1547
        }
1548
    }
1549

1550
    return NULL;
×
1551
}
1552

1553
static SIXELSTATUS
1554
hopscotch_table32_grow(struct hopscotch_table32 *table)
×
1555
{
1556
    SIXELSTATUS status;
1557
    struct hopscotch_table32 tmp;
1558
    size_t i;
1559
    struct hopscotch_slot32 *slot;
1560

1561
    tmp.slots = NULL;
×
1562
    tmp.hopinfo = NULL;
×
1563
    tmp.capacity = 0U;
×
1564
    tmp.count = 0U;
×
1565
    tmp.neighborhood = table->neighborhood;
×
1566
    tmp.allocator = table->allocator;
×
1567

1568
    status = hopscotch_table32_init(&tmp,
×
1569
                                    table->capacity * 2U,
×
1570
                                    table->allocator);
1571
    if (SIXEL_FAILED(status)) {
×
1572
        return status;
×
1573
    }
1574
    if (tmp.neighborhood > table->neighborhood) {
×
1575
        tmp.neighborhood = table->neighborhood;
×
1576
    }
1577

1578
    for (i = 0U; i < table->capacity; ++i) {
×
1579
        slot = &table->slots[i];
×
1580
        if (slot->key == HOPSCOTCH_EMPTY_KEY) {
×
1581
            continue;
×
1582
        }
1583
        status = hopscotch_table32_insert(&tmp,
×
1584
                                          slot->key,
1585
                                          slot->value);
1586
        if (SIXEL_FAILED(status)) {
×
1587
            hopscotch_table32_fini(&tmp);
×
1588
            return status;
×
1589
        }
1590
    }
1591

1592
    hopscotch_table32_fini(table);
×
1593

1594
    table->slots = tmp.slots;
×
1595
    table->hopinfo = tmp.hopinfo;
×
1596
    table->capacity = tmp.capacity;
×
1597
    table->count = tmp.count;
×
1598
    table->neighborhood = tmp.neighborhood;
×
1599
    table->allocator = tmp.allocator;
×
1600

1601
    return SIXEL_OK;
×
1602
}
1603

1604
static SIXELSTATUS
1605
hopscotch_table32_insert(struct hopscotch_table32 *table,
×
1606
                         uint32_t key,
1607
                         uint32_t value)
1608
{
1609
    SIXELSTATUS status;
1610
    struct hopscotch_slot32 *slot;
1611
    size_t index;
1612
    size_t mask;
1613
    size_t distance;
1614
    size_t attempts;
1615
    size_t free_index;
1616
    size_t neighborhood;
1617
    int relocated;
1618
    size_t offset;
1619
    uint32_t hop;
1620
    size_t bit;
1621
    size_t move_index;
1622
    struct hopscotch_slot32 tmp_slot;
1623

1624
    if (table == NULL || table->slots == NULL || table->hopinfo == NULL) {
×
1625
        return SIXEL_BAD_ARGUMENT;
×
1626
    }
1627
    if (table->capacity == 0U) {
×
1628
        return SIXEL_BAD_ARGUMENT;
×
1629
    }
1630

1631
    slot = hopscotch_table32_lookup(table, key);
×
1632
    if (slot != NULL) {
×
1633
        slot->value = value;
×
1634
        return SIXEL_OK;
×
1635
    }
1636

1637
    if (table->count * 2U >= table->capacity) {
×
1638
        status = hopscotch_table32_grow(table);
×
1639
        if (SIXEL_FAILED(status)) {
×
1640
            return status;
×
1641
        }
1642
        return hopscotch_table32_insert(table, key, value);
×
1643
    }
1644

1645
    mask = table->capacity - 1U;
×
1646
    neighborhood = table->neighborhood;
×
1647
    index = (size_t)key & mask;
×
1648
    slot = NULL;
×
1649
    free_index = index;
×
1650
    distance = 0U;
×
1651
    for (attempts = 0U; attempts < HOPSCOTCH_INSERT_RANGE; ++attempts) {
×
1652
        free_index = (index + attempts) & mask;
×
1653
        slot = &table->slots[free_index];
×
1654
        if (slot->key == HOPSCOTCH_EMPTY_KEY) {
×
1655
            distance = attempts;
×
1656
            break;
×
1657
        }
1658
    }
1659
    if (slot == NULL || slot->key != HOPSCOTCH_EMPTY_KEY) {
×
1660
        status = hopscotch_table32_grow(table);
×
1661
        if (SIXEL_FAILED(status)) {
×
1662
            return status;
×
1663
        }
1664
        return hopscotch_table32_insert(table, key, value);
×
1665
    }
1666

1667
    /*
1668
     * Relocation diagram:
1669
     *
1670
     *   free slot <--- hop window <--- [base bucket]
1671
     *      ^ move resident outward until distance < neighborhood
1672
     */
1673
    while (distance >= neighborhood) {
×
1674
        relocated = 0;
×
1675
        for (offset = neighborhood - 1U; offset > 0U; --offset) {
×
1676
            size_t base;
1677

1678
            base = (free_index + table->capacity - offset) & mask;
×
1679
            hop = table->hopinfo[base];
×
1680
            if (hop == 0U) {
×
1681
                continue;
×
1682
            }
1683
            for (bit = 0U; bit < offset; ++bit) {
×
1684
                if ((hop & (1U << bit)) == 0U) {
×
1685
                    continue;
×
1686
                }
1687
                move_index = (base + bit) & mask;
×
1688
                tmp_slot = table->slots[move_index];
×
1689
                table->slots[free_index] = tmp_slot;
×
1690
                table->slots[move_index].key = HOPSCOTCH_EMPTY_KEY;
×
1691
                table->slots[move_index].value = 0U;
×
1692
                table->hopinfo[base] &= (uint32_t)~(1U << bit);
×
1693
                table->hopinfo[base] |= (uint32_t)(1U << offset);
×
1694
                free_index = move_index;
×
1695
                if (free_index >= index) {
×
1696
                    distance = free_index - index;
×
1697
                } else {
1698
                    distance = free_index + table->capacity - index;
×
1699
                }
1700
                relocated = 1;
×
1701
                break;
×
1702
            }
1703
            if (relocated) {
×
1704
                break;
×
1705
            }
1706
        }
1707
        if (!relocated) {
×
1708
            status = hopscotch_table32_grow(table);
×
1709
            if (SIXEL_FAILED(status)) {
×
1710
                return status;
×
1711
            }
1712
            return hopscotch_table32_insert(table, key, value);
×
1713
        }
1714
    }
1715

1716
    if (distance >= 32U) {
×
1717
        status = hopscotch_table32_grow(table);
×
1718
        if (SIXEL_FAILED(status)) {
×
1719
            return status;
×
1720
        }
1721
        return hopscotch_table32_insert(table, key, value);
×
1722
    }
1723

1724
    table->slots[free_index].key = key;
×
1725
    table->slots[free_index].value = value;
×
1726
    table->hopinfo[index] |= (uint32_t)(1U << distance);
×
1727
    table->count++;
×
1728

1729
    return SIXEL_OK;
×
1730
}
1731

1732
/*
1733
 * The cuckoo hash backend stores entries in fixed-width buckets.
1734
 *
1735
 *   [bucket 0] -> key0 key1 key2 key3
1736
 *                 val0 val1 val2 val3
1737
 *   [bucket 1] -> ...
1738
 *
1739
 * Each key is compared against the 128-bit lane using SIMD instructions.
1740
 * Two hash functions map a key to its primary and secondary buckets.  When
1741
 * both are full, the eviction loop "kicks" an entry toward its alternate
1742
 * bucket, as illustrated below:
1743
 *
1744
 *   bucket A --kick--> bucket B --kick--> bucket A ...
1745
 *
1746
 * A tiny stash buffers entries when the table momentarily fills up.  This
1747
 * keeps lookups fast while letting us grow the table lazily.
1748
 */
1749
static size_t
1750
cuckoo_round_buckets(size_t hint)
239✔
1751
{
1752
    size_t desired;
1753
    size_t buckets;
1754
    size_t prev;
1755

1756
    if (hint < CUCKOO_BUCKET_SIZE) {
239!
1757
        hint = CUCKOO_BUCKET_SIZE;
×
1758
    }
1759
    if (hint > SIZE_MAX / 2U) {
239!
1760
        hint = SIZE_MAX / 2U;
×
1761
    }
1762
    desired = (hint * 2U + CUCKOO_BUCKET_SIZE - 1U) / CUCKOO_BUCKET_SIZE;
239✔
1763
    if (desired == 0U) {
239!
1764
        desired = 1U;
×
1765
    }
1766

1767
    buckets = 1U;
239✔
1768
    while (buckets < desired) {
4,302✔
1769
        prev = buckets;
4,063✔
1770
        if (buckets > SIZE_MAX / 2U) {
4,063!
1771
            buckets = prev;
×
1772
            break;
×
1773
        }
1774
        buckets <<= 1U;
4,063✔
1775
        if (buckets < prev) {
4,063!
1776
            buckets = prev;
×
1777
            break;
×
1778
        }
1779
    }
1780
    if (buckets == 0U) {
239!
1781
        buckets = 1U;
×
1782
    }
1783

1784
    return buckets;
239✔
1785
}
1786

1787
static size_t
1788
cuckoo_hash_primary(uint32_t key, size_t mask)
18,541,056✔
1789
{
1790
    uint32_t mix;
1791

1792
    mix = key * 0x9e3779b1U;
18,541,056✔
1793
    return (size_t)(mix & (uint32_t)mask);
18,541,056✔
1794
}
1795

1796
static size_t
1797
cuckoo_hash_secondary(uint32_t key, size_t mask)
1,995,770✔
1798
{
1799
    uint32_t mix;
1800

1801
    mix = key ^ 0x85ebca6bU;
1,995,770✔
1802
    mix ^= mix >> 13;
1,995,770✔
1803
    mix *= 0xc2b2ae35U;
1,995,770✔
1804
    return (size_t)(mix & (uint32_t)mask);
1,995,770✔
1805
}
1806

1807
static size_t
1808
cuckoo_hash_alternate(uint32_t key, size_t bucket, size_t mask)
×
1809
{
1810
    size_t primary;
1811
    size_t secondary;
1812

1813
    primary = cuckoo_hash_primary(key, mask);
×
1814
    secondary = cuckoo_hash_secondary(key, mask);
×
1815
    if (primary == bucket) {
×
1816
        if (secondary == primary) {
×
1817
            secondary = (secondary + 1U) & mask;
×
1818
        }
1819
        return secondary;
×
1820
    }
1821

1822
    return primary;
×
1823
}
1824

1825
static uint32_t *
1826
cuckoo_bucket_find(struct cuckoo_bucket32 *bucket, uint32_t key)
19,538,941✔
1827
{
1828
    size_t i;
1829

1830
#if defined(SIXEL_USE_SSE2)
1831
    __m128i needle;
1832
    __m128i keys;
1833
    __m128i cmp;
1834
    int mask;
1835

1836
    needle = _mm_set1_epi32((int)key);
1837
    keys = _mm_loadu_si128((const __m128i *)bucket->key);
1838
    cmp = _mm_cmpeq_epi32(needle, keys);
1839
    mask = _mm_movemask_ps(_mm_castsi128_ps(cmp));
1840
    if ((mask & 1) != 0 && bucket->value[0] != 0U) {
1841
        return &bucket->value[0];
1842
    }
1843
    if ((mask & 2) != 0 && bucket->value[1] != 0U) {
1844
        return &bucket->value[1];
1845
    }
1846
    if ((mask & 4) != 0 && bucket->value[2] != 0U) {
1847
        return &bucket->value[2];
1848
    }
1849
    if ((mask & 8) != 0 && bucket->value[3] != 0U) {
1850
        return &bucket->value[3];
1851
    }
1852
#elif defined(SIXEL_USE_NEON)
1853
    uint32x4_t needle;
1854
    uint32x4_t keys;
1855
    uint32x4_t cmp;
1856

1857
    needle = vdupq_n_u32(key);
1858
    keys = vld1q_u32(bucket->key);
1859
    cmp = vceqq_u32(needle, keys);
1860
    if (vgetq_lane_u32(cmp, 0) != 0U && bucket->value[0] != 0U) {
1861
        return &bucket->value[0];
1862
    }
1863
    if (vgetq_lane_u32(cmp, 1) != 0U && bucket->value[1] != 0U) {
1864
        return &bucket->value[1];
1865
    }
1866
    if (vgetq_lane_u32(cmp, 2) != 0U && bucket->value[2] != 0U) {
1867
        return &bucket->value[2];
1868
    }
1869
    if (vgetq_lane_u32(cmp, 3) != 0U && bucket->value[3] != 0U) {
1870
        return &bucket->value[3];
1871
    }
1872
#else
1873
    for (i = 0U; i < CUCKOO_BUCKET_SIZE; ++i) {
35,635,723✔
1874
        if (bucket->value[i] != 0U && bucket->key[i] == key) {
31,644,183✔
1875
            return &bucket->value[i];
15,547,401✔
1876
        }
1877
    }
5,249,408✔
1878
#endif
1879

1880
    return NULL;
3,991,540✔
1881
}
9,296,267✔
1882

1883
static int
1884
cuckoo_bucket_insert_direct(struct cuckoo_bucket32 *bucket,
997,885✔
1885
                            uint32_t key,
1886
                            uint32_t value)
1887
{
1888
    size_t i;
1889

1890
    for (i = 0U; i < CUCKOO_BUCKET_SIZE; ++i) {
1,029,007!
1891
        if (bucket->value[i] == 0U) {
1,029,007✔
1892
            bucket->key[i] = key;
997,885✔
1893
            bucket->value[i] = value;
997,885✔
1894
            return 1;
997,885✔
1895
        }
1896
    }
9,174✔
1897

1898
    return 0;
×
1899
}
325,595✔
1900

1901
static SIXELSTATUS
1902
cuckoo_table32_init(struct cuckoo_table32 *table,
239✔
1903
                    size_t expected,
1904
                    sixel_allocator_t *allocator)
1905
{
1906
    size_t buckets;
1907
    size_t i;
1908
    size_t j;
1909

1910
    if (table == NULL || allocator == NULL) {
239!
1911
        return SIXEL_BAD_ARGUMENT;
×
1912
    }
1913

1914
    buckets = cuckoo_round_buckets(expected);
239✔
1915
    if (buckets == 0U
239!
1916
        || buckets > SIZE_MAX / sizeof(struct cuckoo_bucket32)) {
239!
1917
        sixel_helper_set_additional_message(
×
1918
            "unable to size cuckoo bucket array.");
1919
        return SIXEL_BAD_ALLOCATION;
×
1920
    }
1921

1922
    table->buckets = (struct cuckoo_bucket32 *)sixel_allocator_malloc(
239✔
1923
        allocator, buckets * sizeof(struct cuckoo_bucket32));
99✔
1924
    if (table->buckets == NULL) {
239!
1925
        sixel_helper_set_additional_message(
×
1926
            "unable to allocate cuckoo buckets.");
1927
        return SIXEL_BAD_ALLOCATION;
×
1928
    }
1929

1930
    table->bucket_count = buckets;
239✔
1931
    table->bucket_mask = buckets - 1U;
239✔
1932
    table->stash_count = 0U;
239✔
1933
    table->entry_count = 0U;
239✔
1934
    table->allocator = allocator;
239✔
1935
    for (i = 0U; i < buckets; ++i) {
31,326,447✔
1936
        for (j = 0U; j < CUCKOO_BUCKET_SIZE; ++j) {
156,631,040✔
1937
            table->buckets[i].key[j] = CUCKOO_EMPTY_KEY;
125,304,832✔
1938
            table->buckets[i].value[j] = 0U;
125,304,832✔
1939
        }
51,904,512✔
1940
    }
12,976,128✔
1941
    for (i = 0U; i < CUCKOO_STASH_SIZE; ++i) {
7,887✔
1942
        table->stash_key[i] = CUCKOO_EMPTY_KEY;
7,648✔
1943
        table->stash_value[i] = 0U;
7,648✔
1944
    }
3,168✔
1945

1946
    return SIXEL_OK;
239✔
1947
}
99✔
1948

1949
static void
1950
cuckoo_table32_clear(struct cuckoo_table32 *table)
236✔
1951
{
1952
    size_t i;
1953
    size_t j;
1954

1955
    if (table == NULL || table->buckets == NULL) {
236!
1956
        return;
×
1957
    }
1958

1959
    table->stash_count = 0U;
236✔
1960
    table->entry_count = 0U;
236✔
1961
    for (i = 0U; i < table->bucket_count; ++i) {
30,933,228✔
1962
        for (j = 0U; j < CUCKOO_BUCKET_SIZE; ++j) {
154,664,960✔
1963
            table->buckets[i].key[j] = CUCKOO_EMPTY_KEY;
123,731,968✔
1964
            table->buckets[i].value[j] = 0U;
123,731,968✔
1965
        }
51,380,224✔
1966
    }
12,845,056✔
1967
    for (i = 0U; i < CUCKOO_STASH_SIZE; ++i) {
7,788✔
1968
        table->stash_key[i] = CUCKOO_EMPTY_KEY;
7,552✔
1969
        table->stash_value[i] = 0U;
7,552✔
1970
    }
3,136✔
1971
}
98✔
1972

1973
static void
1974
cuckoo_table32_fini(struct cuckoo_table32 *table)
239✔
1975
{
1976
    if (table == NULL || table->allocator == NULL) {
239!
1977
        return;
×
1978
    }
1979
    if (table->buckets != NULL) {
239!
1980
        sixel_allocator_free(table->allocator, table->buckets);
239✔
1981
        table->buckets = NULL;
239✔
1982
    }
99✔
1983
    table->bucket_count = 0U;
239✔
1984
    table->bucket_mask = 0U;
239✔
1985
    table->stash_count = 0U;
239✔
1986
    table->entry_count = 0U;
239✔
1987
}
99✔
1988

1989
static uint32_t *
1990
cuckoo_table32_lookup(struct cuckoo_table32 *table, uint32_t key)
17,543,171✔
1991
{
1992
    size_t index;
1993
    size_t i;
1994
    uint32_t *slot;
1995

1996
    if (table == NULL || table->buckets == NULL) {
17,543,171!
1997
        return NULL;
×
1998
    }
1999

2000
    index = cuckoo_hash_primary(key, table->bucket_mask);
17,543,171✔
2001
    slot = cuckoo_bucket_find(&table->buckets[index], key);
17,543,171✔
2002
    if (slot != NULL) {
17,543,171✔
2003
        return slot;
15,547,401✔
2004
    }
2005

2006
    index = cuckoo_hash_secondary(key, table->bucket_mask);
1,995,770✔
2007
    slot = cuckoo_bucket_find(&table->buckets[index], key);
1,995,770✔
2008
    if (slot != NULL) {
1,995,770!
2009
        return slot;
×
2010
    }
2011

2012
    for (i = 0U; i < table->stash_count; ++i) {
1,995,770!
2013
        if (table->stash_value[i] != 0U && table->stash_key[i] == key) {
×
2014
            return &table->stash_value[i];
×
2015
        }
2016
    }
2017

2018
    return NULL;
1,995,770✔
2019
}
8,645,077✔
2020

2021
static SIXELSTATUS
2022
cuckoo_table32_grow(struct cuckoo_table32 *table)
×
2023
{
2024
    struct cuckoo_table32 tmp;
2025
    struct cuckoo_bucket32 *old_buckets;
2026
    size_t old_count;
2027
    size_t i;
2028
    size_t j;
2029
    SIXELSTATUS status;
2030

2031
    if (table == NULL || table->allocator == NULL) {
×
2032
        return SIXEL_BAD_ARGUMENT;
×
2033
    }
2034

2035
    tmp.buckets = NULL;
×
2036
    tmp.bucket_count = 0U;
×
2037
    tmp.bucket_mask = 0U;
×
2038
    tmp.stash_count = 0U;
×
2039
    tmp.entry_count = 0U;
×
2040
    tmp.allocator = table->allocator;
×
2041
    for (i = 0U; i < CUCKOO_STASH_SIZE; ++i) {
×
2042
        tmp.stash_key[i] = CUCKOO_EMPTY_KEY;
×
2043
        tmp.stash_value[i] = 0U;
×
2044
    }
2045

2046
    status = cuckoo_table32_init(&tmp,
×
2047
                                 (table->entry_count + 1U) * 2U,
×
2048
                                 table->allocator);
2049
    if (SIXEL_FAILED(status)) {
×
2050
        return status;
×
2051
    }
2052

2053
    old_buckets = table->buckets;
×
2054
    old_count = table->bucket_count;
×
2055
    for (i = 0U; i < old_count; ++i) {
×
2056
        for (j = 0U; j < CUCKOO_BUCKET_SIZE; ++j) {
×
2057
            if (old_buckets[i].value[j] != 0U) {
×
2058
                status = cuckoo_table32_insert(&tmp,
×
2059
                                               old_buckets[i].key[j],
×
2060
                                               old_buckets[i].value[j]);
×
2061
                if (SIXEL_FAILED(status)) {
×
2062
                    cuckoo_table32_fini(&tmp);
×
2063
                    return status;
×
2064
                }
2065
            }
2066
        }
2067
    }
2068
    for (i = 0U; i < table->stash_count; ++i) {
×
2069
        if (table->stash_value[i] != 0U) {
×
2070
            status = cuckoo_table32_insert(&tmp,
×
2071
                                           table->stash_key[i],
2072
                                           table->stash_value[i]);
2073
            if (SIXEL_FAILED(status)) {
×
2074
                cuckoo_table32_fini(&tmp);
×
2075
                return status;
×
2076
            }
2077
        }
2078
    }
2079

2080
    sixel_allocator_free(table->allocator, old_buckets);
×
2081
    *table = tmp;
×
2082

2083
    return SIXEL_OK;
×
2084
}
2085

2086
static SIXELSTATUS
2087
cuckoo_table32_insert(struct cuckoo_table32 *table,
997,885✔
2088
                      uint32_t key,
2089
                      uint32_t value)
2090
{
2091
    uint32_t *slot;
2092
    uint32_t cur_key;
2093
    uint32_t cur_value;
2094
    uint32_t victim_key;
2095
    uint32_t victim_value;
2096
    size_t bucket_index;
2097
    size_t kicks;
2098
    size_t victim_slot;
2099
    struct cuckoo_bucket32 *bucket;
2100
    SIXELSTATUS status;
2101

2102
    if (table == NULL || table->buckets == NULL) {
997,885!
2103
        return SIXEL_BAD_ARGUMENT;
×
2104
    }
2105

2106
    slot = cuckoo_table32_lookup(table, key);
997,885✔
2107
    if (slot != NULL) {
997,885!
2108
        *slot = value;
×
2109
        return SIXEL_OK;
×
2110
    }
2111

2112
    cur_key = key;
997,885✔
2113
    cur_value = value;
997,885✔
2114
    bucket_index = cuckoo_hash_primary(cur_key, table->bucket_mask);
997,885✔
2115
    for (kicks = 0U; kicks < CUCKOO_MAX_KICKS; ++kicks) {
997,885!
2116
        bucket = &table->buckets[bucket_index];
997,885✔
2117
        if (cuckoo_bucket_insert_direct(bucket, cur_key, cur_value)) {
997,885!
2118
            table->entry_count++;
997,885✔
2119
            return SIXEL_OK;
997,885✔
2120
        }
2121
        victim_slot = (size_t)((cur_key + kicks) &
×
2122
                               (CUCKOO_BUCKET_SIZE - 1U));
2123
        victim_key = bucket->key[victim_slot];
×
2124
        victim_value = bucket->value[victim_slot];
×
2125
        bucket->key[victim_slot] = cur_key;
×
2126
        bucket->value[victim_slot] = cur_value;
×
2127
        cur_key = victim_key;
×
2128
        cur_value = victim_value;
×
2129
        bucket_index = cuckoo_hash_alternate(cur_key,
×
2130
                                             bucket_index,
2131
                                             table->bucket_mask);
2132
    }
2133

2134
    if (table->stash_count < CUCKOO_STASH_SIZE) {
×
2135
        table->stash_key[table->stash_count] = cur_key;
×
2136
        table->stash_value[table->stash_count] = cur_value;
×
2137
        table->stash_count++;
×
2138
        table->entry_count++;
×
2139
        return SIXEL_OK;
×
2140
    }
2141

2142
    status = cuckoo_table32_grow(table);
×
2143
    if (SIXEL_FAILED(status)) {
×
2144
        return status;
×
2145
    }
2146

2147
    return cuckoo_table32_insert(table, cur_key, cur_value);
×
2148
}
325,595✔
2149

2150
static SIXELSTATUS
2151
computeHistogram_robinhood(unsigned char const *data,
2152
                           unsigned int length,
2153
                           unsigned long depth,
2154
                           unsigned int step,
2155
                           unsigned int max_sample,
2156
                           tupletable2 * const colorfreqtableP,
2157
                           struct histogram_control const *control,
2158
                           sixel_allocator_t *allocator);
2159

2160
static SIXELSTATUS
2161
computeHistogram_hopscotch(unsigned char const *data,
2162
                           unsigned int length,
2163
                           unsigned long depth,
2164
                           unsigned int step,
2165
                           unsigned int max_sample,
2166
                           tupletable2 * const colorfreqtableP,
2167
                           struct histogram_control const *control,
2168
                           sixel_allocator_t *allocator);
2169

2170
static SIXELSTATUS
2171
computeHistogram(unsigned char const    /* in */  *data,
235✔
2172
                 unsigned int           /* in */  length,
2173
                 unsigned long const    /* in */  depth,
2174
                 tupletable2 * const    /* out */ colorfreqtableP,
2175
                 int const              /* in */  qualityMode,
2176
                 sixel_allocator_t      /* in */  *allocator)
2177
{
2178
    SIXELSTATUS status = SIXEL_FALSE;
235✔
2179
    typedef uint32_t unit_t;
2180
    unsigned int i, n;
2181
    unit_t *histogram = NULL;
235✔
2182
    unit_t *refmap = NULL;
235✔
2183
    unit_t *ref;
2184
    unsigned int bucket_index;
2185
    unsigned int step;
2186
    unsigned int max_sample;
2187
    size_t hist_size;
2188
    unit_t bucket_value;
2189
    unsigned int component;
2190
    unsigned int reconstructed;
2191
    struct histogram_control control;
2192

2193
    switch (qualityMode) {
235!
2194
    case SIXEL_QUALITY_LOW:
104✔
2195
        max_sample = 18383;
202✔
2196
        break;
202✔
2197
    case SIXEL_QUALITY_HIGH:
20✔
2198
        max_sample = 1118383;
30✔
2199
        break;
30✔
2200
    case SIXEL_QUALITY_FULL:
3✔
2201
    default:
2202
        max_sample = 4003079;
3✔
2203
        break;
3✔
2204
    }
2205

2206
    step = length / depth / max_sample * depth;
235✔
2207
    if (step <= 0) {
235✔
2208
        step = depth;
156✔
2209
    }
52✔
2210

2211
    quant_trace(stderr, "making histogram...\n");
235✔
2212

2213
    control = histogram_control_make((unsigned int)depth);
235✔
2214
    if (histogram_lut_policy == SIXEL_LUT_POLICY_ROBINHOOD
235!
2215
        || histogram_lut_policy == SIXEL_LUT_POLICY_HOPSCOTCH) {
235!
2216
        if (histogram_lut_policy == SIXEL_LUT_POLICY_ROBINHOOD) {
×
2217
            status = computeHistogram_robinhood(data,
×
2218
                                                length,
2219
                                                depth,
2220
                                                step,
2221
                                                max_sample,
2222
                                                colorfreqtableP,
2223
                                                &control,
2224
                                                allocator);
2225
        } else {
2226
            status = computeHistogram_hopscotch(data,
×
2227
                                                length,
2228
                                                depth,
2229
                                                step,
2230
                                                max_sample,
2231
                                                colorfreqtableP,
2232
                                                &control,
2233
                                                allocator);
2234
        }
2235
        goto end;
×
2236
    }
2237

2238
    hist_size = histogram_dense_size((unsigned int)depth, &control);
235✔
2239
    histogram = (unit_t *)sixel_allocator_calloc(allocator,
344✔
2240
                                                 hist_size,
109✔
2241
                                                 sizeof(unit_t));
2242
    if (histogram == NULL) {
235!
2243
        sixel_helper_set_additional_message(
×
2244
            "unable to allocate memory for histogram.");
2245
        status = SIXEL_BAD_ALLOCATION;
×
2246
        goto end;
×
2247
    }
2248
    ref = refmap = (unit_t *)sixel_allocator_malloc(allocator,
344✔
2249
                                                    hist_size *
109✔
2250
                                                    sizeof(unit_t));
2251
    if (refmap == NULL) {
235!
2252
        sixel_helper_set_additional_message(
×
2253
            "unable to allocate memory for lookup table.");
2254
        status = SIXEL_BAD_ALLOCATION;
×
2255
        goto end;
×
2256
    }
2257

2258
    for (i = 0; i < length; i += step) {
3,093,871✔
2259
        bucket_index = computeHash(data + i,
4,698,928✔
2260
                                   (unsigned int)depth,
1,605,292✔
2261
                                   &control);
2262
        if (histogram[bucket_index] == 0) {
3,093,636✔
2263
            *ref++ = bucket_index;
190,407✔
2264
        }
63,579✔
2265
        if (histogram[bucket_index] < UINT32_MAX) {
3,093,636!
2266
            histogram[bucket_index]++;
3,093,636✔
2267
        }
1,605,292✔
2268
    }
1,605,292✔
2269

2270
    colorfreqtableP->size = (unsigned int)(ref - refmap);
235✔
2271

2272
    status = alloctupletable(&colorfreqtableP->table,
344✔
2273
                             depth,
109✔
2274
                             (unsigned int)(ref - refmap),
235✔
2275
                             allocator);
109✔
2276
    if (SIXEL_FAILED(status)) {
235!
2277
        goto end;
×
2278
    }
2279
    for (i = 0; i < colorfreqtableP->size; ++i) {
190,642✔
2280
        bucket_value = refmap[i];
190,407✔
2281
        if (histogram[bucket_value] > 0) {
190,407!
2282
            colorfreqtableP->table[i]->value = histogram[bucket_value];
190,407✔
2283
            for (n = 0; n < depth; n++) {
761,628✔
2284
                component = (unsigned int)
571,221✔
2285
                    ((bucket_value >> (n * control.channel_bits)) &
761,958✔
2286
                     control.channel_mask);
571,221✔
2287
                reconstructed = histogram_reconstruct(component,
571,221✔
2288
                                                      &control);
2289
                colorfreqtableP->table[i]->tuple[depth - 1 - n]
571,221✔
2290
                    = (sample)reconstructed;
761,958✔
2291
            }
190,737✔
2292
        }
63,579✔
2293
    }
63,579✔
2294

2295
    quant_trace(stderr, "%u colors found\n", colorfreqtableP->size);
235✔
2296

2297
    status = SIXEL_OK;
235✔
2298

2299
end:
126✔
2300
    sixel_allocator_free(allocator, refmap);
235✔
2301
    sixel_allocator_free(allocator, histogram);
235✔
2302

2303
    return status;
235✔
2304
}
2305

2306
static SIXELSTATUS
2307
computeHistogram_robinhood(unsigned char const *data,
×
2308
                           unsigned int length,
2309
                           unsigned long depth,
2310
                           unsigned int step,
2311
                           unsigned int max_sample,
2312
                           tupletable2 * const colorfreqtableP,
2313
                           struct histogram_control const *control,
2314
                           sixel_allocator_t *allocator)
2315
{
2316
    SIXELSTATUS status = SIXEL_FALSE;
×
2317
    struct robinhood_table32 table;
2318
    size_t expected;
2319
    size_t cap_limit;
2320
    size_t index;
2321
    unsigned int depth_u;
2322
    unsigned int i;
2323
    unsigned int n;
2324
    uint32_t bucket_index;
2325
    uint32_t entry_key;
2326
    struct robinhood_slot32 *slot;
2327
    unsigned int component;
2328
    unsigned int reconstructed;
2329

2330
    /*
2331
     * The ASCII sketch below shows how the sparse table stores samples:
2332
     *
2333
     *   [hash]->(key,value,distance)  Robin Hood probing keeps dense tails.
2334
     */
2335
    table.slots = NULL;
×
2336
    table.capacity = 0U;
×
2337
    table.count = 0U;
×
2338
    table.allocator = allocator;
×
2339
    cap_limit = (size_t)1U << 20;
×
2340
    expected = max_sample;
×
2341
    if (expected < 256U) {
×
2342
        expected = 256U;
×
2343
    }
2344
    if (expected > cap_limit) {
×
2345
        expected = cap_limit;
×
2346
    }
2347

2348
    status = robinhood_table32_init(&table, expected, allocator);
×
2349
    if (SIXEL_FAILED(status)) {
×
2350
        sixel_helper_set_additional_message(
×
2351
            "unable to allocate robinhood histogram.");
2352
        goto end;
×
2353
    }
2354

2355
    depth_u = (unsigned int)depth;
×
2356
    for (i = 0U; i < length; i += step) {
×
2357
        bucket_index = computeHash(data + i, depth_u, control);
×
2358
        slot = robinhood_table32_lookup(&table, bucket_index);
×
2359
        if (slot == NULL) {
×
2360
            status = robinhood_table32_insert(&table,
×
2361
                                              bucket_index,
2362
                                              1U);
2363
            if (SIXEL_FAILED(status)) {
×
2364
                sixel_helper_set_additional_message(
×
2365
                    "unable to grow robinhood histogram.");
2366
                goto end;
×
2367
            }
2368
        } else if (slot->value < UINT32_MAX) {
×
2369
            slot->value++;
×
2370
        }
2371
    }
2372

2373
    if (table.count > UINT_MAX) {
×
2374
        sixel_helper_set_additional_message(
×
2375
            "too many unique colors for histogram.");
2376
        status = SIXEL_BAD_ARGUMENT;
×
2377
        goto end;
×
2378
    }
2379

2380
    colorfreqtableP->size = (unsigned int)table.count;
×
2381
    status = alloctupletable(&colorfreqtableP->table,
×
2382
                             depth_u,
2383
                             (unsigned int)table.count,
×
2384
                             allocator);
2385
    if (SIXEL_FAILED(status)) {
×
2386
        goto end;
×
2387
    }
2388

2389
    index = 0U;
×
2390
    /*
2391
     * Stream slots in the hash traversal order to avoid qsort overhead.
2392
     * This favors throughput over identical palette ordering.
2393
     */
2394
    for (i = 0U; i < table.capacity; ++i) {
×
2395
        slot = &table.slots[i];
×
2396
        if (slot->value == 0U) {
×
2397
            continue;
×
2398
        }
2399
        if (index >= colorfreqtableP->size) {
×
2400
            break;
×
2401
        }
2402
        entry_key = slot->key;
×
2403
        colorfreqtableP->table[index]->value = slot->value;
×
2404
        for (n = 0U; n < depth_u; ++n) {
×
2405
            component = (unsigned int)
×
2406
                ((entry_key >> (n * control->channel_bits))
×
2407
                 & control->channel_mask);
×
2408
            reconstructed = histogram_reconstruct(component, control);
×
2409
            colorfreqtableP->table[index]->tuple[depth_u - 1U - n]
×
2410
                = (sample)reconstructed;
×
2411
        }
2412
        index++;
×
2413
    }
2414

2415
    status = SIXEL_OK;
×
2416

2417
end:
2418
    robinhood_table32_fini(&table);
×
2419

2420
    return status;
×
2421
}
2422

2423
static SIXELSTATUS
2424
computeHistogram_hopscotch(unsigned char const *data,
×
2425
                           unsigned int length,
2426
                           unsigned long depth,
2427
                           unsigned int step,
2428
                           unsigned int max_sample,
2429
                           tupletable2 * const colorfreqtableP,
2430
                           struct histogram_control const *control,
2431
                           sixel_allocator_t *allocator)
2432
{
2433
    SIXELSTATUS status = SIXEL_FALSE;
×
2434
    struct hopscotch_table32 table;
2435
    size_t expected;
2436
    size_t cap_limit;
2437
    size_t index;
2438
    unsigned int depth_u;
2439
    unsigned int i;
2440
    unsigned int n;
2441
    uint32_t bucket_index;
2442
    uint32_t entry_key;
2443
    struct hopscotch_slot32 *slot;
2444
    unsigned int component;
2445
    unsigned int reconstructed;
2446

2447
    /*
2448
     * Hopscotch hashing stores the local neighbourhood using the map below:
2449
     *
2450
     *   [home] hopinfo bits ---> |slot+0|slot+1|slot+2| ...
2451
     *                              ^ entries hop within this window.
2452
     */
2453
    table.slots = NULL;
×
2454
    table.hopinfo = NULL;
×
2455
    table.capacity = 0U;
×
2456
    table.count = 0U;
×
2457
    table.neighborhood = HOPSCOTCH_DEFAULT_NEIGHBORHOOD;
×
2458
    table.allocator = allocator;
×
2459
    cap_limit = (size_t)1U << 20;
×
2460
    expected = max_sample;
×
2461
    if (expected < 256U) {
×
2462
        expected = 256U;
×
2463
    }
2464
    if (expected > cap_limit) {
×
2465
        expected = cap_limit;
×
2466
    }
2467

2468
    status = hopscotch_table32_init(&table, expected, allocator);
×
2469
    if (SIXEL_FAILED(status)) {
×
2470
        sixel_helper_set_additional_message(
×
2471
            "unable to allocate hopscotch histogram.");
2472
        goto end;
×
2473
    }
2474

2475
    depth_u = (unsigned int)depth;
×
2476
    for (i = 0U; i < length; i += step) {
×
2477
        bucket_index = computeHash(data + i, depth_u, control);
×
2478
        slot = hopscotch_table32_lookup(&table, bucket_index);
×
2479
        if (slot == NULL) {
×
2480
            status = hopscotch_table32_insert(&table,
×
2481
                                              bucket_index,
2482
                                              1U);
2483
            if (SIXEL_FAILED(status)) {
×
2484
                sixel_helper_set_additional_message(
×
2485
                    "unable to grow hopscotch histogram.");
2486
                goto end;
×
2487
            }
2488
        } else if (slot->value < UINT32_MAX) {
×
2489
            slot->value++;
×
2490
        }
2491
    }
2492

2493
    if (table.count > UINT_MAX) {
×
2494
        sixel_helper_set_additional_message(
×
2495
            "too many unique colors for histogram.");
2496
        status = SIXEL_BAD_ARGUMENT;
×
2497
        goto end;
×
2498
    }
2499

2500
    colorfreqtableP->size = (unsigned int)table.count;
×
2501
    status = alloctupletable(&colorfreqtableP->table,
×
2502
                             depth_u,
2503
                             (unsigned int)table.count,
×
2504
                             allocator);
2505
    if (SIXEL_FAILED(status)) {
×
2506
        goto end;
×
2507
    }
2508

2509
    index = 0U;
×
2510
    /*
2511
     * Stream slots in the hash traversal order to avoid qsort overhead.
2512
     * This favors throughput over identical palette ordering.
2513
     */
2514
    for (i = 0U; i < table.capacity; ++i) {
×
2515
        slot = &table.slots[i];
×
2516
        if (slot->key == HOPSCOTCH_EMPTY_KEY || slot->value == 0U) {
×
2517
            continue;
×
2518
        }
2519
        if (index >= colorfreqtableP->size) {
×
2520
            break;
×
2521
        }
2522
        entry_key = slot->key;
×
2523
        colorfreqtableP->table[index]->value = slot->value;
×
2524
        for (n = 0U; n < depth_u; ++n) {
×
2525
            component = (unsigned int)
×
2526
                ((entry_key >> (n * control->channel_bits))
×
2527
                 & control->channel_mask);
×
2528
            reconstructed = histogram_reconstruct(component, control);
×
2529
            colorfreqtableP->table[index]->tuple[depth_u - 1U - n]
×
2530
                = (sample)reconstructed;
×
2531
        }
2532
        index++;
×
2533
    }
2534

2535
    status = SIXEL_OK;
×
2536

2537
end:
2538
    hopscotch_table32_fini(&table);
×
2539

2540
    return status;
×
2541
}
2542

2543
SIXELSTATUS
2544
sixel_quant_cache_prepare(unsigned short **cachetable,
239✔
2545
                          size_t *cachetable_size,
2546
                          int lut_policy,
2547
                          int reqcolor,
2548
                          sixel_allocator_t *allocator)
2549
{
2550
    SIXELSTATUS status;
2551
    struct histogram_control control;
2552
    struct cuckoo_table32 *table;
2553
    size_t expected;
2554
    size_t buckets;
2555
    size_t cap_limit;
2556
    int normalized;
2557

2558
    if (cachetable == NULL || allocator == NULL) {
239!
2559
        return SIXEL_BAD_ARGUMENT;
×
2560
    }
2561

2562
    /*
2563
     * The cache pointer always references the same ladder:
2564
     *
2565
     *   cache -> cuckoo_table32 -> buckets
2566
     *                           -> stash
2567
     */
2568
    normalized = lut_policy;
239✔
2569
    if (normalized == SIXEL_LUT_POLICY_AUTO) {
239!
2570
        normalized = histogram_lut_policy;
239✔
2571
    }
99✔
2572
    if (normalized == SIXEL_LUT_POLICY_AUTO) {
239!
2573
        normalized = SIXEL_LUT_POLICY_6BIT;
239✔
2574
    }
99✔
2575

2576
    control = histogram_control_make_for_policy(3U, normalized);
239✔
2577
    if (control.channel_shift == 0U) {
239!
2578
        expected = (size_t)reqcolor * 64U;
×
2579
        cap_limit = (size_t)1U << 20;
×
2580
        if (expected < 512U) {
×
2581
            expected = 512U;
×
2582
        }
2583
        if (expected > cap_limit) {
×
2584
            expected = cap_limit;
×
2585
        }
2586
    } else {
2587
        expected = histogram_dense_size(3U, &control);
239✔
2588
        if (expected == 0U) {
239!
2589
            expected = CUCKOO_BUCKET_SIZE;
×
2590
        }
2591
    }
2592

2593
    table = (struct cuckoo_table32 *)*cachetable;
239✔
2594
    if (table != NULL) {
239!
2595
        buckets = cuckoo_round_buckets(expected);
×
2596
        if (table->bucket_count < buckets) {
×
2597
            cuckoo_table32_fini(table);
×
2598
            sixel_allocator_free(allocator, table);
×
2599
            table = NULL;
×
2600
            *cachetable = NULL;
×
2601
        } else {
2602
            cuckoo_table32_clear(table);
×
2603
        }
2604
    }
2605
    if (table == NULL) {
239!
2606
        table = (struct cuckoo_table32 *)sixel_allocator_malloc(
239✔
2607
            allocator, sizeof(struct cuckoo_table32));
99✔
2608
        if (table == NULL) {
239!
2609
            sixel_helper_set_additional_message(
×
2610
                "unable to allocate cuckoo cache state.");
2611
            return SIXEL_BAD_ALLOCATION;
×
2612
        }
2613
        memset(table, 0, sizeof(struct cuckoo_table32));
239✔
2614
        status = cuckoo_table32_init(table, expected, allocator);
239✔
2615
        if (SIXEL_FAILED(status)) {
239!
2616
            sixel_allocator_free(allocator, table);
×
2617
            sixel_helper_set_additional_message(
×
2618
                "unable to initialize cuckoo cache.");
2619
            return status;
×
2620
        }
2621
        *cachetable = (unsigned short *)table;
239✔
2622
    }
99✔
2623

2624
    if (cachetable_size != NULL) {
239!
2625
        *cachetable_size = table->bucket_count * CUCKOO_BUCKET_SIZE;
239✔
2626
    }
99✔
2627

2628
    return SIXEL_OK;
239✔
2629
}
99✔
2630

2631
void
2632
sixel_quant_cache_clear(unsigned short *cachetable,
236✔
2633
                        int lut_policy)
2634
{
2635
    struct cuckoo_table32 *table;
2636

2637
    (void)lut_policy;
98✔
2638
    if (cachetable == NULL) {
236!
2639
        return;
×
2640
    }
2641

2642
    table = (struct cuckoo_table32 *)cachetable;
236✔
2643
    cuckoo_table32_clear(table);
236✔
2644
}
98✔
2645

2646
void
2647
sixel_quant_cache_release(unsigned short *cachetable,
504✔
2648
                          int lut_policy,
2649
                          sixel_allocator_t *allocator)
2650
{
2651
    struct cuckoo_table32 *table;
2652

2653
    (void)lut_policy;
174✔
2654
    if (cachetable == NULL || allocator == NULL) {
504!
2655
        return;
265✔
2656
    }
2657

2658
    table = (struct cuckoo_table32 *)cachetable;
239✔
2659
    cuckoo_table32_fini(table);
239✔
2660
    sixel_allocator_free(allocator, table);
239✔
2661
}
174✔
2662

2663

2664
static int
2665
computeColorMapFromInput(unsigned char const *data,
235✔
2666
                         unsigned int const length,
2667
                         unsigned int const depth,
2668
                         unsigned int const reqColors,
2669
                         int const methodForLargest,
2670
                         int const methodForRep,
2671
                         int const qualityMode,
2672
                         int const force_palette,
2673
                         tupletable2 * const colormapP,
2674
                         unsigned int *origcolors,
2675
                         sixel_allocator_t *allocator)
2676
{
2677
/*----------------------------------------------------------------------------
2678
   Produce a colormap containing the best colors to represent the
2679
   image stream in file 'ifP'.  Figure it out using the median cut
2680
   technique.
2681

2682
   The colormap will have 'reqcolors' or fewer colors in it, unless
2683
   'allcolors' is true, in which case it will have all the colors that
2684
   are in the input.
2685

2686
   The colormap has the same maxval as the input.
2687

2688
   Put the colormap in newly allocated storage as a tupletable2
2689
   and return its address as *colormapP.  Return the number of colors in
2690
   it as *colorsP and its maxval as *colormapMaxvalP.
2691

2692
   Return the characteristics of the input file as
2693
   *formatP and *freqPamP.  (This information is not really
2694
   relevant to our colormap mission; just a fringe benefit).
2695
-----------------------------------------------------------------------------*/
2696
    SIXELSTATUS status = SIXEL_FALSE;
235✔
2697
    tupletable2 colorfreqtable = {0, NULL};
235✔
2698
    unsigned int i;
2699
    unsigned int n;
2700

2701
    status = computeHistogram(data, length, depth,
344✔
2702
                              &colorfreqtable, qualityMode, allocator);
109✔
2703
    if (SIXEL_FAILED(status)) {
235!
2704
        goto end;
×
2705
    }
2706
    if (origcolors) {
235!
2707
        *origcolors = colorfreqtable.size;
235✔
2708
    }
109✔
2709

2710
    if (colorfreqtable.size <= reqColors) {
235✔
2711
        quant_trace(stderr,
284✔
2712
                    "Image already has few enough colors (<=%d).  "
2713
                    "Keeping same colors.\n", reqColors);
94✔
2714
        /* *colormapP = colorfreqtable; */
2715
        colormapP->size = colorfreqtable.size;
190✔
2716
        status = alloctupletable(&colormapP->table, depth, colorfreqtable.size, allocator);
190✔
2717
        if (SIXEL_FAILED(status)) {
190!
2718
            goto end;
×
2719
        }
2720
        for (i = 0; i < colorfreqtable.size; ++i) {
2,956✔
2721
            colormapP->table[i]->value = colorfreqtable.table[i]->value;
2,766✔
2722
            for (n = 0; n < depth; ++n) {
11,064✔
2723
                colormapP->table[i]->tuple[n] = colorfreqtable.table[i]->tuple[n];
8,298✔
2724
            }
3,480✔
2725
        }
1,160✔
2726
    } else {
94✔
2727
        quant_trace(stderr, "choosing %d colors...\n", reqColors);
45✔
2728
        status = mediancut(colorfreqtable, depth, reqColors,
60✔
2729
                           methodForLargest, methodForRep, colormapP, allocator);
15✔
2730
        if (SIXEL_FAILED(status)) {
45!
2731
            goto end;
×
2732
        }
2733
        quant_trace(stderr, "%d colors are choosed.\n", colorfreqtable.size);
45✔
2734
    }
2735

2736
    if (force_palette) {
235!
NEW
2737
        status = force_palette_completion(colormapP, depth, reqColors,
×
2738
                                          colorfreqtable, allocator);
NEW
2739
        if (SIXEL_FAILED(status)) {
×
NEW
2740
            goto end;
×
2741
        }
2742
    }
2743

2744
    status = SIXEL_OK;
235✔
2745

2746
end:
126✔
2747
    sixel_allocator_free(allocator, colorfreqtable.table);
235✔
2748
    return status;
235✔
2749
}
2750

2751

2752
/* diffuse error energy to surround pixels (normal strategy) */
2753
static void
2754
error_diffuse_normal(
50,778,513✔
2755
    unsigned char /* in */    *data,      /* base address of pixel buffer */
2756
    int           /* in */    pos,        /* address of the destination pixel */
2757
    int           /* in */    depth,      /* color depth in bytes */
2758
    int           /* in */    error,      /* error energy */
2759
    int           /* in */    numerator,  /* numerator of diffusion coefficient */
2760
    int           /* in */    denominator /* denominator of diffusion coefficient */)
2761
{
2762
    int c;
2763

2764
    data += pos * depth;
50,778,513✔
2765

2766
    c = *data + (error * numerator * 2 / denominator + 1) / 2;
50,778,513✔
2767
    if (c < 0) {
50,778,513✔
2768
        c = 0;
2,039,547✔
2769
    }
690,913✔
2770
    if (c >= 1 << 8) {
50,778,513✔
2771
        c = (1 << 8) - 1;
651,403✔
2772
    }
217,243✔
2773
    *data = (unsigned char)c;
50,778,513✔
2774
}
50,778,513✔
2775

2776
/* error diffusion with fast strategy */
2777
static void
2778
error_diffuse_fast(
169,469,067✔
2779
    unsigned char /* in */    *data,      /* base address of pixel buffer */
2780
    int           /* in */    pos,        /* address of the destination pixel */
2781
    int           /* in */    depth,      /* color depth in bytes */
2782
    int           /* in */    error,      /* error energy */
2783
    int           /* in */    numerator,  /* numerator of diffusion coefficient */
2784
    int           /* in */    denominator /* denominator of diffusion coefficient */)
2785
{
2786
    int c;
2787

2788
    data += pos * depth;
169,469,067✔
2789

2790
    c = *data + error * numerator / denominator;
169,469,067✔
2791
    if (c < 0) {
169,469,067✔
2792
        c = 0;
7,485,418✔
2793
    }
2,822,832✔
2794
    if (c >= 1 << 8) {
169,469,067✔
2795
        c = (1 << 8) - 1;
455,515✔
2796
    }
148,827✔
2797
    *data = (unsigned char)c;
169,469,067✔
2798
}
169,469,067✔
2799

2800

2801
/* error diffusion with precise strategy */
2802
static void
2803
error_diffuse_precise(
6,111,369✔
2804
    unsigned char /* in */    *data,      /* base address of pixel buffer */
2805
    int           /* in */    pos,        /* address of the destination pixel */
2806
    int           /* in */    depth,      /* color depth in bytes */
2807
    int           /* in */    error,      /* error energy */
2808
    int           /* in */    numerator,  /* numerator of diffusion coefficient */
2809
    int           /* in */    denominator /* denominator of diffusion coefficient */)
2810
{
2811
    int c;
2812

2813
    data += pos * depth;
6,111,369✔
2814

2815
    c = (int)(*data + error * numerator / (double)denominator + 0.5);
6,111,369✔
2816
    if (c < 0) {
6,111,369✔
2817
        c = 0;
70,495✔
2818
    }
25,489✔
2819
    if (c >= 1 << 8) {
6,111,369!
2820
        c = (1 << 8) - 1;
×
2821
    }
2822
    *data = (unsigned char)c;
6,111,369✔
2823
}
6,111,369✔
2824

2825

2826
typedef void (*diffuse_fixed_carry_mode)(int32_t *carry_curr,
2827
                                         int32_t *carry_next,
2828
                                         int32_t *carry_far,
2829
                                         int width,
2830
                                         int height,
2831
                                         int depth,
2832
                                         int x,
2833
                                         int y,
2834
                                         int32_t error,
2835
                                         int direction,
2836
                                         int channel);
2837

2838

2839
typedef void (*diffuse_varerr_mode)(unsigned char *data,
2840
                                    int width,
2841
                                    int height,
2842
                                    int x,
2843
                                    int y,
2844
                                    int depth,
2845
                                    int32_t error,
2846
                                    int index,
2847
                                    int direction);
2848

2849
typedef void (*diffuse_varerr_carry_mode)(int32_t *carry_curr,
2850
                                          int32_t *carry_next,
2851
                                          int32_t *carry_far,
2852
                                          int width,
2853
                                          int height,
2854
                                          int depth,
2855
                                          int x,
2856
                                          int y,
2857
                                          int32_t error,
2858
                                          int index,
2859
                                          int direction,
2860
                                          int channel);
2861

2862
static int
2863
zhoufang_index_from_byte(unsigned char value)
×
2864
{
2865
    double px;
2866
    double remapped;
2867
    int scale_index;
2868
    double scale;
2869
    double jitter;
2870
    int index;
2871

2872
    px = (double)value / 255.0;
×
2873
    remapped = px;
×
2874
    if (remapped >= 0.5) {
×
2875
        remapped = 1.0 - remapped;
×
2876
    }
2877
    if (remapped < 0.0) {
×
2878
        remapped = 0.0;
×
2879
    }
2880
    if (remapped > 0.5) {
×
2881
        remapped = 0.5;
×
2882
    }
2883

2884
    scale_index = (int)(remapped * 128.0);
×
2885
    if (scale_index < 0) {
×
2886
        scale_index = 0;
×
2887
    }
2888
    if (scale_index > 127) {
×
2889
        scale_index = 127;
×
2890
    }
2891

2892
    scale = zhoufang_threshold_gain[scale_index] / 100.0;
×
2893
    jitter = ((double)(rand() & 127) / 128.0) * scale;
×
2894
    remapped += (0.5 - remapped) * jitter;
×
2895
    if (remapped < 0.0) {
×
2896
        remapped = 0.0;
×
2897
    }
2898
    if (remapped > 0.5) {
×
2899
        remapped = 0.5;
×
2900
    }
2901

2902
    index = (int)(remapped * 255.0 + 0.5);
×
2903
    if (index > 127) {
×
2904
        index = 127;
×
2905
    }
2906
    if (index < 0) {
×
2907
        index = 0;
×
2908
    }
2909

2910
    return index;
×
2911
}
2912

2913

2914
static int32_t
2915
diffuse_varerr_term(int32_t error, int weight, int denom)
×
2916
{
2917
    int64_t delta;
2918

2919
    delta = (int64_t)error * (int64_t)weight;
×
2920
    if (delta >= 0) {
×
2921
        delta = (delta + denom / 2) / denom;
×
2922
    } else {
2923
        delta = (delta - denom / 2) / denom;
×
2924
    }
2925

2926
    return (int32_t)delta;
×
2927
}
2928

2929

2930
static int32_t
2931
diffuse_fixed_term(int32_t error, int numerator, int denominator)
×
2932
{
2933
    int64_t delta;
2934

2935
    delta = (int64_t)error * (int64_t)numerator;
×
2936
    if (delta >= 0) {
×
2937
        delta = (delta + denominator / 2) / denominator;
×
2938
    } else {
2939
        delta = (delta - denominator / 2) / denominator;
×
2940
    }
2941

2942
    return (int32_t)delta;
×
2943
}
2944

2945

2946
static void
2947
diffuse_varerr_apply_direct(unsigned char *target, int depth, size_t offset,
×
2948
                            int32_t delta)
2949
{
2950
    int64_t value;
2951
    int result;
2952

2953
    value = (int64_t)target[offset * depth] << VARERR_SCALE_SHIFT;
×
2954
    value += delta;
×
2955
    if (value < 0) {
×
2956
        value = 0;
×
2957
    } else {
2958
        int64_t max_value;
2959

2960
        max_value = VARERR_MAX_VALUE;
×
2961
        if (value > max_value) {
×
2962
            value = max_value;
×
2963
        }
2964
    }
2965

2966
    result = (int)((value + VARERR_ROUND) >> VARERR_SCALE_SHIFT);
×
2967
    if (result < 0) {
×
2968
        result = 0;
×
2969
    }
2970
    if (result > 255) {
×
2971
        result = 255;
×
2972
    }
2973
    target[offset * depth] = (unsigned char)result;
×
2974
}
×
2975

2976

2977
static void
2978
diffuse_lso2(unsigned char *data, int width, int height,
×
2979
             int x, int y, int depth, int32_t error,
2980
             int index, int direction)
2981
{
2982
    const int (*table)[7];
2983
    const int *entry;
2984
    int denom;
2985
    int32_t term_r = 0;
×
2986
    int32_t term_r2 = 0;
×
2987
    int32_t term_dl = 0;
×
2988
    int32_t term_d = 0;
×
2989
    int32_t term_dr = 0;
×
2990
    int32_t term_d2 = 0;
×
2991
    size_t offset;
2992

2993
    if (error == 0) {
×
2994
        return;
×
2995
    }
2996
    if (index < 0) {
×
2997
        index = 0;
×
2998
    }
2999
    if (index > 255) {
×
3000
        index = 255;
×
3001
    }
3002

3003
    table = lso2_table();
×
3004
    entry = table[index];
×
3005
    denom = entry[6];
×
3006
    if (denom == 0) {
×
3007
        return;
×
3008
    }
3009

3010
    term_r = diffuse_varerr_term(error, entry[0], denom);
×
3011
    term_r2 = diffuse_varerr_term(error, entry[1], denom);
×
3012
    term_dl = diffuse_varerr_term(error, entry[2], denom);
×
3013
    term_d = diffuse_varerr_term(error, entry[3], denom);
×
3014
    term_dr = diffuse_varerr_term(error, entry[4], denom);
×
3015
    term_d2 = diffuse_varerr_term(error, entry[5], denom);
×
3016

3017

3018
    if (direction >= 0) {
×
3019
        if (x + 1 < width) {
×
3020
            offset = (size_t)y * (size_t)width + (size_t)(x + 1);
×
3021
            diffuse_varerr_apply_direct(data, depth, offset, term_r);
×
3022
        }
3023
        if (x + 2 < width) {
×
3024
            offset = (size_t)y * (size_t)width + (size_t)(x + 2);
×
3025
            diffuse_varerr_apply_direct(data, depth, offset, term_r2);
×
3026
        }
3027
        if (y + 1 < height && x - 1 >= 0) {
×
3028
            offset = (size_t)(y + 1) * (size_t)width;
×
3029
            offset += (size_t)(x - 1);
×
3030
            diffuse_varerr_apply_direct(data, depth, offset, term_dl);
×
3031
        }
3032
        if (y + 1 < height) {
×
3033
            offset = (size_t)(y + 1) * (size_t)width + (size_t)x;
×
3034
            diffuse_varerr_apply_direct(data, depth, offset, term_d);
×
3035
        }
3036
        if (y + 1 < height && x + 1 < width) {
×
3037
            offset = (size_t)(y + 1) * (size_t)width;
×
3038
            offset += (size_t)(x + 1);
×
3039
            diffuse_varerr_apply_direct(data, depth, offset, term_dr);
×
3040
        }
3041
        if (y + 2 < height) {
×
3042
            offset = (size_t)(y + 2) * (size_t)width + (size_t)x;
×
3043
            diffuse_varerr_apply_direct(data, depth, offset, term_d2);
×
3044
        }
3045
    } else {
3046
        if (x - 1 >= 0) {
×
3047
            offset = (size_t)y * (size_t)width + (size_t)(x - 1);
×
3048
            diffuse_varerr_apply_direct(data, depth, offset, term_r);
×
3049
        }
3050
        if (x - 2 >= 0) {
×
3051
            offset = (size_t)y * (size_t)width + (size_t)(x - 2);
×
3052
            diffuse_varerr_apply_direct(data, depth, offset, term_r2);
×
3053
        }
3054
        if (y + 1 < height && x + 1 < width) {
×
3055
            offset = (size_t)(y + 1) * (size_t)width;
×
3056
            offset += (size_t)(x + 1);
×
3057
            diffuse_varerr_apply_direct(data, depth, offset, term_dl);
×
3058
        }
3059
        if (y + 1 < height) {
×
3060
            offset = (size_t)(y + 1) * (size_t)width + (size_t)x;
×
3061
            diffuse_varerr_apply_direct(data, depth, offset, term_d);
×
3062
        }
3063
        if (y + 1 < height && x - 1 >= 0) {
×
3064
            offset = (size_t)(y + 1) * (size_t)width;
×
3065
            offset += (size_t)(x - 1);
×
3066
            diffuse_varerr_apply_direct(data, depth, offset, term_dr);
×
3067
        }
3068
        if (y + 2 < height) {
×
3069
            offset = (size_t)(y + 2) * (size_t)width + (size_t)x;
×
3070
            diffuse_varerr_apply_direct(data, depth, offset, term_d2);
×
3071
        }
3072
    }
3073
}
3074

3075

3076
static void
3077
diffuse_lso3(unsigned char *data, int width, int height,
×
3078
             int x, int y, int depth, int32_t error,
3079
             int index, int direction)
3080
{
3081
    const int (*table)[7];
3082
    const int *entry;
3083
    int denom;
3084
    int32_t term_r = 0;
×
3085
    int32_t term_r2 = 0;
×
3086
    int32_t term_dl = 0;
×
3087
    int32_t term_d = 0;
×
3088
    int32_t term_dr = 0;
×
3089
    int32_t term_d2 = 0;
×
3090
    size_t offset;
3091

3092
    if (error == 0) {
×
3093
        return;
×
3094
    }
3095
    if (index < 0) {
×
3096
        index = 0;
×
3097
    }
3098
    if (index > 255) {
×
3099
        index = 255;
×
3100
    }
3101

3102
    table = lso3_table();
×
3103
    entry = table[index];
×
3104
    denom = entry[6];
×
3105
    if (denom == 0) {
×
3106
        return;
×
3107
    }
3108

3109
    term_r = diffuse_varerr_term(error, entry[0], denom);
×
3110
    term_r2 = diffuse_varerr_term(error, entry[1], denom);
×
3111
    term_dl = diffuse_varerr_term(error, entry[2], denom);
×
3112
    term_d = diffuse_varerr_term(error, entry[3], denom);
×
3113
    term_dr = diffuse_varerr_term(error, entry[4], denom);
×
3114
    term_d2 = error - term_r - term_r2 - term_dl - term_d - term_dr;
×
3115

3116
    if (direction >= 0) {
×
3117
        if (x + 1 < width) {
×
3118
            offset = (size_t)y * (size_t)width + (size_t)(x + 1);
×
3119
            diffuse_varerr_apply_direct(data, depth, offset, term_r);
×
3120
        }
3121
        if (x + 2 < width) {
×
3122
            offset = (size_t)y * (size_t)width + (size_t)(x + 2);
×
3123
            diffuse_varerr_apply_direct(data, depth, offset, term_r2);
×
3124
        }
3125
        if (y + 1 < height && x - 1 >= 0) {
×
3126
            offset = (size_t)(y + 1) * (size_t)width;
×
3127
            offset += (size_t)(x - 1);
×
3128
            diffuse_varerr_apply_direct(data, depth, offset, term_dl);
×
3129
        }
3130
        if (y + 1 < height) {
×
3131
            offset = (size_t)(y + 1) * (size_t)width + (size_t)x;
×
3132
            diffuse_varerr_apply_direct(data, depth, offset, term_d);
×
3133
        }
3134
        if (y + 1 < height && x + 1 < width) {
×
3135
            offset = (size_t)(y + 1) * (size_t)width;
×
3136
            offset += (size_t)(x + 1);
×
3137
            diffuse_varerr_apply_direct(data, depth, offset, term_dr);
×
3138
        }
3139
        if (y + 2 < height) {
×
3140
            offset = (size_t)(y + 2) * (size_t)width + (size_t)x;
×
3141
            diffuse_varerr_apply_direct(data, depth, offset, term_d2);
×
3142
        }
3143
    } else {
3144
        if (x - 1 >= 0) {
×
3145
            offset = (size_t)y * (size_t)width + (size_t)(x - 1);
×
3146
            diffuse_varerr_apply_direct(data, depth, offset, term_r);
×
3147
        }
3148
        if (x - 1 >= 0) {
×
3149
            offset = (size_t)y * (size_t)width + (size_t)(x - 2);
×
3150
            diffuse_varerr_apply_direct(data, depth, offset, term_r2);
×
3151
        }
3152
        if (y + 1 < height && x + 1 < width) {
×
3153
            offset = (size_t)(y + 1) * (size_t)width;
×
3154
            offset += (size_t)(x + 1);
×
3155
            diffuse_varerr_apply_direct(data, depth, offset, term_dl);
×
3156
        }
3157
        if (y + 1 < height) {
×
3158
            offset = (size_t)(y + 1) * (size_t)width + (size_t)x;
×
3159
            diffuse_varerr_apply_direct(data, depth, offset, term_d);
×
3160
        }
3161
        if (y + 1 < height && x - 1 >= 0) {
×
3162
            offset = (size_t)(y + 1) * (size_t)width;
×
3163
            offset += (size_t)(x - 1);
×
3164
            diffuse_varerr_apply_direct(data, depth, offset, term_dr);
×
3165
        }
3166
        if (y + 2 < height) {
×
3167
            offset = (size_t)(y + 2) * (size_t)width + (size_t)x;
×
3168
            diffuse_varerr_apply_direct(data, depth, offset, term_d2);
×
3169
        }
3170
    }
3171
}
3172

3173

3174
static void
3175
diffuse_lso2_carry(int32_t *carry_curr, int32_t *carry_next, int32_t *carry_far,
×
3176
                   int width, int height, int depth,
3177
                   int x, int y, int32_t error,
3178
                   int index, int direction, int channel)
3179
{
3180
    const int (*table)[7];
3181
    const int *entry;
3182
    int denom;
3183
    int32_t term_r = 0;
×
3184
    int32_t term_r2 = 0;
×
3185
    int32_t term_dl = 0;
×
3186
    int32_t term_d = 0;
×
3187
    int32_t term_dr = 0;
×
3188
    int32_t term_d2 = 0;
×
3189
    size_t base;
3190

3191
    if (error == 0) {
×
3192
        return;
×
3193
    }
3194
    if (index < 0) {
×
3195
        index = 0;
×
3196
    }
3197
    if (index > 255) {
×
3198
        index = 255;
×
3199
    }
3200

3201
    table = lso2_table();
×
3202
    entry = table[index];
×
3203
    denom = entry[6];
×
3204
    if (denom == 0) {
×
3205
        return;
×
3206
    }
3207

3208
    term_r = diffuse_varerr_term(error, entry[0], denom);
×
3209
    term_r2 = diffuse_varerr_term(error, entry[1], denom);
×
3210
    term_dl = diffuse_varerr_term(error, entry[2], denom);
×
3211
    term_d = diffuse_varerr_term(error, entry[3], denom);
×
3212
    term_dr = diffuse_varerr_term(error, entry[4], denom);
×
3213
    term_d2 = error - term_r - term_r2 - term_dl - term_d - term_dr;
×
3214

3215
    if (direction >= 0) {
×
3216
        if (x + 1 < width) {
×
3217
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
3218
            carry_curr[base] += term_r;
×
3219
        }
3220
        if (x + 2 < width) {
×
3221
            base = ((size_t)(x + 2) * (size_t)depth) + (size_t)channel;
×
3222
            carry_curr[base] += term_r2;
×
3223
        }
3224
        if (y + 1 < height && x - 1 >= 0) {
×
3225
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
3226
            carry_next[base] += term_dl;
×
3227
        }
3228
        if (y + 1 < height) {
×
3229
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
3230
            carry_next[base] += term_d;
×
3231
        }
3232
        if (y + 1 < height && x + 1 < width) {
×
3233
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
3234
            carry_next[base] += term_dr;
×
3235
        }
3236
        if (y + 2 < height) {
×
3237
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
3238
            carry_far[base] += term_d2;
×
3239
        }
3240
    } else {
3241
        if (x - 1 >= 0) {
×
3242
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
3243
            carry_curr[base] += term_r;
×
3244
        }
3245
        if (x - 2 >= 0) {
×
3246
            base = ((size_t)(x - 2) * (size_t)depth) + (size_t)channel;
×
3247
            carry_curr[base] += term_r;
×
3248
        }
3249
        if (y + 1 < height && x + 1 < width) {
×
3250
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
3251
            carry_next[base] += term_dl;
×
3252
        }
3253
        if (y + 1 < height) {
×
3254
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
3255
            carry_next[base] += term_d;
×
3256
        }
3257
        if (y + 1 < height && x - 1 >= 0) {
×
3258
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
3259
            carry_next[base] += term_dr;
×
3260
        }
3261
        if (y + 2 < height) {
×
3262
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
3263
            carry_far[base] += term_d2;
×
3264
        }
3265
    }
3266
}
3267

3268

3269
static void
3270
diffuse_lso3_carry(int32_t *carry_curr, int32_t *carry_next, int32_t *carry_far,
×
3271
                   int width, int height, int depth,
3272
                   int x, int y, int32_t error,
3273
                   int index, int direction, int channel)
3274
{
3275
    const int (*table)[7];
3276
    const int *entry;
3277
    int denom;
3278
    int32_t term_r = 0;
×
3279
    int32_t term_r2 = 0;
×
3280
    int32_t term_dl = 0;
×
3281
    int32_t term_d = 0;
×
3282
    int32_t term_dr = 0;
×
3283
    int32_t term_d2 = 0;
×
3284
    size_t base;
3285

3286
    if (error == 0) {
×
3287
        return;
×
3288
    }
3289
    if (index < 0) {
×
3290
        index = 0;
×
3291
    }
3292
    if (index > 255) {
×
3293
        index = 255;
×
3294
    }
3295

3296
    table = lso3_table();
×
3297
    entry = table[index];
×
3298
    denom = entry[6];
×
3299
    if (denom == 0) {
×
3300
        return;
×
3301
    }
3302

3303
    term_r = diffuse_varerr_term(error, entry[0], denom);
×
3304
    term_r2 = diffuse_varerr_term(error, entry[1], denom);
×
3305
    term_dl = diffuse_varerr_term(error, entry[2], denom);
×
3306
    term_d = diffuse_varerr_term(error, entry[3], denom);
×
3307
    term_dr = diffuse_varerr_term(error, entry[4], denom);
×
3308
    term_d2 = diffuse_varerr_term(error, entry[5], denom);
×
3309

3310
    if (direction >= 0) {
×
3311
        if (x + 1 < width) {
×
3312
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
3313
            carry_curr[base] += term_r;
×
3314
        }
3315
        if (x + 2 < width) {
×
3316
            base = ((size_t)(x + 2) * (size_t)depth) + (size_t)channel;
×
3317
            carry_curr[base] += term_r2;
×
3318
        }
3319
        if (y + 1 < height && x - 1 >= 0) {
×
3320
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
3321
            carry_next[base] += term_dl;
×
3322
        }
3323
        if (y + 1 < height) {
×
3324
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
3325
            carry_next[base] += term_d;
×
3326
        }
3327
        if (y + 1 < height && x + 1 < width) {
×
3328
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
3329
            carry_next[base] += term_dr;
×
3330
        }
3331
        if (y + 2 < height) {
×
3332
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
3333
            carry_far[base] += term_d2;
×
3334
        }
3335
    } else {
3336
        if (x - 1 >= 0) {
×
3337
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
3338
            carry_curr[base] += term_r;
×
3339
        }
3340
        if (x - 2 >= 0) {
×
3341
            base = ((size_t)(x - 2) * (size_t)depth) + (size_t)channel;
×
3342
            carry_curr[base] += term_r2;
×
3343
        }
3344
        if (y + 1 < height && x + 1 < width) {
×
3345
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
3346
            carry_next[base] += term_dl;
×
3347
        }
3348
        if (y + 1 < height) {
×
3349
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
3350
            carry_next[base] += term_d;
×
3351
        }
3352
        if (y + 1 < height && x - 1 >= 0) {
×
3353
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
3354
            carry_next[base] += term_dr;
×
3355
        }
3356
        if (y + 2 < height) {
×
3357
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
3358
            carry_far[base] += term_d2;
×
3359
        }
3360
    }
3361
}
3362

3363

3364
static void
3365
scanline_params(int serpentine, int index, int limit,
42,994✔
3366
                int *start, int *end, int *step, int *direction)
3367
{
3368
    if (serpentine && (index & 1)) {
42,994!
3369
        *start = limit - 1;
×
3370
        *end = -1;
×
3371
        *step = -1;
×
3372
        *direction = -1;
×
3373
    } else {
3374
        *start = 0;
42,994✔
3375
        *end = limit;
42,994✔
3376
        *step = 1;
42,994✔
3377
        *direction = 1;
42,994✔
3378
    }
3379
}
42,994✔
3380

3381

3382
static SIXELSTATUS
3383
apply_palette_positional(
×
3384
    sixel_index_t *result,
3385
    unsigned char *data,
3386
    int width,
3387
    int height,
3388
    int depth,
3389
    unsigned char *palette,
3390
    int reqcolor,
3391
    int methodForDiffuse,
3392
    int methodForScan,
3393
    int foptimize_palette,
3394
    int (*f_lookup)(const unsigned char *pixel,
3395
                    int depth,
3396
                    const unsigned char *palette,
3397
                    int reqcolor,
3398
                    unsigned short *cachetable,
3399
                    int complexion),
3400
    unsigned short *indextable,
3401
    int complexion,
3402
    unsigned char copy[],
3403
    unsigned char new_palette[],
3404
    unsigned short migration_map[],
3405
    int *ncolors)
3406
{
3407
    int serpentine;
3408
    int y;
3409
    float (*f_mask)(int x, int y, int c);
3410

3411
    switch (methodForDiffuse) {
×
3412
    case SIXEL_DIFFUSE_A_DITHER:
3413
        f_mask = mask_a;
×
3414
        break;
×
3415
    case SIXEL_DIFFUSE_X_DITHER:
×
3416
    default:
3417
        f_mask = mask_x;
×
3418
        break;
×
3419
    }
3420

3421
    serpentine = (methodForScan == SIXEL_SCAN_SERPENTINE);
×
3422

3423
    if (foptimize_palette) {
×
3424
        int x;
3425

3426
        *ncolors = 0;
×
3427
        memset(new_palette, 0x00,
×
3428
               (size_t)SIXEL_PALETTE_MAX * (size_t)depth);
3429
        memset(migration_map, 0x00,
×
3430
               sizeof(unsigned short) * (size_t)SIXEL_PALETTE_MAX);
3431
        for (y = 0; y < height; ++y) {
×
3432
            int start;
3433
            int end;
3434
            int step;
3435
            int direction;
3436

3437
            scanline_params(serpentine, y, width,
×
3438
                            &start, &end, &step, &direction);
3439
            (void)direction;
3440
            for (x = start; x != end; x += step) {
×
3441
                int pos;
3442
                int d;
3443
                int color_index;
3444

3445
                pos = y * width + x;
×
3446
                for (d = 0; d < depth; ++d) {
×
3447
                    int val;
3448

3449
                    val = data[pos * depth + d]
×
3450
                        + f_mask(x, y, d) * 32;
×
3451
                    copy[d] = val < 0 ? 0
×
3452
                               : val > 255 ? 255 : val;
×
3453
                }
3454
                color_index = f_lookup(copy, depth, palette,
×
3455
                                       reqcolor, indextable,
3456
                                       complexion);
3457
                if (migration_map[color_index] == 0) {
×
3458
                    result[pos] = *ncolors;
×
3459
                    for (d = 0; d < depth; ++d) {
×
3460
                        new_palette[*ncolors * depth + d]
×
3461
                            = palette[color_index * depth + d];
×
3462
                    }
3463
                    ++*ncolors;
×
3464
                    migration_map[color_index] = *ncolors;
×
3465
                } else {
3466
                    result[pos] = migration_map[color_index] - 1;
×
3467
                }
3468
            }
3469
        }
3470
        memcpy(palette, new_palette, (size_t)(*ncolors * depth));
×
3471
    } else {
3472
        int x;
3473

3474
        for (y = 0; y < height; ++y) {
×
3475
            int start;
3476
            int end;
3477
            int step;
3478
            int direction;
3479

3480
            scanline_params(serpentine, y, width,
×
3481
                            &start, &end, &step, &direction);
3482
            (void)direction;
3483
            for (x = start; x != end; x += step) {
×
3484
                int pos;
3485
                int d;
3486

3487
                pos = y * width + x;
×
3488
                for (d = 0; d < depth; ++d) {
×
3489
                    int val;
3490

3491
                    val = data[pos * depth + d]
×
3492
                        + f_mask(x, y, d) * 32;
×
3493
                    copy[d] = val < 0 ? 0
×
3494
                               : val > 255 ? 255 : val;
×
3495
                }
3496
                result[pos] = f_lookup(copy, depth, palette,
×
3497
                                       reqcolor, indextable,
3498
                                       complexion);
3499
            }
3500
        }
3501
        *ncolors = reqcolor;
×
3502
    }
3503

3504
    return SIXEL_OK;
×
3505
}
3506

3507

3508
static SIXELSTATUS
3509
apply_palette_variable(
×
3510
    sixel_index_t *result,
3511
    unsigned char *data,
3512
    int width,
3513
    int height,
3514
    int depth,
3515
    unsigned char *palette,
3516
    int reqcolor,
3517
    int methodForScan,
3518
    int foptimize_palette,
3519
    int (*f_lookup)(const unsigned char *pixel,
3520
                    int depth,
3521
                    const unsigned char *palette,
3522
                    int reqcolor,
3523
                    unsigned short *cachetable,
3524
                    int complexion),
3525
    unsigned short *indextable,
3526
    int complexion,
3527
    unsigned char new_palette[],
3528
    unsigned short migration_map[],
3529
    int *ncolors,
3530
    int methodForDiffuse,
3531
    int methodForCarry)
3532
{
3533
    SIXELSTATUS status = SIXEL_FALSE;
×
3534
#if _MSC_VER
3535
    enum { max_channels = 4 };
3536
#else
3537
    const int max_channels = 4;
×
3538
#endif
3539
    int serpentine;
3540
    int y;
3541
    diffuse_varerr_mode varerr_diffuse;
3542
    diffuse_varerr_carry_mode varerr_diffuse_carry;
3543
    int use_carry;
3544
    size_t carry_len;
3545
    int32_t *carry_curr = NULL;
×
3546
    int32_t *carry_next = NULL;
×
3547
    int32_t *carry_far = NULL;
×
3548
    unsigned char corrected[max_channels];
3549
    int32_t sample_scaled[max_channels];
3550
    int32_t accum_scaled[max_channels];
3551
    int start;
3552
    int end;
3553
    int step;
3554
    int direction;
3555
    int x;
3556
    int pos;
3557
    size_t base;
3558
    size_t carry_base;
3559
    const unsigned char *source_pixel;
3560
    int color_index;
3561
    int output_index;
3562
    int n;
3563
    int palette_value;
3564
    int diff;
3565
    int table_index;
3566
    int64_t accum;
3567
    int64_t clamped;
3568
    int32_t target_scaled;
3569
    int32_t error_scaled;
3570
    int32_t *tmp;
3571

3572
    if (depth > max_channels) {
×
3573
        status = SIXEL_BAD_ARGUMENT;
×
3574
        goto end;
×
3575
    }
3576

3577
    use_carry = (methodForCarry == SIXEL_CARRY_ENABLE);
×
3578
    carry_len = 0;
×
3579

3580
    switch (methodForDiffuse) {
×
3581
    case SIXEL_DIFFUSE_LSO2:
3582
        varerr_diffuse = diffuse_lso2;
×
3583
        varerr_diffuse_carry = diffuse_lso2_carry;
×
3584
        break;
×
3585
    case SIXEL_DIFFUSE_LSO3:
3586
        varerr_diffuse = diffuse_lso3;
×
3587
        varerr_diffuse_carry = diffuse_lso3_carry;
×
3588
        srand((unsigned int)time(NULL));
×
3589
        break;
×
3590
    default:
3591
        varerr_diffuse = diffuse_lso2;
×
3592
        varerr_diffuse_carry = diffuse_lso2_carry;
×
3593
        break;
×
3594
    }
3595

3596
    if (use_carry) {
×
3597
        carry_len = (size_t)width * (size_t)depth;
×
3598
        carry_curr = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
3599
        if (carry_curr == NULL) {
×
3600
            status = SIXEL_BAD_ALLOCATION;
×
3601
            goto end;
×
3602
        }
3603
        carry_next = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
3604
        if (carry_next == NULL) {
×
3605
            status = SIXEL_BAD_ALLOCATION;
×
3606
            goto end;
×
3607
        }
3608
        carry_far = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
3609
        if (carry_far == NULL) {
×
3610
            status = SIXEL_BAD_ALLOCATION;
×
3611
            goto end;
×
3612
        }
3613
    }
3614

3615
    serpentine = (methodForScan == SIXEL_SCAN_SERPENTINE);
×
3616

3617
    if (foptimize_palette) {
×
3618
        *ncolors = 0;
×
3619
        memset(new_palette, 0x00,
×
3620
               (size_t)SIXEL_PALETTE_MAX * (size_t)depth);
3621
        memset(migration_map, 0x00,
×
3622
               sizeof(unsigned short) * (size_t)SIXEL_PALETTE_MAX);
3623
    }
3624

3625
    for (y = 0; y < height; ++y) {
×
3626
        scanline_params(serpentine, y, width,
×
3627
                        &start, &end, &step, &direction);
3628
        for (x = start; x != end; x += step) {
×
3629
            pos = y * width + x;
×
3630
            base = (size_t)pos * (size_t)depth;
×
3631
            carry_base = (size_t)x * (size_t)depth;
×
3632
            if (use_carry) {
×
3633
                for (n = 0; n < depth; ++n) {
×
3634
                    accum = ((int64_t)data[base + n]
×
3635
                             << VARERR_SCALE_SHIFT)
×
3636
                          + carry_curr[carry_base + (size_t)n];
×
3637
                    if (accum < INT32_MIN) {
×
3638
                        accum = INT32_MIN;
×
3639
                    } else if (accum > INT32_MAX) {
×
3640
                        accum = INT32_MAX;
×
3641
                    }
3642
                    carry_curr[carry_base + (size_t)n] = 0;
×
3643
                    clamped = accum;
×
3644
                    if (clamped < 0) {
×
3645
                        clamped = 0;
×
3646
                    } else if (clamped > VARERR_MAX_VALUE) {
×
3647
                        clamped = VARERR_MAX_VALUE;
×
3648
                    }
3649
                    accum_scaled[n] = (int32_t)clamped;
×
3650
                    corrected[n]
3651
                        = (unsigned char)((clamped + VARERR_ROUND)
×
3652
                                          >> VARERR_SCALE_SHIFT);
×
3653
                }
3654
                source_pixel = corrected;
×
3655
            } else {
3656
                for (n = 0; n < depth; ++n) {
×
3657
                    sample_scaled[n]
3658
                        = (int32_t)data[base + n]
×
3659
                        << VARERR_SCALE_SHIFT;
×
3660
                    corrected[n] = data[base + n];
×
3661
                }
3662
                source_pixel = data + base;
×
3663
            }
3664

3665
            color_index = f_lookup(source_pixel, depth, palette,
×
3666
                                   reqcolor, indextable,
3667
                                   complexion);
3668

3669
            if (foptimize_palette) {
×
3670
                if (migration_map[color_index] == 0) {
×
3671
                    output_index = *ncolors;
×
3672
                    for (n = 0; n < depth; ++n) {
×
3673
                        new_palette[output_index * depth + n]
×
3674
                            = palette[color_index * depth + n];
×
3675
                    }
3676
                    ++*ncolors;
×
3677
                    migration_map[color_index] = *ncolors;
×
3678
                } else {
3679
                    output_index = migration_map[color_index] - 1;
×
3680
                }
3681
                result[pos] = output_index;
×
3682
            } else {
3683
                output_index = color_index;
×
3684
                result[pos] = output_index;
×
3685
            }
3686

3687
            for (n = 0; n < depth; ++n) {
×
3688
                if (foptimize_palette) {
×
3689
                    palette_value = new_palette[output_index * depth + n];
×
3690
                } else {
3691
                    palette_value = palette[color_index * depth + n];
×
3692
                }
3693
                diff = (int)source_pixel[n] - palette_value;
×
3694
                if (diff < 0) {
×
3695
                    diff = -diff;
×
3696
                }
3697
                if (diff > 255) {
×
3698
                    diff = 255;
×
3699
                }
3700
                table_index = diff;
×
3701
                if (methodForDiffuse == SIXEL_DIFFUSE_LSO3) {
×
3702
                    table_index = zhoufang_index_from_byte(
×
3703
                        (unsigned char)source_pixel[n]);
×
3704
                }
3705
                if (use_carry) {
×
3706
                    target_scaled = (int32_t)palette_value
×
3707
                                  << VARERR_SCALE_SHIFT;
3708
                    error_scaled = accum_scaled[n] - target_scaled;
×
3709
                    varerr_diffuse_carry(carry_curr, carry_next, carry_far,
×
3710
                                         width, height, depth,
3711
                                         x, y, error_scaled,
3712
                                         table_index,
3713
                                         direction, n);
3714
                } else {
3715
                    target_scaled = (int32_t)palette_value
×
3716
                                  << VARERR_SCALE_SHIFT;
3717
                    error_scaled = sample_scaled[n] - target_scaled;
×
3718
                    varerr_diffuse(data + n, width, height,
×
3719
                                   x, y, depth, error_scaled,
3720
                                   table_index,
3721
                                   direction);
3722
                }
3723
            }
3724
        }
3725
        if (use_carry) {
×
3726
            tmp = carry_curr;
×
3727
            carry_curr = carry_next;
×
3728
            carry_next = carry_far;
×
3729
            carry_far = tmp;
×
3730
            if (carry_len > 0) {
×
3731
                memset(carry_far, 0x00, carry_len * sizeof(int32_t));
×
3732
            }
3733
        }
3734
    }
3735

3736
    if (foptimize_palette) {
×
3737
        memcpy(palette, new_palette, (size_t)(*ncolors * depth));
×
3738
    } else {
3739
        *ncolors = reqcolor;
×
3740
    }
3741

3742
    status = SIXEL_OK;
×
3743

3744
end:
3745
    free(carry_next);
×
3746
    free(carry_curr);
×
3747
    free(carry_far);
×
3748
    return status;
×
3749
}
3750

3751

3752
static SIXELSTATUS
3753
apply_palette_fixed(
257✔
3754
    sixel_index_t *result,
3755
    unsigned char *data,
3756
    int width,
3757
    int height,
3758
    int depth,
3759
    unsigned char *palette,
3760
    int reqcolor,
3761
    int methodForScan,
3762
    int foptimize_palette,
3763
    int (*f_lookup)(const unsigned char *pixel,
3764
                    int depth,
3765
                    const unsigned char *palette,
3766
                    int reqcolor,
3767
                    unsigned short *cachetable,
3768
                    int complexion),
3769
    unsigned short *indextable,
3770
    int complexion,
3771
    unsigned char new_palette[],
3772
    unsigned short migration_map[],
3773
    int *ncolors,
3774
    int methodForDiffuse,
3775
    int methodForCarry)
3776
{
152✔
3777
#if _MSC_VER
3778
    enum { max_channels = 4 };
3779
#else
3780
    const int max_channels = 4;
257✔
3781
#endif
3782
    SIXELSTATUS status = SIXEL_FALSE;
257✔
3783
    int serpentine;
3784
    int y;
3785
    void (*f_diffuse)(unsigned char *data,
3786
                      int width,
3787
                      int height,
3788
                      int x,
3789
                      int y,
3790
                      int depth,
3791
                      int offset,
3792
                      int direction);
3793
    diffuse_fixed_carry_mode f_diffuse_carry;
3794
    int use_carry;
3795
    size_t carry_len;
3796
    int32_t *carry_curr = NULL;
257✔
3797
    int32_t *carry_next = NULL;
257✔
3798
    int32_t *carry_far = NULL;
257✔
3799
    unsigned char corrected[max_channels];
152✔
3800
    int32_t accum_scaled[max_channels];
152✔
3801
    int start;
3802
    int end;
3803
    int step;
3804
    int direction;
3805
    int x;
3806
    int pos;
3807
    size_t base;
3808
    size_t carry_base;
3809
    const unsigned char *source_pixel;
3810
    int color_index;
3811
    int output_index;
3812
    int n;
3813
    int palette_value;
3814
    int64_t accum;
3815
    int64_t clamped;
3816
    int32_t target_scaled;
3817
    int32_t error_scaled;
3818
    int offset;
3819
    int32_t *tmp;
3820

3821
    if (depth > max_channels) {
257!
3822
        status = SIXEL_BAD_ARGUMENT;
×
3823
        goto end;
×
3824
    }
3825

3826
    use_carry = (methodForCarry == SIXEL_CARRY_ENABLE);
257✔
3827
    carry_len = 0;
257✔
3828

3829
    if (depth != 3) {
257!
3830
        f_diffuse = diffuse_none;
×
3831
        f_diffuse_carry = diffuse_none_carry;
×
3832
        use_carry = 0;
×
3833
    } else {
3834
        switch (methodForDiffuse) {
257!
3835
        case SIXEL_DIFFUSE_NONE:
86✔
3836
            f_diffuse = diffuse_none;
157✔
3837
            f_diffuse_carry = diffuse_none_carry;
157✔
3838
            break;
157✔
3839
        case SIXEL_DIFFUSE_ATKINSON:
44✔
3840
            f_diffuse = diffuse_atkinson;
67✔
3841
            f_diffuse_carry = diffuse_atkinson_carry;
67✔
3842
            break;
67✔
3843
        case SIXEL_DIFFUSE_FS:
16✔
3844
            f_diffuse = diffuse_fs;
24✔
3845
            f_diffuse_carry = diffuse_fs_carry;
24✔
3846
            break;
24✔
3847
        case SIXEL_DIFFUSE_JAJUNI:
2✔
3848
            f_diffuse = diffuse_jajuni;
3✔
3849
            f_diffuse_carry = diffuse_jajuni_carry;
3✔
3850
            break;
3✔
3851
        case SIXEL_DIFFUSE_STUCKI:
2✔
3852
            f_diffuse = diffuse_stucki;
3✔
3853
            f_diffuse_carry = diffuse_stucki_carry;
3✔
3854
            break;
3✔
3855
        case SIXEL_DIFFUSE_BURKES:
2✔
3856
            f_diffuse = diffuse_burkes;
3✔
3857
            f_diffuse_carry = diffuse_burkes_carry;
3✔
3858
            break;
3✔
3859
        case SIXEL_DIFFUSE_LSO1:
3860
            f_diffuse = diffuse_lso1;
×
3861
            f_diffuse_carry = diffuse_lso1_carry;
×
3862
            break;
×
3863
        default:
3864
            quant_trace(stderr,
×
3865
                        "Internal error: invalid methodForDiffuse: %d\n",
3866
                        methodForDiffuse);
3867
            f_diffuse = diffuse_none;
×
3868
            f_diffuse_carry = diffuse_none_carry;
×
3869
            break;
×
3870
        }
3871
    }
3872

3873
    if (use_carry) {
257!
3874
        carry_len = (size_t)width * (size_t)depth;
×
3875
        if (carry_len > 0) {
×
3876
            carry_curr = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
3877
            if (carry_curr == NULL) {
×
3878
                status = SIXEL_BAD_ALLOCATION;
×
3879
                goto end;
×
3880
            }
3881
            carry_next = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
3882
            if (carry_next == NULL) {
×
3883
                status = SIXEL_BAD_ALLOCATION;
×
3884
                goto end;
×
3885
            }
3886
            carry_far = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
3887
            if (carry_far == NULL) {
×
3888
                status = SIXEL_BAD_ALLOCATION;
×
3889
                goto end;
×
3890
            }
3891
        } else {
3892
            use_carry = 0;
×
3893
        }
3894
    }
3895

3896
    serpentine = (methodForScan == SIXEL_SCAN_SERPENTINE);
257✔
3897

3898
    if (foptimize_palette) {
257✔
3899
        *ncolors = 0;
178✔
3900
        memset(new_palette, 0x00,
178✔
3901
               (size_t)SIXEL_PALETTE_MAX * (size_t)depth);
112✔
3902
        memset(migration_map, 0x00,
178✔
3903
               sizeof(unsigned short) * (size_t)SIXEL_PALETTE_MAX);
3904
    } else {
66✔
3905
        *ncolors = reqcolor;
79✔
3906
    }
3907

3908
    for (y = 0; y < height; ++y) {
43,251✔
3909
        scanline_params(serpentine, y, width,
42,994✔
3910
                        &start, &end, &step, &direction);
3911
        for (x = start; x != end; x += step) {
19,978,700✔
3912
            pos = y * width + x;
19,935,706✔
3913
            base = (size_t)pos * (size_t)depth;
19,935,706✔
3914
            carry_base = (size_t)x * (size_t)depth;
19,935,706✔
3915
            if (use_carry) {
19,935,706!
3916
                for (n = 0; n < depth; ++n) {
×
3917
                    accum = ((int64_t)data[base + n]
×
3918
                             << VARERR_SCALE_SHIFT)
×
3919
                           + carry_curr[carry_base + (size_t)n];
×
3920
                    if (accum < INT32_MIN) {
×
3921
                        accum = INT32_MIN;
×
3922
                    } else if (accum > INT32_MAX) {
×
3923
                        accum = INT32_MAX;
×
3924
                    }
3925
                    clamped = accum;
×
3926
                    if (clamped < 0) {
×
3927
                        clamped = 0;
×
3928
                    } else if (clamped > VARERR_MAX_VALUE) {
×
3929
                        clamped = VARERR_MAX_VALUE;
×
3930
                    }
3931
                    accum_scaled[n] = (int32_t)clamped;
×
3932
                    corrected[n]
3933
                        = (unsigned char)((clamped + VARERR_ROUND)
×
3934
                                          >> VARERR_SCALE_SHIFT);
×
3935
                    data[base + n] = corrected[n];
×
3936
                    carry_curr[carry_base + (size_t)n] = 0;
×
3937
                }
3938
                source_pixel = corrected;
×
3939
            } else {
3940
                source_pixel = data + base;
19,935,706✔
3941
            }
3942

3943
            color_index = f_lookup(source_pixel, depth, palette,
29,385,328✔
3944
                                   reqcolor, indextable,
9,449,622✔
3945
                                   complexion);
9,449,622✔
3946

3947
            if (foptimize_palette) {
19,935,706✔
3948
                if (migration_map[color_index] == 0) {
7,834,206✔
3949
                    output_index = *ncolors;
9,502✔
3950
                    for (n = 0; n < depth; ++n) {
38,008✔
3951
                        new_palette[output_index * depth + n]
28,506✔
3952
                            = palette[color_index * depth + n];
38,094✔
3953
                    }
9,588✔
3954
                    ++*ncolors;
9,502✔
3955
                    migration_map[color_index] = *ncolors;
9,502✔
3956
                } else {
3,196✔
3957
                    output_index = migration_map[color_index] - 1;
7,824,704✔
3958
                }
3959
                result[pos] = output_index;
7,834,206✔
3960
            } else {
3,609,802✔
3961
                output_index = color_index;
12,101,500✔
3962
                result[pos] = output_index;
12,101,500✔
3963
            }
3964

3965
            for (n = 0; n < depth; ++n) {
79,742,824✔
3966
                if (foptimize_palette) {
59,807,118✔
3967
                    palette_value = new_palette[output_index * depth + n];
23,502,618✔
3968
                } else {
10,829,406✔
3969
                    palette_value = palette[color_index * depth + n];
36,304,500✔
3970
                }
3971
                if (use_carry) {
59,807,118!
3972
                    target_scaled = (int32_t)palette_value
×
3973
                                  << VARERR_SCALE_SHIFT;
3974
                    error_scaled = accum_scaled[n] - target_scaled;
×
3975
                    f_diffuse_carry(carry_curr, carry_next, carry_far,
×
3976
                                    width, height, depth,
3977
                                    x, y, error_scaled, direction, n);
3978
                } else {
3979
                    offset = (int)source_pixel[n] - palette_value;
59,807,118✔
3980
                    f_diffuse(data + n, width, height, x, y,
88,155,984✔
3981
                              depth, offset, direction);
28,348,866✔
3982
                }
3983
            }
28,348,866✔
3984
        }
9,449,622✔
3985
        if (use_carry) {
42,994!
3986
            tmp = carry_curr;
×
3987
            carry_curr = carry_next;
×
3988
            carry_next = carry_far;
×
3989
            carry_far = tmp;
×
3990
            if (carry_len > 0) {
×
3991
                memset(carry_far, 0x00, carry_len * sizeof(int32_t));
×
3992
            }
3993
        }
3994
    }
20,222✔
3995

3996
    if (foptimize_palette) {
257✔
3997
        memcpy(palette, new_palette, (size_t)(*ncolors * depth));
178✔
3998
    }
66✔
3999

4000
    status = SIXEL_OK;
257✔
4001

4002
end:
152✔
4003
    free(carry_far);
257✔
4004
    free(carry_next);
257✔
4005
    free(carry_curr);
257✔
4006
    return status;
257✔
4007
}
4008

4009

4010
static void
4011
diffuse_none(unsigned char *data, int width, int height,
18,248,814✔
4012
             int x, int y, int depth, int error, int direction)
4013
{
4014
    /* unused */ (void) data;
14,469,498✔
4015
    /* unused */ (void) width;
14,469,498✔
4016
    /* unused */ (void) height;
14,469,498✔
4017
    /* unused */ (void) x;
14,469,498✔
4018
    /* unused */ (void) y;
14,469,498✔
4019
    /* unused */ (void) depth;
14,469,498✔
4020
    /* unused */ (void) error;
14,469,498✔
4021
    /* unused */ (void) direction;
14,469,498✔
4022
}
18,248,814✔
4023

4024

4025
static void
4026
diffuse_none_carry(int32_t *carry_curr, int32_t *carry_next,
×
4027
                   int32_t *carry_far, int width, int height,
4028
                   int depth, int x, int y, int32_t error,
4029
                   int direction, int channel)
4030
{
4031
    /* unused */ (void) carry_curr;
4032
    /* unused */ (void) carry_next;
4033
    /* unused */ (void) carry_far;
4034
    /* unused */ (void) width;
4035
    /* unused */ (void) height;
4036
    /* unused */ (void) depth;
4037
    /* unused */ (void) x;
4038
    /* unused */ (void) y;
4039
    /* unused */ (void) error;
4040
    /* unused */ (void) direction;
4041
    /* unused */ (void) channel;
4042
}
×
4043

4044

4045
static void
4046
diffuse_fs(unsigned char *data, int width, int height,
12,621,744✔
4047
           int x, int y, int depth, int error, int direction)
4048
{
4049
    /* Floyd Steinberg Method
4050
     *          curr    7/16
4051
     *  3/16    5/48    1/16
4052
     */
4053
    int pos;
4054
    int forward;
4055

4056
    pos = y * width + x;
12,621,744✔
4057
    forward = direction >= 0;
12,621,744✔
4058

4059
    if (forward) {
12,621,744!
4060
        if (x < width - 1) {
12,621,744✔
4061
            error_diffuse_normal(data, pos + 1, depth, error, 7, 16);
12,594,798✔
4062
        }
4,198,266✔
4063
        if (y < height - 1) {
12,621,744✔
4064
            if (x > 0) {
12,591,828✔
4065
                error_diffuse_normal(data,
16,753,272✔
4066
                                     pos + width - 1,
12,564,954✔
4067
                                     depth, error, 3, 16);
4,188,318✔
4068
            }
4,188,318✔
4069
            error_diffuse_normal(data,
16,789,104✔
4070
                                 pos + width,
4,197,276✔
4071
                                 depth, error, 5, 16);
4,197,276✔
4072
            if (x < width - 1) {
12,591,828✔
4073
                error_diffuse_normal(data,
16,753,272✔
4074
                                     pos + width + 1,
12,564,954✔
4075
                                     depth, error, 1, 16);
4,188,318✔
4076
            }
4,188,318✔
4077
        }
4,197,276✔
4078
    } else {
4,207,248✔
4079
        if (x > 0) {
×
4080
            error_diffuse_normal(data, pos - 1, depth, error, 7, 16);
×
4081
        }
4082
        if (y < height - 1) {
×
4083
            if (x < width - 1) {
×
4084
                error_diffuse_normal(data,
×
4085
                                     pos + width + 1,
×
4086
                                     depth, error, 3, 16);
4087
            }
4088
            error_diffuse_normal(data,
×
4089
                                 pos + width,
4090
                                 depth, error, 5, 16);
4091
            if (x > 0) {
×
4092
                error_diffuse_normal(data,
×
4093
                                     pos + width - 1,
×
4094
                                     depth, error, 1, 16);
4095
            }
4096
        }
4097
    }
4098
}
12,621,744✔
4099

4100

4101
static void
4102
diffuse_fs_carry(int32_t *carry_curr, int32_t *carry_next,
×
4103
                 int32_t *carry_far, int width, int height,
4104
                 int depth, int x, int y, int32_t error,
4105
                 int direction, int channel)
4106
{
4107
    /* Floyd Steinberg Method
4108
     *          curr    7/16
4109
     *  3/16    5/48    1/16
4110
     */
4111
    int forward;
4112

4113
    /* unused */ (void) carry_far;
4114
    if (error == 0) {
×
4115
        return;
×
4116
    }
4117

4118
    forward = direction >= 0;
×
4119
    if (forward) {
×
4120
        if (x + 1 < width) {
×
4121
            size_t base;
4122
            int32_t term;
4123

4124
            base = ((size_t)(x + 1) * (size_t)depth)
×
4125
                 + (size_t)channel;
×
4126
            term = diffuse_fixed_term(error, 7, 16);
×
4127
            carry_curr[base] += term;
×
4128
        }
4129
        if (y + 1 < height) {
×
4130
            if (x > 0) {
×
4131
                size_t base;
4132
                int32_t term;
4133

4134
                base = ((size_t)(x - 1) * (size_t)depth)
×
4135
                     + (size_t)channel;
×
4136
                term = diffuse_fixed_term(error, 3, 16);
×
4137
                carry_next[base] += term;
×
4138
            }
4139
            {
4140
                size_t base;
4141
                int32_t term;
4142

4143
                base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
4144
                term = diffuse_fixed_term(error, 5, 16);
×
4145
                carry_next[base] += term;
×
4146
            }
4147
            if (x + 1 < width) {
×
4148
                size_t base;
4149
                int32_t term;
4150

4151
                base = ((size_t)(x + 1) * (size_t)depth)
×
4152
                     + (size_t)channel;
×
4153
                term = diffuse_fixed_term(error, 1, 16);
×
4154
                carry_next[base] += term;
×
4155
            }
4156
        }
4157
    } else {
4158
        if (x - 1 >= 0) {
×
4159
            size_t base;
4160
            int32_t term;
4161

4162
            base = ((size_t)(x - 1) * (size_t)depth)
×
4163
                 + (size_t)channel;
×
4164
            term = diffuse_fixed_term(error, 7, 16);
×
4165
            carry_curr[base] += term;
×
4166
        }
4167
        if (y + 1 < height) {
×
4168
            if (x + 1 < width) {
×
4169
                size_t base;
4170
                int32_t term;
4171

4172
                base = ((size_t)(x + 1) * (size_t)depth)
×
4173
                     + (size_t)channel;
×
4174
                term = diffuse_fixed_term(error, 3, 16);
×
4175
                carry_next[base] += term;
×
4176
            }
4177
            {
4178
                size_t base;
4179
                int32_t term;
4180

4181
                base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
4182
                term = diffuse_fixed_term(error, 5, 16);
×
4183
                carry_next[base] += term;
×
4184
            }
4185
            if (x - 1 >= 0) {
×
4186
                size_t base;
4187
                int32_t term;
4188

4189
                base = ((size_t)(x - 1) * (size_t)depth)
×
4190
                     + (size_t)channel;
×
4191
                term = diffuse_fixed_term(error, 1, 16);
×
4192
                carry_next[base] += term;
×
4193
            }
4194
        }
4195
    }
4196
}
4197

4198

4199
static void
4200
diffuse_atkinson(unsigned char *data, int width, int height,
28,352,460✔
4201
                 int x, int y, int depth, int error, int direction)
4202
{
4203
    /* Atkinson's Method
4204
     *          curr    1/8    1/8
4205
     *   1/8     1/8    1/8
4206
     *           1/8
4207
     */
4208
    int pos;
4209
    int sign;
4210

4211
    pos = y * width + x;
28,352,460✔
4212
    sign = direction >= 0 ? 1 : -1;
28,352,460!
4213

4214
    if (x + sign >= 0 && x + sign < width) {
28,352,460!
4215
        error_diffuse_fast(data, pos + sign, depth, error, 1, 8);
28,296,945✔
4216
    }
9,458,715✔
4217
    if (x + sign * 2 >= 0 && x + sign * 2 < width) {
28,352,460!
4218
        error_diffuse_fast(data, pos + sign * 2, depth, error, 1, 8);
28,241,430✔
4219
    }
9,440,010✔
4220
    if (y < height - 1) {
28,352,460✔
4221
        int row;
4222

4223
        row = pos + width;
28,278,756✔
4224
        if (x - sign >= 0 && x - sign < width) {
28,278,756!
4225
            error_diffuse_fast(data,
37,657,392✔
4226
                               row + (-sign),
9,433,950✔
4227
                               depth, error, 1, 8);
9,433,950✔
4228
        }
9,433,950✔
4229
        error_diffuse_fast(data, row, depth, error, 1, 8);
28,278,756✔
4230
        if (x + sign >= 0 && x + sign < width) {
28,278,756!
4231
            error_diffuse_fast(data,
37,657,392✔
4232
                               row + sign,
9,433,950✔
4233
                               depth, error, 1, 8);
9,433,950✔
4234
        }
9,433,950✔
4235
    }
9,452,586✔
4236
    if (y < height - 2) {
28,352,460✔
4237
        error_diffuse_fast(data, pos + width * 2, depth, error, 1, 8);
28,205,052✔
4238
    }
9,427,752✔
4239
}
28,352,460✔
4240

4241

4242
static void
4243
diffuse_atkinson_carry(int32_t *carry_curr, int32_t *carry_next,
×
4244
                       int32_t *carry_far, int width, int height,
4245
                       int depth, int x, int y, int32_t error,
4246
                       int direction, int channel)
4247
{
4248
    /* Atkinson's Method
4249
     *          curr    1/8    1/8
4250
     *   1/8     1/8    1/8
4251
     *           1/8
4252
     */
4253
    int sign;
4254
    int32_t term;
4255

4256
    if (error == 0) {
×
4257
        return;
×
4258
    }
4259

4260
    term = diffuse_fixed_term(error, 1, 8);
×
4261
    sign = direction >= 0 ? 1 : -1;
×
4262
    if (x + sign >= 0 && x + sign < width) {
×
4263
        size_t base;
4264

4265
        base = ((size_t)(x + sign) * (size_t)depth)
×
4266
             + (size_t)channel;
×
4267
        carry_curr[base] += term;
×
4268
    }
4269
    if (x + sign * 2 >= 0 && x + sign * 2 < width) {
×
4270
        size_t base;
4271

4272
        base = ((size_t)(x + sign * 2) * (size_t)depth)
×
4273
             + (size_t)channel;
×
4274
        carry_curr[base] += term;
×
4275
    }
4276
    if (y + 1 < height) {
×
4277
        if (x - sign >= 0 && x - sign < width) {
×
4278
            size_t base;
4279

4280
            base = ((size_t)(x - sign) * (size_t)depth)
×
4281
                 + (size_t)channel;
×
4282
            carry_next[base] += term;
×
4283
        }
4284
        {
4285
            size_t base;
4286

4287
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
4288
            carry_next[base] += term;
×
4289
        }
4290
        if (x + sign >= 0 && x + sign < width) {
×
4291
            size_t base;
4292

4293
            base = ((size_t)(x + sign) * (size_t)depth)
×
4294
                 + (size_t)channel;
×
4295
            carry_next[base] += term;
×
4296
        }
4297
    }
4298
    if (y + 2 < height) {
×
4299
        size_t base;
4300

4301
        base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
4302
        carry_far[base] += term;
×
4303
    }
4304
}
4305

4306

4307
static void
4308
diffuse_jajuni(unsigned char *data, int width, int height,
396,900✔
4309
               int x, int y, int depth, int error, int direction)
4310
{
4311
    /* Jarvis, Judice & Ninke Method
4312
     *                  curr    7/48    5/48
4313
     *  3/48    5/48    7/48    5/48    3/48
4314
     *  1/48    3/48    5/48    3/48    1/48
4315
     */
4316
    int pos;
4317
    int sign;
4318
    static const int row0_offsets[] = { 1, 2 };
4319
    static const int row0_weights[] = { 7, 5 };
4320
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4321
    static const int row1_weights[] = { 3, 5, 7, 5, 3 };
4322
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
4323
    static const int row2_weights[] = { 1, 3, 5, 3, 1 };
4324
    int i;
4325

4326
    pos = y * width + x;
396,900✔
4327
    sign = direction >= 0 ? 1 : -1;
396,900!
4328

4329
    for (i = 0; i < 2; ++i) {
1,190,700✔
4330
        int neighbor;
4331

4332
        neighbor = x + sign * row0_offsets[i];
793,800✔
4333
        if (neighbor < 0 || neighbor >= width) {
793,800!
4334
            continue;
5,670✔
4335
        }
4336
        error_diffuse_precise(data,
1,050,840✔
4337
                              pos + (neighbor - x),
788,130✔
4338
                              depth, error,
262,710✔
4339
                              row0_weights[i], 48);
788,130✔
4340
    }
262,710✔
4341
    if (y < height - 1) {
396,900✔
4342
        int row;
4343

4344
        row = pos + width;
395,010✔
4345
        for (i = 0; i < 5; ++i) {
2,370,060✔
4346
            int neighbor;
4347

4348
            neighbor = x + sign * row1_offsets[i];
1,975,050✔
4349
            if (neighbor < 0 || neighbor >= width) {
1,975,050✔
4350
                continue;
11,286✔
4351
            }
4352
            error_diffuse_precise(data,
2,618,352✔
4353
                                  row + (neighbor - x),
1,963,764✔
4354
                                  depth, error,
654,588✔
4355
                                  row1_weights[i], 48);
1,963,764✔
4356
        }
654,588✔
4357
    }
131,670✔
4358
    if (y < height - 2) {
396,900✔
4359
        int row;
4360

4361
        row = pos + width * 2;
393,120✔
4362
        for (i = 0; i < 5; ++i) {
2,358,720✔
4363
            int neighbor;
4364

4365
            neighbor = x + sign * row2_offsets[i];
1,965,600✔
4366
            if (neighbor < 0 || neighbor >= width) {
1,965,600✔
4367
                continue;
11,232✔
4368
            }
4369
            error_diffuse_precise(data,
2,605,824✔
4370
                                  row + (neighbor - x),
1,954,368✔
4371
                                  depth, error,
651,456✔
4372
                                  row2_weights[i], 48);
1,954,368✔
4373
        }
651,456✔
4374
    }
131,040✔
4375
}
396,900✔
4376

4377

4378
static void
4379
diffuse_jajuni_carry(int32_t *carry_curr, int32_t *carry_next,
×
4380
                     int32_t *carry_far, int width, int height,
4381
                     int depth, int x, int y, int32_t error,
4382
                     int direction, int channel)
4383
{
4384
    /* Jarvis, Judice & Ninke Method
4385
     *                  curr    7/48    5/48
4386
     *  3/48    5/48    7/48    5/48    3/48
4387
     *  1/48    3/48    5/48    3/48    1/48
4388
     */
4389
    static const int row0_offsets[] = { 1, 2 };
4390
    static const int row0_weights[] = { 7, 5 };
4391
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4392
    static const int row1_weights[] = { 3, 5, 7, 5, 3 };
4393
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
4394
    static const int row2_weights[] = { 1, 3, 5, 3, 1 };
4395
    int sign;
4396
    int i;
4397

4398
    if (error == 0) {
×
4399
        return;
×
4400
    }
4401

4402
    sign = direction >= 0 ? 1 : -1;
×
4403
    for (i = 0; i < 2; ++i) {
×
4404
        int neighbor;
4405
        int32_t term;
4406

4407
        neighbor = x + sign * row0_offsets[i];
×
4408
        if (neighbor < 0 || neighbor >= width) {
×
4409
            continue;
×
4410
        }
4411
        term = diffuse_fixed_term(error, row0_weights[i], 48);
×
4412
        carry_curr[((size_t)neighbor * (size_t)depth)
×
4413
                   + (size_t)channel] += term;
×
4414
    }
4415
    if (y + 1 < height) {
×
4416
        for (i = 0; i < 5; ++i) {
×
4417
            int neighbor;
4418
            int32_t term;
4419

4420
            neighbor = x + sign * row1_offsets[i];
×
4421
            if (neighbor < 0 || neighbor >= width) {
×
4422
                continue;
×
4423
            }
4424
            term = diffuse_fixed_term(error, row1_weights[i], 48);
×
4425
            carry_next[((size_t)neighbor * (size_t)depth)
×
4426
                       + (size_t)channel] += term;
×
4427
        }
4428
    }
4429
    if (y + 2 < height) {
×
4430
        for (i = 0; i < 5; ++i) {
×
4431
            int neighbor;
4432
            int32_t term;
4433

4434
            neighbor = x + sign * row2_offsets[i];
×
4435
            if (neighbor < 0 || neighbor >= width) {
×
4436
                continue;
×
4437
            }
4438
            term = diffuse_fixed_term(error, row2_weights[i], 48);
×
4439
            carry_far[((size_t)neighbor * (size_t)depth)
×
4440
                      + (size_t)channel] += term;
×
4441
        }
4442
    }
4443
}
4444

4445

4446
static void
4447
diffuse_stucki(unsigned char *data, int width, int height,
119,700✔
4448
               int x, int y, int depth, int error, int direction)
4449
{
4450
    /* Stucki's Method
4451
     *                  curr    8/48    4/48
4452
     *  2/48    4/48    8/48    4/48    2/48
4453
     *  1/48    2/48    4/48    2/48    1/48
4454
     */
4455
    int pos;
4456
    int sign;
4457
    static const int row0_offsets[] = { 1, 2 };
4458
    static const int row0_num[] = { 1, 1 };
4459
    static const int row0_den[] = { 6, 12 };
4460
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4461
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
4462
    static const int row1_den[] = { 24, 12, 6, 12, 24 };
4463
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
4464
    static const int row2_num[] = { 1, 1, 1, 1, 1 };
4465
    static const int row2_den[] = { 48, 24, 12, 24, 48 };
4466
    int i;
4467

4468
    pos = y * width + x;
119,700✔
4469
    sign = direction >= 0 ? 1 : -1;
119,700!
4470

4471
    for (i = 0; i < 2; ++i) {
359,100✔
4472
        int neighbor;
4473

4474
        neighbor = x + sign * row0_offsets[i];
239,400✔
4475
        if (neighbor < 0 || neighbor >= width) {
239,400!
4476
            continue;
2,700✔
4477
        }
4478
        error_diffuse_precise(data,
315,600✔
4479
                              pos + (neighbor - x),
236,700✔
4480
                              depth, error,
78,900✔
4481
                              row0_num[i], row0_den[i]);
236,700✔
4482
    }
78,900✔
4483
    if (y < height - 1) {
119,700✔
4484
        int row;
4485

4486
        row = pos + width;
118,503✔
4487
        for (i = 0; i < 5; ++i) {
711,018✔
4488
            int neighbor;
4489

4490
            neighbor = x + sign * row1_offsets[i];
592,515✔
4491
            if (neighbor < 0 || neighbor >= width) {
592,515✔
4492
                continue;
5,346✔
4493
            }
4494
            error_diffuse_precise(data,
782,892✔
4495
                                  row + (neighbor - x),
587,169✔
4496
                                  depth, error,
195,723✔
4497
                                  row1_num[i], row1_den[i]);
587,169✔
4498
        }
195,723✔
4499
    }
39,501✔
4500
    if (y < height - 2) {
119,700✔
4501
        int row;
4502

4503
        row = pos + width * 2;
117,306✔
4504
        for (i = 0; i < 5; ++i) {
703,836✔
4505
            int neighbor;
4506

4507
            neighbor = x + sign * row2_offsets[i];
586,530✔
4508
            if (neighbor < 0 || neighbor >= width) {
586,530✔
4509
                continue;
5,292✔
4510
            }
4511
            error_diffuse_precise(data,
774,984✔
4512
                                  row + (neighbor - x),
581,238✔
4513
                                  depth, error,
193,746✔
4514
                                  row2_num[i], row2_den[i]);
581,238✔
4515
        }
193,746✔
4516
    }
39,102✔
4517
}
119,700✔
4518

4519

4520
static void
4521
diffuse_stucki_carry(int32_t *carry_curr, int32_t *carry_next,
×
4522
                     int32_t *carry_far, int width, int height,
4523
                     int depth, int x, int y, int32_t error,
4524
                     int direction, int channel)
4525
{
4526
    /* Stucki's Method
4527
     *                  curr    8/48    4/48
4528
     *  2/48    4/48    8/48    4/48    2/48
4529
     *  1/48    2/48    4/48    2/48    1/48
4530
     */
4531
    static const int row0_offsets[] = { 1, 2 };
4532
    static const int row0_num[] = { 1, 1 };
4533
    static const int row0_den[] = { 6, 12 };
4534
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4535
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
4536
    static const int row1_den[] = { 24, 12, 6, 12, 24 };
4537
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
4538
    static const int row2_num[] = { 1, 1, 1, 1, 1 };
4539
    static const int row2_den[] = { 48, 24, 12, 24, 48 };
4540
    int sign;
4541
    int i;
4542

4543
    if (error == 0) {
×
4544
        return;
×
4545
    }
4546

4547
    sign = direction >= 0 ? 1 : -1;
×
4548
    for (i = 0; i < 2; ++i) {
×
4549
        int neighbor;
4550
        int32_t term;
4551

4552
        neighbor = x + sign * row0_offsets[i];
×
4553
        if (neighbor < 0 || neighbor >= width) {
×
4554
            continue;
×
4555
        }
4556
        term = diffuse_fixed_term(error, row0_num[i], row0_den[i]);
×
4557
        carry_curr[((size_t)neighbor * (size_t)depth)
×
4558
                   + (size_t)channel] += term;
×
4559
    }
4560
    if (y + 1 < height) {
×
4561
        for (i = 0; i < 5; ++i) {
×
4562
            int neighbor;
4563
            int32_t term;
4564

4565
            neighbor = x + sign * row1_offsets[i];
×
4566
            if (neighbor < 0 || neighbor >= width) {
×
4567
                continue;
×
4568
            }
4569
            term = diffuse_fixed_term(error, row1_num[i], row1_den[i]);
×
4570
            carry_next[((size_t)neighbor * (size_t)depth)
×
4571
                       + (size_t)channel] += term;
×
4572
        }
4573
    }
4574
    if (y + 2 < height) {
×
4575
        for (i = 0; i < 5; ++i) {
×
4576
            int neighbor;
4577
            int32_t term;
4578

4579
            neighbor = x + sign * row2_offsets[i];
×
4580
            if (neighbor < 0 || neighbor >= width) {
×
4581
                continue;
×
4582
            }
4583
            term = diffuse_fixed_term(error, row2_num[i], row2_den[i]);
×
4584
            carry_far[((size_t)neighbor * (size_t)depth)
×
4585
                      + (size_t)channel] += term;
×
4586
        }
4587
    }
4588
}
4589

4590

4591
static void
4592
diffuse_burkes(unsigned char *data, int width, int height,
67,500✔
4593
               int x, int y, int depth, int error, int direction)
4594
{
4595
    /* Burkes' Method
4596
     *                  curr    4/16    2/16
4597
     *  1/16    2/16    4/16    2/16    1/16
4598
     */
4599
    int pos;
4600
    int sign;
4601
    static const int row0_offsets[] = { 1, 2 };
4602
    static const int row0_num[] = { 1, 1 };
4603
    static const int row0_den[] = { 4, 8 };
4604
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4605
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
4606
    static const int row1_den[] = { 16, 8, 4, 8, 16 };
4607
    int i;
4608

4609
    pos = y * width + x;
67,500✔
4610
    sign = direction >= 0 ? 1 : -1;
67,500!
4611

4612
    for (i = 0; i < 2; ++i) {
202,500✔
4613
        int neighbor;
4614

4615
        neighbor = x + sign * row0_offsets[i];
135,000✔
4616
        if (neighbor < 0 || neighbor >= width) {
135,000!
4617
            continue;
2,025✔
4618
        }
4619
        error_diffuse_normal(data,
177,300✔
4620
                             pos + (neighbor - x),
132,975✔
4621
                             depth, error,
44,325✔
4622
                             row0_num[i], row0_den[i]);
132,975✔
4623
    }
44,325✔
4624
    if (y < height - 1) {
67,500✔
4625
        int row;
4626

4627
        row = pos + width;
66,600✔
4628
        for (i = 0; i < 5; ++i) {
399,600✔
4629
            int neighbor;
4630

4631
            neighbor = x + sign * row1_offsets[i];
333,000✔
4632
            if (neighbor < 0 || neighbor >= width) {
333,000✔
4633
                continue;
3,996✔
4634
            }
4635
            error_diffuse_normal(data,
438,672✔
4636
                                 row + (neighbor - x),
329,004✔
4637
                                 depth, error,
109,668✔
4638
                                 row1_num[i], row1_den[i]);
329,004✔
4639
        }
109,668✔
4640
    }
22,200✔
4641
}
67,500✔
4642

4643
static void
4644
diffuse_burkes_carry(int32_t *carry_curr, int32_t *carry_next,
×
4645
                     int32_t *carry_far, int width, int height,
4646
                     int depth, int x, int y, int32_t error,
4647
                     int direction, int channel)
4648
{
4649
    /* Burkes' Method
4650
     *                  curr    4/16    2/16
4651
     *  1/16    2/16    4/16    2/16    1/16
4652
     */
4653
    static const int row0_offsets[] = { 1, 2 };
4654
    static const int row0_num[] = { 1, 1 };
4655
    static const int row0_den[] = { 4, 8 };
4656
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4657
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
4658
    static const int row1_den[] = { 16, 8, 4, 8, 16 };
4659
    int sign;
4660
    int i;
4661

4662
    /* unused */ (void) carry_far;
4663

4664
    if (error == 0) {
×
4665
        return;
×
4666
    }
4667

4668
    sign = direction >= 0 ? 1 : -1;
×
4669
    for (i = 0; i < 2; ++i) {
×
4670
        int neighbor;
4671
        int32_t term;
4672

4673
        neighbor = x + sign * row0_offsets[i];
×
4674
        if (neighbor < 0 || neighbor >= width) {
×
4675
            continue;
×
4676
        }
4677
        term = diffuse_fixed_term(error, row0_num[i], row0_den[i]);
×
4678
        carry_curr[((size_t)neighbor * (size_t)depth)
×
4679
                   + (size_t)channel] += term;
×
4680
    }
4681
    if (y + 1 < height) {
×
4682
        for (i = 0; i < 5; ++i) {
×
4683
            int neighbor;
4684
            int32_t term;
4685

4686
            neighbor = x + sign * row1_offsets[i];
×
4687
            if (neighbor < 0 || neighbor >= width) {
×
4688
                continue;
×
4689
            }
4690
            term = diffuse_fixed_term(error, row1_num[i], row1_den[i]);
×
4691
            carry_next[((size_t)neighbor * (size_t)depth)
×
4692
                       + (size_t)channel] += term;
×
4693
        }
4694
    }
4695
}
4696

4697
static void
4698
diffuse_lso1(unsigned char *data, int width, int height,
×
4699
             int x, int y, int depth, int error, int direction)
4700
{
4701
    int pos;
4702
    int sign;
4703

4704
    pos = y * width + x;
×
4705
    sign = direction >= 0 ? 1 : -1;
×
4706

4707
    /* lso1 (libsixel original) method:
4708
     *
4709
     * libsixel-specific error diffusion (dithering) to improve sixel
4710
     * compression; by steering error propagation so out-of-palette
4711
     * intermediate colors render as horizontal bands rather than grainy
4712
     * noise, we increase RLE more effective.
4713
     *
4714
     *          curr
4715
     *   1/8    4/8    1/8
4716
     *          2/8
4717
     */
4718
    if (y < height - 1) {
×
4719
        int row;
4720

4721
        row = pos + width;
×
4722
        if (x - sign >= 0 && x - sign < width) {
×
4723
            error_diffuse_fast(data,
×
4724
                               row + (-sign),
4725
                               depth, error, 1, 8);
4726
        }
4727
        error_diffuse_fast(data, row, depth, error, 4, 8);
×
4728
        if (x + sign >= 0 && x + sign < width) {
×
4729
            error_diffuse_fast(data,
×
4730
                               row + sign,
4731
                               depth, error, 1, 8);
4732
        }
4733
    }
4734
    if (y < height - 2) {
×
4735
        error_diffuse_fast(data, pos + width * 2, depth, error, 2, 8);
×
4736
    }
4737
}
×
4738

4739

4740
static void
4741
diffuse_lso1_carry(int32_t *carry_curr, int32_t *carry_next,
×
4742
                   int32_t *carry_far, int width, int height,
4743
                   int depth, int x, int y, int32_t error,
4744
                   int direction, int channel)
4745
{
4746
    int sign;
4747
    int32_t edge_term;
4748
    int32_t center_term;
4749
    int32_t far_term;
4750

4751
    /* unused */ (void) carry_curr;
4752
    if (error == 0) {
×
4753
        return;
×
4754
    }
4755

4756
    sign = direction >= 0 ? 1 : -1;
×
4757
    edge_term = diffuse_fixed_term(error, 1, 8);
×
4758
    center_term = diffuse_fixed_term(error, 4, 8);
×
4759
    far_term = diffuse_fixed_term(error, 2, 8);
×
4760

4761
    if (y + 1 < height) {
×
4762
        if (x - sign >= 0 && x - sign < width) {
×
4763
            size_t base;
4764

4765
            base = ((size_t)(x - sign) * (size_t)depth)
×
4766
                 + (size_t)channel;
×
4767
            carry_next[base] += edge_term;
×
4768
        }
4769
        {
4770
            size_t base;
4771

4772
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
4773
            carry_next[base] += center_term;
×
4774
        }
4775
        if (x + sign >= 0 && x + sign < width) {
×
4776
            size_t base;
4777

4778
            base = ((size_t)(x + sign) * (size_t)depth)
×
4779
                 + (size_t)channel;
×
4780
            carry_next[base] += edge_term;
×
4781
        }
4782
    }
4783
    if (y + 2 < height) {
×
4784
        size_t base;
4785

4786
        base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
4787
        carry_far[base] += far_term;
×
4788
    }
4789
}
4790

4791
static float
4792
mask_a (int x, int y, int c)
×
4793
{
4794
    return ((((x + c * 67) + y * 236) * 119) & 255 ) / 128.0 - 1.0;
×
4795
}
4796

4797
static float
4798
mask_x (int x, int y, int c)
×
4799
{
4800
    return ((((x + c * 29) ^ y* 149) * 1234) & 511 ) / 256.0 - 1.0;
×
4801
}
4802

4803
/* lookup closest color from palette with "normal" strategy */
4804
static int
4805
lookup_normal(unsigned char const * const pixel,
849,900✔
4806
              int const depth,
4807
              unsigned char const * const palette,
4808
              int const reqcolor,
4809
              unsigned short * const cachetable,
4810
              int const complexion)
4811
{
4812
    int result;
4813
    int diff;
4814
    int r;
4815
    int i;
4816
    int n;
4817
    int distant;
4818

4819
    result = (-1);
849,900✔
4820
    diff = INT_MAX;
849,900✔
4821

4822
    /* don't use cachetable in 'normal' strategy */
4823
    (void) cachetable;
283,300✔
4824

4825
    for (i = 0; i < reqcolor; i++) {
15,309,900✔
4826
        distant = 0;
14,460,000✔
4827
        r = pixel[0] - palette[i * depth + 0];
14,460,000✔
4828
        distant += r * r * complexion;
14,460,000✔
4829
        for (n = 1; n < depth; ++n) {
43,380,000✔
4830
            r = pixel[n] - palette[i * depth + n];
28,920,000✔
4831
            distant += r * r;
28,920,000✔
4832
        }
9,640,000✔
4833
        if (distant < diff) {
14,460,000✔
4834
            diff = distant;
2,211,216✔
4835
            result = i;
2,211,216✔
4836
        }
736,152✔
4837
    }
4,820,000✔
4838

4839
    return result;
849,900✔
4840
}
4841

4842

4843
/*
4844
 * Shared fast lookup flow:
4845
 *
4846
 *   pixel --> quantize --> cuckoo cache --> palette index
4847
 */
4848
static int
4849
lookup_fast_common(unsigned char const *pixel,
16,545,286✔
4850
                   unsigned char const *palette,
4851
                   int reqcolor,
4852
                   unsigned short *cachetable,
4853
                   int complexion,
4854
                   struct histogram_control control)
4855
{
4856
    int result;
4857
    unsigned int hash;
4858
    int diff;
4859
    int i;
4860
    int distant;
4861
    struct cuckoo_table32 *table;
4862
    uint32_t *slot;
4863
    SIXELSTATUS status;
4864

4865
    result = (-1);
16,545,286✔
4866
    diff = INT_MAX;
16,545,286✔
4867
    hash = computeHash(pixel, 3U, &control);
16,545,286✔
4868

4869
    table = (struct cuckoo_table32 *)cachetable;
16,545,286✔
4870
    if (table != NULL) {
16,545,286!
4871
        slot = cuckoo_table32_lookup(table, hash);
16,545,286✔
4872
        if (slot != NULL && *slot != 0U) {
16,545,286!
4873
            return (int)(*slot - 1U);
15,547,401✔
4874
        }
4875
    }
325,595✔
4876

4877
    for (i = 0; i < reqcolor; i++) {
124,910,445✔
4878
        distant = (pixel[0] - palette[i * 3 + 0])
164,819,522✔
4879
                * (pixel[0] - palette[i * 3 + 0]) * complexion
123,912,560✔
4880
                + (pixel[1] - palette[i * 3 + 1])
164,819,522✔
4881
                * (pixel[1] - palette[i * 3 + 1])
123,912,560✔
4882
                + (pixel[2] - palette[i * 3 + 2])
164,819,522✔
4883
                * (pixel[2] - palette[i * 3 + 2]);
123,912,560✔
4884
        if (distant < diff) {
123,912,560✔
4885
            diff = distant;
6,236,045✔
4886
            result = i;
6,236,045✔
4887
        }
2,012,527✔
4888
    }
40,906,962✔
4889

4890
    if (table != NULL && result >= 0) {
997,885!
4891
        status = cuckoo_table32_insert(table,
1,323,480✔
4892
                                       hash,
325,595✔
4893
                                       (uint32_t)(result + 1));
997,885✔
4894
        if (SIXEL_FAILED(status)) {
997,885!
4895
            /* ignore cache update failure */
4896
        }
4897
    }
325,595✔
4898

4899
    return result;
997,885✔
4900
}
8,319,482✔
4901

4902
/* lookup closest color from palette with "fast" strategy */
4903
static int
4904
lookup_fast(unsigned char const * const pixel,
16,545,286✔
4905
            int const depth,
4906
            unsigned char const * const palette,
4907
            int const reqcolor,
4908
            unsigned short * const cachetable,
4909
            int const complexion)
4910
{
4911
    struct histogram_control control;
4912

4913
    (void)depth;
8,319,482✔
4914

4915
    control = histogram_control_make(3U);
16,545,286✔
4916

4917
    return lookup_fast_common(pixel,
24,864,768✔
4918
                              palette,
8,319,482✔
4919
                              reqcolor,
8,319,482✔
4920
                              cachetable,
8,319,482✔
4921
                              complexion,
8,319,482✔
4922
                              control);
4923
}
4924

4925
static int
4926
lookup_fast_robinhood(unsigned char const * const pixel,
×
4927
                      int const depth,
4928
                      unsigned char const * const palette,
4929
                      int const reqcolor,
4930
                      unsigned short * const cachetable,
4931
                      int const complexion)
4932
{
4933
    struct histogram_control control;
4934

4935
    (void)depth;
4936

4937
    control = histogram_control_make_for_policy(3U,
×
4938
                                                SIXEL_LUT_POLICY_ROBINHOOD);
4939

4940
    return lookup_fast_common(pixel,
×
4941
                              palette,
4942
                              reqcolor,
4943
                              cachetable,
4944
                              complexion,
4945
                              control);
4946
}
4947

4948
static int
4949
lookup_fast_hopscotch(unsigned char const * const pixel,
×
4950
                      int const depth,
4951
                      unsigned char const * const palette,
4952
                      int const reqcolor,
4953
                      unsigned short * const cachetable,
4954
                      int const complexion)
4955
{
4956
    struct histogram_control control;
4957

4958
    (void)depth;
4959

4960
    control = histogram_control_make_for_policy(3U,
×
4961
                                                SIXEL_LUT_POLICY_HOPSCOTCH);
4962

4963
    return lookup_fast_common(pixel,
×
4964
                              palette,
4965
                              reqcolor,
4966
                              cachetable,
4967
                              complexion,
4968
                              control);
4969
}
4970

4971

4972
static int
4973
lookup_mono_darkbg(unsigned char const * const pixel,
1,730,520✔
4974
                   int const depth,
4975
                   unsigned char const * const palette,
4976
                   int const reqcolor,
4977
                   unsigned short * const cachetable,
4978
                   int const complexion)
4979
{
4980
    int n;
4981
    int distant;
4982

4983
    /* unused */ (void) palette;
576,840✔
4984
    /* unused */ (void) cachetable;
576,840✔
4985
    /* unused */ (void) complexion;
576,840✔
4986

4987
    distant = 0;
1,730,520✔
4988
    for (n = 0; n < depth; ++n) {
6,922,080✔
4989
        distant += pixel[n];
5,191,560✔
4990
    }
1,730,520✔
4991
    return distant >= 128 * reqcolor ? 1: 0;
1,730,520✔
4992
}
4993

4994

4995
static int
4996
lookup_mono_lightbg(unsigned char const * const pixel,
810,000✔
4997
                    int const depth,
4998
                    unsigned char const * const palette,
4999
                    int const reqcolor,
5000
                    unsigned short * const cachetable,
5001
                    int const complexion)
5002
{
5003
    int n;
5004
    int distant;
5005

5006
    /* unused */ (void) palette;
270,000✔
5007
    /* unused */ (void) cachetable;
270,000✔
5008
    /* unused */ (void) complexion;
270,000✔
5009

5010
    distant = 0;
810,000✔
5011
    for (n = 0; n < depth; ++n) {
3,240,000✔
5012
        distant += pixel[n];
2,430,000✔
5013
    }
810,000✔
5014
    return distant < 128 * reqcolor ? 1: 0;
810,000✔
5015
}
5016

5017

5018
/* choose colors using median-cut method */
5019
SIXELSTATUS
5020
sixel_quant_make_palette(
235✔
5021
    unsigned char          /* out */ **result,
5022
    unsigned char const    /* in */  *data,
5023
    unsigned int           /* in */  length,
5024
    int                    /* in */  pixelformat,
5025
    unsigned int           /* in */  reqcolors,
5026
    unsigned int           /* in */  *ncolors,
5027
    unsigned int           /* in */  *origcolors,
5028
    int                    /* in */  methodForLargest,
5029
    int                    /* in */  methodForRep,
5030
    int                    /* in */  qualityMode,
5031
    int                    /* in */  force_palette,
5032
    sixel_allocator_t      /* in */  *allocator)
5033
{
5034
    SIXELSTATUS status = SIXEL_FALSE;
235✔
5035
    unsigned int i;
5036
    unsigned int n;
5037
    int ret;
5038
    tupletable2 colormap;
5039
    unsigned int depth;
5040
    int result_depth;
5041

5042
    result_depth = sixel_helper_compute_depth(pixelformat);
235✔
5043
    if (result_depth <= 0) {
235!
5044
        *result = NULL;
×
5045
        goto end;
×
5046
    }
5047

5048
    depth = (unsigned int)result_depth;
235✔
5049

5050
    ret = computeColorMapFromInput(data, length, depth,
344✔
5051
                                   reqcolors, methodForLargest,
109✔
5052
                                   methodForRep, qualityMode,
109✔
5053
                                   force_palette, &colormap,
109✔
5054
                                   origcolors, allocator);
109✔
5055
    if (ret != 0) {
235!
5056
        *result = NULL;
×
5057
        goto end;
×
5058
    }
5059
    *ncolors = colormap.size;
235✔
5060
    quant_trace(stderr, "tupletable size: %d\n", *ncolors);
235✔
5061
    *result = (unsigned char *)sixel_allocator_malloc(allocator, *ncolors * depth);
235✔
5062
    for (i = 0; i < *ncolors; i++) {
10,621✔
5063
        for (n = 0; n < depth; ++n) {
41,544✔
5064
            (*result)[i * depth + n] = colormap.table[i]->tuple[n];
31,158✔
5065
        }
11,100✔
5066
    }
3,700✔
5067

5068
    sixel_allocator_free(allocator, colormap.table);
235✔
5069

5070
    status = SIXEL_OK;
235✔
5071

5072
end:
126✔
5073
    return status;
235✔
5074
}
5075

5076

5077
/* apply color palette into specified pixel buffers */
5078
SIXELSTATUS
5079
sixel_quant_apply_palette(
257✔
5080
    sixel_index_t     /* out */ *result,
5081
    unsigned char     /* in */  *data,
5082
    int               /* in */  width,
5083
    int               /* in */  height,
5084
    int               /* in */  depth,
5085
    unsigned char     /* in */  *palette,
5086
    int               /* in */  reqcolor,
5087
    int               /* in */  methodForDiffuse,
5088
    int               /* in */  methodForScan,
5089
    int               /* in */  methodForCarry,
5090
    int               /* in */  foptimize,
5091
    int               /* in */  foptimize_palette,
5092
    int               /* in */  complexion,
5093
    unsigned short    /* in */  *cachetable,
5094
    int               /* in */  *ncolors,
5095
    sixel_allocator_t /* in */  *allocator)
5096
{
152✔
5097
#if _MSC_VER
5098
    enum { max_depth = 4 };
5099
#else
5100
    const size_t max_depth = 4;
257✔
5101
#endif
5102
    unsigned char copy[max_depth];
152✔
5103
    SIXELSTATUS status = SIXEL_FALSE;
257✔
5104
    int sum1, sum2;
5105
    int n;
5106
    unsigned short *indextable;
5107
    size_t cache_size;
5108
    int allocated_cache;
5109
    int use_cache;
5110
    int cache_policy;
5111
    unsigned char new_palette[SIXEL_PALETTE_MAX * 4];
5112
    unsigned short migration_map[SIXEL_PALETTE_MAX];
5113
    int (*f_lookup)(unsigned char const * const pixel,
5114
                    int const depth,
5115
                    unsigned char const * const palette,
5116
                    int const reqcolor,
5117
                    unsigned short * const cachetable,
5118
                    int const complexion);
5119
    int use_varerr;
5120
    int use_positional;
5121
    int carry_mode;
5122

5123
    /* check bad reqcolor */
5124
    if (reqcolor < 1) {
257!
5125
        status = SIXEL_BAD_ARGUMENT;
×
5126
        sixel_helper_set_additional_message(
×
5127
            "sixel_quant_apply_palette: "
5128
            "a bad argument is detected, reqcolor < 0.");
5129
        goto end;
×
5130
    }
5131

5132
    /* NOTE: diffuse_jajuni, diffuse_stucki, and diffuse_burkes reference at
5133
     * minimum the position pos + width * 1 - 2, so width must be at least 2
5134
     * to avoid underflow.
5135
     * On the other hand, diffuse_fs and diffuse_atkinson
5136
     * reference pos + width * 1 - 1, but since these functions are only called
5137
     * when width >= 1, they do not cause underflow.
5138
     */
5139
    use_varerr = (depth == 3
362✔
5140
                  && (methodForDiffuse == SIXEL_DIFFUSE_LSO2
514!
5141
                      || methodForDiffuse == SIXEL_DIFFUSE_LSO3));
257!
5142
    use_positional = (methodForDiffuse == SIXEL_DIFFUSE_A_DITHER
362✔
5143
                      || methodForDiffuse == SIXEL_DIFFUSE_X_DITHER);
257!
5144
    carry_mode = (methodForCarry == SIXEL_CARRY_ENABLE)
257✔
5145
               ? SIXEL_CARRY_ENABLE
5146
               : SIXEL_CARRY_DISABLE;
152!
5147

5148
    f_lookup = NULL;
257✔
5149
    if (reqcolor == 2) {
257✔
5150
        sum1 = 0;
19✔
5151
        sum2 = 0;
19✔
5152
        for (n = 0; n < depth; ++n) {
76✔
5153
            sum1 += palette[n];
57✔
5154
        }
21✔
5155
        for (n = depth; n < depth + depth; ++n) {
76✔
5156
            sum2 += palette[n];
57✔
5157
        }
21✔
5158
        if (sum1 == 0 && sum2 == 255 * 3) {
19!
5159
            f_lookup = lookup_mono_darkbg;
12✔
5160
        } else if (sum1 == 255 * 3 && sum2 == 0) {
11!
5161
            f_lookup = lookup_mono_lightbg;
3✔
5162
        }
1✔
5163
    }
7✔
5164
    if (f_lookup == NULL) {
257✔
5165
        if (foptimize && depth == 3) {
242!
5166
            f_lookup = lookup_fast;
236✔
5167
        } else {
98✔
5168
            f_lookup = lookup_normal;
6✔
5169
        }
5170
    }
100✔
5171

5172
    if (f_lookup == lookup_fast) {
257✔
5173
        if (histogram_lut_policy == SIXEL_LUT_POLICY_ROBINHOOD) {
236!
5174
            f_lookup = lookup_fast_robinhood;
×
5175
        } else if (histogram_lut_policy == SIXEL_LUT_POLICY_HOPSCOTCH) {
236!
5176
            f_lookup = lookup_fast_hopscotch;
×
5177
        }
5178
    }
98✔
5179

5180
    indextable = cachetable;
257✔
5181
    allocated_cache = 0;
257✔
5182
    cache_policy = SIXEL_LUT_POLICY_AUTO;
257✔
5183
    use_cache = 0;
257✔
5184
    if (f_lookup == lookup_fast) {
257✔
5185
        cache_policy = histogram_lut_policy;
236✔
5186
        use_cache = 1;
236✔
5187
    } else if (f_lookup == lookup_fast_robinhood) {
119!
5188
        cache_policy = SIXEL_LUT_POLICY_ROBINHOOD;
×
5189
        use_cache = 1;
×
5190
    } else if (f_lookup == lookup_fast_hopscotch) {
21!
5191
        cache_policy = SIXEL_LUT_POLICY_HOPSCOTCH;
×
5192
        use_cache = 1;
×
5193
    }
5194
    if (cache_policy == SIXEL_LUT_POLICY_AUTO) {
257!
5195
        cache_policy = SIXEL_LUT_POLICY_6BIT;
257✔
5196
    }
105✔
5197
    if (use_cache) {
257✔
5198
        if (cachetable == NULL) {
236!
5199
            status = sixel_quant_cache_prepare(&indextable,
×
5200
                                               &cache_size,
5201
                                               cache_policy,
5202
                                               reqcolor,
5203
                                               allocator);
5204
            if (SIXEL_FAILED(status)) {
×
5205
                quant_trace(stderr,
×
5206
                            "Unable to allocate lookup cache.\n");
5207
                goto end;
×
5208
            }
5209
            allocated_cache = 1;
×
5210
        } else {
5211
            sixel_quant_cache_clear(indextable, cache_policy);
236✔
5212
        }
5213
    }
98✔
5214

5215
    if (use_positional) {
257!
5216
        status = apply_palette_positional(result, data, width, height,
×
5217
                                          depth, palette, reqcolor,
5218
                                          methodForDiffuse, methodForScan,
5219
                                          foptimize_palette, f_lookup,
5220
                                          indextable, complexion, copy,
5221
                                          new_palette, migration_map,
5222
                                          ncolors);
5223
    } else if (use_varerr) {
257!
5224
        status = apply_palette_variable(result, data, width, height,
×
5225
                                        depth, palette, reqcolor,
5226
                                        methodForScan, foptimize_palette,
5227
                                        f_lookup, indextable, complexion,
5228
                                        new_palette, migration_map,
5229
                                        ncolors,
5230
                                        methodForDiffuse,
5231
                                        carry_mode);
5232
    } else {
5233
        status = apply_palette_fixed(result, data, width, height,
362✔
5234
                                     depth, palette, reqcolor,
105✔
5235
                                     methodForScan, foptimize_palette,
105✔
5236
                                     f_lookup, indextable, complexion,
105✔
5237
                                     new_palette, migration_map,
105✔
5238
                                     ncolors, methodForDiffuse,
105✔
5239
                                     carry_mode);
105✔
5240
    }
5241
    if (SIXEL_FAILED(status)) {
257!
5242
        goto end;
×
5243
    }
5244

5245
    if (allocated_cache) {
257!
5246
        sixel_quant_cache_release(indextable,
×
5247
                                  cache_policy,
5248
                                  allocator);
5249
    }
5250

5251
    status = SIXEL_OK;
257✔
5252

5253
end:
152✔
5254
    return status;
257✔
5255
}
5256

5257

5258
void
5259
sixel_quant_free_palette(
235✔
5260
    unsigned char       /* in */ *data,
5261
    sixel_allocator_t   /* in */ *allocator)
5262
{
5263
    sixel_allocator_free(allocator, data);
235✔
5264
}
235✔
5265

5266

5267
#if HAVE_TESTS
5268
static int
5269
test1(void)
×
5270
{
5271
    int nret = EXIT_FAILURE;
×
5272
    sample minval[1] = { 1 };
×
5273
    sample maxval[1] = { 2 };
×
5274
    unsigned int retval;
5275

5276
    retval = largestByLuminosity(minval, maxval, 1);
×
5277
    if (retval != 0) {
×
5278
        goto error;
×
5279
    }
5280
    nret = EXIT_SUCCESS;
×
5281

5282
error:
5283
    return nret;
×
5284
}
5285

5286

5287
int
5288
sixel_quant_tests_main(void)
×
5289
{
5290
    int nret = EXIT_FAILURE;
×
5291
    size_t i;
5292
    typedef int (* testcase)(void);
5293

5294
    static testcase const testcases[] = {
5295
        test1,
5296
    };
5297

5298
    for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) {
×
5299
        nret = testcases[i]();
×
5300
        if (nret != EXIT_SUCCESS) {
×
5301
            goto error;
×
5302
        }
5303
    }
5304

5305
    nret = EXIT_SUCCESS;
×
5306

5307
error:
5308
    return nret;
×
5309
}
5310
#endif  /* HAVE_TESTS */
5311

5312
/* emacs Local Variables:      */
5313
/* emacs mode: c               */
5314
/* emacs tab-width: 4          */
5315
/* emacs indent-tabs-mode: nil */
5316
/* emacs c-basic-offset: 4     */
5317
/* emacs End:                  */
5318
/* vim: set expandtab ts=4 sts=4 sw=4 : */
5319
/* EOF */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc