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

saitoha / libsixel / 19023942966

03 Nov 2025 04:28AM UTC coverage: 44.603% (-0.05%) from 44.655%
19023942966

push

github

saitoha
feat: add -6,--6reversible option for palette generation

6969 of 23422 branches covered (29.75%)

30 of 84 new or added lines in 3 files covered. (35.71%)

2 existing lines in 1 file now uncovered.

10005 of 22431 relevant lines covered (44.6%)

1092199.81 hits per line

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

39.75
/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
#include "compat_stub.h"
80

81
static unsigned char const sixel_reversible_tones[101] = {
82
     0,  3,  5,  8, 10, 13, 15, 18, 20, 23,
83
    26, 28, 31, 33, 36, 38, 41, 43, 46, 48,
84
    51, 54, 56, 59, 61, 64, 66, 69, 71, 74,
85
    77, 79, 82, 84, 87, 89, 92, 94, 97, 99,
86
   102, 105, 107, 110, 112, 115, 117, 120, 122, 125,
87
   128, 130, 133, 135, 138, 140, 143, 145, 148, 150,
88
   153, 156, 158, 161, 163, 166, 168, 171, 173, 176,
89
   179, 181, 184, 186, 189, 191, 194, 196, 199, 201,
90
   204, 207, 209, 212, 214, 217, 219, 222, 224, 227,
91
   230, 232, 235, 237, 240, 242, 245, 247, 250, 252,
92
   255
93
};
94

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

120
static float mask_a(int x, int y, int c);
121
static float mask_x(int x, int y, int c);
122
static void diffuse_none(unsigned char *data, int width, int height,
123
                         int x, int y, int depth, int error, int direction);
124
static void diffuse_fs(unsigned char *data, int width, int height,
125
                       int x, int y, int depth, int error, int direction);
126
static void diffuse_atkinson(unsigned char *data, int width, int height,
127
                             int x, int y, int depth, int error,
128
                             int direction);
129
static void diffuse_jajuni(unsigned char *data, int width, int height,
130
                           int x, int y, int depth, int error,
131
                           int direction);
132
static void diffuse_stucki(unsigned char *data, int width, int height,
133
                           int x, int y, int depth, int error,
134
                           int direction);
135
static void diffuse_burkes(unsigned char *data, int width, int height,
136
                           int x, int y, int depth, int error,
137
                           int direction);
138
static void diffuse_sierra1(unsigned char *data, int width, int height,
139
                            int x, int y, int depth, int error,
140
                            int direction);
141
static void diffuse_sierra2(unsigned char *data, int width, int height,
142
                            int x, int y, int depth, int error,
143
                            int direction);
144
static void diffuse_sierra3(unsigned char *data, int width, int height,
145
                            int x, int y, int depth, int error,
146
                            int direction);
147
static void diffuse_lso1(unsigned char *data, int width, int height,
148
                         int x, int y, int depth, int error, int direction);
149
static void diffuse_none_carry(int32_t *carry_curr, int32_t *carry_next,
150
                               int32_t *carry_far, int width, int height,
151
                               int depth, int x, int y, int32_t error,
152
                               int direction, int channel);
153
static void diffuse_fs_carry(int32_t *carry_curr, int32_t *carry_next,
154
                             int32_t *carry_far, int width, int height,
155
                             int depth, int x, int y, int32_t error,
156
                             int direction, int channel);
157
static void diffuse_atkinson_carry(int32_t *carry_curr, int32_t *carry_next,
158
                                   int32_t *carry_far, int width,
159
                                   int height, int depth, int x, int y,
160
                                   int32_t error, int direction,
161
                                   int channel);
162
static void diffuse_jajuni_carry(int32_t *carry_curr, int32_t *carry_next,
163
                                 int32_t *carry_far, int width, int height,
164
                                 int depth, int x, int y, int32_t error,
165
                                 int direction, int channel);
166
static void diffuse_stucki_carry(int32_t *carry_curr, int32_t *carry_next,
167
                                 int32_t *carry_far, int width, int height,
168
                                 int depth, int x, int y, int32_t error,
169
                                 int direction, int channel);
170
static void diffuse_burkes_carry(int32_t *carry_curr, int32_t *carry_next,
171
                                 int32_t *carry_far, int width, int height,
172
                                 int depth, int x, int y, int32_t error,
173
                                 int direction, int channel);
174
static void diffuse_sierra1_carry(int32_t *carry_curr, int32_t *carry_next,
175
                                  int32_t *carry_far, int width, int height,
176
                                  int depth, int x, int y, int32_t error,
177
                                  int direction, int channel);
178
static void diffuse_sierra2_carry(int32_t *carry_curr, int32_t *carry_next,
179
                                  int32_t *carry_far, int width, int height,
180
                                  int depth, int x, int y, int32_t error,
181
                                  int direction, int channel);
182
static void diffuse_sierra3_carry(int32_t *carry_curr, int32_t *carry_next,
183
                                  int32_t *carry_far, int width, int height,
184
                                  int depth, int x, int y, int32_t error,
185
                                  int direction, int channel);
186
static void diffuse_lso1_carry(int32_t *carry_curr, int32_t *carry_next,
187
                               int32_t *carry_far, int width, int height,
188
                               int depth, int x, int y, int32_t error,
189
                               int direction, int channel);
190

191
static const int (*
192
lso2_table(void))[7]
×
193
{
194
#include "lso2.h"
195
    return var_coefs;
196
}
197

198
static const int (*
199
lso3_table(void))[7]
×
200
{
201
/* Auto-generated by gen_varcoefs.awk */
202
#include "lso3.h"
203
    return var_coefs;
204
}
205

206

207
#define VARERR_SCALE_SHIFT 12
208
#define VARERR_SCALE       (1 << VARERR_SCALE_SHIFT)
209
#define VARERR_ROUND       (1 << (VARERR_SCALE_SHIFT - 1))
210
#define VARERR_MAX_VALUE   (255 * VARERR_SCALE)
211

212
#if HAVE_DEBUG
213
#define quant_trace fprintf
214
#else
215
static inline void quant_trace(FILE *f, ...) { (void) f; }
216
#endif
217

218
/*****************************************************************************
219
 *
220
 * quantization
221
 *
222
 *****************************************************************************/
223

224
typedef struct box* boxVector;
225
struct box {
226
    unsigned int ind;
227
    unsigned int colors;
228
    unsigned int sum;
229
};
230

231
typedef unsigned long sample;
232
typedef sample * tuple;
233

234
static unsigned int
NEW
235
sixel_quant_reversible_index(unsigned int sample)
×
236
{
237
    unsigned int index;
238

NEW
239
    if (sample > 255U) {
×
NEW
240
        sample = 255U;
×
241
    }
NEW
242
    index = (sample * 100U + 127U) / 255U;
×
NEW
243
    if (index > 100U) {
×
NEW
244
        index = 100U;
×
245
    }
246

NEW
247
    return index;
×
248
}
249

250
static unsigned char
NEW
251
sixel_quant_reversible_value(unsigned int sample)
×
252
{
253
    unsigned int index;
254

NEW
255
    index = sixel_quant_reversible_index(sample);
×
256

NEW
257
    return sixel_reversible_tones[index];
×
258
}
259

260
static void
NEW
261
sixel_quant_reversible_pixel(unsigned char const *src,
×
262
                             unsigned int depth,
263
                             unsigned char *dst)
264
{
265
    unsigned int plane;
266

NEW
267
    for (plane = 0U; plane < depth; ++plane) {
×
NEW
268
        dst[plane] = sixel_quant_reversible_value(src[plane]);
×
269
    }
NEW
270
}
×
271

272
static void
NEW
273
sixel_quant_reversible_tuple(sample *tuple,
×
274
                             unsigned int depth)
275
{
276
    unsigned int plane;
277
    unsigned int sample_value;
278

NEW
279
    for (plane = 0U; plane < depth; ++plane) {
×
NEW
280
        sample_value = (unsigned int)tuple[plane];
×
NEW
281
        tuple[plane] =
×
NEW
282
            (sample)sixel_quant_reversible_value(sample_value);
×
283
    }
NEW
284
}
×
285

286
static void
NEW
287
sixel_quant_reversible_palette(unsigned char *palette,
×
288
                               unsigned int colors,
289
                               unsigned int depth)
290
{
291
    unsigned int index;
292
    unsigned int plane;
293
    unsigned int sample_value;
294
    size_t offset;
295

NEW
296
    for (index = 0U; index < colors; ++index) {
×
NEW
297
        for (plane = 0U; plane < depth; ++plane) {
×
NEW
298
            offset = (size_t)index * (size_t)depth + (size_t)plane;
×
NEW
299
            sample_value = (unsigned int)palette[offset];
×
NEW
300
            palette[offset] =
×
NEW
301
                sixel_quant_reversible_value(sample_value);
×
302
        }
303
    }
NEW
304
}
×
305

306
struct tupleint {
307
    /* An ordered pair of a tuple value and an integer, such as you
308
       would find in a tuple table or tuple hash.
309
       Note that this is a variable length structure.
310
    */
311
    unsigned int value;
312
    sample tuple[1];
313
    /* This is actually a variable size array -- its size is the
314
       depth of the tuple in question.  Some compilers do not let us
315
       declare a variable length array.
316
    */
317
};
318
typedef struct tupleint ** tupletable;
319

320
typedef struct {
321
    unsigned int size;
322
    tupletable table;
323
} tupletable2;
324

325
static unsigned int compareplanePlane;
326
static tupletable2 const *force_palette_source;
327
    /* This is a parameter to compareplane().  We use this global variable
328
       so that compareplane() can be called by qsort(), to compare two
329
       tuples.  qsort() doesn't pass any arguments except the two tuples.
330
    */
331
static int
332
compareplane(const void * const arg1,
10,960,527✔
333
             const void * const arg2)
334
{
335
    int lhs, rhs;
336

337
    typedef const struct tupleint * const * const sortarg;
338
    sortarg comparandPP  = (sortarg) arg1;
10,960,527✔
339
    sortarg comparatorPP = (sortarg) arg2;
10,960,527✔
340
    lhs = (int)(*comparandPP)->tuple[compareplanePlane];
10,960,527✔
341
    rhs = (int)(*comparatorPP)->tuple[compareplanePlane];
10,960,527✔
342

343
    return lhs - rhs;
10,960,527✔
344
}
345

346

347
static int
348
sumcompare(const void * const b1, const void * const b2)
7,829,364✔
349
{
350
    return (int)((boxVector)b2)->sum - (int)((boxVector)b1)->sum;
7,829,364✔
351
}
352

353

354
static SIXELSTATUS
355
alloctupletable(
512✔
356
    tupletable          /* out */ *result,
357
    unsigned int const  /* in */  depth,
358
    unsigned int const  /* in */  size,
359
    sixel_allocator_t   /* in */  *allocator)
360
{
361
    SIXELSTATUS status = SIXEL_FALSE;
512✔
362
    enum { message_buffer_size = 256 };
363
    char message[message_buffer_size];
364
    int nwrite;
365
    unsigned int mainTableSize;
366
    unsigned int tupleIntSize;
367
    unsigned int allocSize;
368
    void * pool;
369
    tupletable tbl;
370
    unsigned int i;
371

372
    if (UINT_MAX / sizeof(struct tupleint) < size) {
512!
373
        nwrite = sixel_compat_snprintf(
×
374
            message,
375
            sizeof(message),
376
            "size %u is too big for arithmetic",
377
            size);
378
        if (nwrite > 0) {
×
379
            sixel_helper_set_additional_message(message);
×
380
        }
381
        status = SIXEL_RUNTIME_ERROR;
×
382
        goto end;
×
383
    }
384

385
    mainTableSize = size * sizeof(struct tupleint *);
512✔
386
    tupleIntSize = sizeof(struct tupleint) - sizeof(sample)
512✔
387
        + depth * sizeof(sample);
232✔
388

389
    /* To save the enormous amount of time it could take to allocate
390
       each individual tuple, we do a trick here and allocate everything
391
       as a single malloc block and suballocate internally.
392
    */
393
    if ((UINT_MAX - mainTableSize) / tupleIntSize < size) {
512!
394
        nwrite = sixel_compat_snprintf(
×
395
            message,
396
            sizeof(message),
397
            "size %u is too big for arithmetic",
398
            size);
399
        if (nwrite > 0) {
×
400
            sixel_helper_set_additional_message(message);
×
401
        }
402
        status = SIXEL_RUNTIME_ERROR;
×
403
        goto end;
×
404
    }
405

406
    allocSize = mainTableSize + size * tupleIntSize;
512✔
407

408
    pool = sixel_allocator_malloc(allocator, allocSize);
512✔
409
    if (pool == NULL) {
512!
410
        sixel_compat_snprintf(
×
411
            message,
412
            sizeof(message),
413
            "unable to allocate %u bytes for a %u-entry tuple table",
414
            allocSize,
415
            size);
416
        sixel_helper_set_additional_message(message);
×
417
        status = SIXEL_BAD_ALLOCATION;
×
418
        goto end;
×
419
    }
420
    tbl = (tupletable) pool;
512✔
421

422
    for (i = 0; i < size; ++i)
303,893✔
423
        tbl[i] = (struct tupleint *)
303,381✔
424
            ((char*)pool + mainTableSize + i * tupleIntSize);
303,381✔
425

426
    *result = tbl;
512✔
427

428
    status = SIXEL_OK;
512✔
429

430
end:
280✔
431
    return status;
512✔
432
}
433

434

435
/*
436
** Here is the fun part, the median-cut colormap generator.  This is based
437
** on Paul Heckbert's paper "Color Image Quantization for Frame Buffer
438
** Display", SIGGRAPH '82 Proceedings, page 297.
439
*/
440

441
static tupletable2
442
newColorMap(unsigned int const newcolors, unsigned int const depth, sixel_allocator_t *allocator)
66✔
443
{
444
    SIXELSTATUS status = SIXEL_FALSE;
66✔
445
    tupletable2 colormap;
446
    unsigned int i;
447

448
    colormap.size = 0;
66✔
449
    status = alloctupletable(&colormap.table, depth, newcolors, allocator);
66✔
450
    if (SIXEL_FAILED(status)) {
66!
451
        goto end;
×
452
    }
453
    if (colormap.table) {
88!
454
        for (i = 0; i < newcolors; ++i) {
13,062✔
455
            unsigned int plane;
456
            for (plane = 0; plane < depth; ++plane)
51,984✔
457
                colormap.table[i]->tuple[plane] = 0;
38,988✔
458
        }
4,332✔
459
        colormap.size = newcolors;
66✔
460
    }
22✔
461

462
end:
463
    return colormap;
66✔
464
}
465

466

467
static boxVector
468
newBoxVector(
66✔
469
    unsigned int const  /* in */ colors,
470
    unsigned int const  /* in */ sum,
471
    unsigned int const  /* in */ newcolors,
472
    sixel_allocator_t   /* in */ *allocator)
473
{
474
    boxVector bv;
475

476
    bv = (boxVector)sixel_allocator_malloc(allocator,
88✔
477
                                           sizeof(struct box) * (size_t)newcolors);
66✔
478
    if (bv == NULL) {
66!
479
        quant_trace(stderr, "out of memory allocating box vector table\n");
×
480
        return NULL;
×
481
    }
482

483
    /* Set up the initial box. */
484
    bv[0].ind = 0;
66✔
485
    bv[0].colors = colors;
66✔
486
    bv[0].sum = sum;
66✔
487

488
    return bv;
66✔
489
}
22✔
490

491

492
static void
493
findBoxBoundaries(tupletable2  const colorfreqtable,
12,930✔
494
                  unsigned int const depth,
495
                  unsigned int const boxStart,
496
                  unsigned int const boxSize,
497
                  sample             minval[],
498
                  sample             maxval[])
499
{
500
/*----------------------------------------------------------------------------
501
  Go through the box finding the minimum and maximum of each
502
  component - the boundaries of the box.
503
-----------------------------------------------------------------------------*/
504
    unsigned int plane;
505
    unsigned int i;
506

507
    for (plane = 0; plane < depth; ++plane) {
51,720✔
508
        minval[plane] = colorfreqtable.table[boxStart]->tuple[plane];
38,790✔
509
        maxval[plane] = minval[plane];
38,790✔
510
    }
12,930✔
511

512
    for (i = 1; i < boxSize; ++i) {
1,908,590✔
513
        for (plane = 0; plane < depth; ++plane) {
7,582,640✔
514
            sample const v = colorfreqtable.table[boxStart + i]->tuple[plane];
5,686,980✔
515
            if (v < minval[plane]) minval[plane] = v;
5,686,980✔
516
            if (v > maxval[plane]) maxval[plane] = v;
5,686,980✔
517
        }
1,897,638✔
518
    }
632,546✔
519
}
12,930✔
520

521

522

523
static unsigned int
524
largestByNorm(sample minval[], sample maxval[], unsigned int const depth)
12,165✔
525
{
526

527
    unsigned int largestDimension;
528
    unsigned int plane;
529
    sample largestSpreadSoFar;
530

531
    largestSpreadSoFar = 0;
12,165✔
532
    largestDimension = 0;
12,165✔
533
    for (plane = 0; plane < depth; ++plane) {
48,660✔
534
        sample const spread = maxval[plane]-minval[plane];
36,495✔
535
        if (spread > largestSpreadSoFar) {
36,495✔
536
            largestDimension = plane;
21,318✔
537
            largestSpreadSoFar = spread;
21,318✔
538
        }
7,038✔
539
    }
12,165✔
540
    return largestDimension;
12,165✔
541
}
542

543

544

545
static unsigned int
546
largestByLuminosity(sample minval[], sample maxval[], unsigned int const depth)
765✔
547
{
548
/*----------------------------------------------------------------------------
549
   This subroutine presumes that the tuple type is either
550
   BLACKANDWHITE, GRAYSCALE, or RGB (which implies pamP->depth is 1 or 3).
551
   To save time, we don't actually check it.
552
-----------------------------------------------------------------------------*/
553
    unsigned int retval;
554

555
    double lumin_factor[3] = {0.2989, 0.5866, 0.1145};
765✔
556

557
    if (depth == 1) {
765!
558
        retval = 0;
×
559
    } else {
560
        /* An RGB tuple */
561
        unsigned int largestDimension;
562
        unsigned int plane;
563
        double largestSpreadSoFar;
564

565
        largestSpreadSoFar = 0.0;
765✔
566
        largestDimension = 0;
765✔
567

568
        for (plane = 0; plane < 3; ++plane) {
3,060✔
569
            double const spread =
2,295✔
570
                lumin_factor[plane] * (maxval[plane]-minval[plane]);
2,295✔
571
            if (spread > largestSpreadSoFar) {
2,295✔
572
                largestDimension = plane;
1,331✔
573
                largestSpreadSoFar = spread;
1,331✔
574
            }
451✔
575
        }
765✔
576
        retval = largestDimension;
765✔
577
    }
578
    return retval;
765✔
579
}
580

581

582

583
static void
584
centerBox(unsigned int const boxStart,
10,692✔
585
          unsigned int const boxSize,
586
          tupletable2  const colorfreqtable,
587
          unsigned int const depth,
588
          tuple        const newTuple)
589
{
590

591
    unsigned int plane;
592
    sample minval, maxval;
593
    unsigned int i;
594

595
    for (plane = 0; plane < depth; ++plane) {
42,768✔
596
        minval = maxval = colorfreqtable.table[boxStart]->tuple[plane];
32,076✔
597

598
        for (i = 1; i < boxSize; ++i) {
732,150✔
599
            sample v = colorfreqtable.table[boxStart + i]->tuple[plane];
700,074✔
600
            minval = minval < v ? minval: v;
700,074✔
601
            maxval = maxval > v ? maxval: v;
700,074✔
602
        }
233,040✔
603
        newTuple[plane] = (minval + maxval) / 2;
32,076✔
604
    }
10,692✔
605
}
10,692✔
606

607

608

609
static void
610
averageColors(unsigned int const boxStart,
768✔
611
              unsigned int const boxSize,
612
              tupletable2  const colorfreqtable,
613
              unsigned int const depth,
614
              tuple        const newTuple)
615
{
616
    unsigned int plane;
617
    sample sum;
618
    unsigned int i;
619

620
    for (plane = 0; plane < depth; ++plane) {
3,072✔
621
        sum = 0;
2,304✔
622

623
        for (i = 0; i < boxSize; ++i) {
43,962✔
624
            sum += colorfreqtable.table[boxStart + i]->tuple[plane];
41,658✔
625
        }
13,878✔
626

627
        newTuple[plane] = sum / boxSize;
2,304✔
628
    }
768✔
629
}
768✔
630

631

632

633
static void
634
averagePixels(unsigned int const boxStart,
1,536✔
635
              unsigned int const boxSize,
636
              tupletable2 const colorfreqtable,
637
              unsigned int const depth,
638
              tuple const newTuple)
639
{
640

641
    unsigned int n;
642
        /* Number of tuples represented by the box */
643
    unsigned int plane;
644
    unsigned int i;
645

646
    /* Count the tuples in question */
647
    n = 0;  /* initial value */
1,536✔
648
    for (i = 0; i < boxSize; ++i) {
28,453✔
649
        n += (unsigned int)colorfreqtable.table[boxStart + i]->value;
26,917✔
650
    }
8,937✔
651

652
    for (plane = 0; plane < depth; ++plane) {
6,144✔
653
        sample sum;
654

655
        sum = 0;
4,608✔
656

657
        for (i = 0; i < boxSize; ++i) {
85,359✔
658
            sum += colorfreqtable.table[boxStart + i]->tuple[plane]
107,562✔
659
                * (unsigned int)colorfreqtable.table[boxStart + i]->value;
80,751✔
660
        }
26,811✔
661

662
        newTuple[plane] = sum / n;
4,608✔
663
    }
1,536✔
664
}
1,536✔
665

666

667

668
static tupletable2
669
colormapFromBv(unsigned int const newcolors,
66✔
670
               boxVector const bv,
671
               unsigned int const boxes,
672
               tupletable2 const colorfreqtable,
673
               unsigned int const depth,
674
               int const methodForRep,
675
               int const use_reversible,
676
               sixel_allocator_t *allocator)
677
{
678
    /*
679
    ** Ok, we've got enough boxes.  Now choose a representative color for
680
    ** each box.  There are a number of possible ways to make this choice.
681
    ** One would be to choose the center of the box; this ignores any structure
682
    ** within the boxes.  Another method would be to average all the colors in
683
    ** the box - this is the method specified in Heckbert's paper.  A third
684
    ** method is to average all the pixels in the box.
685
    */
686
    tupletable2 colormap;
687
    unsigned int bi;
688

689
    colormap = newColorMap(newcolors, depth, allocator);
66✔
690
    if (!colormap.size) {
66!
691
        return colormap;
×
692
    }
693

694
    for (bi = 0; bi < boxes; ++bi) {
13,062✔
695
        switch (methodForRep) {
12,996!
696
        case SIXEL_REP_CENTER_BOX:
7,128✔
697
            centerBox(bv[bi].ind, bv[bi].colors,
14,256✔
698
                      colorfreqtable, depth,
3,564✔
699
                      colormap.table[bi]->tuple);
10,692✔
700
            break;
10,692✔
701
        case SIXEL_REP_AVERAGE_COLORS:
512✔
702
            averageColors(bv[bi].ind, bv[bi].colors,
1,024✔
703
                          colorfreqtable, depth,
256✔
704
                          colormap.table[bi]->tuple);
768✔
705
            break;
768✔
706
        case SIXEL_REP_AVERAGE_PIXELS:
1,024✔
707
            averagePixels(bv[bi].ind, bv[bi].colors,
2,048✔
708
                          colorfreqtable, depth,
512✔
709
                          colormap.table[bi]->tuple);
1,536✔
710
            break;
1,536✔
711
        default:
712
            quant_trace(stderr, "Internal error: "
×
713
                                "invalid value of methodForRep: %d\n",
714
                        methodForRep);
715
        }
716
        if (use_reversible) {
12,996!
NEW
717
            sixel_quant_reversible_tuple(colormap.table[bi]->tuple,
×
718
                                         depth);
719
        }
720
    }
4,332✔
721
    return colormap;
66✔
722
}
22✔
723

724

725
static int
726
force_palette_compare(const void *lhs, const void *rhs)
×
727
{
728
    unsigned int left;
729
    unsigned int right;
730
    unsigned int left_value;
731
    unsigned int right_value;
732

733
    left = *(const unsigned int *)lhs;
×
734
    right = *(const unsigned int *)rhs;
×
735
    left_value = force_palette_source->table[left]->value;
×
736
    right_value = force_palette_source->table[right]->value;
×
737
    if (left_value > right_value) {
×
738
        return -1;
×
739
    }
740
    if (left_value < right_value) {
×
741
        return 1;
×
742
    }
743
    if (left < right) {
×
744
        return -1;
×
745
    }
746
    if (left > right) {
×
747
        return 1;
×
748
    }
749
    return 0;
×
750
}
751

752

753
static SIXELSTATUS
754
force_palette_completion(tupletable2 *colormapP,
×
755
                         unsigned int depth,
756
                         unsigned int reqColors,
757
                         tupletable2 const colorfreqtable,
758
                         sixel_allocator_t *allocator)
759
{
760
    /*
761
     * We enqueue "losers" from the histogram so that we can revive them:
762
     *
763
     *   histogram --> sort by hit count --> append to palette tail
764
     *        ^                             |
765
     *        +-----------------------------+
766
     *
767
     * The ASCII loop shows how discarded colors walk back into the
768
     * palette when the user demands an exact size.
769
     */
770
    SIXELSTATUS status = SIXEL_FALSE;
×
771
    tupletable new_table = NULL;
×
772
    unsigned int *order = NULL;
×
773
    unsigned int current;
774
    unsigned int fill;
775
    unsigned int candidate;
776
    unsigned int plane;
777
    unsigned int source;
778

779
    current = colormapP->size;
×
780
    if (current >= reqColors) {
×
781
        return SIXEL_OK;
×
782
    }
783

784
    status = alloctupletable(&new_table, depth, reqColors, allocator);
×
785
    if (SIXEL_FAILED(status)) {
×
786
        goto end;
×
787
    }
788

789
    if (colorfreqtable.size > 0U) {
×
790
        order = (unsigned int *)sixel_allocator_malloc(
×
791
            allocator, colorfreqtable.size * sizeof(unsigned int));
×
792
        if (order == NULL) {
×
793
            status = SIXEL_BAD_ALLOCATION;
×
794
            goto end;
×
795
        }
796
        for (candidate = 0; candidate < colorfreqtable.size; ++candidate) {
×
797
            order[candidate] = candidate;
×
798
        }
799
        force_palette_source = &colorfreqtable;
×
800
        qsort(order, colorfreqtable.size, sizeof(unsigned int),
×
801
              force_palette_compare);
802
        force_palette_source = NULL;
×
803
    }
804

805
    for (fill = 0; fill < current; ++fill) {
×
806
        new_table[fill]->value = colormapP->table[fill]->value;
×
807
        for (plane = 0; plane < depth; ++plane) {
×
808
            new_table[fill]->tuple[plane] =
×
809
                colormapP->table[fill]->tuple[plane];
×
810
        }
811
    }
812

813
    candidate = 0U;
×
814
    fill = current;
×
815
    if (order != NULL) {
×
816
        while (fill < reqColors && candidate < colorfreqtable.size) {
×
817
            unsigned int index;
818

819
            index = order[candidate];
×
820
            new_table[fill]->value = colorfreqtable.table[index]->value;
×
821
            for (plane = 0; plane < depth; ++plane) {
×
822
                new_table[fill]->tuple[plane] =
×
823
                    colorfreqtable.table[index]->tuple[plane];
×
824
            }
825
            ++fill;
×
826
            ++candidate;
×
827
        }
828
    }
829

830
    if (fill < reqColors && fill == 0U) {
×
831
        new_table[fill]->value = 0U;
×
832
        for (plane = 0; plane < depth; ++plane) {
×
833
            new_table[fill]->tuple[plane] = 0U;
×
834
        }
835
        ++fill;
×
836
    }
837

838
    source = 0U;
×
839
    while (fill < reqColors) {
×
840
        new_table[fill]->value = new_table[source]->value;
×
841
        for (plane = 0; plane < depth; ++plane) {
×
842
            new_table[fill]->tuple[plane] = new_table[source]->tuple[plane];
×
843
        }
844
        ++fill;
×
845
        ++source;
×
846
        if (source >= fill) {
×
847
            source = 0U;
×
848
        }
849
    }
850

851
    sixel_allocator_free(allocator, colormapP->table);
×
852
    colormapP->table = new_table;
×
853
    colormapP->size = reqColors;
×
854
    status = SIXEL_OK;
×
855

856
end:
857
    if (status != SIXEL_OK && new_table != NULL) {
×
858
        sixel_allocator_free(allocator, new_table);
×
859
    }
860
    if (order != NULL) {
×
861
        sixel_allocator_free(allocator, order);
×
862
    }
863
    return status;
×
864
}
865

866

867
static SIXELSTATUS
868
splitBox(boxVector const bv,
12,930✔
869
         unsigned int *const boxesP,
870
         unsigned int const bi,
871
         tupletable2 const colorfreqtable,
872
         unsigned int const depth,
873
         int const methodForLargest)
874
{
875
/*----------------------------------------------------------------------------
876
   Split Box 'bi' in the box vector bv (so that bv contains one more box
877
   than it did as input).  Split it so that each new box represents about
878
   half of the pixels in the distribution given by 'colorfreqtable' for
879
   the colors in the original box, but with distinct colors in each of the
880
   two new boxes.
881

882
   Assume the box contains at least two colors.
883
-----------------------------------------------------------------------------*/
884
    SIXELSTATUS status = SIXEL_FALSE;
12,930✔
885
    unsigned int const boxStart = bv[bi].ind;
12,930✔
886
    unsigned int const boxSize  = bv[bi].colors;
12,930✔
887
    unsigned int const sm       = bv[bi].sum;
12,930✔
888

889
    enum { max_depth= 16 };
890
    sample minval[max_depth];
891
    sample maxval[max_depth];
892

893
    /* assert(max_depth >= depth); */
894

895
    unsigned int largestDimension;
896
        /* number of the plane with the largest spread */
897
    unsigned int medianIndex;
898
    unsigned int lowersum;
899
        /* Number of pixels whose value is "less than" the median */
900

901
    findBoxBoundaries(colorfreqtable, depth, boxStart, boxSize,
17,240✔
902
                      minval, maxval);
4,310✔
903

904
    /* Find the largest dimension, and sort by that component.  I have
905
       included two methods for determining the "largest" dimension;
906
       first by simply comparing the range in RGB space, and second by
907
       transforming into luminosities before the comparison.
908
    */
909
    switch (methodForLargest) {
12,930!
910
    case SIXEL_LARGE_NORM:
8,110✔
911
        largestDimension = largestByNorm(minval, maxval, depth);
12,165✔
912
        break;
12,165✔
913
    case SIXEL_LARGE_LUM:
510✔
914
        largestDimension = largestByLuminosity(minval, maxval, depth);
765✔
915
        break;
765✔
916
    default:
917
        sixel_helper_set_additional_message(
×
918
            "Internal error: invalid value of methodForLargest.");
919
        status = SIXEL_LOGIC_ERROR;
×
920
        goto end;
×
921
    }
922

923
    /* TODO: I think this sort should go after creating a box,
924
       not before splitting.  Because you need the sort to use
925
       the SIXEL_REP_CENTER_BOX method of choosing a color to
926
       represent the final boxes
927
    */
928

929
    /* Set the gross global variable 'compareplanePlane' as a
930
       parameter to compareplane(), which is called by qsort().
931
    */
932
    compareplanePlane = largestDimension;
12,930✔
933
    qsort((char*) &colorfreqtable.table[boxStart], boxSize,
12,930✔
934
          sizeof(colorfreqtable.table[boxStart]),
935
          compareplane);
936

937
    {
938
        /* Now find the median based on the counts, so that about half
939
           the pixels (not colors, pixels) are in each subdivision.  */
940

941
        unsigned int i;
942

943
        lowersum = colorfreqtable.table[boxStart]->value; /* initial value */
12,930✔
944
        for (i = 1; i < boxSize - 1 && lowersum < sm / 2; ++i) {
873,221✔
945
            lowersum += colorfreqtable.table[boxStart + i]->value;
860,291✔
946
        }
286,433✔
947
        medianIndex = i;
12,930✔
948
    }
949
    /* Split the box, and sort to bring the biggest boxes to the top.  */
950

951
    bv[bi].colors = medianIndex;
12,930✔
952
    bv[bi].sum = lowersum;
12,930✔
953
    bv[*boxesP].ind = boxStart + medianIndex;
12,930✔
954
    bv[*boxesP].colors = boxSize - medianIndex;
12,930✔
955
    bv[*boxesP].sum = sm - lowersum;
12,930✔
956
    ++(*boxesP);
12,930✔
957
    qsort((char*) bv, *boxesP, sizeof(struct box), sumcompare);
12,930✔
958

959
    status = SIXEL_OK;
12,930✔
960

961
end:
8,620✔
962
    return status;
12,930✔
963
}
964

965

966

967
static SIXELSTATUS
968
mediancut(tupletable2 const colorfreqtable,
66✔
969
          unsigned int const depth,
970
          unsigned int const newcolors,
971
          int const methodForLargest,
972
          int const methodForRep,
973
          int const use_reversible,
974
          tupletable2 *const colormapP,
975
          sixel_allocator_t *allocator)
976
{
977
/*----------------------------------------------------------------------------
978
   Compute a set of only 'newcolors' colors that best represent an
979
   image whose pixels are summarized by the histogram
980
   'colorfreqtable'.  Each tuple in that table has depth 'depth'.
981
   colorfreqtable.table[i] tells the number of pixels in the subject image
982
   have a particular color.
983

984
   As a side effect, sort 'colorfreqtable'.
985
-----------------------------------------------------------------------------*/
986
    boxVector bv;
987
    unsigned int bi;
988
    unsigned int boxes;
989
    int multicolorBoxesExist;
990
    unsigned int i;
991
    unsigned int sum;
992
    SIXELSTATUS status = SIXEL_FALSE;
66✔
993

994
    sum = 0;
66✔
995

996
    for (i = 0; i < colorfreqtable.size; ++i) {
284,919✔
997
        sum += colorfreqtable.table[i]->value;
284,853✔
998
    }
94,807✔
999

1000
    /* There is at least one box that contains at least 2 colors; ergo,
1001
       there is more splitting we can do.  */
1002
    bv = newBoxVector(colorfreqtable.size, sum, newcolors, allocator);
66✔
1003
    if (bv == NULL) {
66!
1004
        goto end;
×
1005
    }
1006
    boxes = 1;
66✔
1007
    multicolorBoxesExist = (colorfreqtable.size > 1);
66✔
1008

1009
    /* Main loop: split boxes until we have enough. */
1010
    while (boxes < newcolors && multicolorBoxesExist) {
12,996!
1011
        /* Find the first splittable box. */
1012
        for (bi = 0; bi < boxes && bv[bi].colors < 2; ++bi)
65,222!
1013
            ;
1014
        if (bi >= boxes) {
12,930!
1015
            multicolorBoxesExist = 0;
×
1016
        } else {
1017
            status = splitBox(bv, &boxes, bi,
17,240✔
1018
                              colorfreqtable, depth,
4,310✔
1019
                              methodForLargest);
4,310✔
1020
            if (SIXEL_FAILED(status)) {
12,930!
1021
                goto end;
×
1022
            }
1023
        }
1024
    }
1025
    *colormapP = colormapFromBv(newcolors, bv, boxes,
88✔
1026
                                colorfreqtable, depth,
22✔
1027
                                methodForRep, use_reversible,
22✔
1028
                                allocator);
22✔
1029

1030
    sixel_allocator_free(allocator, bv);
66✔
1031

1032
    status = SIXEL_OK;
66✔
1033

1034
end:
44✔
1035
    return status;
66✔
1036
}
1037

1038

1039
static int histogram_lut_policy = SIXEL_LUT_POLICY_AUTO;
1040

1041
void
1042
sixel_quant_set_lut_policy(int lut_policy)
537✔
1043
{
1044
    int normalized;
1045

1046
    normalized = SIXEL_LUT_POLICY_AUTO;
537✔
1047
    if (lut_policy == SIXEL_LUT_POLICY_5BIT
766!
1048
        || lut_policy == SIXEL_LUT_POLICY_6BIT
537!
1049
        || lut_policy == SIXEL_LUT_POLICY_ROBINHOOD
537!
1050
        || lut_policy == SIXEL_LUT_POLICY_HOPSCOTCH) {
537!
1051
        normalized = lut_policy;
×
1052
    }
1053

1054
    histogram_lut_policy = normalized;
537✔
1055
}
537✔
1056

1057
struct histogram_control {
1058
    unsigned int channel_shift;
1059
    unsigned int channel_bits;
1060
    unsigned int channel_mask;
1061
    int reversible_rounding;         /* nonzero biases quantization upward */
1062
};
1063

1064
static struct histogram_control
1065
histogram_control_make(unsigned int depth);
1066
static struct histogram_control
1067
histogram_control_make_for_policy(unsigned int depth, int lut_policy);
1068
static size_t histogram_dense_size(unsigned int depth,
1069
                                   struct histogram_control const
1070
                                       *control);
1071
static unsigned int histogram_reconstruct(unsigned int quantized,
1072
                                          struct histogram_control const
1073
                                              *control);
1074

1075
static size_t
1076
histogram_dense_size(unsigned int depth,
519✔
1077
                     struct histogram_control const *control)
1078
{
1079
    size_t size;
1080
    unsigned int exponent;
1081
    unsigned int i;
1082

1083
    size = 1U;
519✔
1084
    exponent = depth * control->channel_bits;
519✔
1085
    for (i = 0U; i < exponent; ++i) {
9,861✔
1086
        if (size > SIZE_MAX / 2U) {
9,342!
1087
            size = SIZE_MAX;
×
1088
            break;
×
1089
        }
1090
        size <<= 1U;
9,342✔
1091
    }
4,014✔
1092

1093
    return size;
519✔
1094
}
1095

1096
static struct histogram_control
1097
histogram_control_make_for_policy(unsigned int depth, int lut_policy)
23,025,805✔
1098
{
1099
    struct histogram_control control;
1100

1101
    control.reversible_rounding = 0;
23,025,805✔
1102
    /*
1103
     * The ASCII ladder below shows how each policy selects bucket width.
1104
     *
1105
     *   auto / 6bit RGB : |--6--|
1106
     *   forced 5bit     : |---5---|
1107
     *   robinhood       : |------8------|
1108
     *   alpha fallback  : |---5---|  (avoids 2^(6*4) buckets)
1109
     */
1110
    control.channel_shift = 2U;
23,025,805✔
1111
    if (depth > 3U) {
23,025,805!
1112
        control.channel_shift = 3U;
×
1113
    }
1114
    if (lut_policy == SIXEL_LUT_POLICY_5BIT) {
23,025,805!
1115
        control.channel_shift = 3U;
×
1116
    } else if (lut_policy == SIXEL_LUT_POLICY_6BIT) {
23,025,805✔
1117
        control.channel_shift = 2U;
263✔
1118
        if (depth > 3U) {
263!
1119
            control.channel_shift = 3U;
×
1120
        }
1121
    } else if (lut_policy == SIXEL_LUT_POLICY_ROBINHOOD
23,025,649!
1122
               || lut_policy == SIXEL_LUT_POLICY_HOPSCOTCH) {
23,025,542!
1123
        control.channel_shift = 0U;
×
1124
    }
1125
    control.channel_bits = 8U - control.channel_shift;
23,025,805✔
1126
    control.channel_mask = (1U << control.channel_bits) - 1U;
23,025,805✔
1127

1128
    return control;
23,025,805✔
1129
}
1130

1131
static unsigned int
1132
histogram_reconstruct(unsigned int quantized,
862,857✔
1133
                      struct histogram_control const *control)
1134
{
1135
    unsigned int value;
1136

1137
    value = quantized << control->channel_shift;
862,857✔
1138
    if (quantized == control->channel_mask) {
862,857✔
1139
        value = 255U;
424✔
1140
    } else {
226✔
1141
        if (control->channel_shift > 0U) {
862,433!
1142
            value |= (1U << (control->channel_shift - 1U));
862,433✔
1143
        }
287,675✔
1144
    }
1145
    if (value > 255U) {
862,857!
1146
        value = 255U;
×
1147
    }
1148

1149
    return value;
862,857✔
1150
}
1151

1152
static unsigned int
1153
histogram_quantize(unsigned int sample8,
79,571,784✔
1154
                   struct histogram_control const *control)
1155
{
1156
    unsigned int quantized;
1157
    unsigned int shift;
1158
    unsigned int mask;
1159
    unsigned int rounding;
1160

1161
    /*
1162
     * In reversible mode we already rounded once when snapping to
1163
     * sixel_reversible_tones[].  If we rounded to the nearest midpoint
1164
     * again, the second pass would fall back to the lower bucket and break
1165
     * the round-trip.  Biasing towards the upper edge keeps the bucket
1166
     * stable after decoding and encoding again.  Non-reversible runs keep
1167
     * symmetric midpoints to avoid nudging colors upwards.
1168
     *
1169
     *        midpoint bias        upper-edge bias
1170
     *   |----*----|----*----|    |----|----*----|
1171
     *   0         8        16    0    8        16
1172
     *
1173
     * The asterisk marks the midpoint captured by a bucket.  Moving that
1174
     * midpoint to the upper edge keeps reversible tones from drifting.
1175
     */
1176
    shift = control->channel_shift;
79,571,784✔
1177
    mask = control->channel_mask;
79,571,784✔
1178
    if (shift == 0U) {
79,571,784!
1179
        quantized = sample8;
×
1180
    } else {
1181
        if (control->reversible_rounding) {
79,571,784!
NEW
1182
            rounding = 1U << shift;
×
1183
        } else {
1184
            rounding = 1U << (shift - 1U);
79,571,784✔
1185
        }
1186
        quantized = (sample8 + rounding) >> shift;
79,571,784✔
1187
        if (quantized > mask) {
79,571,784✔
1188
            quantized = mask;
1,028,383✔
1189
        }
350,179✔
1190
    }
1191

1192
    return quantized;
79,571,784✔
1193
}
1194

1195
static uint32_t
1196
histogram_pack_color(unsigned char const *data,
26,523,928✔
1197
                     unsigned int const depth,
1198
                     struct histogram_control const *control)
1199
{
1200
    uint32_t packed;
1201
    unsigned int n;
1202
    unsigned int sample8;
1203
    unsigned int bits;
1204

1205
    packed = 0U;
26,523,928✔
1206
    bits = control->channel_bits;
26,523,928✔
1207
    if (control->channel_shift == 0U) {
26,523,928!
1208
        /*
1209
         * The channel shift being zero means each component keeps eight
1210
         * bits.  We therefore pack pixels in RGB order, as illustrated
1211
         * below:
1212
         *
1213
         *      R   G   B
1214
         *     [ ]-[ ]-[ ]
1215
         *      |   |   |
1216
         *      v   v   v
1217
         *     0xRRGGBB
1218
         */
1219
        for (n = 0U; n < depth; ++n) {
×
1220
            packed |= (uint32_t)data[depth - 1U - n] << (n * bits);
×
1221
        }
1222
        return packed;
×
1223
    }
1224

1225
    for (n = 0U; n < depth; ++n) {
106,095,712✔
1226
        sample8 = (unsigned int)data[depth - 1U - n];
79,571,784✔
1227
        packed |= histogram_quantize(sample8, control) << (n * bits);
79,571,784✔
1228
    }
36,659,328✔
1229

1230
    return packed;
26,523,928✔
1231
}
12,219,776✔
1232

1233
static uint32_t
1234
histogram_hash_mix(uint32_t key)
23,025,286✔
1235
{
1236
    uint32_t hash;
1237

1238
    /*
1239
     * Multiplicative mixing with two avalanching rounds keeps nearby
1240
     * colors far apart in the hash domain.  The final tweak avoids the
1241
     * 0xffffffff sentinel used by hopscotch slots.
1242
     */
1243
    hash = key * 0x9e3779b9U;
23,025,286✔
1244
    hash ^= hash >> 16;
23,025,286✔
1245
    hash *= 0x7feb352dU;
23,025,286✔
1246
    hash ^= hash >> 15;
23,025,286✔
1247
    hash *= 0x846ca68bU;
23,025,286✔
1248
    hash ^= hash >> 16;
23,025,286✔
1249
    if (hash == 0xffffffffU) {
23,025,286!
1250
        hash ^= 0x632be59bU;
×
1251
    }
1252

1253
    return hash;
23,025,286✔
1254
}
1255

1256
static unsigned int
1257
computeHash(unsigned char const *data,
23,025,286✔
1258
            unsigned int const depth,
1259
            struct histogram_control const *control)
1260
{
1261
    uint32_t packed;
1262

1263
    packed = histogram_pack_color(data, depth, control);
23,025,286✔
1264

1265
    return histogram_hash_mix(packed);
23,025,286✔
1266
}
1267

1268
#define CUCKOO_BUCKET_SIZE 4U
1269
#define CUCKOO_MAX_KICKS 128U
1270
#define CUCKOO_STASH_SIZE 32U
1271
#define CUCKOO_EMPTY_KEY 0xffffffffU
1272

1273
struct cuckoo_bucket32 {
1274
    uint32_t key[CUCKOO_BUCKET_SIZE];
1275
    uint32_t value[CUCKOO_BUCKET_SIZE];
1276
};
1277

1278
struct cuckoo_table32 {
1279
    struct cuckoo_bucket32 *buckets;
1280
    uint32_t stash_key[CUCKOO_STASH_SIZE];
1281
    uint32_t stash_value[CUCKOO_STASH_SIZE];
1282
    size_t bucket_count;
1283
    size_t bucket_mask;
1284
    size_t stash_count;
1285
    size_t entry_count;
1286
    sixel_allocator_t *allocator;
1287
};
1288

1289
static size_t cuckoo_round_buckets(size_t hint);
1290
static size_t cuckoo_hash_primary(uint32_t key, size_t mask);
1291
static size_t cuckoo_hash_secondary(uint32_t key, size_t mask);
1292
static size_t cuckoo_hash_alternate(uint32_t key,
1293
                                    size_t bucket,
1294
                                    size_t mask);
1295
static uint32_t *cuckoo_bucket_find(struct cuckoo_bucket32 *bucket,
1296
                                    uint32_t key);
1297
static int cuckoo_bucket_insert_direct(struct cuckoo_bucket32 *bucket,
1298
                                       uint32_t key,
1299
                                       uint32_t value);
1300
static SIXELSTATUS cuckoo_table32_init(struct cuckoo_table32 *table,
1301
                                       size_t expected,
1302
                                       sixel_allocator_t *allocator);
1303
static void cuckoo_table32_clear(struct cuckoo_table32 *table);
1304
static void cuckoo_table32_fini(struct cuckoo_table32 *table);
1305
static uint32_t *cuckoo_table32_lookup(struct cuckoo_table32 *table,
1306
                                       uint32_t key);
1307
static SIXELSTATUS cuckoo_table32_insert(struct cuckoo_table32 *table,
1308
                                         uint32_t key,
1309
                                         uint32_t value);
1310
static SIXELSTATUS cuckoo_table32_grow(struct cuckoo_table32 *table);
1311

1312
struct robinhood_slot32 {
1313
    uint32_t key;
1314
    uint32_t color;
1315
    uint32_t value;
1316
    uint16_t distance;
1317
    uint16_t pad;
1318
};
1319

1320
struct robinhood_table32 {
1321
    struct robinhood_slot32 *slots;
1322
    size_t capacity;
1323
    size_t count;
1324
    sixel_allocator_t *allocator;
1325
};
1326

1327
static size_t robinhood_round_capacity(size_t hint);
1328
static SIXELSTATUS robinhood_table32_init(struct robinhood_table32 *table,
1329
                                         size_t expected,
1330
                                         sixel_allocator_t *allocator);
1331
static void robinhood_table32_fini(struct robinhood_table32 *table);
1332
static struct robinhood_slot32 *
1333
robinhood_table32_lookup(struct robinhood_table32 *table,
1334
                         uint32_t key,
1335
                         uint32_t color);
1336
static SIXELSTATUS robinhood_table32_insert(struct robinhood_table32 *table,
1337
                                           uint32_t key,
1338
                                           uint32_t color,
1339
                                           uint32_t value);
1340
static SIXELSTATUS robinhood_table32_grow(struct robinhood_table32 *table);
1341
static struct robinhood_slot32 *
1342
robinhood_table32_place(struct robinhood_table32 *table,
1343
                        struct robinhood_slot32 entry);
1344

1345
#define HOPSCOTCH_EMPTY_KEY 0xffffffffU
1346
#define HOPSCOTCH_DEFAULT_NEIGHBORHOOD 32U
1347
#define HOPSCOTCH_INSERT_RANGE 256U
1348

1349
struct hopscotch_slot32 {
1350
    uint32_t key;
1351
    uint32_t color;
1352
    uint32_t value;
1353
};
1354

1355
struct hopscotch_table32 {
1356
    struct hopscotch_slot32 *slots;
1357
    uint32_t *hopinfo;
1358
    size_t capacity;
1359
    size_t count;
1360
    size_t neighborhood;
1361
    sixel_allocator_t *allocator;
1362
};
1363

1364
static SIXELSTATUS hopscotch_table32_init(struct hopscotch_table32 *table,
1365
                                          size_t expected,
1366
                                          sixel_allocator_t *allocator);
1367
static void hopscotch_table32_fini(struct hopscotch_table32 *table);
1368
static struct hopscotch_slot32 *
1369
hopscotch_table32_lookup(struct hopscotch_table32 *table,
1370
                         uint32_t key,
1371
                         uint32_t color);
1372
static SIXELSTATUS hopscotch_table32_insert(struct hopscotch_table32 *table,
1373
                                            uint32_t key,
1374
                                            uint32_t color,
1375
                                            uint32_t value);
1376
static SIXELSTATUS hopscotch_table32_grow(struct hopscotch_table32 *table);
1377

1378
static struct histogram_control
1379
histogram_control_make(unsigned int depth)
23,025,542✔
1380
{
1381
    struct histogram_control control;
1382

1383
    control = histogram_control_make_for_policy(depth,
33,505,140✔
1384
                                                histogram_lut_policy);
10,479,598✔
1385

1386
    return control;
23,025,542✔
1387
}
1388

1389
static size_t
1390
robinhood_round_capacity(size_t hint)
×
1391
{
1392
    size_t capacity;
1393

1394
    capacity = 16U;
×
1395
    if (hint < capacity) {
×
1396
        return capacity;
×
1397
    }
1398

1399
    capacity = hint - 1U;
×
1400
    capacity |= capacity >> 1;
×
1401
    capacity |= capacity >> 2;
×
1402
    capacity |= capacity >> 4;
×
1403
    capacity |= capacity >> 8;
×
1404
    capacity |= capacity >> 16;
×
1405
#if SIZE_MAX > UINT32_MAX
1406
    capacity |= capacity >> 32;
×
1407
#endif
1408
    if (capacity == SIZE_MAX) {
×
1409
        return SIZE_MAX;
×
1410
    }
1411
    capacity++;
×
1412
    if (capacity < 16U) {
×
1413
        capacity = 16U;
×
1414
    }
1415

1416
    return capacity;
×
1417
}
1418

1419
static SIXELSTATUS
1420
robinhood_table32_init(struct robinhood_table32 *table,
×
1421
                       size_t expected,
1422
                       sixel_allocator_t *allocator)
1423
{
1424
    size_t hint;
1425
    size_t capacity;
1426

1427
    table->slots = NULL;
×
1428
    table->capacity = 0U;
×
1429
    table->count = 0U;
×
1430
    table->allocator = allocator;
×
1431

1432
    if (expected < 16U) {
×
1433
        expected = 16U;
×
1434
    }
1435
    if (expected > SIZE_MAX / 2U) {
×
1436
        hint = SIZE_MAX / 2U;
×
1437
    } else {
1438
        hint = expected * 2U;
×
1439
    }
1440
    capacity = robinhood_round_capacity(hint);
×
1441
    if (capacity == SIZE_MAX && hint != SIZE_MAX) {
×
1442
        return SIXEL_BAD_ALLOCATION;
×
1443
    }
1444

1445
    table->slots = (struct robinhood_slot32 *)
×
1446
        sixel_allocator_calloc(allocator,
×
1447
                               capacity,
1448
                               sizeof(struct robinhood_slot32));
1449
    if (table->slots == NULL) {
×
1450
        table->capacity = 0U;
×
1451
        table->count = 0U;
×
1452
        return SIXEL_BAD_ALLOCATION;
×
1453
    }
1454
    table->capacity = capacity;
×
1455
    table->count = 0U;
×
1456

1457
    return SIXEL_OK;
×
1458
}
1459

1460
static void
1461
robinhood_table32_fini(struct robinhood_table32 *table)
×
1462
{
1463
    if (table->slots != NULL) {
×
1464
        sixel_allocator_free(table->allocator, table->slots);
×
1465
        table->slots = NULL;
×
1466
    }
1467
    table->capacity = 0U;
×
1468
    table->count = 0U;
×
1469
    table->allocator = NULL;
×
1470
}
×
1471

1472
static struct robinhood_slot32 *
1473
robinhood_table32_lookup(struct robinhood_table32 *table,
×
1474
                         uint32_t key,
1475
                         uint32_t color)
1476
{
1477
    size_t mask;
1478
    size_t index;
1479
    uint16_t distance;
1480
    struct robinhood_slot32 *slot;
1481

1482
    if (table->capacity == 0U || table->slots == NULL) {
×
1483
        return NULL;
×
1484
    }
1485

1486
    mask = table->capacity - 1U;
×
1487
    index = (size_t)(key & mask);
×
1488
    distance = 0U;
×
1489

1490
    for (;;) {
1491
        slot = &table->slots[index];
×
1492
        if (slot->value == 0U) {
×
1493
            return NULL;
×
1494
        }
1495
        if (slot->key == key && slot->color == color) {
×
1496
            return slot;
×
1497
        }
1498
        if (slot->distance < distance) {
×
1499
            return NULL;
×
1500
        }
1501
        index = (index + 1U) & mask;
×
1502
        distance++;
×
1503
    }
1504
}
1505

1506
static struct robinhood_slot32 *
1507
robinhood_table32_place(struct robinhood_table32 *table,
×
1508
                        struct robinhood_slot32 entry)
1509
{
1510
    size_t mask;
1511
    size_t index;
1512
    struct robinhood_slot32 *slot;
1513
    struct robinhood_slot32 tmp;
1514

1515
    mask = table->capacity - 1U;
×
1516
    index = (size_t)(entry.key & mask);
×
1517

1518
    for (;;) {
1519
        slot = &table->slots[index];
×
1520
        if (slot->value == 0U) {
×
1521
            *slot = entry;
×
1522
            table->count++;
×
1523
            return slot;
×
1524
        }
1525
        if (slot->key == entry.key && slot->color == entry.color) {
×
1526
            slot->value = entry.value;
×
1527
            return slot;
×
1528
        }
1529
        if (slot->distance < entry.distance) {
×
1530
            tmp = *slot;
×
1531
            *slot = entry;
×
1532
            entry = tmp;
×
1533
        }
1534
        index = (index + 1U) & mask;
×
1535
        entry.distance++;
×
1536
    }
1537
}
1538

1539
static SIXELSTATUS
1540
robinhood_table32_grow(struct robinhood_table32 *table)
×
1541
{
1542
    struct robinhood_slot32 *old_slots;
1543
    size_t old_capacity;
1544
    size_t new_capacity;
1545
    size_t i;
1546

1547
    if (table->allocator == NULL) {
×
1548
        return SIXEL_BAD_ALLOCATION;
×
1549
    }
1550
    if (table->capacity == 0U) {
×
1551
        new_capacity = 16U;
×
1552
    } else {
1553
        if (table->capacity > SIZE_MAX / 2U) {
×
1554
            return SIXEL_BAD_ALLOCATION;
×
1555
        }
1556
        new_capacity = table->capacity << 1U;
×
1557
    }
1558
    new_capacity = robinhood_round_capacity(new_capacity);
×
1559
    if (new_capacity <= table->capacity) {
×
1560
        return SIXEL_BAD_ALLOCATION;
×
1561
    }
1562

1563
    old_slots = table->slots;
×
1564
    old_capacity = table->capacity;
×
1565
    table->slots = (struct robinhood_slot32 *)
×
1566
        sixel_allocator_calloc(table->allocator,
×
1567
                               new_capacity,
1568
                               sizeof(struct robinhood_slot32));
1569
    if (table->slots == NULL) {
×
1570
        table->slots = old_slots;
×
1571
        table->capacity = old_capacity;
×
1572
        return SIXEL_BAD_ALLOCATION;
×
1573
    }
1574
    table->capacity = new_capacity;
×
1575
    table->count = 0U;
×
1576

1577
    for (i = 0U; i < old_capacity; ++i) {
×
1578
        struct robinhood_slot32 entry;
1579

1580
        if (old_slots[i].value == 0U) {
×
1581
            continue;
×
1582
        }
1583
        entry.key = old_slots[i].key;
×
1584
        entry.color = old_slots[i].color;
×
1585
        entry.value = old_slots[i].value;
×
1586
        entry.distance = 0U;
×
1587
        entry.pad = 0U;  /* ensure padding bytes are initialized */
×
1588
        (void)robinhood_table32_place(table, entry);
×
1589
    }
1590

1591
    sixel_allocator_free(table->allocator, old_slots);
×
1592

1593
    return SIXEL_OK;
×
1594
}
1595

1596
static SIXELSTATUS
1597
robinhood_table32_insert(struct robinhood_table32 *table,
×
1598
                         uint32_t key,
1599
                         uint32_t color,
1600
                         uint32_t value)
1601
{
1602
    SIXELSTATUS status;
1603
    struct robinhood_slot32 entry;
1604

1605
    if (table->slots == NULL || table->capacity == 0U) {
×
1606
        return SIXEL_BAD_ARGUMENT;
×
1607
    }
1608
    if (table->count * 2U >= table->capacity) {
×
1609
        status = robinhood_table32_grow(table);
×
1610
        if (SIXEL_FAILED(status)) {
×
1611
            return status;
×
1612
        }
1613
    }
1614

1615
    entry.key = key;
×
1616
    entry.color = color;
×
1617
    entry.value = value;
×
1618
    entry.distance = 0U;
×
1619
    entry.pad = 0U;  /* ensure padding bytes are initialized */
×
1620
    (void)robinhood_table32_place(table, entry);
×
1621

1622
    return SIXEL_OK;
×
1623
}
1624

1625
static SIXELSTATUS
1626
hopscotch_table32_init(struct hopscotch_table32 *table,
×
1627
                       size_t expected,
1628
                       sixel_allocator_t *allocator)
1629
{
1630
    size_t hint;
1631
    size_t capacity;
1632
    size_t i;
1633

1634
    if (table == NULL) {
×
1635
        return SIXEL_BAD_ARGUMENT;
×
1636
    }
1637

1638
    table->slots = NULL;
×
1639
    table->hopinfo = NULL;
×
1640
    table->capacity = 0U;
×
1641
    table->count = 0U;
×
1642
    table->neighborhood = HOPSCOTCH_DEFAULT_NEIGHBORHOOD;
×
1643
    table->allocator = allocator;
×
1644

1645
    if (expected < 16U) {
×
1646
        expected = 16U;
×
1647
    }
1648
    if (expected > SIZE_MAX / 2U) {
×
1649
        hint = SIZE_MAX / 2U;
×
1650
    } else {
1651
        hint = expected * 2U;
×
1652
    }
1653
    capacity = robinhood_round_capacity(hint);
×
1654
    if (capacity == SIZE_MAX && hint != SIZE_MAX) {
×
1655
        return SIXEL_BAD_ALLOCATION;
×
1656
    }
1657
    if (capacity < table->neighborhood) {
×
1658
        capacity = table->neighborhood;
×
1659
    }
1660

1661
    table->slots = (struct hopscotch_slot32 *)
×
1662
        sixel_allocator_malloc(allocator,
×
1663
                               capacity * sizeof(struct hopscotch_slot32));
1664
    if (table->slots == NULL) {
×
1665
        return SIXEL_BAD_ALLOCATION;
×
1666
    }
1667
    table->hopinfo = (uint32_t *)
×
1668
        sixel_allocator_calloc(allocator,
×
1669
                               capacity,
1670
                               sizeof(uint32_t));
1671
    if (table->hopinfo == NULL) {
×
1672
        sixel_allocator_free(allocator, table->slots);
×
1673
        table->slots = NULL;
×
1674
        return SIXEL_BAD_ALLOCATION;
×
1675
    }
1676

1677
    for (i = 0U; i < capacity; ++i) {
×
1678
        table->slots[i].key = HOPSCOTCH_EMPTY_KEY;
×
1679
        table->slots[i].color = 0U;
×
1680
        table->slots[i].value = 0U;
×
1681
    }
1682
    table->capacity = capacity;
×
1683
    table->count = 0U;
×
1684
    if (table->neighborhood > 32U) {
×
1685
        table->neighborhood = 32U;
×
1686
    }
1687
    if (table->neighborhood > table->capacity) {
×
1688
        table->neighborhood = table->capacity;
×
1689
    }
1690

1691
    return SIXEL_OK;
×
1692
}
1693

1694
static void
1695
hopscotch_table32_fini(struct hopscotch_table32 *table)
×
1696
{
1697
    sixel_allocator_t *allocator;
1698

1699
    if (table == NULL) {
×
1700
        return;
×
1701
    }
1702

1703
    allocator = table->allocator;
×
1704
    if (allocator != NULL) {
×
1705
        if (table->slots != NULL) {
×
1706
            sixel_allocator_free(allocator, table->slots);
×
1707
        }
1708
        if (table->hopinfo != NULL) {
×
1709
            sixel_allocator_free(allocator, table->hopinfo);
×
1710
        }
1711
    }
1712
    table->slots = NULL;
×
1713
    table->hopinfo = NULL;
×
1714
    table->capacity = 0U;
×
1715
    table->count = 0U;
×
1716
}
1717

1718
static struct hopscotch_slot32 *
1719
hopscotch_table32_lookup(struct hopscotch_table32 *table,
×
1720
                         uint32_t key,
1721
                         uint32_t color)
1722
{
1723
    size_t index;
1724
    size_t bit;
1725
    size_t candidate;
1726
    uint32_t hop;
1727
    size_t mask;
1728
    size_t neighborhood;
1729

1730
    if (table == NULL || table->slots == NULL || table->hopinfo == NULL) {
×
1731
        return NULL;
×
1732
    }
1733
    if (table->capacity == 0U) {
×
1734
        return NULL;
×
1735
    }
1736

1737
    mask = table->capacity - 1U;
×
1738
    index = (size_t)key & mask;
×
1739
    hop = table->hopinfo[index];
×
1740
    neighborhood = table->neighborhood;
×
1741
    for (bit = 0U; bit < neighborhood; ++bit) {
×
1742
        if ((hop & (1U << bit)) == 0U) {
×
1743
            continue;
×
1744
        }
1745
        candidate = (index + bit) & mask;
×
1746
        if (table->slots[candidate].key == key
×
1747
            && table->slots[candidate].color == color) {
×
1748
            return &table->slots[candidate];
×
1749
        }
1750
    }
1751

1752
    return NULL;
×
1753
}
1754

1755
static SIXELSTATUS
1756
hopscotch_table32_grow(struct hopscotch_table32 *table)
×
1757
{
1758
    SIXELSTATUS status;
1759
    struct hopscotch_table32 tmp;
1760
    size_t i;
1761
    struct hopscotch_slot32 *slot;
1762

1763
    tmp.slots = NULL;
×
1764
    tmp.hopinfo = NULL;
×
1765
    tmp.capacity = 0U;
×
1766
    tmp.count = 0U;
×
1767
    tmp.neighborhood = table->neighborhood;
×
1768
    tmp.allocator = table->allocator;
×
1769

1770
    status = hopscotch_table32_init(&tmp,
×
1771
                                    table->capacity * 2U,
×
1772
                                    table->allocator);
1773
    if (SIXEL_FAILED(status)) {
×
1774
        return status;
×
1775
    }
1776
    if (tmp.neighborhood > table->neighborhood) {
×
1777
        tmp.neighborhood = table->neighborhood;
×
1778
    }
1779

1780
    for (i = 0U; i < table->capacity; ++i) {
×
1781
        slot = &table->slots[i];
×
1782
        if (slot->key == HOPSCOTCH_EMPTY_KEY) {
×
1783
            continue;
×
1784
        }
1785
        status = hopscotch_table32_insert(&tmp,
×
1786
                                          slot->key,
1787
                                          slot->color,
1788
                                          slot->value);
1789
        if (SIXEL_FAILED(status)) {
×
1790
            hopscotch_table32_fini(&tmp);
×
1791
            return status;
×
1792
        }
1793
    }
1794

1795
    hopscotch_table32_fini(table);
×
1796

1797
    table->slots = tmp.slots;
×
1798
    table->hopinfo = tmp.hopinfo;
×
1799
    table->capacity = tmp.capacity;
×
1800
    table->count = tmp.count;
×
1801
    table->neighborhood = tmp.neighborhood;
×
1802
    table->allocator = tmp.allocator;
×
1803

1804
    return SIXEL_OK;
×
1805
}
1806

1807
static SIXELSTATUS
1808
hopscotch_table32_insert(struct hopscotch_table32 *table,
×
1809
                         uint32_t key,
1810
                         uint32_t color,
1811
                         uint32_t value)
1812
{
1813
    SIXELSTATUS status;
1814
    struct hopscotch_slot32 *slot;
1815
    size_t index;
1816
    size_t mask;
1817
    size_t distance;
1818
    size_t attempts;
1819
    size_t free_index;
1820
    size_t neighborhood;
1821
    int relocated;
1822
    size_t offset;
1823
    uint32_t hop;
1824
    size_t bit;
1825
    size_t move_index;
1826
    struct hopscotch_slot32 tmp_slot;
1827

1828
    if (table == NULL || table->slots == NULL || table->hopinfo == NULL) {
×
1829
        return SIXEL_BAD_ARGUMENT;
×
1830
    }
1831
    if (table->capacity == 0U) {
×
1832
        return SIXEL_BAD_ARGUMENT;
×
1833
    }
1834

1835
    slot = hopscotch_table32_lookup(table, key, color);
×
1836
    if (slot != NULL) {
×
1837
        slot->value = value;
×
1838
        return SIXEL_OK;
×
1839
    }
1840

1841
    if (table->count * 2U >= table->capacity) {
×
1842
        status = hopscotch_table32_grow(table);
×
1843
        if (SIXEL_FAILED(status)) {
×
1844
            return status;
×
1845
        }
1846
        return hopscotch_table32_insert(table, key, color, value);
×
1847
    }
1848

1849
    mask = table->capacity - 1U;
×
1850
    neighborhood = table->neighborhood;
×
1851
    index = (size_t)key & mask;
×
1852
    slot = NULL;
×
1853
    free_index = index;
×
1854
    distance = 0U;
×
1855
    for (attempts = 0U; attempts < HOPSCOTCH_INSERT_RANGE; ++attempts) {
×
1856
        free_index = (index + attempts) & mask;
×
1857
        slot = &table->slots[free_index];
×
1858
        if (slot->key == HOPSCOTCH_EMPTY_KEY) {
×
1859
            distance = attempts;
×
1860
            break;
×
1861
        }
1862
    }
1863
    if (slot == NULL || slot->key != HOPSCOTCH_EMPTY_KEY) {
×
1864
        status = hopscotch_table32_grow(table);
×
1865
        if (SIXEL_FAILED(status)) {
×
1866
            return status;
×
1867
        }
1868
        return hopscotch_table32_insert(table, key, color, value);
×
1869
    }
1870

1871
    /*
1872
     * Relocation diagram:
1873
     *
1874
     *   free slot <--- hop window <--- [base bucket]
1875
     *      ^ move resident outward until distance < neighborhood
1876
     */
1877
    while (distance >= neighborhood) {
×
1878
        relocated = 0;
×
1879
        for (offset = neighborhood - 1U; offset > 0U; --offset) {
×
1880
            size_t base;
1881

1882
            base = (free_index + table->capacity - offset) & mask;
×
1883
            hop = table->hopinfo[base];
×
1884
            if (hop == 0U) {
×
1885
                continue;
×
1886
            }
1887
            for (bit = 0U; bit < offset; ++bit) {
×
1888
                if ((hop & (1U << bit)) == 0U) {
×
1889
                    continue;
×
1890
                }
1891
                move_index = (base + bit) & mask;
×
1892
                tmp_slot = table->slots[move_index];
×
1893
                table->slots[free_index] = tmp_slot;
×
1894
                table->slots[move_index].key = HOPSCOTCH_EMPTY_KEY;
×
1895
                table->slots[move_index].color = 0U;
×
1896
                table->slots[move_index].value = 0U;
×
1897
                table->hopinfo[base] &= (uint32_t)~(1U << bit);
×
1898
                table->hopinfo[base] |= (uint32_t)(1U << offset);
×
1899
                free_index = move_index;
×
1900
                if (free_index >= index) {
×
1901
                    distance = free_index - index;
×
1902
                } else {
1903
                    distance = free_index + table->capacity - index;
×
1904
                }
1905
                relocated = 1;
×
1906
                break;
×
1907
            }
1908
            if (relocated) {
×
1909
                break;
×
1910
            }
1911
        }
1912
        if (!relocated) {
×
1913
            status = hopscotch_table32_grow(table);
×
1914
            if (SIXEL_FAILED(status)) {
×
1915
                return status;
×
1916
            }
1917
            return hopscotch_table32_insert(table, key, color, value);
×
1918
        }
1919
    }
1920

1921
    if (distance >= 32U) {
×
1922
        status = hopscotch_table32_grow(table);
×
1923
        if (SIXEL_FAILED(status)) {
×
1924
            return status;
×
1925
        }
1926
        return hopscotch_table32_insert(table, key, color, value);
×
1927
    }
1928

1929
    table->slots[free_index].key = key;
×
1930
    table->slots[free_index].color = color;
×
1931
    table->slots[free_index].value = value;
×
1932
    table->hopinfo[index] |= (uint32_t)(1U << distance);
×
1933
    table->count++;
×
1934

1935
    return SIXEL_OK;
×
1936
}
1937

1938
/*
1939
 * The cuckoo hash backend stores entries in fixed-width buckets.
1940
 *
1941
 *   [bucket 0] -> key0 key1 key2 key3
1942
 *                 val0 val1 val2 val3
1943
 *   [bucket 1] -> ...
1944
 *
1945
 * Each key is compared against the 128-bit lane using SIMD instructions.
1946
 * Two hash functions map a key to its primary and secondary buckets.  When
1947
 * both are full, the eviction loop "kicks" an entry toward its alternate
1948
 * bucket, as illustrated below:
1949
 *
1950
 *   bucket A --kick--> bucket B --kick--> bucket A ...
1951
 *
1952
 * A tiny stash buffers entries when the table momentarily fills up.  This
1953
 * keeps lookups fast while letting us grow the table lazily.
1954
 */
1955
static size_t
1956
cuckoo_round_buckets(size_t hint)
263✔
1957
{
1958
    size_t desired;
1959
    size_t buckets;
1960
    size_t prev;
1961

1962
    if (hint < CUCKOO_BUCKET_SIZE) {
263!
1963
        hint = CUCKOO_BUCKET_SIZE;
×
1964
    }
1965
    if (hint > SIZE_MAX / 2U) {
263!
1966
        hint = SIZE_MAX / 2U;
×
1967
    }
1968
    desired = (hint * 2U + CUCKOO_BUCKET_SIZE - 1U) / CUCKOO_BUCKET_SIZE;
263✔
1969
    if (desired == 0U) {
263!
1970
        desired = 1U;
×
1971
    }
1972

1973
    buckets = 1U;
263✔
1974
    while (buckets < desired) {
4,734✔
1975
        prev = buckets;
4,471✔
1976
        if (buckets > SIZE_MAX / 2U) {
4,471!
1977
            buckets = prev;
×
1978
            break;
×
1979
        }
1980
        buckets <<= 1U;
4,471✔
1981
        if (buckets < prev) {
4,471!
1982
            buckets = prev;
×
1983
            break;
×
1984
        }
1985
    }
1986
    if (buckets == 0U) {
263!
1987
        buckets = 1U;
×
1988
    }
1989

1990
    return buckets;
263✔
1991
}
1992

1993
static size_t
1994
cuckoo_hash_primary(uint32_t key, size_t mask)
26,230,484✔
1995
{
1996
    uint32_t mix;
1997

1998
    mix = key * 0x9e3779b1U;
26,230,484✔
1999
    return (size_t)(mix & (uint32_t)mask);
26,230,484✔
2000
}
2001

2002
static size_t
2003
cuckoo_hash_secondary(uint32_t key, size_t mask)
3,205,422✔
2004
{
2005
    uint32_t mix;
2006

2007
    mix = key ^ 0x85ebca6bU;
3,205,422✔
2008
    mix ^= mix >> 13;
3,205,422✔
2009
    mix *= 0xc2b2ae35U;
3,205,422✔
2010
    return (size_t)(mix & (uint32_t)mask);
3,205,422✔
2011
}
2012

2013
static size_t
2014
cuckoo_hash_alternate(uint32_t key, size_t bucket, size_t mask)
14✔
2015
{
2016
    size_t primary;
2017
    size_t secondary;
2018

2019
    primary = cuckoo_hash_primary(key, mask);
14✔
2020
    secondary = cuckoo_hash_secondary(key, mask);
14✔
2021
    if (primary == bucket) {
14!
2022
        if (secondary == primary) {
14!
2023
            secondary = (secondary + 1U) & mask;
×
2024
        }
2025
        return secondary;
14✔
2026
    }
2027

2028
    return primary;
×
2029
}
2✔
2030

2031
static uint32_t *
2032
cuckoo_bucket_find(struct cuckoo_bucket32 *bucket, uint32_t key)
27,833,286✔
2033
{
2034
    size_t i;
2035

2036
#if defined(SIXEL_USE_SSE2)
2037
    __m128i needle;
2038
    __m128i keys;
2039
    __m128i cmp;
2040
    int mask;
2041

2042
    needle = _mm_set1_epi32((int)key);
2043
    keys = _mm_loadu_si128((const __m128i *)bucket->key);
2044
    cmp = _mm_cmpeq_epi32(needle, keys);
2045
    mask = _mm_movemask_ps(_mm_castsi128_ps(cmp));
2046
    if ((mask & 1) != 0 && bucket->value[0] != 0U) {
2047
        return &bucket->value[0];
2048
    }
2049
    if ((mask & 2) != 0 && bucket->value[1] != 0U) {
2050
        return &bucket->value[1];
2051
    }
2052
    if ((mask & 4) != 0 && bucket->value[2] != 0U) {
2053
        return &bucket->value[2];
2054
    }
2055
    if ((mask & 8) != 0 && bucket->value[3] != 0U) {
2056
        return &bucket->value[3];
2057
    }
2058
#elif defined(SIXEL_USE_NEON)
2059
    uint32x4_t needle;
2060
    uint32x4_t keys;
2061
    uint32x4_t cmp;
2062

2063
    needle = vdupq_n_u32(key);
2064
    keys = vld1q_u32(bucket->key);
2065
    cmp = vceqq_u32(needle, keys);
2066
    if (vgetq_lane_u32(cmp, 0) != 0U && bucket->value[0] != 0U) {
2067
        return &bucket->value[0];
2068
    }
2069
    if (vgetq_lane_u32(cmp, 1) != 0U && bucket->value[1] != 0U) {
2070
        return &bucket->value[1];
2071
    }
2072
    if (vgetq_lane_u32(cmp, 2) != 0U && bucket->value[2] != 0U) {
2073
        return &bucket->value[2];
2074
    }
2075
    if (vgetq_lane_u32(cmp, 3) != 0U && bucket->value[3] != 0U) {
2076
        return &bucket->value[3];
2077
    }
2078
#else
2079
    for (i = 0U; i < CUCKOO_BUCKET_SIZE; ++i) {
54,347,076✔
2080
        if (bucket->value[i] != 0U && bucket->key[i] == key) {
47,936,484✔
2081
            return &bucket->value[i];
21,422,694✔
2082
        }
2083
    }
8,572,072✔
2084
#endif
2085

2086
    return NULL;
6,410,592✔
2087
}
12,032,884✔
2088

2089
static int
2090
cuckoo_bucket_insert_direct(struct cuckoo_bucket32 *bucket,
1,602,606✔
2091
                            uint32_t key,
2092
                            uint32_t value)
2093
{
2094
    size_t i;
2095

2096
    for (i = 0U; i < CUCKOO_BUCKET_SIZE; ++i) {
1,738,657✔
2097
        if (bucket->value[i] == 0U) {
1,738,643✔
2098
            bucket->key[i] = key;
1,602,592✔
2099
            bucket->value[i] = value;
1,602,592✔
2100
            return 1;
1,602,592✔
2101
        }
2102
    }
41,657✔
2103

2104
    return 0;
14✔
2105
}
517,780✔
2106

2107
static SIXELSTATUS
2108
cuckoo_table32_init(struct cuckoo_table32 *table,
260✔
2109
                    size_t expected,
2110
                    sixel_allocator_t *allocator)
2111
{
2112
    size_t buckets;
2113
    size_t i;
2114
    size_t j;
2115

2116
    if (table == NULL || allocator == NULL) {
260!
2117
        return SIXEL_BAD_ARGUMENT;
×
2118
    }
2119

2120
    buckets = cuckoo_round_buckets(expected);
260✔
2121
    if (buckets == 0U
260!
2122
        || buckets > SIZE_MAX / sizeof(struct cuckoo_bucket32)) {
260!
2123
        sixel_helper_set_additional_message(
×
2124
            "unable to size cuckoo bucket array.");
2125
        return SIXEL_BAD_ALLOCATION;
×
2126
    }
2127

2128
    table->buckets = (struct cuckoo_bucket32 *)sixel_allocator_malloc(
260✔
2129
        allocator, buckets * sizeof(struct cuckoo_bucket32));
106✔
2130
    if (table->buckets == NULL) {
260!
2131
        sixel_helper_set_additional_message(
×
2132
            "unable to allocate cuckoo buckets.");
2133
        return SIXEL_BAD_ALLOCATION;
×
2134
    }
2135

2136
    table->bucket_count = buckets;
260✔
2137
    table->bucket_mask = buckets - 1U;
260✔
2138
    table->stash_count = 0U;
260✔
2139
    table->entry_count = 0U;
260✔
2140
    table->allocator = allocator;
260✔
2141
    for (i = 0U; i < buckets; ++i) {
34,078,980✔
2142
        for (j = 0U; j < CUCKOO_BUCKET_SIZE; ++j) {
170,393,600✔
2143
            table->buckets[i].key[j] = CUCKOO_EMPTY_KEY;
136,314,880✔
2144
            table->buckets[i].value[j] = 0U;
136,314,880✔
2145
        }
55,574,528✔
2146
    }
13,893,632✔
2147
    for (i = 0U; i < CUCKOO_STASH_SIZE; ++i) {
8,580✔
2148
        table->stash_key[i] = CUCKOO_EMPTY_KEY;
8,320✔
2149
        table->stash_value[i] = 0U;
8,320✔
2150
    }
3,392✔
2151

2152
    return SIXEL_OK;
260✔
2153
}
106✔
2154

2155
static void
2156
cuckoo_table32_clear(struct cuckoo_table32 *table)
263✔
2157
{
2158
    size_t i;
2159
    size_t j;
2160

2161
    if (table == NULL || table->buckets == NULL) {
263!
2162
        return;
×
2163
    }
2164

2165
    table->stash_count = 0U;
263✔
2166
    table->entry_count = 0U;
263✔
2167
    for (i = 0U; i < table->bucket_count; ++i) {
34,472,199✔
2168
        for (j = 0U; j < CUCKOO_BUCKET_SIZE; ++j) {
172,359,680✔
2169
            table->buckets[i].key[j] = CUCKOO_EMPTY_KEY;
137,887,744✔
2170
            table->buckets[i].value[j] = 0U;
137,887,744✔
2171
        }
56,098,816✔
2172
    }
14,024,704✔
2173
    for (i = 0U; i < CUCKOO_STASH_SIZE; ++i) {
8,679✔
2174
        table->stash_key[i] = CUCKOO_EMPTY_KEY;
8,416✔
2175
        table->stash_value[i] = 0U;
8,416✔
2176
    }
3,424✔
2177
}
107✔
2178

2179
static void
2180
cuckoo_table32_fini(struct cuckoo_table32 *table)
260✔
2181
{
2182
    if (table == NULL || table->allocator == NULL) {
260!
2183
        return;
×
2184
    }
2185
    if (table->buckets != NULL) {
260!
2186
        sixel_allocator_free(table->allocator, table->buckets);
260✔
2187
        table->buckets = NULL;
260✔
2188
    }
106✔
2189
    table->bucket_count = 0U;
260✔
2190
    table->bucket_mask = 0U;
260✔
2191
    table->stash_count = 0U;
260✔
2192
    table->entry_count = 0U;
260✔
2193
}
106✔
2194

2195
static uint32_t *
2196
cuckoo_table32_lookup(struct cuckoo_table32 *table, uint32_t key)
24,627,878✔
2197
{
2198
    size_t index;
2199
    size_t i;
2200
    uint32_t *slot;
2201

2202
    if (table == NULL || table->buckets == NULL) {
24,627,878!
2203
        return NULL;
×
2204
    }
2205

2206
    index = cuckoo_hash_primary(key, table->bucket_mask);
24,627,878✔
2207
    slot = cuckoo_bucket_find(&table->buckets[index], key);
24,627,878✔
2208
    if (slot != NULL) {
24,627,878✔
2209
        return slot;
21,422,470✔
2210
    }
2211

2212
    index = cuckoo_hash_secondary(key, table->bucket_mask);
3,205,408✔
2213
    slot = cuckoo_bucket_find(&table->buckets[index], key);
3,205,408✔
2214
    if (slot != NULL) {
3,205,408✔
2215
        return slot;
224✔
2216
    }
2217

2218
    for (i = 0U; i < table->stash_count; ++i) {
3,205,184!
2219
        if (table->stash_value[i] != 0U && table->stash_key[i] == key) {
×
2220
            return &table->stash_value[i];
×
2221
        }
2222
    }
2223

2224
    return NULL;
3,205,184✔
2225
}
10,997,260✔
2226

2227
static SIXELSTATUS
2228
cuckoo_table32_grow(struct cuckoo_table32 *table)
×
2229
{
2230
    struct cuckoo_table32 tmp;
2231
    struct cuckoo_bucket32 *old_buckets;
2232
    size_t old_count;
2233
    size_t i;
2234
    size_t j;
2235
    SIXELSTATUS status;
2236

2237
    if (table == NULL || table->allocator == NULL) {
×
2238
        return SIXEL_BAD_ARGUMENT;
×
2239
    }
2240

2241
    tmp.buckets = NULL;
×
2242
    tmp.bucket_count = 0U;
×
2243
    tmp.bucket_mask = 0U;
×
2244
    tmp.stash_count = 0U;
×
2245
    tmp.entry_count = 0U;
×
2246
    tmp.allocator = table->allocator;
×
2247
    for (i = 0U; i < CUCKOO_STASH_SIZE; ++i) {
×
2248
        tmp.stash_key[i] = CUCKOO_EMPTY_KEY;
×
2249
        tmp.stash_value[i] = 0U;
×
2250
    }
2251

2252
    status = cuckoo_table32_init(&tmp,
×
2253
                                 (table->entry_count + 1U) * 2U,
×
2254
                                 table->allocator);
2255
    if (SIXEL_FAILED(status)) {
×
2256
        return status;
×
2257
    }
2258

2259
    old_buckets = table->buckets;
×
2260
    old_count = table->bucket_count;
×
2261
    for (i = 0U; i < old_count; ++i) {
×
2262
        for (j = 0U; j < CUCKOO_BUCKET_SIZE; ++j) {
×
2263
            if (old_buckets[i].value[j] != 0U) {
×
2264
                status = cuckoo_table32_insert(&tmp,
×
2265
                                               old_buckets[i].key[j],
×
2266
                                               old_buckets[i].value[j]);
×
2267
                if (SIXEL_FAILED(status)) {
×
2268
                    cuckoo_table32_fini(&tmp);
×
2269
                    return status;
×
2270
                }
2271
            }
2272
        }
2273
    }
2274
    for (i = 0U; i < table->stash_count; ++i) {
×
2275
        if (table->stash_value[i] != 0U) {
×
2276
            status = cuckoo_table32_insert(&tmp,
×
2277
                                           table->stash_key[i],
2278
                                           table->stash_value[i]);
2279
            if (SIXEL_FAILED(status)) {
×
2280
                cuckoo_table32_fini(&tmp);
×
2281
                return status;
×
2282
            }
2283
        }
2284
    }
2285

2286
    sixel_allocator_free(table->allocator, old_buckets);
×
2287
    *table = tmp;
×
2288

2289
    return SIXEL_OK;
×
2290
}
2291

2292
static SIXELSTATUS
2293
cuckoo_table32_insert(struct cuckoo_table32 *table,
1,602,592✔
2294
                      uint32_t key,
2295
                      uint32_t value)
2296
{
2297
    uint32_t *slot;
2298
    uint32_t cur_key;
2299
    uint32_t cur_value;
2300
    uint32_t victim_key;
2301
    uint32_t victim_value;
2302
    size_t bucket_index;
2303
    size_t kicks;
2304
    size_t victim_slot;
2305
    struct cuckoo_bucket32 *bucket;
2306
    SIXELSTATUS status;
2307

2308
    if (table == NULL || table->buckets == NULL) {
1,602,592!
2309
        return SIXEL_BAD_ARGUMENT;
×
2310
    }
2311

2312
    slot = cuckoo_table32_lookup(table, key);
1,602,592✔
2313
    if (slot != NULL) {
1,602,592!
2314
        *slot = value;
×
2315
        return SIXEL_OK;
×
2316
    }
2317

2318
    cur_key = key;
1,602,592✔
2319
    cur_value = value;
1,602,592✔
2320
    bucket_index = cuckoo_hash_primary(cur_key, table->bucket_mask);
1,602,592✔
2321
    for (kicks = 0U; kicks < CUCKOO_MAX_KICKS; ++kicks) {
1,602,606!
2322
        bucket = &table->buckets[bucket_index];
1,602,606✔
2323
        if (cuckoo_bucket_insert_direct(bucket, cur_key, cur_value)) {
1,602,606✔
2324
            table->entry_count++;
1,602,592✔
2325
            return SIXEL_OK;
1,602,592✔
2326
        }
2327
        victim_slot = (size_t)((cur_key + kicks) &
14✔
2328
                               (CUCKOO_BUCKET_SIZE - 1U));
2329
        victim_key = bucket->key[victim_slot];
14✔
2330
        victim_value = bucket->value[victim_slot];
14✔
2331
        bucket->key[victim_slot] = cur_key;
14✔
2332
        bucket->value[victim_slot] = cur_value;
14✔
2333
        cur_key = victim_key;
14✔
2334
        cur_value = victim_value;
14✔
2335
        bucket_index = cuckoo_hash_alternate(cur_key,
16✔
2336
                                             bucket_index,
2✔
2337
                                             table->bucket_mask);
2✔
2338
    }
2✔
2339

2340
    if (table->stash_count < CUCKOO_STASH_SIZE) {
×
2341
        table->stash_key[table->stash_count] = cur_key;
×
2342
        table->stash_value[table->stash_count] = cur_value;
×
2343
        table->stash_count++;
×
2344
        table->entry_count++;
×
2345
        return SIXEL_OK;
×
2346
    }
2347

2348
    status = cuckoo_table32_grow(table);
×
2349
    if (SIXEL_FAILED(status)) {
×
2350
        return status;
×
2351
    }
2352

2353
    return cuckoo_table32_insert(table, cur_key, cur_value);
×
2354
}
517,778✔
2355

2356
static SIXELSTATUS
2357
computeHistogram_robinhood(unsigned char const *data,
2358
                           unsigned int length,
2359
                           unsigned long depth,
2360
                           unsigned int step,
2361
                           unsigned int max_sample,
2362
                           tupletable2 * const colorfreqtableP,
2363
                           struct histogram_control const *control,
2364
                           int use_reversible,
2365
                           sixel_allocator_t *allocator);
2366

2367
static SIXELSTATUS
2368
computeHistogram_hopscotch(unsigned char const *data,
2369
                           unsigned int length,
2370
                           unsigned long depth,
2371
                           unsigned int step,
2372
                           unsigned int max_sample,
2373
                           tupletable2 * const colorfreqtableP,
2374
                           struct histogram_control const *control,
2375
                           int use_reversible,
2376
                           sixel_allocator_t *allocator);
2377

2378
static SIXELSTATUS
2379
computeHistogram(unsigned char const    /* in */  *data,
256✔
2380
                 unsigned int           /* in */  length,
2381
                 unsigned long const    /* in */  depth,
2382
                 tupletable2 * const    /* out */ colorfreqtableP,
2383
                 int const              /* in */  qualityMode,
2384
                 int const              /* in */  use_reversible,
2385
                 sixel_allocator_t      /* in */  *allocator)
2386
{
2387
    SIXELSTATUS status = SIXEL_FALSE;
256✔
2388
    typedef uint32_t unit_t;
2389
    unsigned int i, n;
2390
    unit_t *histogram = NULL;
256✔
2391
    unit_t *refmap = NULL;
256✔
2392
    unit_t *ref;
2393
    unsigned int bucket_index;
2394
    unsigned int step;
2395
    unsigned int max_sample;
2396
    size_t hist_size;
2397
    unit_t bucket_value;
2398
    unsigned int component;
2399
    unsigned int reconstructed;
2400
    struct histogram_control control;
2401
    unsigned int depth_u;
2402
    unsigned char reversible_pixel[4];
2403

2404
    switch (qualityMode) {
256!
2405
    case SIXEL_QUALITY_LOW:
118✔
2406
        max_sample = 18383;
223✔
2407
        break;
223✔
2408
    case SIXEL_QUALITY_HIGH:
20✔
2409
        max_sample = 1118383;
30✔
2410
        break;
30✔
2411
    case SIXEL_QUALITY_FULL:
3✔
2412
    default:
2413
        max_sample = 4003079;
3✔
2414
        break;
3✔
2415
    }
2416

2417
    step = length / depth / max_sample * depth;
256✔
2418
    if (step <= 0) {
256✔
2419
        step = depth;
156✔
2420
    }
52✔
2421

2422
    quant_trace(stderr, "making histogram...\n");
256✔
2423

2424
    depth_u = (unsigned int)depth;
256✔
2425
    control = histogram_control_make(depth_u);
256✔
2426
    if (use_reversible) {
256!
NEW
2427
        control.reversible_rounding = 1;
×
2428
    }
2429
    if (histogram_lut_policy == SIXEL_LUT_POLICY_ROBINHOOD
256!
2430
        || histogram_lut_policy == SIXEL_LUT_POLICY_HOPSCOTCH) {
256!
2431
        if (histogram_lut_policy == SIXEL_LUT_POLICY_ROBINHOOD) {
×
2432
            status = computeHistogram_robinhood(data,
×
2433
                                                length,
2434
                                                depth,
2435
                                                step,
2436
                                                max_sample,
2437
                                                colorfreqtableP,
2438
                                                &control,
2439
                                                use_reversible,
2440
                                                allocator);
2441
        } else {
2442
            status = computeHistogram_hopscotch(data,
×
2443
                                                length,
2444
                                                depth,
2445
                                                step,
2446
                                                max_sample,
2447
                                                colorfreqtableP,
2448
                                                &control,
2449
                                                use_reversible,
2450
                                                allocator);
2451
        }
2452
        goto end;
×
2453
    }
2454

2455
    hist_size = histogram_dense_size((unsigned int)depth, &control);
256✔
2456
    histogram = (unit_t *)sixel_allocator_calloc(allocator,
372✔
2457
                                                 hist_size,
116✔
2458
                                                 sizeof(unit_t));
2459
    if (histogram == NULL) {
256!
2460
        sixel_helper_set_additional_message(
×
2461
            "unable to allocate memory for histogram.");
2462
        status = SIXEL_BAD_ALLOCATION;
×
2463
        goto end;
×
2464
    }
2465
    ref = refmap = (unit_t *)sixel_allocator_malloc(allocator,
372✔
2466
                                                    hist_size *
116✔
2467
                                                    sizeof(unit_t));
2468
    if (refmap == NULL) {
256!
2469
        sixel_helper_set_additional_message(
×
2470
            "unable to allocate memory for lookup table.");
2471
        status = SIXEL_BAD_ALLOCATION;
×
2472
        goto end;
×
2473
    }
2474

2475
    for (i = 0; i < length; i += step) {
3,498,898✔
2476
        if (use_reversible) {
3,498,642!
NEW
2477
            sixel_quant_reversible_pixel(data + i,
×
2478
                                         depth_u,
2479
                                         reversible_pixel);
NEW
2480
            bucket_index = histogram_pack_color(reversible_pixel,
×
2481
                                                depth_u,
2482
                                                &control);
2483
        } else {
2484
            bucket_index = histogram_pack_color(data + i,
5,238,936✔
2485
                                                depth_u,
1,740,294✔
2486
                                                &control);
2487
        }
2488
        if (histogram[bucket_index] == 0) {
3,498,642✔
2489
            *ref++ = bucket_index;
287,619✔
2490
        }
95,967✔
2491
        if (histogram[bucket_index] < UINT32_MAX) {
3,498,642!
2492
            histogram[bucket_index]++;
3,498,642✔
2493
        }
1,740,294✔
2494
    }
1,740,294✔
2495

2496
    colorfreqtableP->size = (unsigned int)(ref - refmap);
256✔
2497

2498
    status = alloctupletable(&colorfreqtableP->table,
372✔
2499
                             depth,
116✔
2500
                             (unsigned int)(ref - refmap),
256✔
2501
                             allocator);
116✔
2502
    if (SIXEL_FAILED(status)) {
256!
2503
        goto end;
×
2504
    }
2505
    for (i = 0; i < colorfreqtableP->size; ++i) {
287,875✔
2506
        bucket_value = refmap[i];
287,619✔
2507
        if (histogram[bucket_value] > 0) {
287,619!
2508
            colorfreqtableP->table[i]->value = histogram[bucket_value];
287,619✔
2509
            for (n = 0; n < depth; n++) {
1,150,476✔
2510
                component = (unsigned int)
862,857✔
2511
                    ((bucket_value >> (n * control.channel_bits)) &
1,150,758✔
2512
                     control.channel_mask);
862,857✔
2513
                reconstructed = histogram_reconstruct(component,
862,857✔
2514
                                                      &control);
2515
                if (use_reversible) {
862,857!
NEW
2516
                    reconstructed =
×
NEW
2517
                        (unsigned int)sixel_quant_reversible_value(
×
2518
                            reconstructed);
2519
                }
2520
                colorfreqtableP->table[i]->tuple[depth - 1 - n]
862,857✔
2521
                    = (sample)reconstructed;
1,150,758✔
2522
            }
287,901✔
2523
        }
95,967✔
2524
    }
95,967✔
2525

2526
    quant_trace(stderr, "%u colors found\n", colorfreqtableP->size);
256✔
2527

2528
    status = SIXEL_OK;
256✔
2529

2530
end:
140✔
2531
    sixel_allocator_free(allocator, refmap);
256✔
2532
    sixel_allocator_free(allocator, histogram);
256✔
2533

2534
    return status;
256✔
2535
}
2536

2537
static SIXELSTATUS
2538
computeHistogram_robinhood(unsigned char const *data,
×
2539
                           unsigned int length,
2540
                           unsigned long depth,
2541
                           unsigned int step,
2542
                           unsigned int max_sample,
2543
                           tupletable2 * const colorfreqtableP,
2544
                           struct histogram_control const *control,
2545
                           int use_reversible,
2546
                           sixel_allocator_t *allocator)
2547
{
2548
    SIXELSTATUS status = SIXEL_FALSE;
×
2549
    struct robinhood_table32 table;
2550
    size_t expected;
2551
    size_t cap_limit;
2552
    size_t index;
2553
    unsigned int depth_u;
2554
    unsigned int i;
2555
    unsigned int n;
2556
    uint32_t bucket_color;
2557
    uint32_t bucket_hash;
2558
    uint32_t entry_color;
2559
    struct robinhood_slot32 *slot;
2560
    unsigned int component;
2561
    unsigned int reconstructed;
2562
    unsigned char reversible_pixel[4];
2563

2564
    /*
2565
     * The ASCII sketch below shows how the sparse table stores samples:
2566
     *
2567
     *   [hash]->(key,value,distance)  Robin Hood probing keeps dense tails.
2568
     */
2569
    table.slots = NULL;
×
2570
    table.capacity = 0U;
×
2571
    table.count = 0U;
×
2572
    table.allocator = allocator;
×
2573
    cap_limit = (size_t)1U << 20;
×
2574
    expected = max_sample;
×
2575
    if (expected < 256U) {
×
2576
        expected = 256U;
×
2577
    }
2578
    if (expected > cap_limit) {
×
2579
        expected = cap_limit;
×
2580
    }
2581

2582
    status = robinhood_table32_init(&table, expected, allocator);
×
2583
    if (SIXEL_FAILED(status)) {
×
2584
        sixel_helper_set_additional_message(
×
2585
            "unable to allocate robinhood histogram.");
2586
        goto end;
×
2587
    }
2588

2589
    depth_u = (unsigned int)depth;
×
2590
    for (i = 0U; i < length; i += step) {
×
NEW
2591
        if (use_reversible) {
×
NEW
2592
            sixel_quant_reversible_pixel(data + i, depth_u,
×
2593
                                         reversible_pixel);
NEW
2594
            bucket_color = histogram_pack_color(reversible_pixel,
×
2595
                                                depth_u, control);
2596
        } else {
NEW
2597
            bucket_color = histogram_pack_color(data + i,
×
2598
                                                depth_u, control);
2599
        }
UNCOV
2600
        bucket_hash = histogram_hash_mix(bucket_color);
×
2601
        /*
2602
         * Hash probing uses the mixed key while the slot stores the
2603
         * original quantized RGB value:
2604
         *
2605
         *   hash --> [slot]
2606
         *             |color|count|
2607
         */
2608
        slot = robinhood_table32_lookup(&table,
×
2609
                                        bucket_hash,
2610
                                        bucket_color);
2611
        if (slot == NULL) {
×
2612
            status = robinhood_table32_insert(&table,
×
2613
                                              bucket_hash,
2614
                                              bucket_color,
2615
                                              1U);
2616
            if (SIXEL_FAILED(status)) {
×
2617
                sixel_helper_set_additional_message(
×
2618
                    "unable to grow robinhood histogram.");
2619
                goto end;
×
2620
            }
2621
        } else if (slot->value < UINT32_MAX) {
×
2622
            slot->value++;
×
2623
        }
2624
    }
2625

2626
    if (table.count > UINT_MAX) {
×
2627
        sixel_helper_set_additional_message(
×
2628
            "too many unique colors for histogram.");
2629
        status = SIXEL_BAD_ARGUMENT;
×
2630
        goto end;
×
2631
    }
2632

2633
    colorfreqtableP->size = (unsigned int)table.count;
×
2634
    status = alloctupletable(&colorfreqtableP->table,
×
2635
                             depth_u,
2636
                             (unsigned int)table.count,
×
2637
                             allocator);
2638
    if (SIXEL_FAILED(status)) {
×
2639
        goto end;
×
2640
    }
2641

2642
    index = 0U;
×
2643
    /*
2644
     * Stream slots in the hash traversal order to avoid qsort overhead.
2645
     * This favors throughput over identical palette ordering.
2646
     */
2647
    for (i = 0U; i < table.capacity; ++i) {
×
2648
        slot = &table.slots[i];
×
2649
        if (slot->value == 0U) {
×
2650
            continue;
×
2651
        }
2652
        if (index >= colorfreqtableP->size) {
×
2653
            break;
×
2654
        }
2655
        entry_color = slot->color;
×
2656
        colorfreqtableP->table[index]->value = slot->value;
×
2657
        for (n = 0U; n < depth_u; ++n) {
×
2658
            component = (unsigned int)
×
2659
                ((entry_color >> (n * control->channel_bits))
×
2660
                 & control->channel_mask);
×
2661
            reconstructed = histogram_reconstruct(component, control);
×
NEW
2662
            if (use_reversible) {
×
NEW
2663
                reconstructed =
×
NEW
2664
                    (unsigned int)sixel_quant_reversible_value(
×
2665
                        reconstructed);
2666
            }
2667
            colorfreqtableP->table[index]->tuple[depth_u - 1U - n]
×
2668
                = (sample)reconstructed;
×
2669
        }
2670
        index++;
×
2671
    }
2672

2673
    status = SIXEL_OK;
×
2674

2675
end:
2676
    robinhood_table32_fini(&table);
×
2677

2678
    return status;
×
2679
}
2680

2681
static SIXELSTATUS
2682
computeHistogram_hopscotch(unsigned char const *data,
×
2683
                           unsigned int length,
2684
                           unsigned long depth,
2685
                           unsigned int step,
2686
                           unsigned int max_sample,
2687
                           tupletable2 * const colorfreqtableP,
2688
                           struct histogram_control const *control,
2689
                           int use_reversible,
2690
                           sixel_allocator_t *allocator)
2691
{
2692
    SIXELSTATUS status = SIXEL_FALSE;
×
2693
    struct hopscotch_table32 table;
2694
    size_t expected;
2695
    size_t cap_limit;
2696
    size_t index;
2697
    unsigned int depth_u;
2698
    unsigned int i;
2699
    unsigned int n;
2700
    uint32_t bucket_color;
2701
    uint32_t bucket_hash;
2702
    uint32_t entry_color;
2703
    struct hopscotch_slot32 *slot;
2704
    unsigned int component;
2705
    unsigned int reconstructed;
2706
    unsigned char reversible_pixel[4];
2707

2708
    /*
2709
     * Hopscotch hashing stores the local neighbourhood using the map below:
2710
     *
2711
     *   [home] hopinfo bits ---> |slot+0|slot+1|slot+2| ...
2712
     *                              ^ entries hop within this window.
2713
     */
2714
    table.slots = NULL;
×
2715
    table.hopinfo = NULL;
×
2716
    table.capacity = 0U;
×
2717
    table.count = 0U;
×
2718
    table.neighborhood = HOPSCOTCH_DEFAULT_NEIGHBORHOOD;
×
2719
    table.allocator = allocator;
×
2720
    cap_limit = (size_t)1U << 20;
×
2721
    expected = max_sample;
×
2722
    if (expected < 256U) {
×
2723
        expected = 256U;
×
2724
    }
2725
    if (expected > cap_limit) {
×
2726
        expected = cap_limit;
×
2727
    }
2728

2729
    status = hopscotch_table32_init(&table, expected, allocator);
×
2730
    if (SIXEL_FAILED(status)) {
×
2731
        sixel_helper_set_additional_message(
×
2732
            "unable to allocate hopscotch histogram.");
2733
        goto end;
×
2734
    }
2735

2736
    depth_u = (unsigned int)depth;
×
2737
    for (i = 0U; i < length; i += step) {
×
NEW
2738
        if (use_reversible) {
×
NEW
2739
            sixel_quant_reversible_pixel(data + i, depth_u,
×
2740
                                         reversible_pixel);
NEW
2741
            bucket_color = histogram_pack_color(reversible_pixel,
×
2742
                                                depth_u, control);
2743
        } else {
NEW
2744
            bucket_color = histogram_pack_color(data + i,
×
2745
                                                depth_u, control);
2746
        }
UNCOV
2747
        bucket_hash = histogram_hash_mix(bucket_color);
×
2748
        /*
2749
         * Hopscotch buckets mirror the robinhood layout, keeping the
2750
         * quantized color next to the count so we never derive it from
2751
         * the scrambled hash key.
2752
         */
2753
        slot = hopscotch_table32_lookup(&table,
×
2754
                                        bucket_hash,
2755
                                        bucket_color);
2756
        if (slot == NULL) {
×
2757
            status = hopscotch_table32_insert(&table,
×
2758
                                              bucket_hash,
2759
                                              bucket_color,
2760
                                              1U);
2761
            if (SIXEL_FAILED(status)) {
×
2762
                sixel_helper_set_additional_message(
×
2763
                    "unable to grow hopscotch histogram.");
2764
                goto end;
×
2765
            }
2766
        } else if (slot->value < UINT32_MAX) {
×
2767
            slot->value++;
×
2768
        }
2769
    }
2770

2771
    if (table.count > UINT_MAX) {
×
2772
        sixel_helper_set_additional_message(
×
2773
            "too many unique colors for histogram.");
2774
        status = SIXEL_BAD_ARGUMENT;
×
2775
        goto end;
×
2776
    }
2777

2778
    colorfreqtableP->size = (unsigned int)table.count;
×
2779
    status = alloctupletable(&colorfreqtableP->table,
×
2780
                             depth_u,
2781
                             (unsigned int)table.count,
×
2782
                             allocator);
2783
    if (SIXEL_FAILED(status)) {
×
2784
        goto end;
×
2785
    }
2786

2787
    index = 0U;
×
2788
    /*
2789
     * Stream slots in the hash traversal order to avoid qsort overhead.
2790
     * This favors throughput over identical palette ordering.
2791
     */
2792
    for (i = 0U; i < table.capacity; ++i) {
×
2793
        slot = &table.slots[i];
×
2794
        if (slot->key == HOPSCOTCH_EMPTY_KEY || slot->value == 0U) {
×
2795
            continue;
×
2796
        }
2797
        if (index >= colorfreqtableP->size) {
×
2798
            break;
×
2799
        }
2800
        entry_color = slot->color;
×
2801
        colorfreqtableP->table[index]->value = slot->value;
×
2802
        for (n = 0U; n < depth_u; ++n) {
×
2803
            component = (unsigned int)
×
2804
                ((entry_color >> (n * control->channel_bits))
×
2805
                 & control->channel_mask);
×
2806
            reconstructed = histogram_reconstruct(component, control);
×
NEW
2807
            if (use_reversible) {
×
NEW
2808
                reconstructed =
×
NEW
2809
                    (unsigned int)sixel_quant_reversible_value(
×
2810
                        reconstructed);
2811
            }
2812
            colorfreqtableP->table[index]->tuple[depth_u - 1U - n]
×
2813
                = (sample)reconstructed;
×
2814
        }
2815
        index++;
×
2816
    }
2817

2818
    status = SIXEL_OK;
×
2819

2820
end:
2821
    hopscotch_table32_fini(&table);
×
2822

2823
    return status;
×
2824
}
2825

2826
SIXELSTATUS
2827
sixel_quant_cache_prepare(unsigned short **cachetable,
263✔
2828
                          size_t *cachetable_size,
2829
                          int lut_policy,
2830
                          int reqcolor,
2831
                          sixel_allocator_t *allocator)
2832
{
2833
    SIXELSTATUS status;
2834
    struct histogram_control control;
2835
    struct cuckoo_table32 *table;
2836
    size_t expected;
2837
    size_t buckets;
2838
    size_t cap_limit;
2839
    int normalized;
2840

2841
    if (cachetable == NULL || allocator == NULL) {
263!
2842
        return SIXEL_BAD_ARGUMENT;
×
2843
    }
2844

2845
    /*
2846
     * The cache pointer always references the same ladder:
2847
     *
2848
     *   cache -> cuckoo_table32 -> buckets
2849
     *                           -> stash
2850
     */
2851
    normalized = lut_policy;
263✔
2852
    if (normalized == SIXEL_LUT_POLICY_AUTO) {
263!
2853
        normalized = histogram_lut_policy;
263✔
2854
    }
107✔
2855
    if (normalized == SIXEL_LUT_POLICY_AUTO) {
263!
2856
        normalized = SIXEL_LUT_POLICY_6BIT;
263✔
2857
    }
107✔
2858

2859
    control = histogram_control_make_for_policy(3U, normalized);
263✔
2860
    if (control.channel_shift == 0U) {
263!
2861
        expected = (size_t)reqcolor * 64U;
×
2862
        cap_limit = (size_t)1U << 20;
×
2863
        if (expected < 512U) {
×
2864
            expected = 512U;
×
2865
        }
2866
        if (expected > cap_limit) {
×
2867
            expected = cap_limit;
×
2868
        }
2869
    } else {
2870
        expected = histogram_dense_size(3U, &control);
263✔
2871
        if (expected == 0U) {
263!
2872
            expected = CUCKOO_BUCKET_SIZE;
×
2873
        }
2874
    }
2875

2876
    table = (struct cuckoo_table32 *)*cachetable;
263✔
2877
    if (table != NULL) {
263✔
2878
        buckets = cuckoo_round_buckets(expected);
3✔
2879
        if (table->bucket_count < buckets) {
3!
2880
            cuckoo_table32_fini(table);
×
2881
            sixel_allocator_free(allocator, table);
×
2882
            table = NULL;
×
2883
            *cachetable = NULL;
×
2884
        } else {
2885
            cuckoo_table32_clear(table);
3✔
2886
        }
2887
    }
1✔
2888
    if (table == NULL) {
263✔
2889
        table = (struct cuckoo_table32 *)sixel_allocator_malloc(
260✔
2890
            allocator, sizeof(struct cuckoo_table32));
106✔
2891
        if (table == NULL) {
260!
2892
            sixel_helper_set_additional_message(
×
2893
                "unable to allocate cuckoo cache state.");
2894
            return SIXEL_BAD_ALLOCATION;
×
2895
        }
2896
        memset(table, 0, sizeof(struct cuckoo_table32));
260✔
2897
        status = cuckoo_table32_init(table, expected, allocator);
260✔
2898
        if (SIXEL_FAILED(status)) {
260!
2899
            sixel_allocator_free(allocator, table);
×
2900
            sixel_helper_set_additional_message(
×
2901
                "unable to initialize cuckoo cache.");
2902
            return status;
×
2903
        }
2904
        *cachetable = (unsigned short *)table;
260✔
2905
    }
106✔
2906

2907
    if (cachetable_size != NULL) {
263!
2908
        *cachetable_size = table->bucket_count * CUCKOO_BUCKET_SIZE;
263✔
2909
    }
107✔
2910

2911
    return SIXEL_OK;
263✔
2912
}
107✔
2913

2914
void
2915
sixel_quant_cache_clear(unsigned short *cachetable,
260✔
2916
                        int lut_policy)
2917
{
2918
    struct cuckoo_table32 *table;
2919

2920
    (void)lut_policy;
106✔
2921
    if (cachetable == NULL) {
260!
2922
        return;
×
2923
    }
2924

2925
    table = (struct cuckoo_table32 *)cachetable;
260✔
2926
    cuckoo_table32_clear(table);
260✔
2927
}
106✔
2928

2929
void
2930
sixel_quant_cache_release(unsigned short *cachetable,
525✔
2931
                          int lut_policy,
2932
                          sixel_allocator_t *allocator)
2933
{
2934
    struct cuckoo_table32 *table;
2935

2936
    (void)lut_policy;
181✔
2937
    if (cachetable == NULL || allocator == NULL) {
525!
2938
        return;
265✔
2939
    }
2940

2941
    table = (struct cuckoo_table32 *)cachetable;
260✔
2942
    cuckoo_table32_fini(table);
260✔
2943
    sixel_allocator_free(allocator, table);
260✔
2944
}
181✔
2945

2946

2947
static int
2948
computeColorMapFromInput(unsigned char const *data,
256✔
2949
                         unsigned int const length,
2950
                         unsigned int const depth,
2951
                         unsigned int const reqColors,
2952
                         int const methodForLargest,
2953
                         int const methodForRep,
2954
                         int const qualityMode,
2955
                         int const force_palette,
2956
                         int const use_reversible,
2957
                         tupletable2 * const colormapP,
2958
                         unsigned int *origcolors,
2959
                         sixel_allocator_t *allocator)
2960
{
2961
/*----------------------------------------------------------------------------
2962
   Produce a colormap containing the best colors to represent the
2963
   image stream in file 'ifP'.  Figure it out using the median cut
2964
   technique.
2965

2966
   The colormap will have 'reqcolors' or fewer colors in it, unless
2967
   'allcolors' is true, in which case it will have all the colors that
2968
   are in the input.
2969

2970
   The colormap has the same maxval as the input.
2971

2972
   Put the colormap in newly allocated storage as a tupletable2
2973
   and return its address as *colormapP.  Return the number of colors in
2974
   it as *colorsP and its maxval as *colormapMaxvalP.
2975

2976
   Return the characteristics of the input file as
2977
   *formatP and *freqPamP.  (This information is not really
2978
   relevant to our colormap mission; just a fringe benefit).
2979
-----------------------------------------------------------------------------*/
2980
    SIXELSTATUS status = SIXEL_FALSE;
256✔
2981
    tupletable2 colorfreqtable = {0, NULL};
256✔
2982
    unsigned int i;
2983
    unsigned int n;
2984

2985
    status = computeHistogram(data, length, depth,
372✔
2986
                              &colorfreqtable, qualityMode,
116✔
2987
                              use_reversible, allocator);
116✔
2988
    if (SIXEL_FAILED(status)) {
256!
2989
        goto end;
×
2990
    }
2991
    if (origcolors) {
256!
2992
        *origcolors = colorfreqtable.size;
256✔
2993
    }
116✔
2994

2995
    if (colorfreqtable.size <= reqColors) {
256✔
2996
        quant_trace(stderr,
284✔
2997
                    "Image already has few enough colors (<=%d).  "
2998
                    "Keeping same colors.\n", reqColors);
94✔
2999
        /* *colormapP = colorfreqtable; */
3000
        colormapP->size = colorfreqtable.size;
190✔
3001
        status = alloctupletable(&colormapP->table, depth, colorfreqtable.size, allocator);
190✔
3002
        if (SIXEL_FAILED(status)) {
190!
3003
            goto end;
×
3004
        }
3005
        for (i = 0; i < colorfreqtable.size; ++i) {
2,956✔
3006
            colormapP->table[i]->value = colorfreqtable.table[i]->value;
2,766✔
3007
            for (n = 0; n < depth; ++n) {
11,064✔
3008
                colormapP->table[i]->tuple[n] = colorfreqtable.table[i]->tuple[n];
8,298✔
3009
            }
3,480✔
3010
            if (use_reversible) {
2,766!
NEW
3011
                sixel_quant_reversible_tuple(colormapP->table[i]->tuple,
×
3012
                                             depth);
3013
            }
3014
        }
1,160✔
3015
    } else {
94✔
3016
        quant_trace(stderr, "choosing %d colors...\n", reqColors);
66✔
3017
        status = mediancut(colorfreqtable, depth, reqColors,
88✔
3018
                           methodForLargest, methodForRep,
22✔
3019
                           use_reversible, colormapP, allocator);
22✔
3020
        if (SIXEL_FAILED(status)) {
66!
3021
            goto end;
×
3022
        }
3023
        quant_trace(stderr, "%d colors are choosed.\n", colorfreqtable.size);
66✔
3024
    }
3025

3026
    if (force_palette) {
256!
3027
        status = force_palette_completion(colormapP, depth, reqColors,
×
3028
                                          colorfreqtable, allocator);
3029
        if (SIXEL_FAILED(status)) {
×
3030
            goto end;
×
3031
        }
3032
    }
3033

3034
    status = SIXEL_OK;
256✔
3035

3036
end:
140✔
3037
    sixel_allocator_free(allocator, colorfreqtable.table);
256✔
3038
    return status;
256✔
3039
}
3040

3041

3042
/* diffuse error energy to surround pixels (normal strategy) */
3043
static void
3044
error_diffuse_normal(
128,311,857✔
3045
    unsigned char /* in */    *data,      /* base address of pixel buffer */
3046
    int           /* in */    pos,        /* address of the destination pixel */
3047
    int           /* in */    depth,      /* color depth in bytes */
3048
    int           /* in */    error,      /* error energy */
3049
    int           /* in */    numerator,  /* numerator of diffusion coefficient */
3050
    int           /* in */    denominator /* denominator of diffusion coefficient */)
3051
{
3052
    int c;
3053

3054
    data += pos * depth;
128,311,857✔
3055

3056
    c = *data + (error * numerator * 2 / denominator + 1) / 2;
128,311,857✔
3057
    if (c < 0) {
128,311,857✔
3058
        c = 0;
2,632,929✔
3059
    }
879,627✔
3060
    if (c >= 1 << 8) {
128,311,857✔
3061
        c = (1 << 8) - 1;
736,047✔
3062
    }
234,601✔
3063
    *data = (unsigned char)c;
128,311,857✔
3064
}
128,311,857✔
3065

3066
/* error diffusion with fast strategy */
3067
static void
3068
error_diffuse_fast(
169,469,067✔
3069
    unsigned char /* in */    *data,      /* base address of pixel buffer */
3070
    int           /* in */    pos,        /* address of the destination pixel */
3071
    int           /* in */    depth,      /* color depth in bytes */
3072
    int           /* in */    error,      /* error energy */
3073
    int           /* in */    numerator,  /* numerator of diffusion coefficient */
3074
    int           /* in */    denominator /* denominator of diffusion coefficient */)
3075
{
3076
    int c;
3077

3078
    data += pos * depth;
169,469,067✔
3079

3080
    c = *data + error * numerator / denominator;
169,469,067✔
3081
    if (c < 0) {
169,469,067✔
3082
        c = 0;
7,485,418✔
3083
    }
2,822,832✔
3084
    if (c >= 1 << 8) {
169,469,067✔
3085
        c = (1 << 8) - 1;
455,515✔
3086
    }
148,827✔
3087
    *data = (unsigned char)c;
169,469,067✔
3088
}
169,469,067✔
3089

3090

3091
/* error diffusion with precise strategy */
3092
static void
3093
error_diffuse_precise(
6,111,369✔
3094
    unsigned char /* in */    *data,      /* base address of pixel buffer */
3095
    int           /* in */    pos,        /* address of the destination pixel */
3096
    int           /* in */    depth,      /* color depth in bytes */
3097
    int           /* in */    error,      /* error energy */
3098
    int           /* in */    numerator,  /* numerator of diffusion coefficient */
3099
    int           /* in */    denominator /* denominator of diffusion coefficient */)
3100
{
3101
    int c;
3102

3103
    data += pos * depth;
6,111,369✔
3104

3105
    c = (int)(*data + error * numerator / (double)denominator + 0.5);
6,111,369✔
3106
    if (c < 0) {
6,111,369✔
3107
        c = 0;
70,495✔
3108
    }
25,489✔
3109
    if (c >= 1 << 8) {
6,111,369!
3110
        c = (1 << 8) - 1;
×
3111
    }
3112
    *data = (unsigned char)c;
6,111,369✔
3113
}
6,111,369✔
3114

3115

3116
typedef void (*diffuse_fixed_carry_mode)(int32_t *carry_curr,
3117
                                         int32_t *carry_next,
3118
                                         int32_t *carry_far,
3119
                                         int width,
3120
                                         int height,
3121
                                         int depth,
3122
                                         int x,
3123
                                         int y,
3124
                                         int32_t error,
3125
                                         int direction,
3126
                                         int channel);
3127

3128

3129
typedef void (*diffuse_varerr_mode)(unsigned char *data,
3130
                                    int width,
3131
                                    int height,
3132
                                    int x,
3133
                                    int y,
3134
                                    int depth,
3135
                                    int32_t error,
3136
                                    int index,
3137
                                    int direction);
3138

3139
typedef void (*diffuse_varerr_carry_mode)(int32_t *carry_curr,
3140
                                          int32_t *carry_next,
3141
                                          int32_t *carry_far,
3142
                                          int width,
3143
                                          int height,
3144
                                          int depth,
3145
                                          int x,
3146
                                          int y,
3147
                                          int32_t error,
3148
                                          int index,
3149
                                          int direction,
3150
                                          int channel);
3151

3152
static int
3153
zhoufang_index_from_byte(unsigned char value)
×
3154
{
3155
    double px;
3156
    double remapped;
3157
    int scale_index;
3158
    double scale;
3159
    double jitter;
3160
    int index;
3161

3162
    px = (double)value / 255.0;
×
3163
    remapped = px;
×
3164
    if (remapped >= 0.5) {
×
3165
        remapped = 1.0 - remapped;
×
3166
    }
3167
    if (remapped < 0.0) {
×
3168
        remapped = 0.0;
×
3169
    }
3170
    if (remapped > 0.5) {
×
3171
        remapped = 0.5;
×
3172
    }
3173

3174
    scale_index = (int)(remapped * 128.0);
×
3175
    if (scale_index < 0) {
×
3176
        scale_index = 0;
×
3177
    }
3178
    if (scale_index > 127) {
×
3179
        scale_index = 127;
×
3180
    }
3181

3182
    scale = zhoufang_threshold_gain[scale_index] / 100.0;
×
3183
    jitter = ((double)(rand() & 127) / 128.0) * scale;
×
3184
    remapped += (0.5 - remapped) * jitter;
×
3185
    if (remapped < 0.0) {
×
3186
        remapped = 0.0;
×
3187
    }
3188
    if (remapped > 0.5) {
×
3189
        remapped = 0.5;
×
3190
    }
3191

3192
    index = (int)(remapped * 255.0 + 0.5);
×
3193
    if (index > 127) {
×
3194
        index = 127;
×
3195
    }
3196
    if (index < 0) {
×
3197
        index = 0;
×
3198
    }
3199

3200
    return index;
×
3201
}
3202

3203

3204
static int32_t
3205
diffuse_varerr_term(int32_t error, int weight, int denom)
×
3206
{
3207
    int64_t delta;
3208

3209
    delta = (int64_t)error * (int64_t)weight;
×
3210
    if (delta >= 0) {
×
3211
        delta = (delta + denom / 2) / denom;
×
3212
    } else {
3213
        delta = (delta - denom / 2) / denom;
×
3214
    }
3215

3216
    return (int32_t)delta;
×
3217
}
3218

3219

3220
static int32_t
3221
diffuse_fixed_term(int32_t error, int numerator, int denominator)
×
3222
{
3223
    int64_t delta;
3224

3225
    delta = (int64_t)error * (int64_t)numerator;
×
3226
    if (delta >= 0) {
×
3227
        delta = (delta + denominator / 2) / denominator;
×
3228
    } else {
3229
        delta = (delta - denominator / 2) / denominator;
×
3230
    }
3231

3232
    return (int32_t)delta;
×
3233
}
3234

3235

3236
static void
3237
diffuse_varerr_apply_direct(unsigned char *target, int depth, size_t offset,
×
3238
                            int32_t delta)
3239
{
3240
    int64_t value;
3241
    int result;
3242

3243
    value = (int64_t)target[offset * depth] << VARERR_SCALE_SHIFT;
×
3244
    value += delta;
×
3245
    if (value < 0) {
×
3246
        value = 0;
×
3247
    } else {
3248
        int64_t max_value;
3249

3250
        max_value = VARERR_MAX_VALUE;
×
3251
        if (value > max_value) {
×
3252
            value = max_value;
×
3253
        }
3254
    }
3255

3256
    result = (int)((value + VARERR_ROUND) >> VARERR_SCALE_SHIFT);
×
3257
    if (result < 0) {
×
3258
        result = 0;
×
3259
    }
3260
    if (result > 255) {
×
3261
        result = 255;
×
3262
    }
3263
    target[offset * depth] = (unsigned char)result;
×
3264
}
×
3265

3266

3267
static void
3268
diffuse_lso2(unsigned char *data, int width, int height,
×
3269
             int x, int y, int depth, int32_t error,
3270
             int index, int direction)
3271
{
3272
    const int (*table)[7];
3273
    const int *entry;
3274
    int denom;
3275
    int32_t term_r = 0;
×
3276
    int32_t term_r2 = 0;
×
3277
    int32_t term_dl = 0;
×
3278
    int32_t term_d = 0;
×
3279
    int32_t term_dr = 0;
×
3280
    int32_t term_d2 = 0;
×
3281
    size_t offset;
3282

3283
    if (error == 0) {
×
3284
        return;
×
3285
    }
3286
    if (index < 0) {
×
3287
        index = 0;
×
3288
    }
3289
    if (index > 255) {
×
3290
        index = 255;
×
3291
    }
3292

3293
    table = lso2_table();
×
3294
    entry = table[index];
×
3295
    denom = entry[6];
×
3296
    if (denom == 0) {
×
3297
        return;
×
3298
    }
3299

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

3307

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

3365

3366
static void
3367
diffuse_lso3(unsigned char *data, int width, int height,
×
3368
             int x, int y, int depth, int32_t error,
3369
             int index, int direction)
3370
{
3371
    const int (*table)[7];
3372
    const int *entry;
3373
    int denom;
3374
    int32_t term_r = 0;
×
3375
    int32_t term_r2 = 0;
×
3376
    int32_t term_dl = 0;
×
3377
    int32_t term_d = 0;
×
3378
    int32_t term_dr = 0;
×
3379
    int32_t term_d2 = 0;
×
3380
    size_t offset;
3381

3382
    if (error == 0) {
×
3383
        return;
×
3384
    }
3385
    if (index < 0) {
×
3386
        index = 0;
×
3387
    }
3388
    if (index > 255) {
×
3389
        index = 255;
×
3390
    }
3391

3392
    table = lso3_table();
×
3393
    entry = table[index];
×
3394
    denom = entry[6];
×
3395
    if (denom == 0) {
×
3396
        return;
×
3397
    }
3398

3399
    term_r = diffuse_varerr_term(error, entry[0], denom);
×
3400
    term_r2 = diffuse_varerr_term(error, entry[1], denom);
×
3401
    term_dl = diffuse_varerr_term(error, entry[2], denom);
×
3402
    term_d = diffuse_varerr_term(error, entry[3], denom);
×
3403
    term_dr = diffuse_varerr_term(error, entry[4], denom);
×
3404
    term_d2 = error - term_r - term_r2 - term_dl - term_d - term_dr;
×
3405

3406
    if (direction >= 0) {
×
3407
        if (x + 1 < width) {
×
3408
            offset = (size_t)y * (size_t)width + (size_t)(x + 1);
×
3409
            diffuse_varerr_apply_direct(data, depth, offset, term_r);
×
3410
        }
3411
        if (x + 2 < width) {
×
3412
            offset = (size_t)y * (size_t)width + (size_t)(x + 2);
×
3413
            diffuse_varerr_apply_direct(data, depth, offset, term_r2);
×
3414
        }
3415
        if (y + 1 < height && x - 1 >= 0) {
×
3416
            offset = (size_t)(y + 1) * (size_t)width;
×
3417
            offset += (size_t)(x - 1);
×
3418
            diffuse_varerr_apply_direct(data, depth, offset, term_dl);
×
3419
        }
3420
        if (y + 1 < height) {
×
3421
            offset = (size_t)(y + 1) * (size_t)width + (size_t)x;
×
3422
            diffuse_varerr_apply_direct(data, depth, offset, term_d);
×
3423
        }
3424
        if (y + 1 < height && x + 1 < width) {
×
3425
            offset = (size_t)(y + 1) * (size_t)width;
×
3426
            offset += (size_t)(x + 1);
×
3427
            diffuse_varerr_apply_direct(data, depth, offset, term_dr);
×
3428
        }
3429
        if (y + 2 < height) {
×
3430
            offset = (size_t)(y + 2) * (size_t)width + (size_t)x;
×
3431
            diffuse_varerr_apply_direct(data, depth, offset, term_d2);
×
3432
        }
3433
    } else {
3434
        if (x - 1 >= 0) {
×
3435
            offset = (size_t)y * (size_t)width + (size_t)(x - 1);
×
3436
            diffuse_varerr_apply_direct(data, depth, offset, term_r);
×
3437
        }
3438
        if (x - 1 >= 0) {
×
3439
            offset = (size_t)y * (size_t)width + (size_t)(x - 2);
×
3440
            diffuse_varerr_apply_direct(data, depth, offset, term_r2);
×
3441
        }
3442
        if (y + 1 < height && x + 1 < width) {
×
3443
            offset = (size_t)(y + 1) * (size_t)width;
×
3444
            offset += (size_t)(x + 1);
×
3445
            diffuse_varerr_apply_direct(data, depth, offset, term_dl);
×
3446
        }
3447
        if (y + 1 < height) {
×
3448
            offset = (size_t)(y + 1) * (size_t)width + (size_t)x;
×
3449
            diffuse_varerr_apply_direct(data, depth, offset, term_d);
×
3450
        }
3451
        if (y + 1 < height && x - 1 >= 0) {
×
3452
            offset = (size_t)(y + 1) * (size_t)width;
×
3453
            offset += (size_t)(x - 1);
×
3454
            diffuse_varerr_apply_direct(data, depth, offset, term_dr);
×
3455
        }
3456
        if (y + 2 < height) {
×
3457
            offset = (size_t)(y + 2) * (size_t)width + (size_t)x;
×
3458
            diffuse_varerr_apply_direct(data, depth, offset, term_d2);
×
3459
        }
3460
    }
3461
}
3462

3463

3464
static void
3465
diffuse_lso2_carry(int32_t *carry_curr, int32_t *carry_next, int32_t *carry_far,
×
3466
                   int width, int height, int depth,
3467
                   int x, int y, int32_t error,
3468
                   int index, int direction, int channel)
3469
{
3470
    const int (*table)[7];
3471
    const int *entry;
3472
    int denom;
3473
    int32_t term_r = 0;
×
3474
    int32_t term_r2 = 0;
×
3475
    int32_t term_dl = 0;
×
3476
    int32_t term_d = 0;
×
3477
    int32_t term_dr = 0;
×
3478
    int32_t term_d2 = 0;
×
3479
    size_t base;
3480

3481
    if (error == 0) {
×
3482
        return;
×
3483
    }
3484
    if (index < 0) {
×
3485
        index = 0;
×
3486
    }
3487
    if (index > 255) {
×
3488
        index = 255;
×
3489
    }
3490

3491
    table = lso2_table();
×
3492
    entry = table[index];
×
3493
    denom = entry[6];
×
3494
    if (denom == 0) {
×
3495
        return;
×
3496
    }
3497

3498
    term_r = diffuse_varerr_term(error, entry[0], denom);
×
3499
    term_r2 = diffuse_varerr_term(error, entry[1], denom);
×
3500
    term_dl = diffuse_varerr_term(error, entry[2], denom);
×
3501
    term_d = diffuse_varerr_term(error, entry[3], denom);
×
3502
    term_dr = diffuse_varerr_term(error, entry[4], denom);
×
3503
    term_d2 = error - term_r - term_r2 - term_dl - term_d - term_dr;
×
3504

3505
    if (direction >= 0) {
×
3506
        if (x + 1 < width) {
×
3507
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
3508
            carry_curr[base] += term_r;
×
3509
        }
3510
        if (x + 2 < width) {
×
3511
            base = ((size_t)(x + 2) * (size_t)depth) + (size_t)channel;
×
3512
            carry_curr[base] += term_r2;
×
3513
        }
3514
        if (y + 1 < height && x - 1 >= 0) {
×
3515
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
3516
            carry_next[base] += term_dl;
×
3517
        }
3518
        if (y + 1 < height) {
×
3519
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
3520
            carry_next[base] += term_d;
×
3521
        }
3522
        if (y + 1 < height && x + 1 < width) {
×
3523
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
3524
            carry_next[base] += term_dr;
×
3525
        }
3526
        if (y + 2 < height) {
×
3527
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
3528
            carry_far[base] += term_d2;
×
3529
        }
3530
    } else {
3531
        if (x - 1 >= 0) {
×
3532
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
3533
            carry_curr[base] += term_r;
×
3534
        }
3535
        if (x - 2 >= 0) {
×
3536
            base = ((size_t)(x - 2) * (size_t)depth) + (size_t)channel;
×
3537
            carry_curr[base] += term_r;
×
3538
        }
3539
        if (y + 1 < height && x + 1 < width) {
×
3540
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
3541
            carry_next[base] += term_dl;
×
3542
        }
3543
        if (y + 1 < height) {
×
3544
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
3545
            carry_next[base] += term_d;
×
3546
        }
3547
        if (y + 1 < height && x - 1 >= 0) {
×
3548
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
3549
            carry_next[base] += term_dr;
×
3550
        }
3551
        if (y + 2 < height) {
×
3552
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
3553
            carry_far[base] += term_d2;
×
3554
        }
3555
    }
3556
}
3557

3558

3559
static void
3560
diffuse_lso3_carry(int32_t *carry_curr, int32_t *carry_next, int32_t *carry_far,
×
3561
                   int width, int height, int depth,
3562
                   int x, int y, int32_t error,
3563
                   int index, int direction, int channel)
3564
{
3565
    const int (*table)[7];
3566
    const int *entry;
3567
    int denom;
3568
    int32_t term_r = 0;
×
3569
    int32_t term_r2 = 0;
×
3570
    int32_t term_dl = 0;
×
3571
    int32_t term_d = 0;
×
3572
    int32_t term_dr = 0;
×
3573
    int32_t term_d2 = 0;
×
3574
    size_t base;
3575

3576
    if (error == 0) {
×
3577
        return;
×
3578
    }
3579
    if (index < 0) {
×
3580
        index = 0;
×
3581
    }
3582
    if (index > 255) {
×
3583
        index = 255;
×
3584
    }
3585

3586
    table = lso3_table();
×
3587
    entry = table[index];
×
3588
    denom = entry[6];
×
3589
    if (denom == 0) {
×
3590
        return;
×
3591
    }
3592

3593
    term_r = diffuse_varerr_term(error, entry[0], denom);
×
3594
    term_r2 = diffuse_varerr_term(error, entry[1], denom);
×
3595
    term_dl = diffuse_varerr_term(error, entry[2], denom);
×
3596
    term_d = diffuse_varerr_term(error, entry[3], denom);
×
3597
    term_dr = diffuse_varerr_term(error, entry[4], denom);
×
3598
    term_d2 = diffuse_varerr_term(error, entry[5], denom);
×
3599

3600
    if (direction >= 0) {
×
3601
        if (x + 1 < width) {
×
3602
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
3603
            carry_curr[base] += term_r;
×
3604
        }
3605
        if (x + 2 < width) {
×
3606
            base = ((size_t)(x + 2) * (size_t)depth) + (size_t)channel;
×
3607
            carry_curr[base] += term_r2;
×
3608
        }
3609
        if (y + 1 < height && x - 1 >= 0) {
×
3610
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
3611
            carry_next[base] += term_dl;
×
3612
        }
3613
        if (y + 1 < height) {
×
3614
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
3615
            carry_next[base] += term_d;
×
3616
        }
3617
        if (y + 1 < height && x + 1 < width) {
×
3618
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
3619
            carry_next[base] += term_dr;
×
3620
        }
3621
        if (y + 2 < height) {
×
3622
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
3623
            carry_far[base] += term_d2;
×
3624
        }
3625
    } else {
3626
        if (x - 1 >= 0) {
×
3627
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
3628
            carry_curr[base] += term_r;
×
3629
        }
3630
        if (x - 2 >= 0) {
×
3631
            base = ((size_t)(x - 2) * (size_t)depth) + (size_t)channel;
×
3632
            carry_curr[base] += term_r2;
×
3633
        }
3634
        if (y + 1 < height && x + 1 < width) {
×
3635
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
3636
            carry_next[base] += term_dl;
×
3637
        }
3638
        if (y + 1 < height) {
×
3639
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
3640
            carry_next[base] += term_d;
×
3641
        }
3642
        if (y + 1 < height && x - 1 >= 0) {
×
3643
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
3644
            carry_next[base] += term_dr;
×
3645
        }
3646
        if (y + 2 < height) {
×
3647
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
3648
            carry_far[base] += term_d2;
×
3649
        }
3650
    }
3651
}
3652

3653

3654
static void
3655
scanline_params(int serpentine, int index, int limit,
53,794✔
3656
                int *start, int *end, int *step, int *direction)
3657
{
3658
    if (serpentine && (index & 1)) {
53,794✔
3659
        *start = limit - 1;
675✔
3660
        *end = -1;
675✔
3661
        *step = -1;
675✔
3662
        *direction = -1;
675✔
3663
    } else {
225✔
3664
        *start = 0;
53,119✔
3665
        *end = limit;
53,119✔
3666
        *step = 1;
53,119✔
3667
        *direction = 1;
53,119✔
3668
    }
3669
}
53,794✔
3670

3671

3672
static SIXELSTATUS
3673
apply_palette_positional(
×
3674
    sixel_index_t *result,
3675
    unsigned char *data,
3676
    int width,
3677
    int height,
3678
    int depth,
3679
    unsigned char *palette,
3680
    int reqcolor,
3681
    int methodForDiffuse,
3682
    int methodForScan,
3683
    int foptimize_palette,
3684
    int (*f_lookup)(const unsigned char *pixel,
3685
                    int depth,
3686
                    const unsigned char *palette,
3687
                    int reqcolor,
3688
                    unsigned short *cachetable,
3689
                    int complexion),
3690
    unsigned short *indextable,
3691
    int complexion,
3692
    unsigned char copy[],
3693
    unsigned char new_palette[],
3694
    unsigned short migration_map[],
3695
    int *ncolors)
3696
{
3697
    int serpentine;
3698
    int y;
3699
    float (*f_mask)(int x, int y, int c);
3700

3701
    switch (methodForDiffuse) {
×
3702
    case SIXEL_DIFFUSE_A_DITHER:
3703
        f_mask = mask_a;
×
3704
        break;
×
3705
    case SIXEL_DIFFUSE_X_DITHER:
×
3706
    default:
3707
        f_mask = mask_x;
×
3708
        break;
×
3709
    }
3710

3711
    serpentine = (methodForScan == SIXEL_SCAN_SERPENTINE);
×
3712

3713
    if (foptimize_palette) {
×
3714
        int x;
3715

3716
        *ncolors = 0;
×
3717
        memset(new_palette, 0x00,
×
3718
               (size_t)SIXEL_PALETTE_MAX * (size_t)depth);
3719
        memset(migration_map, 0x00,
×
3720
               sizeof(unsigned short) * (size_t)SIXEL_PALETTE_MAX);
3721
        for (y = 0; y < height; ++y) {
×
3722
            int start;
3723
            int end;
3724
            int step;
3725
            int direction;
3726

3727
            scanline_params(serpentine, y, width,
×
3728
                            &start, &end, &step, &direction);
3729
            (void)direction;
3730
            for (x = start; x != end; x += step) {
×
3731
                int pos;
3732
                int d;
3733
                int color_index;
3734

3735
                pos = y * width + x;
×
3736
                for (d = 0; d < depth; ++d) {
×
3737
                    int val;
3738

3739
                    val = data[pos * depth + d]
×
3740
                        + f_mask(x, y, d) * 32;
×
3741
                    copy[d] = val < 0 ? 0
×
3742
                               : val > 255 ? 255 : val;
×
3743
                }
3744
                color_index = f_lookup(copy, depth, palette,
×
3745
                                       reqcolor, indextable,
3746
                                       complexion);
3747
                if (migration_map[color_index] == 0) {
×
3748
                    result[pos] = *ncolors;
×
3749
                    for (d = 0; d < depth; ++d) {
×
3750
                        new_palette[*ncolors * depth + d]
×
3751
                            = palette[color_index * depth + d];
×
3752
                    }
3753
                    ++*ncolors;
×
3754
                    migration_map[color_index] = *ncolors;
×
3755
                } else {
3756
                    result[pos] = migration_map[color_index] - 1;
×
3757
                }
3758
            }
3759
        }
3760
        memcpy(palette, new_palette, (size_t)(*ncolors * depth));
×
3761
    } else {
3762
        int x;
3763

3764
        for (y = 0; y < height; ++y) {
×
3765
            int start;
3766
            int end;
3767
            int step;
3768
            int direction;
3769

3770
            scanline_params(serpentine, y, width,
×
3771
                            &start, &end, &step, &direction);
3772
            (void)direction;
3773
            for (x = start; x != end; x += step) {
×
3774
                int pos;
3775
                int d;
3776

3777
                pos = y * width + x;
×
3778
                for (d = 0; d < depth; ++d) {
×
3779
                    int val;
3780

3781
                    val = data[pos * depth + d]
×
3782
                        + f_mask(x, y, d) * 32;
×
3783
                    copy[d] = val < 0 ? 0
×
3784
                               : val > 255 ? 255 : val;
×
3785
                }
3786
                result[pos] = f_lookup(copy, depth, palette,
×
3787
                                       reqcolor, indextable,
3788
                                       complexion);
3789
            }
3790
        }
3791
        *ncolors = reqcolor;
×
3792
    }
3793

3794
    return SIXEL_OK;
×
3795
}
3796

3797

3798
static SIXELSTATUS
3799
apply_palette_variable(
×
3800
    sixel_index_t *result,
3801
    unsigned char *data,
3802
    int width,
3803
    int height,
3804
    int depth,
3805
    unsigned char *palette,
3806
    int reqcolor,
3807
    int methodForScan,
3808
    int foptimize_palette,
3809
    int (*f_lookup)(const unsigned char *pixel,
3810
                    int depth,
3811
                    const unsigned char *palette,
3812
                    int reqcolor,
3813
                    unsigned short *cachetable,
3814
                    int complexion),
3815
    unsigned short *indextable,
3816
    int complexion,
3817
    unsigned char new_palette[],
3818
    unsigned short migration_map[],
3819
    int *ncolors,
3820
    int methodForDiffuse,
3821
    int methodForCarry)
3822
{
3823
    SIXELSTATUS status = SIXEL_FALSE;
×
3824
#if _MSC_VER
3825
    enum { max_channels = 4 };
3826
#else
3827
    const int max_channels = 4;
×
3828
#endif
3829
    int serpentine;
3830
    int y;
3831
    diffuse_varerr_mode varerr_diffuse;
3832
    diffuse_varerr_carry_mode varerr_diffuse_carry;
3833
    int use_carry;
3834
    size_t carry_len;
3835
    int32_t *carry_curr = NULL;
×
3836
    int32_t *carry_next = NULL;
×
3837
    int32_t *carry_far = NULL;
×
3838
    unsigned char corrected[max_channels];
3839
    int32_t sample_scaled[max_channels];
3840
    int32_t accum_scaled[max_channels];
3841
    int start;
3842
    int end;
3843
    int step;
3844
    int direction;
3845
    int x;
3846
    int pos;
3847
    size_t base;
3848
    size_t carry_base;
3849
    const unsigned char *source_pixel;
3850
    int color_index;
3851
    int output_index;
3852
    int n;
3853
    int palette_value;
3854
    int diff;
3855
    int table_index;
3856
    int64_t accum;
3857
    int64_t clamped;
3858
    int32_t target_scaled;
3859
    int32_t error_scaled;
3860
    int32_t *tmp;
3861

3862
    if (depth > max_channels) {
×
3863
        status = SIXEL_BAD_ARGUMENT;
×
3864
        goto end;
×
3865
    }
3866

3867
    use_carry = (methodForCarry == SIXEL_CARRY_ENABLE);
×
3868
    carry_len = 0;
×
3869

3870
    switch (methodForDiffuse) {
×
3871
    case SIXEL_DIFFUSE_LSO2:
3872
        varerr_diffuse = diffuse_lso2;
×
3873
        varerr_diffuse_carry = diffuse_lso2_carry;
×
3874
        break;
×
3875
    case SIXEL_DIFFUSE_LSO3:
3876
        varerr_diffuse = diffuse_lso3;
×
3877
        varerr_diffuse_carry = diffuse_lso3_carry;
×
3878
        srand((unsigned int)time(NULL));
×
3879
        break;
×
3880
    default:
3881
        varerr_diffuse = diffuse_lso2;
×
3882
        varerr_diffuse_carry = diffuse_lso2_carry;
×
3883
        break;
×
3884
    }
3885

3886
    if (use_carry) {
×
3887
        carry_len = (size_t)width * (size_t)depth;
×
3888
        carry_curr = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
3889
        if (carry_curr == NULL) {
×
3890
            status = SIXEL_BAD_ALLOCATION;
×
3891
            goto end;
×
3892
        }
3893
        carry_next = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
3894
        if (carry_next == NULL) {
×
3895
            status = SIXEL_BAD_ALLOCATION;
×
3896
            goto end;
×
3897
        }
3898
        carry_far = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
3899
        if (carry_far == NULL) {
×
3900
            status = SIXEL_BAD_ALLOCATION;
×
3901
            goto end;
×
3902
        }
3903
    }
3904

3905
    serpentine = (methodForScan == SIXEL_SCAN_SERPENTINE);
×
3906

3907
    if (foptimize_palette) {
×
3908
        *ncolors = 0;
×
3909
        memset(new_palette, 0x00,
×
3910
               (size_t)SIXEL_PALETTE_MAX * (size_t)depth);
3911
        memset(migration_map, 0x00,
×
3912
               sizeof(unsigned short) * (size_t)SIXEL_PALETTE_MAX);
3913
    }
3914

3915
    for (y = 0; y < height; ++y) {
×
3916
        scanline_params(serpentine, y, width,
×
3917
                        &start, &end, &step, &direction);
3918
        for (x = start; x != end; x += step) {
×
3919
            pos = y * width + x;
×
3920
            base = (size_t)pos * (size_t)depth;
×
3921
            carry_base = (size_t)x * (size_t)depth;
×
3922
            if (use_carry) {
×
3923
                for (n = 0; n < depth; ++n) {
×
3924
                    accum = ((int64_t)data[base + n]
×
3925
                             << VARERR_SCALE_SHIFT)
×
3926
                          + carry_curr[carry_base + (size_t)n];
×
3927
                    if (accum < INT32_MIN) {
×
3928
                        accum = INT32_MIN;
×
3929
                    } else if (accum > INT32_MAX) {
×
3930
                        accum = INT32_MAX;
×
3931
                    }
3932
                    carry_curr[carry_base + (size_t)n] = 0;
×
3933
                    clamped = accum;
×
3934
                    if (clamped < 0) {
×
3935
                        clamped = 0;
×
3936
                    } else if (clamped > VARERR_MAX_VALUE) {
×
3937
                        clamped = VARERR_MAX_VALUE;
×
3938
                    }
3939
                    accum_scaled[n] = (int32_t)clamped;
×
3940
                    corrected[n]
3941
                        = (unsigned char)((clamped + VARERR_ROUND)
×
3942
                                          >> VARERR_SCALE_SHIFT);
×
3943
                }
3944
                source_pixel = corrected;
×
3945
            } else {
3946
                for (n = 0; n < depth; ++n) {
×
3947
                    sample_scaled[n]
3948
                        = (int32_t)data[base + n]
×
3949
                        << VARERR_SCALE_SHIFT;
×
3950
                    corrected[n] = data[base + n];
×
3951
                }
3952
                source_pixel = data + base;
×
3953
            }
3954

3955
            color_index = f_lookup(source_pixel, depth, palette,
×
3956
                                   reqcolor, indextable,
3957
                                   complexion);
3958

3959
            if (foptimize_palette) {
×
3960
                if (migration_map[color_index] == 0) {
×
3961
                    output_index = *ncolors;
×
3962
                    for (n = 0; n < depth; ++n) {
×
3963
                        new_palette[output_index * depth + n]
×
3964
                            = palette[color_index * depth + n];
×
3965
                    }
3966
                    ++*ncolors;
×
3967
                    migration_map[color_index] = *ncolors;
×
3968
                } else {
3969
                    output_index = migration_map[color_index] - 1;
×
3970
                }
3971
                result[pos] = output_index;
×
3972
            } else {
3973
                output_index = color_index;
×
3974
                result[pos] = output_index;
×
3975
            }
3976

3977
            for (n = 0; n < depth; ++n) {
×
3978
                if (foptimize_palette) {
×
3979
                    palette_value = new_palette[output_index * depth + n];
×
3980
                } else {
3981
                    palette_value = palette[color_index * depth + n];
×
3982
                }
3983
                diff = (int)source_pixel[n] - palette_value;
×
3984
                if (diff < 0) {
×
3985
                    diff = -diff;
×
3986
                }
3987
                if (diff > 255) {
×
3988
                    diff = 255;
×
3989
                }
3990
                table_index = diff;
×
3991
                if (methodForDiffuse == SIXEL_DIFFUSE_LSO3) {
×
3992
                    table_index = zhoufang_index_from_byte(
×
3993
                        (unsigned char)source_pixel[n]);
×
3994
                }
3995
                if (use_carry) {
×
3996
                    target_scaled = (int32_t)palette_value
×
3997
                                  << VARERR_SCALE_SHIFT;
3998
                    error_scaled = accum_scaled[n] - target_scaled;
×
3999
                    varerr_diffuse_carry(carry_curr, carry_next, carry_far,
×
4000
                                         width, height, depth,
4001
                                         x, y, error_scaled,
4002
                                         table_index,
4003
                                         direction, n);
4004
                } else {
4005
                    target_scaled = (int32_t)palette_value
×
4006
                                  << VARERR_SCALE_SHIFT;
4007
                    error_scaled = sample_scaled[n] - target_scaled;
×
4008
                    varerr_diffuse(data + n, width, height,
×
4009
                                   x, y, depth, error_scaled,
4010
                                   table_index,
4011
                                   direction);
4012
                }
4013
            }
4014
        }
4015
        if (use_carry) {
×
4016
            tmp = carry_curr;
×
4017
            carry_curr = carry_next;
×
4018
            carry_next = carry_far;
×
4019
            carry_far = tmp;
×
4020
            if (carry_len > 0) {
×
4021
                memset(carry_far, 0x00, carry_len * sizeof(int32_t));
×
4022
            }
4023
        }
4024
    }
4025

4026
    if (foptimize_palette) {
×
4027
        memcpy(palette, new_palette, (size_t)(*ncolors * depth));
×
4028
    } else {
4029
        *ncolors = reqcolor;
×
4030
    }
4031

4032
    status = SIXEL_OK;
×
4033

4034
end:
4035
    free(carry_next);
×
4036
    free(carry_curr);
×
4037
    free(carry_far);
×
4038
    return status;
×
4039
}
4040

4041

4042
static SIXELSTATUS
4043
apply_palette_fixed(
281✔
4044
    sixel_index_t *result,
4045
    unsigned char *data,
4046
    int width,
4047
    int height,
4048
    int depth,
4049
    unsigned char *palette,
4050
    int reqcolor,
4051
    int methodForScan,
4052
    int foptimize_palette,
4053
    int (*f_lookup)(const unsigned char *pixel,
4054
                    int depth,
4055
                    const unsigned char *palette,
4056
                    int reqcolor,
4057
                    unsigned short *cachetable,
4058
                    int complexion),
4059
    unsigned short *indextable,
4060
    int complexion,
4061
    unsigned char new_palette[],
4062
    unsigned short migration_map[],
4063
    int *ncolors,
4064
    int methodForDiffuse,
4065
    int methodForCarry)
4066
{
168✔
4067
#if _MSC_VER
4068
    enum { max_channels = 4 };
4069
#else
4070
    const int max_channels = 4;
281✔
4071
#endif
4072
    SIXELSTATUS status = SIXEL_FALSE;
281✔
4073
    int serpentine;
4074
    int y;
4075
    void (*f_diffuse)(unsigned char *data,
4076
                      int width,
4077
                      int height,
4078
                      int x,
4079
                      int y,
4080
                      int depth,
4081
                      int offset,
4082
                      int direction);
4083
    diffuse_fixed_carry_mode f_diffuse_carry;
4084
    int use_carry;
4085
    size_t carry_len;
4086
    int32_t *carry_curr = NULL;
281✔
4087
    int32_t *carry_next = NULL;
281✔
4088
    int32_t *carry_far = NULL;
281✔
4089
    unsigned char corrected[max_channels];
168✔
4090
    int32_t accum_scaled[max_channels];
168✔
4091
    int start;
4092
    int end;
4093
    int step;
4094
    int direction;
4095
    int x;
4096
    int pos;
4097
    size_t base;
4098
    size_t carry_base;
4099
    const unsigned char *source_pixel;
4100
    int color_index;
4101
    int output_index;
4102
    int n;
4103
    int palette_value;
4104
    int64_t accum;
4105
    int64_t clamped;
4106
    int32_t target_scaled;
4107
    int32_t error_scaled;
4108
    int offset;
4109
    int32_t *tmp;
4110

4111
    if (depth > max_channels) {
281!
4112
        status = SIXEL_BAD_ARGUMENT;
×
4113
        goto end;
×
4114
    }
4115

4116
    use_carry = (methodForCarry == SIXEL_CARRY_ENABLE);
281✔
4117
    carry_len = 0;
281✔
4118

4119
    if (depth != 3) {
281!
4120
        f_diffuse = diffuse_none;
×
4121
        f_diffuse_carry = diffuse_none_carry;
×
4122
        use_carry = 0;
×
4123
    } else {
4124
        switch (methodForDiffuse) {
281!
4125
        case SIXEL_DIFFUSE_NONE:
86✔
4126
            f_diffuse = diffuse_none;
157✔
4127
            f_diffuse_carry = diffuse_none_carry;
157✔
4128
            break;
157✔
4129
        case SIXEL_DIFFUSE_ATKINSON:
44✔
4130
            f_diffuse = diffuse_atkinson;
67✔
4131
            f_diffuse_carry = diffuse_atkinson_carry;
67✔
4132
            break;
67✔
4133
        case SIXEL_DIFFUSE_FS:
32✔
4134
            f_diffuse = diffuse_fs;
48✔
4135
            f_diffuse_carry = diffuse_fs_carry;
48✔
4136
            break;
48✔
4137
        case SIXEL_DIFFUSE_JAJUNI:
2✔
4138
            f_diffuse = diffuse_jajuni;
3✔
4139
            f_diffuse_carry = diffuse_jajuni_carry;
3✔
4140
            break;
3✔
4141
        case SIXEL_DIFFUSE_STUCKI:
2✔
4142
            f_diffuse = diffuse_stucki;
3✔
4143
            f_diffuse_carry = diffuse_stucki_carry;
3✔
4144
            break;
3✔
4145
        case SIXEL_DIFFUSE_BURKES:
2✔
4146
            f_diffuse = diffuse_burkes;
3✔
4147
            f_diffuse_carry = diffuse_burkes_carry;
3✔
4148
            break;
3✔
4149
        case SIXEL_DIFFUSE_SIERRA1:
4150
            f_diffuse = diffuse_sierra1;
×
4151
            f_diffuse_carry = diffuse_sierra1_carry;
×
4152
            break;
×
4153
        case SIXEL_DIFFUSE_SIERRA2:
4154
            f_diffuse = diffuse_sierra2;
×
4155
            f_diffuse_carry = diffuse_sierra2_carry;
×
4156
            break;
×
4157
        case SIXEL_DIFFUSE_SIERRA3:
4158
            f_diffuse = diffuse_sierra3;
×
4159
            f_diffuse_carry = diffuse_sierra3_carry;
×
4160
            break;
×
4161
        case SIXEL_DIFFUSE_LSO1:
4162
            f_diffuse = diffuse_lso1;
×
4163
            f_diffuse_carry = diffuse_lso1_carry;
×
4164
            break;
×
4165
        default:
4166
            quant_trace(stderr,
×
4167
                        "Internal error: invalid methodForDiffuse: %d\n",
4168
                        methodForDiffuse);
4169
            f_diffuse = diffuse_none;
×
4170
            f_diffuse_carry = diffuse_none_carry;
×
4171
            break;
×
4172
        }
4173
    }
4174

4175
    if (use_carry) {
281!
4176
        carry_len = (size_t)width * (size_t)depth;
×
4177
        if (carry_len > 0) {
×
4178
            carry_curr = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
4179
            if (carry_curr == NULL) {
×
4180
                status = SIXEL_BAD_ALLOCATION;
×
4181
                goto end;
×
4182
            }
4183
            carry_next = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
4184
            if (carry_next == NULL) {
×
4185
                status = SIXEL_BAD_ALLOCATION;
×
4186
                goto end;
×
4187
            }
4188
            carry_far = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
4189
            if (carry_far == NULL) {
×
4190
                status = SIXEL_BAD_ALLOCATION;
×
4191
                goto end;
×
4192
            }
4193
        } else {
4194
            use_carry = 0;
×
4195
        }
4196
    }
4197

4198
    serpentine = (methodForScan == SIXEL_SCAN_SERPENTINE);
281✔
4199

4200
    if (foptimize_palette) {
281✔
4201
        *ncolors = 0;
202✔
4202
        memset(new_palette, 0x00,
202✔
4203
               (size_t)SIXEL_PALETTE_MAX * (size_t)depth);
128✔
4204
        memset(migration_map, 0x00,
202✔
4205
               sizeof(unsigned short) * (size_t)SIXEL_PALETTE_MAX);
4206
    } else {
74✔
4207
        *ncolors = reqcolor;
79✔
4208
    }
4209

4210
    for (y = 0; y < height; ++y) {
54,075✔
4211
        scanline_params(serpentine, y, width,
53,794✔
4212
                        &start, &end, &step, &direction);
4213
        for (x = start; x != end; x += step) {
26,469,500✔
4214
            pos = y * width + x;
26,415,706✔
4215
            base = (size_t)pos * (size_t)depth;
26,415,706✔
4216
            carry_base = (size_t)x * (size_t)depth;
26,415,706✔
4217
            if (use_carry) {
26,415,706!
4218
                for (n = 0; n < depth; ++n) {
×
4219
                    accum = ((int64_t)data[base + n]
×
4220
                             << VARERR_SCALE_SHIFT)
×
4221
                           + carry_curr[carry_base + (size_t)n];
×
4222
                    if (accum < INT32_MIN) {
×
4223
                        accum = INT32_MIN;
×
4224
                    } else if (accum > INT32_MAX) {
×
4225
                        accum = INT32_MAX;
×
4226
                    }
4227
                    clamped = accum;
×
4228
                    if (clamped < 0) {
×
4229
                        clamped = 0;
×
4230
                    } else if (clamped > VARERR_MAX_VALUE) {
×
4231
                        clamped = VARERR_MAX_VALUE;
×
4232
                    }
4233
                    accum_scaled[n] = (int32_t)clamped;
×
4234
                    corrected[n]
4235
                        = (unsigned char)((clamped + VARERR_ROUND)
×
4236
                                          >> VARERR_SCALE_SHIFT);
×
4237
                    data[base + n] = corrected[n];
×
4238
                    carry_curr[carry_base + (size_t)n] = 0;
×
4239
                }
4240
                source_pixel = corrected;
×
4241
            } else {
4242
                source_pixel = data + base;
26,415,706✔
4243
            }
4244

4245
            color_index = f_lookup(source_pixel, depth, palette,
38,025,328✔
4246
                                   reqcolor, indextable,
11,609,622✔
4247
                                   complexion);
11,609,622✔
4248

4249
            if (foptimize_palette) {
26,415,706✔
4250
                if (migration_map[color_index] == 0) {
14,314,206✔
4251
                    output_index = *ncolors;
15,636✔
4252
                    for (n = 0; n < depth; ++n) {
62,544✔
4253
                        new_palette[output_index * depth + n]
46,908✔
4254
                            = palette[color_index * depth + n];
62,628✔
4255
                    }
15,720✔
4256
                    ++*ncolors;
15,636✔
4257
                    migration_map[color_index] = *ncolors;
15,636✔
4258
                } else {
5,240✔
4259
                    output_index = migration_map[color_index] - 1;
14,298,570✔
4260
                }
4261
                result[pos] = output_index;
14,314,206✔
4262
            } else {
5,769,802✔
4263
                output_index = color_index;
12,101,500✔
4264
                result[pos] = output_index;
12,101,500✔
4265
            }
4266

4267
            for (n = 0; n < depth; ++n) {
105,662,824✔
4268
                if (foptimize_palette) {
79,247,118✔
4269
                    palette_value = new_palette[output_index * depth + n];
42,942,618✔
4270
                } else {
17,309,406✔
4271
                    palette_value = palette[color_index * depth + n];
36,304,500✔
4272
                }
4273
                if (use_carry) {
79,247,118!
4274
                    target_scaled = (int32_t)palette_value
×
4275
                                  << VARERR_SCALE_SHIFT;
4276
                    error_scaled = accum_scaled[n] - target_scaled;
×
4277
                    f_diffuse_carry(carry_curr, carry_next, carry_far,
×
4278
                                    width, height, depth,
4279
                                    x, y, error_scaled, direction, n);
4280
                } else {
4281
                    offset = (int)source_pixel[n] - palette_value;
79,247,118✔
4282
                    f_diffuse(data + n, width, height, x, y,
114,075,984✔
4283
                              depth, offset, direction);
34,828,866✔
4284
                }
4285
            }
34,828,866✔
4286
        }
11,609,622✔
4287
        if (use_carry) {
53,794!
4288
            tmp = carry_curr;
×
4289
            carry_curr = carry_next;
×
4290
            carry_next = carry_far;
×
4291
            carry_far = tmp;
×
4292
            if (carry_len > 0) {
×
4293
                memset(carry_far, 0x00, carry_len * sizeof(int32_t));
×
4294
            }
4295
        }
4296
    }
23,822✔
4297

4298
    if (foptimize_palette) {
281✔
4299
        memcpy(palette, new_palette, (size_t)(*ncolors * depth));
202✔
4300
    }
74✔
4301

4302
    status = SIXEL_OK;
281✔
4303

4304
end:
168✔
4305
    free(carry_far);
281✔
4306
    free(carry_next);
281✔
4307
    free(carry_curr);
281✔
4308
    return status;
281✔
4309
}
4310

4311

4312
static void
4313
diffuse_none(unsigned char *data, int width, int height,
18,248,814✔
4314
             int x, int y, int depth, int error, int direction)
4315
{
4316
    /* unused */ (void) data;
14,469,498✔
4317
    /* unused */ (void) width;
14,469,498✔
4318
    /* unused */ (void) height;
14,469,498✔
4319
    /* unused */ (void) x;
14,469,498✔
4320
    /* unused */ (void) y;
14,469,498✔
4321
    /* unused */ (void) depth;
14,469,498✔
4322
    /* unused */ (void) error;
14,469,498✔
4323
    /* unused */ (void) direction;
14,469,498✔
4324
}
18,248,814✔
4325

4326

4327
static void
4328
diffuse_none_carry(int32_t *carry_curr, int32_t *carry_next,
×
4329
                   int32_t *carry_far, int width, int height,
4330
                   int depth, int x, int y, int32_t error,
4331
                   int direction, int channel)
4332
{
4333
    /* unused */ (void) carry_curr;
4334
    /* unused */ (void) carry_next;
4335
    /* unused */ (void) carry_far;
4336
    /* unused */ (void) width;
4337
    /* unused */ (void) height;
4338
    /* unused */ (void) depth;
4339
    /* unused */ (void) x;
4340
    /* unused */ (void) y;
4341
    /* unused */ (void) error;
4342
    /* unused */ (void) direction;
4343
    /* unused */ (void) channel;
4344
}
×
4345

4346

4347
static void
4348
diffuse_fs(unsigned char *data, int width, int height,
32,061,744✔
4349
           int x, int y, int depth, int error, int direction)
4350
{
4351
    /* Floyd Steinberg Method
4352
     *          curr    7/16
4353
     *  3/16    5/48    1/16
4354
     */
4355
    int pos;
4356
    int forward;
4357

4358
    pos = y * width + x;
32,061,744✔
4359
    forward = direction >= 0;
32,061,744✔
4360

4361
    if (forward) {
32,061,744✔
4362
        if (x < width - 1) {
30,846,744✔
4363
            error_diffuse_normal(data, pos + 1, depth, error, 7, 16);
30,789,423✔
4364
        }
10,263,141✔
4365
        if (y < height - 1) {
30,846,744✔
4366
            if (x > 0) {
30,779,028✔
4367
                error_diffuse_normal(data,
40,962,456✔
4368
                                     pos + width - 1,
30,721,842✔
4369
                                     depth, error, 3, 16);
10,240,614✔
4370
            }
10,240,614✔
4371
            error_diffuse_normal(data,
41,038,704✔
4372
                                 pos + width,
10,259,676✔
4373
                                 depth, error, 5, 16);
10,259,676✔
4374
            if (x < width - 1) {
30,779,028✔
4375
                error_diffuse_normal(data,
40,962,456✔
4376
                                     pos + width + 1,
30,721,842✔
4377
                                     depth, error, 1, 16);
10,240,614✔
4378
            }
10,240,614✔
4379
        }
10,259,676✔
4380
    } else {
10,282,248✔
4381
        if (x > 0) {
1,215,000✔
4382
            error_diffuse_normal(data, pos - 1, depth, error, 7, 16);
1,212,975✔
4383
        }
404,325✔
4384
        if (y < height - 1) {
1,215,000✔
4385
            if (x < width - 1) {
1,209,600✔
4386
                error_diffuse_normal(data,
1,610,112✔
4387
                                     pos + width + 1,
1,207,584✔
4388
                                     depth, error, 3, 16);
402,528✔
4389
            }
402,528✔
4390
            error_diffuse_normal(data,
1,612,800✔
4391
                                 pos + width,
403,200✔
4392
                                 depth, error, 5, 16);
403,200✔
4393
            if (x > 0) {
1,209,600✔
4394
                error_diffuse_normal(data,
1,610,112✔
4395
                                     pos + width - 1,
1,207,584✔
4396
                                     depth, error, 1, 16);
402,528✔
4397
            }
402,528✔
4398
        }
403,200✔
4399
    }
4400
}
32,061,744✔
4401

4402

4403
static void
4404
diffuse_fs_carry(int32_t *carry_curr, int32_t *carry_next,
×
4405
                 int32_t *carry_far, int width, int height,
4406
                 int depth, int x, int y, int32_t error,
4407
                 int direction, int channel)
4408
{
4409
    /* Floyd Steinberg Method
4410
     *          curr    7/16
4411
     *  3/16    5/48    1/16
4412
     */
4413
    int forward;
4414

4415
    /* unused */ (void) carry_far;
4416
    if (error == 0) {
×
4417
        return;
×
4418
    }
4419

4420
    forward = direction >= 0;
×
4421
    if (forward) {
×
4422
        if (x + 1 < width) {
×
4423
            size_t base;
4424
            int32_t term;
4425

4426
            base = ((size_t)(x + 1) * (size_t)depth)
×
4427
                 + (size_t)channel;
×
4428
            term = diffuse_fixed_term(error, 7, 16);
×
4429
            carry_curr[base] += term;
×
4430
        }
4431
        if (y + 1 < height) {
×
4432
            if (x > 0) {
×
4433
                size_t base;
4434
                int32_t term;
4435

4436
                base = ((size_t)(x - 1) * (size_t)depth)
×
4437
                     + (size_t)channel;
×
4438
                term = diffuse_fixed_term(error, 3, 16);
×
4439
                carry_next[base] += term;
×
4440
            }
4441
            {
4442
                size_t base;
4443
                int32_t term;
4444

4445
                base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
4446
                term = diffuse_fixed_term(error, 5, 16);
×
4447
                carry_next[base] += term;
×
4448
            }
4449
            if (x + 1 < width) {
×
4450
                size_t base;
4451
                int32_t term;
4452

4453
                base = ((size_t)(x + 1) * (size_t)depth)
×
4454
                     + (size_t)channel;
×
4455
                term = diffuse_fixed_term(error, 1, 16);
×
4456
                carry_next[base] += term;
×
4457
            }
4458
        }
4459
    } else {
4460
        if (x - 1 >= 0) {
×
4461
            size_t base;
4462
            int32_t term;
4463

4464
            base = ((size_t)(x - 1) * (size_t)depth)
×
4465
                 + (size_t)channel;
×
4466
            term = diffuse_fixed_term(error, 7, 16);
×
4467
            carry_curr[base] += term;
×
4468
        }
4469
        if (y + 1 < height) {
×
4470
            if (x + 1 < width) {
×
4471
                size_t base;
4472
                int32_t term;
4473

4474
                base = ((size_t)(x + 1) * (size_t)depth)
×
4475
                     + (size_t)channel;
×
4476
                term = diffuse_fixed_term(error, 3, 16);
×
4477
                carry_next[base] += term;
×
4478
            }
4479
            {
4480
                size_t base;
4481
                int32_t term;
4482

4483
                base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
4484
                term = diffuse_fixed_term(error, 5, 16);
×
4485
                carry_next[base] += term;
×
4486
            }
4487
            if (x - 1 >= 0) {
×
4488
                size_t base;
4489
                int32_t term;
4490

4491
                base = ((size_t)(x - 1) * (size_t)depth)
×
4492
                     + (size_t)channel;
×
4493
                term = diffuse_fixed_term(error, 1, 16);
×
4494
                carry_next[base] += term;
×
4495
            }
4496
        }
4497
    }
4498
}
4499

4500

4501
static void
4502
diffuse_atkinson(unsigned char *data, int width, int height,
28,352,460✔
4503
                 int x, int y, int depth, int error, int direction)
4504
{
4505
    /* Atkinson's Method
4506
     *          curr    1/8    1/8
4507
     *   1/8     1/8    1/8
4508
     *           1/8
4509
     */
4510
    int pos;
4511
    int sign;
4512

4513
    pos = y * width + x;
28,352,460✔
4514
    sign = direction >= 0 ? 1 : -1;
28,352,460!
4515

4516
    if (x + sign >= 0 && x + sign < width) {
28,352,460!
4517
        error_diffuse_fast(data, pos + sign, depth, error, 1, 8);
28,296,945✔
4518
    }
9,458,715✔
4519
    if (x + sign * 2 >= 0 && x + sign * 2 < width) {
28,352,460!
4520
        error_diffuse_fast(data, pos + sign * 2, depth, error, 1, 8);
28,241,430✔
4521
    }
9,440,010✔
4522
    if (y < height - 1) {
28,352,460✔
4523
        int row;
4524

4525
        row = pos + width;
28,278,756✔
4526
        if (x - sign >= 0 && x - sign < width) {
28,278,756!
4527
            error_diffuse_fast(data,
37,657,392✔
4528
                               row + (-sign),
9,433,950✔
4529
                               depth, error, 1, 8);
9,433,950✔
4530
        }
9,433,950✔
4531
        error_diffuse_fast(data, row, depth, error, 1, 8);
28,278,756✔
4532
        if (x + sign >= 0 && x + sign < width) {
28,278,756!
4533
            error_diffuse_fast(data,
37,657,392✔
4534
                               row + sign,
9,433,950✔
4535
                               depth, error, 1, 8);
9,433,950✔
4536
        }
9,433,950✔
4537
    }
9,452,586✔
4538
    if (y < height - 2) {
28,352,460✔
4539
        error_diffuse_fast(data, pos + width * 2, depth, error, 1, 8);
28,205,052✔
4540
    }
9,427,752✔
4541
}
28,352,460✔
4542

4543

4544
static void
4545
diffuse_atkinson_carry(int32_t *carry_curr, int32_t *carry_next,
×
4546
                       int32_t *carry_far, int width, int height,
4547
                       int depth, int x, int y, int32_t error,
4548
                       int direction, int channel)
4549
{
4550
    /* Atkinson's Method
4551
     *          curr    1/8    1/8
4552
     *   1/8     1/8    1/8
4553
     *           1/8
4554
     */
4555
    int sign;
4556
    int32_t term;
4557

4558
    if (error == 0) {
×
4559
        return;
×
4560
    }
4561

4562
    term = diffuse_fixed_term(error, 1, 8);
×
4563
    sign = direction >= 0 ? 1 : -1;
×
4564
    if (x + sign >= 0 && x + sign < width) {
×
4565
        size_t base;
4566

4567
        base = ((size_t)(x + sign) * (size_t)depth)
×
4568
             + (size_t)channel;
×
4569
        carry_curr[base] += term;
×
4570
    }
4571
    if (x + sign * 2 >= 0 && x + sign * 2 < width) {
×
4572
        size_t base;
4573

4574
        base = ((size_t)(x + sign * 2) * (size_t)depth)
×
4575
             + (size_t)channel;
×
4576
        carry_curr[base] += term;
×
4577
    }
4578
    if (y + 1 < height) {
×
4579
        if (x - sign >= 0 && x - sign < width) {
×
4580
            size_t base;
4581

4582
            base = ((size_t)(x - sign) * (size_t)depth)
×
4583
                 + (size_t)channel;
×
4584
            carry_next[base] += term;
×
4585
        }
4586
        {
4587
            size_t base;
4588

4589
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
4590
            carry_next[base] += term;
×
4591
        }
4592
        if (x + sign >= 0 && x + sign < width) {
×
4593
            size_t base;
4594

4595
            base = ((size_t)(x + sign) * (size_t)depth)
×
4596
                 + (size_t)channel;
×
4597
            carry_next[base] += term;
×
4598
        }
4599
    }
4600
    if (y + 2 < height) {
×
4601
        size_t base;
4602

4603
        base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
4604
        carry_far[base] += term;
×
4605
    }
4606
}
4607

4608

4609
static void
4610
diffuse_jajuni(unsigned char *data, int width, int height,
396,900✔
4611
               int x, int y, int depth, int error, int direction)
4612
{
4613
    /* Jarvis, Judice & Ninke Method
4614
     *                  curr    7/48    5/48
4615
     *  3/48    5/48    7/48    5/48    3/48
4616
     *  1/48    3/48    5/48    3/48    1/48
4617
     */
4618
    int pos;
4619
    int sign;
4620
    static const int row0_offsets[] = { 1, 2 };
4621
    static const int row0_weights[] = { 7, 5 };
4622
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4623
    static const int row1_weights[] = { 3, 5, 7, 5, 3 };
4624
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
4625
    static const int row2_weights[] = { 1, 3, 5, 3, 1 };
4626
    int i;
4627

4628
    pos = y * width + x;
396,900✔
4629
    sign = direction >= 0 ? 1 : -1;
396,900!
4630

4631
    for (i = 0; i < 2; ++i) {
1,190,700✔
4632
        int neighbor;
4633

4634
        neighbor = x + sign * row0_offsets[i];
793,800✔
4635
        if (neighbor < 0 || neighbor >= width) {
793,800!
4636
            continue;
5,670✔
4637
        }
4638
        error_diffuse_precise(data,
1,050,840✔
4639
                              pos + (neighbor - x),
788,130✔
4640
                              depth, error,
262,710✔
4641
                              row0_weights[i], 48);
788,130✔
4642
    }
262,710✔
4643
    if (y < height - 1) {
396,900✔
4644
        int row;
4645

4646
        row = pos + width;
395,010✔
4647
        for (i = 0; i < 5; ++i) {
2,370,060✔
4648
            int neighbor;
4649

4650
            neighbor = x + sign * row1_offsets[i];
1,975,050✔
4651
            if (neighbor < 0 || neighbor >= width) {
1,975,050✔
4652
                continue;
11,286✔
4653
            }
4654
            error_diffuse_precise(data,
2,618,352✔
4655
                                  row + (neighbor - x),
1,963,764✔
4656
                                  depth, error,
654,588✔
4657
                                  row1_weights[i], 48);
1,963,764✔
4658
        }
654,588✔
4659
    }
131,670✔
4660
    if (y < height - 2) {
396,900✔
4661
        int row;
4662

4663
        row = pos + width * 2;
393,120✔
4664
        for (i = 0; i < 5; ++i) {
2,358,720✔
4665
            int neighbor;
4666

4667
            neighbor = x + sign * row2_offsets[i];
1,965,600✔
4668
            if (neighbor < 0 || neighbor >= width) {
1,965,600✔
4669
                continue;
11,232✔
4670
            }
4671
            error_diffuse_precise(data,
2,605,824✔
4672
                                  row + (neighbor - x),
1,954,368✔
4673
                                  depth, error,
651,456✔
4674
                                  row2_weights[i], 48);
1,954,368✔
4675
        }
651,456✔
4676
    }
131,040✔
4677
}
396,900✔
4678

4679

4680
static void
4681
diffuse_jajuni_carry(int32_t *carry_curr, int32_t *carry_next,
×
4682
                     int32_t *carry_far, int width, int height,
4683
                     int depth, int x, int y, int32_t error,
4684
                     int direction, int channel)
4685
{
4686
    /* Jarvis, Judice & Ninke Method
4687
     *                  curr    7/48    5/48
4688
     *  3/48    5/48    7/48    5/48    3/48
4689
     *  1/48    3/48    5/48    3/48    1/48
4690
     */
4691
    static const int row0_offsets[] = { 1, 2 };
4692
    static const int row0_weights[] = { 7, 5 };
4693
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4694
    static const int row1_weights[] = { 3, 5, 7, 5, 3 };
4695
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
4696
    static const int row2_weights[] = { 1, 3, 5, 3, 1 };
4697
    int sign;
4698
    int i;
4699

4700
    if (error == 0) {
×
4701
        return;
×
4702
    }
4703

4704
    sign = direction >= 0 ? 1 : -1;
×
4705
    for (i = 0; i < 2; ++i) {
×
4706
        int neighbor;
4707
        int32_t term;
4708

4709
        neighbor = x + sign * row0_offsets[i];
×
4710
        if (neighbor < 0 || neighbor >= width) {
×
4711
            continue;
×
4712
        }
4713
        term = diffuse_fixed_term(error, row0_weights[i], 48);
×
4714
        carry_curr[((size_t)neighbor * (size_t)depth)
×
4715
                   + (size_t)channel] += term;
×
4716
    }
4717
    if (y + 1 < height) {
×
4718
        for (i = 0; i < 5; ++i) {
×
4719
            int neighbor;
4720
            int32_t term;
4721

4722
            neighbor = x + sign * row1_offsets[i];
×
4723
            if (neighbor < 0 || neighbor >= width) {
×
4724
                continue;
×
4725
            }
4726
            term = diffuse_fixed_term(error, row1_weights[i], 48);
×
4727
            carry_next[((size_t)neighbor * (size_t)depth)
×
4728
                       + (size_t)channel] += term;
×
4729
        }
4730
    }
4731
    if (y + 2 < height) {
×
4732
        for (i = 0; i < 5; ++i) {
×
4733
            int neighbor;
4734
            int32_t term;
4735

4736
            neighbor = x + sign * row2_offsets[i];
×
4737
            if (neighbor < 0 || neighbor >= width) {
×
4738
                continue;
×
4739
            }
4740
            term = diffuse_fixed_term(error, row2_weights[i], 48);
×
4741
            carry_far[((size_t)neighbor * (size_t)depth)
×
4742
                      + (size_t)channel] += term;
×
4743
        }
4744
    }
4745
}
4746

4747

4748
static void
4749
diffuse_stucki(unsigned char *data, int width, int height,
119,700✔
4750
               int x, int y, int depth, int error, int direction)
4751
{
4752
    /* Stucki's Method
4753
     *                  curr    8/48    4/48
4754
     *  2/48    4/48    8/48    4/48    2/48
4755
     *  1/48    2/48    4/48    2/48    1/48
4756
     */
4757
    int pos;
4758
    int sign;
4759
    static const int row0_offsets[] = { 1, 2 };
4760
    static const int row0_num[] = { 1, 1 };
4761
    static const int row0_den[] = { 6, 12 };
4762
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4763
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
4764
    static const int row1_den[] = { 24, 12, 6, 12, 24 };
4765
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
4766
    static const int row2_num[] = { 1, 1, 1, 1, 1 };
4767
    static const int row2_den[] = { 48, 24, 12, 24, 48 };
4768
    int i;
4769

4770
    pos = y * width + x;
119,700✔
4771
    sign = direction >= 0 ? 1 : -1;
119,700!
4772

4773
    for (i = 0; i < 2; ++i) {
359,100✔
4774
        int neighbor;
4775

4776
        neighbor = x + sign * row0_offsets[i];
239,400✔
4777
        if (neighbor < 0 || neighbor >= width) {
239,400!
4778
            continue;
2,700✔
4779
        }
4780
        error_diffuse_precise(data,
315,600✔
4781
                              pos + (neighbor - x),
236,700✔
4782
                              depth, error,
78,900✔
4783
                              row0_num[i], row0_den[i]);
236,700✔
4784
    }
78,900✔
4785
    if (y < height - 1) {
119,700✔
4786
        int row;
4787

4788
        row = pos + width;
118,503✔
4789
        for (i = 0; i < 5; ++i) {
711,018✔
4790
            int neighbor;
4791

4792
            neighbor = x + sign * row1_offsets[i];
592,515✔
4793
            if (neighbor < 0 || neighbor >= width) {
592,515✔
4794
                continue;
5,346✔
4795
            }
4796
            error_diffuse_precise(data,
782,892✔
4797
                                  row + (neighbor - x),
587,169✔
4798
                                  depth, error,
195,723✔
4799
                                  row1_num[i], row1_den[i]);
587,169✔
4800
        }
195,723✔
4801
    }
39,501✔
4802
    if (y < height - 2) {
119,700✔
4803
        int row;
4804

4805
        row = pos + width * 2;
117,306✔
4806
        for (i = 0; i < 5; ++i) {
703,836✔
4807
            int neighbor;
4808

4809
            neighbor = x + sign * row2_offsets[i];
586,530✔
4810
            if (neighbor < 0 || neighbor >= width) {
586,530✔
4811
                continue;
5,292✔
4812
            }
4813
            error_diffuse_precise(data,
774,984✔
4814
                                  row + (neighbor - x),
581,238✔
4815
                                  depth, error,
193,746✔
4816
                                  row2_num[i], row2_den[i]);
581,238✔
4817
        }
193,746✔
4818
    }
39,102✔
4819
}
119,700✔
4820

4821

4822
static void
4823
diffuse_stucki_carry(int32_t *carry_curr, int32_t *carry_next,
×
4824
                     int32_t *carry_far, int width, int height,
4825
                     int depth, int x, int y, int32_t error,
4826
                     int direction, int channel)
4827
{
4828
    /* Stucki's Method
4829
     *                  curr    8/48    4/48
4830
     *  2/48    4/48    8/48    4/48    2/48
4831
     *  1/48    2/48    4/48    2/48    1/48
4832
     */
4833
    static const int row0_offsets[] = { 1, 2 };
4834
    static const int row0_num[] = { 1, 1 };
4835
    static const int row0_den[] = { 6, 12 };
4836
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4837
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
4838
    static const int row1_den[] = { 24, 12, 6, 12, 24 };
4839
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
4840
    static const int row2_num[] = { 1, 1, 1, 1, 1 };
4841
    static const int row2_den[] = { 48, 24, 12, 24, 48 };
4842
    int sign;
4843
    int i;
4844

4845
    if (error == 0) {
×
4846
        return;
×
4847
    }
4848

4849
    sign = direction >= 0 ? 1 : -1;
×
4850
    for (i = 0; i < 2; ++i) {
×
4851
        int neighbor;
4852
        int32_t term;
4853

4854
        neighbor = x + sign * row0_offsets[i];
×
4855
        if (neighbor < 0 || neighbor >= width) {
×
4856
            continue;
×
4857
        }
4858
        term = diffuse_fixed_term(error, row0_num[i], row0_den[i]);
×
4859
        carry_curr[((size_t)neighbor * (size_t)depth)
×
4860
                   + (size_t)channel] += term;
×
4861
    }
4862
    if (y + 1 < height) {
×
4863
        for (i = 0; i < 5; ++i) {
×
4864
            int neighbor;
4865
            int32_t term;
4866

4867
            neighbor = x + sign * row1_offsets[i];
×
4868
            if (neighbor < 0 || neighbor >= width) {
×
4869
                continue;
×
4870
            }
4871
            term = diffuse_fixed_term(error, row1_num[i], row1_den[i]);
×
4872
            carry_next[((size_t)neighbor * (size_t)depth)
×
4873
                       + (size_t)channel] += term;
×
4874
        }
4875
    }
4876
    if (y + 2 < height) {
×
4877
        for (i = 0; i < 5; ++i) {
×
4878
            int neighbor;
4879
            int32_t term;
4880

4881
            neighbor = x + sign * row2_offsets[i];
×
4882
            if (neighbor < 0 || neighbor >= width) {
×
4883
                continue;
×
4884
            }
4885
            term = diffuse_fixed_term(error, row2_num[i], row2_den[i]);
×
4886
            carry_far[((size_t)neighbor * (size_t)depth)
×
4887
                      + (size_t)channel] += term;
×
4888
        }
4889
    }
4890
}
4891

4892

4893
static void
4894
diffuse_burkes(unsigned char *data, int width, int height,
67,500✔
4895
               int x, int y, int depth, int error, int direction)
4896
{
4897
    /* Burkes' Method
4898
     *                  curr    4/16    2/16
4899
     *  1/16    2/16    4/16    2/16    1/16
4900
     */
4901
    int pos;
4902
    int sign;
4903
    static const int row0_offsets[] = { 1, 2 };
4904
    static const int row0_num[] = { 1, 1 };
4905
    static const int row0_den[] = { 4, 8 };
4906
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4907
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
4908
    static const int row1_den[] = { 16, 8, 4, 8, 16 };
4909
    int i;
4910

4911
    pos = y * width + x;
67,500✔
4912
    sign = direction >= 0 ? 1 : -1;
67,500!
4913

4914
    for (i = 0; i < 2; ++i) {
202,500✔
4915
        int neighbor;
4916

4917
        neighbor = x + sign * row0_offsets[i];
135,000✔
4918
        if (neighbor < 0 || neighbor >= width) {
135,000!
4919
            continue;
2,025✔
4920
        }
4921
        error_diffuse_normal(data,
177,300✔
4922
                             pos + (neighbor - x),
132,975✔
4923
                             depth, error,
44,325✔
4924
                             row0_num[i], row0_den[i]);
132,975✔
4925
    }
44,325✔
4926
    if (y < height - 1) {
67,500✔
4927
        int row;
4928

4929
        row = pos + width;
66,600✔
4930
        for (i = 0; i < 5; ++i) {
399,600✔
4931
            int neighbor;
4932

4933
            neighbor = x + sign * row1_offsets[i];
333,000✔
4934
            if (neighbor < 0 || neighbor >= width) {
333,000✔
4935
                continue;
3,996✔
4936
            }
4937
            error_diffuse_normal(data,
438,672✔
4938
                                 row + (neighbor - x),
329,004✔
4939
                                 depth, error,
109,668✔
4940
                                 row1_num[i], row1_den[i]);
329,004✔
4941
        }
109,668✔
4942
    }
22,200✔
4943
}
67,500✔
4944

4945
static void
4946
diffuse_burkes_carry(int32_t *carry_curr, int32_t *carry_next,
×
4947
                     int32_t *carry_far, int width, int height,
4948
                     int depth, int x, int y, int32_t error,
4949
                     int direction, int channel)
4950
{
4951
    /* Burkes' Method
4952
     *                  curr    4/16    2/16
4953
     *  1/16    2/16    4/16    2/16    1/16
4954
     */
4955
    static const int row0_offsets[] = { 1, 2 };
4956
    static const int row0_num[] = { 1, 1 };
4957
    static const int row0_den[] = { 4, 8 };
4958
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4959
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
4960
    static const int row1_den[] = { 16, 8, 4, 8, 16 };
4961
    int sign;
4962
    int i;
4963

4964
    /* unused */ (void) carry_far;
4965

4966
    if (error == 0) {
×
4967
        return;
×
4968
    }
4969

4970
    sign = direction >= 0 ? 1 : -1;
×
4971
    for (i = 0; i < 2; ++i) {
×
4972
        int neighbor;
4973
        int32_t term;
4974

4975
        neighbor = x + sign * row0_offsets[i];
×
4976
        if (neighbor < 0 || neighbor >= width) {
×
4977
            continue;
×
4978
        }
4979
        term = diffuse_fixed_term(error, row0_num[i], row0_den[i]);
×
4980
        carry_curr[((size_t)neighbor * (size_t)depth)
×
4981
                   + (size_t)channel] += term;
×
4982
    }
4983
    if (y + 1 < height) {
×
4984
        for (i = 0; i < 5; ++i) {
×
4985
            int neighbor;
4986
            int32_t term;
4987

4988
            neighbor = x + sign * row1_offsets[i];
×
4989
            if (neighbor < 0 || neighbor >= width) {
×
4990
                continue;
×
4991
            }
4992
            term = diffuse_fixed_term(error, row1_num[i], row1_den[i]);
×
4993
            carry_next[((size_t)neighbor * (size_t)depth)
×
4994
                       + (size_t)channel] += term;
×
4995
        }
4996
    }
4997
}
4998

4999
static void
5000
diffuse_sierra1(unsigned char *data, int width, int height,
×
5001
                int x, int y, int depth, int error, int direction)
5002
{
5003
    /* Sierra Lite Method
5004
     *          curr    2/4
5005
     *  1/4     1/4
5006
     */
5007
    static const int row0_offsets[] = { 1 };
5008
    static const int row0_num[] = { 1 };
5009
    static const int row0_den[] = { 2 };
5010
    static const int row1_offsets[] = { -1, 0 };
5011
    static const int row1_num[] = { 1, 1 };
5012
    static const int row1_den[] = { 4, 4 };
5013
    int pos;
5014
    int sign;
5015
    int i;
5016
    int neighbor;
5017
    int row;
5018

5019
    pos = y * width + x;
×
5020
    sign = direction >= 0 ? 1 : -1;
×
5021

5022
    for (i = 0; i < 1; ++i) {
×
5023
        neighbor = x + sign * row0_offsets[i];
×
5024
        if (neighbor < 0 || neighbor >= width) {
×
5025
            continue;
×
5026
        }
5027
        error_diffuse_normal(data,
×
5028
                             pos + (neighbor - x),
×
5029
                             depth, error,
5030
                             row0_num[i], row0_den[i]);
×
5031
    }
5032
    if (y < height - 1) {
×
5033
        row = pos + width;
×
5034
        for (i = 0; i < 2; ++i) {
×
5035
            neighbor = x + sign * row1_offsets[i];
×
5036
            if (neighbor < 0 || neighbor >= width) {
×
5037
                continue;
×
5038
            }
5039
            error_diffuse_normal(data,
×
5040
                                 row + (neighbor - x),
×
5041
                                 depth, error,
5042
                                 row1_num[i], row1_den[i]);
×
5043
        }
5044
    }
5045
}
×
5046

5047

5048
static void
5049
diffuse_sierra1_carry(int32_t *carry_curr, int32_t *carry_next,
×
5050
                      int32_t *carry_far, int width, int height,
5051
                      int depth, int x, int y, int32_t error,
5052
                      int direction, int channel)
5053
{
5054
    /* Sierra Lite Method
5055
     *          curr    2/4
5056
     *  1/4     1/4
5057
     */
5058
    static const int row0_offsets[] = { 1 };
5059
    static const int row0_num[] = { 1 };
5060
    static const int row0_den[] = { 2 };
5061
    static const int row1_offsets[] = { -1, 0 };
5062
    static const int row1_num[] = { 1, 1 };
5063
    static const int row1_den[] = { 4, 4 };
5064
    int sign;
5065
    int i;
5066
    int neighbor;
5067
    int32_t term;
5068

5069
    /* unused */ (void) carry_far;
5070

5071
    if (error == 0) {
×
5072
        return;
×
5073
    }
5074

5075
    sign = direction >= 0 ? 1 : -1;
×
5076
    for (i = 0; i < 1; ++i) {
×
5077
        neighbor = x + sign * row0_offsets[i];
×
5078
        if (neighbor < 0 || neighbor >= width) {
×
5079
            continue;
×
5080
        }
5081
        term = diffuse_fixed_term(error, row0_num[i], row0_den[i]);
×
5082
        carry_curr[((size_t)neighbor * (size_t)depth)
×
5083
                   + (size_t)channel] += term;
×
5084
    }
5085
    if (y + 1 < height) {
×
5086
        for (i = 0; i < 2; ++i) {
×
5087
            neighbor = x + sign * row1_offsets[i];
×
5088
            if (neighbor < 0 || neighbor >= width) {
×
5089
                continue;
×
5090
            }
5091
            term = diffuse_fixed_term(error, row1_num[i], row1_den[i]);
×
5092
            carry_next[((size_t)neighbor * (size_t)depth)
×
5093
                       + (size_t)channel] += term;
×
5094
        }
5095
    }
5096
}
5097

5098

5099
static void
5100
diffuse_sierra2(unsigned char *data, int width, int height,
×
5101
                int x, int y, int depth, int error, int direction)
5102
{
5103
    /* Sierra Two-row Method
5104
     *                  curr    4/32    3/32
5105
     *  1/32    2/32    3/32    2/32    1/32
5106
     *                  2/32    3/32    2/32
5107
     */
5108
    static const int row0_offsets[] = { 1, 2 };
5109
    static const int row0_num[] = { 4, 3 };
5110
    static const int row0_den[] = { 32, 32 };
5111
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
5112
    static const int row1_num[] = { 1, 2, 3, 2, 1 };
5113
    static const int row1_den[] = { 32, 32, 32, 32, 32 };
5114
    static const int row2_offsets[] = { -1, 0, 1 };
5115
    static const int row2_num[] = { 2, 3, 2 };
5116
    static const int row2_den[] = { 32, 32, 32 };
5117
    int pos;
5118
    int sign;
5119
    int i;
5120
    int neighbor;
5121
    int row;
5122

5123
    pos = y * width + x;
×
5124
    sign = direction >= 0 ? 1 : -1;
×
5125

5126
    for (i = 0; i < 2; ++i) {
×
5127
        neighbor = x + sign * row0_offsets[i];
×
5128
        if (neighbor < 0 || neighbor >= width) {
×
5129
            continue;
×
5130
        }
5131
        error_diffuse_precise(data,
×
5132
                              pos + (neighbor - x),
×
5133
                              depth, error,
5134
                              row0_num[i], row0_den[i]);
×
5135
    }
5136
    if (y < height - 1) {
×
5137
        row = pos + width;
×
5138
        for (i = 0; i < 5; ++i) {
×
5139
            neighbor = x + sign * row1_offsets[i];
×
5140
            if (neighbor < 0 || neighbor >= width) {
×
5141
                continue;
×
5142
            }
5143
            error_diffuse_precise(data,
×
5144
                                  row + (neighbor - x),
×
5145
                                  depth, error,
5146
                                  row1_num[i], row1_den[i]);
×
5147
        }
5148
    }
5149
    if (y < height - 2) {
×
5150
        row = pos + width * 2;
×
5151
        for (i = 0; i < 3; ++i) {
×
5152
            neighbor = x + sign * row2_offsets[i];
×
5153
            if (neighbor < 0 || neighbor >= width) {
×
5154
                continue;
×
5155
            }
5156
            error_diffuse_precise(data,
×
5157
                                  row + (neighbor - x),
×
5158
                                  depth, error,
5159
                                  row2_num[i], row2_den[i]);
×
5160
        }
5161
    }
5162
}
×
5163

5164

5165
static void
5166
diffuse_sierra2_carry(int32_t *carry_curr, int32_t *carry_next,
×
5167
                      int32_t *carry_far, int width, int height,
5168
                      int depth, int x, int y, int32_t error,
5169
                      int direction, int channel)
5170
{
5171
    /* Sierra Two-row Method
5172
     *                  curr    4/32    3/32
5173
     *  1/32    2/32    3/32    2/32    1/32
5174
     *                  2/32    3/32    2/32
5175
     */
5176
    static const int row0_offsets[] = { 1, 2 };
5177
    static const int row0_num[] = { 4, 3 };
5178
    static const int row0_den[] = { 32, 32 };
5179
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
5180
    static const int row1_num[] = { 1, 2, 3, 2, 1 };
5181
    static const int row1_den[] = { 32, 32, 32, 32, 32 };
5182
    static const int row2_offsets[] = { -1, 0, 1 };
5183
    static const int row2_num[] = { 2, 3, 2 };
5184
    static const int row2_den[] = { 32, 32, 32 };
5185
    int sign;
5186
    int i;
5187
    int neighbor;
5188
    int32_t term;
5189

5190
    if (error == 0) {
×
5191
        return;
×
5192
    }
5193

5194
    sign = direction >= 0 ? 1 : -1;
×
5195
    for (i = 0; i < 2; ++i) {
×
5196
        neighbor = x + sign * row0_offsets[i];
×
5197
        if (neighbor < 0 || neighbor >= width) {
×
5198
            continue;
×
5199
        }
5200
        term = diffuse_fixed_term(error, row0_num[i], row0_den[i]);
×
5201
        carry_curr[((size_t)neighbor * (size_t)depth)
×
5202
                   + (size_t)channel] += term;
×
5203
    }
5204
    if (y + 1 < height) {
×
5205
        for (i = 0; i < 5; ++i) {
×
5206
            neighbor = x + sign * row1_offsets[i];
×
5207
            if (neighbor < 0 || neighbor >= width) {
×
5208
                continue;
×
5209
            }
5210
            term = diffuse_fixed_term(error, row1_num[i], row1_den[i]);
×
5211
            carry_next[((size_t)neighbor * (size_t)depth)
×
5212
                       + (size_t)channel] += term;
×
5213
        }
5214
    }
5215
    if (y + 2 < height) {
×
5216
        for (i = 0; i < 3; ++i) {
×
5217
            neighbor = x + sign * row2_offsets[i];
×
5218
            if (neighbor < 0 || neighbor >= width) {
×
5219
                continue;
×
5220
            }
5221
            term = diffuse_fixed_term(error, row2_num[i], row2_den[i]);
×
5222
            carry_far[((size_t)neighbor * (size_t)depth)
×
5223
                      + (size_t)channel] += term;
×
5224
        }
5225
    }
5226
}
5227

5228

5229
static void
5230
diffuse_sierra3(unsigned char *data, int width, int height,
×
5231
                int x, int y, int depth, int error, int direction)
5232
{
5233
    /* Sierra-3 Method
5234
     *                  curr    5/32    3/32
5235
     *  2/32    4/32    5/32    4/32    2/32
5236
     *                  2/32    3/32    2/32
5237
     */
5238
    static const int row0_offsets[] = { 1, 2 };
5239
    static const int row0_num[] = { 5, 3 };
5240
    static const int row0_den[] = { 32, 32 };
5241
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
5242
    static const int row1_num[] = { 2, 4, 5, 4, 2 };
5243
    static const int row1_den[] = { 32, 32, 32, 32, 32 };
5244
    static const int row2_offsets[] = { -1, 0, 1 };
5245
    static const int row2_num[] = { 2, 3, 2 };
5246
    static const int row2_den[] = { 32, 32, 32 };
5247
    int pos;
5248
    int sign;
5249
    int i;
5250
    int neighbor;
5251
    int row;
5252

5253
    pos = y * width + x;
×
5254
    sign = direction >= 0 ? 1 : -1;
×
5255

5256
    for (i = 0; i < 2; ++i) {
×
5257
        neighbor = x + sign * row0_offsets[i];
×
5258
        if (neighbor < 0 || neighbor >= width) {
×
5259
            continue;
×
5260
        }
5261
        error_diffuse_precise(data,
×
5262
                              pos + (neighbor - x),
×
5263
                              depth, error,
5264
                              row0_num[i], row0_den[i]);
×
5265
    }
5266
    if (y < height - 1) {
×
5267
        row = pos + width;
×
5268
        for (i = 0; i < 5; ++i) {
×
5269
            neighbor = x + sign * row1_offsets[i];
×
5270
            if (neighbor < 0 || neighbor >= width) {
×
5271
                continue;
×
5272
            }
5273
            error_diffuse_precise(data,
×
5274
                                  row + (neighbor - x),
×
5275
                                  depth, error,
5276
                                  row1_num[i], row1_den[i]);
×
5277
        }
5278
    }
5279
    if (y < height - 2) {
×
5280
        row = pos + width * 2;
×
5281
        for (i = 0; i < 3; ++i) {
×
5282
            neighbor = x + sign * row2_offsets[i];
×
5283
            if (neighbor < 0 || neighbor >= width) {
×
5284
                continue;
×
5285
            }
5286
            error_diffuse_precise(data,
×
5287
                                  row + (neighbor - x),
×
5288
                                  depth, error,
5289
                                  row2_num[i], row2_den[i]);
×
5290
        }
5291
    }
5292
}
×
5293

5294

5295
static void
5296
diffuse_sierra3_carry(int32_t *carry_curr, int32_t *carry_next,
×
5297
                      int32_t *carry_far, int width, int height,
5298
                      int depth, int x, int y, int32_t error,
5299
                      int direction, int channel)
5300
{
5301
    /* Sierra-3 Method
5302
     *                  curr    5/32    3/32
5303
     *  2/32    4/32    5/32    4/32    2/32
5304
     *                  2/32    3/32    2/32
5305
     */
5306
    static const int row0_offsets[] = { 1, 2 };
5307
    static const int row0_num[] = { 5, 3 };
5308
    static const int row0_den[] = { 32, 32 };
5309
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
5310
    static const int row1_num[] = { 2, 4, 5, 4, 2 };
5311
    static const int row1_den[] = { 32, 32, 32, 32, 32 };
5312
    static const int row2_offsets[] = { -1, 0, 1 };
5313
    static const int row2_num[] = { 2, 3, 2 };
5314
    static const int row2_den[] = { 32, 32, 32 };
5315
    int sign;
5316
    int i;
5317
    int neighbor;
5318
    int32_t term;
5319

5320
    if (error == 0) {
×
5321
        return;
×
5322
    }
5323

5324
    sign = direction >= 0 ? 1 : -1;
×
5325
    for (i = 0; i < 2; ++i) {
×
5326
        neighbor = x + sign * row0_offsets[i];
×
5327
        if (neighbor < 0 || neighbor >= width) {
×
5328
            continue;
×
5329
        }
5330
        term = diffuse_fixed_term(error, row0_num[i], row0_den[i]);
×
5331
        carry_curr[((size_t)neighbor * (size_t)depth)
×
5332
                   + (size_t)channel] += term;
×
5333
    }
5334
    if (y + 1 < height) {
×
5335
        for (i = 0; i < 5; ++i) {
×
5336
            neighbor = x + sign * row1_offsets[i];
×
5337
            if (neighbor < 0 || neighbor >= width) {
×
5338
                continue;
×
5339
            }
5340
            term = diffuse_fixed_term(error, row1_num[i], row1_den[i]);
×
5341
            carry_next[((size_t)neighbor * (size_t)depth)
×
5342
                       + (size_t)channel] += term;
×
5343
        }
5344
    }
5345
    if (y + 2 < height) {
×
5346
        for (i = 0; i < 3; ++i) {
×
5347
            neighbor = x + sign * row2_offsets[i];
×
5348
            if (neighbor < 0 || neighbor >= width) {
×
5349
                continue;
×
5350
            }
5351
            term = diffuse_fixed_term(error, row2_num[i], row2_den[i]);
×
5352
            carry_far[((size_t)neighbor * (size_t)depth)
×
5353
                      + (size_t)channel] += term;
×
5354
        }
5355
    }
5356
}
5357

5358

5359
static void
5360
diffuse_lso1(unsigned char *data, int width, int height,
×
5361
             int x, int y, int depth, int error, int direction)
5362
{
5363
    int pos;
5364
    int sign;
5365

5366
    pos = y * width + x;
×
5367
    sign = direction >= 0 ? 1 : -1;
×
5368

5369
    /* lso1 (libsixel original) method:
5370
     *
5371
     * libsixel-specific error diffusion (dithering) to improve sixel
5372
     * compression; by steering error propagation so out-of-palette
5373
     * intermediate colors render as horizontal bands rather than grainy
5374
     * noise, we increase RLE more effective.
5375
     *
5376
     *          curr
5377
     *   1/8    4/8    1/8
5378
     *          2/8
5379
     */
5380
    if (y < height - 1) {
×
5381
        int row;
5382

5383
        row = pos + width;
×
5384
        if (x - sign >= 0 && x - sign < width) {
×
5385
            error_diffuse_fast(data,
×
5386
                               row + (-sign),
5387
                               depth, error, 1, 8);
5388
        }
5389
        error_diffuse_fast(data, row, depth, error, 4, 8);
×
5390
        if (x + sign >= 0 && x + sign < width) {
×
5391
            error_diffuse_fast(data,
×
5392
                               row + sign,
5393
                               depth, error, 1, 8);
5394
        }
5395
    }
5396
    if (y < height - 2) {
×
5397
        error_diffuse_fast(data, pos + width * 2, depth, error, 2, 8);
×
5398
    }
5399
}
×
5400

5401

5402
static void
5403
diffuse_lso1_carry(int32_t *carry_curr, int32_t *carry_next,
×
5404
                   int32_t *carry_far, int width, int height,
5405
                   int depth, int x, int y, int32_t error,
5406
                   int direction, int channel)
5407
{
5408
    int sign;
5409
    int32_t edge_term;
5410
    int32_t center_term;
5411
    int32_t far_term;
5412

5413
    /* unused */ (void) carry_curr;
5414
    if (error == 0) {
×
5415
        return;
×
5416
    }
5417

5418
    sign = direction >= 0 ? 1 : -1;
×
5419
    edge_term = diffuse_fixed_term(error, 1, 8);
×
5420
    center_term = diffuse_fixed_term(error, 4, 8);
×
5421
    far_term = diffuse_fixed_term(error, 2, 8);
×
5422

5423
    if (y + 1 < height) {
×
5424
        if (x - sign >= 0 && x - sign < width) {
×
5425
            size_t base;
5426

5427
            base = ((size_t)(x - sign) * (size_t)depth)
×
5428
                 + (size_t)channel;
×
5429
            carry_next[base] += edge_term;
×
5430
        }
5431
        {
5432
            size_t base;
5433

5434
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
5435
            carry_next[base] += center_term;
×
5436
        }
5437
        if (x + sign >= 0 && x + sign < width) {
×
5438
            size_t base;
5439

5440
            base = ((size_t)(x + sign) * (size_t)depth)
×
5441
                 + (size_t)channel;
×
5442
            carry_next[base] += edge_term;
×
5443
        }
5444
    }
5445
    if (y + 2 < height) {
×
5446
        size_t base;
5447

5448
        base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
5449
        carry_far[base] += far_term;
×
5450
    }
5451
}
5452

5453
static float
5454
mask_a (int x, int y, int c)
×
5455
{
5456
    return ((((x + c * 67) + y * 236) * 119) & 255 ) / 128.0 - 1.0;
×
5457
}
5458

5459
static float
5460
mask_x (int x, int y, int c)
×
5461
{
5462
    return ((((x + c * 29) ^ y* 149) * 1234) & 511 ) / 256.0 - 1.0;
×
5463
}
5464

5465
/* lookup closest color from palette with "normal" strategy */
5466
static int
5467
lookup_normal(unsigned char const * const pixel,
849,900✔
5468
              int const depth,
5469
              unsigned char const * const palette,
5470
              int const reqcolor,
5471
              unsigned short * const cachetable,
5472
              int const complexion)
5473
{
5474
    int result;
5475
    int diff;
5476
    int r;
5477
    int i;
5478
    int n;
5479
    int distant;
5480

5481
    result = (-1);
849,900✔
5482
    diff = INT_MAX;
849,900✔
5483

5484
    /* don't use cachetable in 'normal' strategy */
5485
    (void) cachetable;
283,300✔
5486

5487
    for (i = 0; i < reqcolor; i++) {
15,309,900✔
5488
        distant = 0;
14,460,000✔
5489
        r = pixel[0] - palette[i * depth + 0];
14,460,000✔
5490
        distant += r * r * complexion;
14,460,000✔
5491
        for (n = 1; n < depth; ++n) {
43,380,000✔
5492
            r = pixel[n] - palette[i * depth + n];
28,920,000✔
5493
            distant += r * r;
28,920,000✔
5494
        }
9,640,000✔
5495
        if (distant < diff) {
14,460,000✔
5496
            diff = distant;
2,211,216✔
5497
            result = i;
2,211,216✔
5498
        }
736,152✔
5499
    }
4,820,000✔
5500

5501
    return result;
849,900✔
5502
}
5503

5504

5505
/*
5506
 * Shared fast lookup flow:
5507
 *
5508
 *   pixel --> quantize --> cuckoo cache --> palette index
5509
 */
5510
static int
5511
lookup_fast_common(unsigned char const *pixel,
23,025,286✔
5512
                   unsigned char const *palette,
5513
                   int reqcolor,
5514
                   unsigned short *cachetable,
5515
                   int complexion,
5516
                   struct histogram_control control)
5517
{
5518
    int result;
5519
    unsigned int hash;
5520
    int diff;
5521
    int i;
5522
    int distant;
5523
    struct cuckoo_table32 *table;
5524
    uint32_t *slot;
5525
    SIXELSTATUS status;
5526
    unsigned char const *entry;
5527
    unsigned char const *end;
5528
    int pixel0;
5529
    int pixel1;
5530
    int pixel2;
5531
    int delta;
5532

5533
    result = (-1);
23,025,286✔
5534
    diff = INT_MAX;
23,025,286✔
5535
    hash = computeHash(pixel, 3U, &control);
23,025,286✔
5536

5537
    table = (struct cuckoo_table32 *)cachetable;
23,025,286✔
5538
    if (table != NULL) {
23,025,286!
5539
        slot = cuckoo_table32_lookup(table, hash);
23,025,286✔
5540
        if (slot != NULL && *slot != 0U) {
23,025,286!
5541
            return (int)(*slot - 1U);
21,422,694✔
5542
        }
5543
    }
517,778✔
5544

5545
    entry = palette;
1,602,592✔
5546
    end = palette + (size_t)reqcolor * 3;
1,602,592✔
5547
    pixel0 = (int)pixel[0];
1,602,592✔
5548
    pixel1 = (int)pixel[1];
1,602,592✔
5549
    pixel2 = (int)pixel[2];
1,602,592✔
5550
    /*
5551
     * Palette traversal as RGB triplets keeps the stride linear:
5552
     *
5553
     *   i -> [R][G][B]
5554
     *        |  |  |
5555
     *        `--+--'
5556
     *           v
5557
     *         entry
5558
     */
5559
    for (i = 0; entry < end; ++i, entry += 3) {
280,320,144✔
5560
        delta = pixel0 - (int)entry[0];
278,717,552✔
5561
        distant = delta * delta * complexion;
278,717,552✔
5562
        delta = pixel1 - (int)entry[1];
278,717,552✔
5563
        distant += delta * delta;
278,717,552✔
5564
        delta = pixel2 - (int)entry[2];
278,717,552✔
5565
        distant += delta * delta;
278,717,552✔
5566
        if (distant < diff) {
278,717,552✔
5567
            diff = distant;
12,059,504✔
5568
            result = i;
12,059,504✔
5569
        }
3,817,818✔
5570
    }
90,105,810✔
5571

5572
    if (table != NULL && result >= 0) {
1,602,592!
5573
        status = cuckoo_table32_insert(table,
2,120,370✔
5574
                                       hash,
517,778✔
5575
                                       (uint32_t)(result + 1));
1,602,592✔
5576
        if (SIXEL_FAILED(status)) {
1,602,592!
5577
            /* ignore cache update failure */
5578
        }
5579
    }
517,778✔
5580

5581
    return result;
1,602,592✔
5582
}
10,479,482✔
5583

5584
/* lookup closest color from palette with "fast" strategy */
5585
static int
5586
lookup_fast(unsigned char const * const pixel,
23,025,286✔
5587
            int const depth,
5588
            unsigned char const * const palette,
5589
            int const reqcolor,
5590
            unsigned short * const cachetable,
5591
            int const complexion)
5592
{
5593
    struct histogram_control control;
5594

5595
    (void)depth;
10,479,482✔
5596

5597
    control = histogram_control_make(3U);
23,025,286✔
5598

5599
    return lookup_fast_common(pixel,
33,504,768✔
5600
                              palette,
10,479,482✔
5601
                              reqcolor,
10,479,482✔
5602
                              cachetable,
10,479,482✔
5603
                              complexion,
10,479,482✔
5604
                              control);
5605
}
5606

5607
static int
5608
lookup_fast_robinhood(unsigned char const * const pixel,
×
5609
                      int const depth,
5610
                      unsigned char const * const palette,
5611
                      int const reqcolor,
5612
                      unsigned short * const cachetable,
5613
                      int const complexion)
5614
{
5615
    struct histogram_control control;
5616

5617
    (void)depth;
5618

5619
    control = histogram_control_make_for_policy(3U,
×
5620
                                                SIXEL_LUT_POLICY_ROBINHOOD);
5621

5622
    return lookup_fast_common(pixel,
×
5623
                              palette,
5624
                              reqcolor,
5625
                              cachetable,
5626
                              complexion,
5627
                              control);
5628
}
5629

5630
static int
5631
lookup_fast_hopscotch(unsigned char const * const pixel,
×
5632
                      int const depth,
5633
                      unsigned char const * const palette,
5634
                      int const reqcolor,
5635
                      unsigned short * const cachetable,
5636
                      int const complexion)
5637
{
5638
    struct histogram_control control;
5639

5640
    (void)depth;
5641

5642
    control = histogram_control_make_for_policy(3U,
×
5643
                                                SIXEL_LUT_POLICY_HOPSCOTCH);
5644

5645
    return lookup_fast_common(pixel,
×
5646
                              palette,
5647
                              reqcolor,
5648
                              cachetable,
5649
                              complexion,
5650
                              control);
5651
}
5652

5653

5654
static int
5655
lookup_mono_darkbg(unsigned char const * const pixel,
1,730,520✔
5656
                   int const depth,
5657
                   unsigned char const * const palette,
5658
                   int const reqcolor,
5659
                   unsigned short * const cachetable,
5660
                   int const complexion)
5661
{
5662
    int n;
5663
    int distant;
5664

5665
    /* unused */ (void) palette;
576,840✔
5666
    /* unused */ (void) cachetable;
576,840✔
5667
    /* unused */ (void) complexion;
576,840✔
5668

5669
    distant = 0;
1,730,520✔
5670
    for (n = 0; n < depth; ++n) {
6,922,080✔
5671
        distant += pixel[n];
5,191,560✔
5672
    }
1,730,520✔
5673
    return distant >= 128 * reqcolor ? 1: 0;
1,730,520✔
5674
}
5675

5676

5677
static int
5678
lookup_mono_lightbg(unsigned char const * const pixel,
810,000✔
5679
                    int const depth,
5680
                    unsigned char const * const palette,
5681
                    int const reqcolor,
5682
                    unsigned short * const cachetable,
5683
                    int const complexion)
5684
{
5685
    int n;
5686
    int distant;
5687

5688
    /* unused */ (void) palette;
270,000✔
5689
    /* unused */ (void) cachetable;
270,000✔
5690
    /* unused */ (void) complexion;
270,000✔
5691

5692
    distant = 0;
810,000✔
5693
    for (n = 0; n < depth; ++n) {
3,240,000✔
5694
        distant += pixel[n];
2,430,000✔
5695
    }
810,000✔
5696
    return distant < 128 * reqcolor ? 1: 0;
810,000✔
5697
}
5698

5699

5700
/* choose colors using median-cut method */
5701
SIXELSTATUS
5702
sixel_quant_make_palette(
256✔
5703
    unsigned char          /* out */ **result,
5704
    unsigned char const    /* in */  *data,
5705
    unsigned int           /* in */  length,
5706
    int                    /* in */  pixelformat,
5707
    unsigned int           /* in */  reqcolors,
5708
    unsigned int           /* in */  *ncolors,
5709
    unsigned int           /* in */  *origcolors,
5710
    int                    /* in */  methodForLargest,
5711
    int                    /* in */  methodForRep,
5712
    int                    /* in */  qualityMode,
5713
    int                    /* in */  force_palette,
5714
    int                    /* in */  use_reversible,
5715
    sixel_allocator_t      /* in */  *allocator)
5716
{
5717
    SIXELSTATUS status = SIXEL_FALSE;
256✔
5718
    unsigned int i;
5719
    unsigned int n;
5720
    int ret;
5721
    tupletable2 colormap;
5722
    unsigned int depth;
5723
    int result_depth;
5724

5725
    result_depth = sixel_helper_compute_depth(pixelformat);
256✔
5726
    if (result_depth <= 0) {
256!
5727
        *result = NULL;
×
5728
        goto end;
×
5729
    }
5730

5731
    depth = (unsigned int)result_depth;
256✔
5732

5733
    ret = computeColorMapFromInput(data, length, depth,
372✔
5734
                                   reqcolors, methodForLargest,
116✔
5735
                                   methodForRep, qualityMode,
116✔
5736
                                   force_palette, use_reversible,
116✔
5737
                                   &colormap, origcolors, allocator);
116✔
5738
    if (ret != 0) {
256!
5739
        *result = NULL;
×
5740
        goto end;
×
5741
    }
5742
    *ncolors = colormap.size;
256✔
5743
    quant_trace(stderr, "tupletable size: %d\n", *ncolors);
256✔
5744
    *result = (unsigned char *)sixel_allocator_malloc(allocator, *ncolors * depth);
256✔
5745
    for (i = 0; i < *ncolors; i++) {
16,018✔
5746
        for (n = 0; n < depth; ++n) {
63,048✔
5747
            (*result)[i * depth + n] = colormap.table[i]->tuple[n];
47,286✔
5748
        }
16,476✔
5749
    }
5,492✔
5750

5751
    if (use_reversible) {
256!
NEW
5752
        sixel_quant_reversible_palette(*result, *ncolors, depth);
×
5753
    }
5754

5755
    sixel_allocator_free(allocator, colormap.table);
256✔
5756

5757
    status = SIXEL_OK;
256✔
5758

5759
end:
140✔
5760
    return status;
256✔
5761
}
5762

5763

5764
/* apply color palette into specified pixel buffers */
5765
SIXELSTATUS
5766
sixel_quant_apply_palette(
281✔
5767
    sixel_index_t     /* out */ *result,
5768
    unsigned char     /* in */  *data,
5769
    int               /* in */  width,
5770
    int               /* in */  height,
5771
    int               /* in */  depth,
5772
    unsigned char     /* in */  *palette,
5773
    int               /* in */  reqcolor,
5774
    int               /* in */  methodForDiffuse,
5775
    int               /* in */  methodForScan,
5776
    int               /* in */  methodForCarry,
5777
    int               /* in */  foptimize,
5778
    int               /* in */  foptimize_palette,
5779
    int               /* in */  complexion,
5780
    unsigned short    /* in */  *cachetable,
5781
    int               /* in */  *ncolors,
5782
    sixel_allocator_t /* in */  *allocator)
5783
{
168✔
5784
#if _MSC_VER
5785
    enum { max_depth = 4 };
5786
#else
5787
    const size_t max_depth = 4;
281✔
5788
#endif
5789
    unsigned char copy[max_depth];
168✔
5790
    SIXELSTATUS status = SIXEL_FALSE;
281✔
5791
    int sum1, sum2;
5792
    int n;
5793
    unsigned short *indextable;
5794
    size_t cache_size;
5795
    int allocated_cache;
5796
    int use_cache;
5797
    int cache_policy;
5798
    unsigned char new_palette[SIXEL_PALETTE_MAX * 4];
5799
    unsigned short migration_map[SIXEL_PALETTE_MAX];
5800
    int (*f_lookup)(unsigned char const * const pixel,
5801
                    int const depth,
5802
                    unsigned char const * const palette,
5803
                    int const reqcolor,
5804
                    unsigned short * const cachetable,
5805
                    int const complexion);
5806
    int use_varerr;
5807
    int use_positional;
5808
    int carry_mode;
5809

5810
    /* check bad reqcolor */
5811
    if (reqcolor < 1) {
281!
5812
        status = SIXEL_BAD_ARGUMENT;
×
5813
        sixel_helper_set_additional_message(
×
5814
            "sixel_quant_apply_palette: "
5815
            "a bad argument is detected, reqcolor < 0.");
5816
        goto end;
×
5817
    }
5818

5819
    /* NOTE: diffuse_jajuni, diffuse_stucki, and diffuse_burkes reference at
5820
     * minimum the position pos + width * 1 - 2, so width must be at least 2
5821
     * to avoid underflow.
5822
     * On the other hand, diffuse_fs and diffuse_atkinson
5823
     * reference pos + width * 1 - 1, but since these functions are only called
5824
     * when width >= 1, they do not cause underflow.
5825
     */
5826
    use_varerr = (depth == 3
394✔
5827
                  && (methodForDiffuse == SIXEL_DIFFUSE_LSO2
562!
5828
                      || methodForDiffuse == SIXEL_DIFFUSE_LSO3));
281!
5829
    use_positional = (methodForDiffuse == SIXEL_DIFFUSE_A_DITHER
394✔
5830
                      || methodForDiffuse == SIXEL_DIFFUSE_X_DITHER);
281!
5831
    carry_mode = (methodForCarry == SIXEL_CARRY_ENABLE)
281✔
5832
               ? SIXEL_CARRY_ENABLE
5833
               : SIXEL_CARRY_DISABLE;
168!
5834

5835
    f_lookup = NULL;
281✔
5836
    if (reqcolor == 2) {
281✔
5837
        sum1 = 0;
19✔
5838
        sum2 = 0;
19✔
5839
        for (n = 0; n < depth; ++n) {
76✔
5840
            sum1 += palette[n];
57✔
5841
        }
21✔
5842
        for (n = depth; n < depth + depth; ++n) {
76✔
5843
            sum2 += palette[n];
57✔
5844
        }
21✔
5845
        if (sum1 == 0 && sum2 == 255 * 3) {
19!
5846
            f_lookup = lookup_mono_darkbg;
12✔
5847
        } else if (sum1 == 255 * 3 && sum2 == 0) {
11!
5848
            f_lookup = lookup_mono_lightbg;
3✔
5849
        }
1✔
5850
    }
7✔
5851
    if (f_lookup == NULL) {
281✔
5852
        if (foptimize && depth == 3) {
266!
5853
            f_lookup = lookup_fast;
260✔
5854
        } else {
106✔
5855
            f_lookup = lookup_normal;
6✔
5856
        }
5857
    }
108✔
5858

5859
    if (f_lookup == lookup_fast) {
281✔
5860
        if (histogram_lut_policy == SIXEL_LUT_POLICY_ROBINHOOD) {
260!
5861
            f_lookup = lookup_fast_robinhood;
×
5862
        } else if (histogram_lut_policy == SIXEL_LUT_POLICY_HOPSCOTCH) {
260!
5863
            f_lookup = lookup_fast_hopscotch;
×
5864
        }
5865
    }
106✔
5866

5867
    indextable = cachetable;
281✔
5868
    allocated_cache = 0;
281✔
5869
    cache_policy = SIXEL_LUT_POLICY_AUTO;
281✔
5870
    use_cache = 0;
281✔
5871
    if (f_lookup == lookup_fast) {
281✔
5872
        cache_policy = histogram_lut_policy;
260✔
5873
        use_cache = 1;
260✔
5874
    } else if (f_lookup == lookup_fast_robinhood) {
127!
5875
        cache_policy = SIXEL_LUT_POLICY_ROBINHOOD;
×
5876
        use_cache = 1;
×
5877
    } else if (f_lookup == lookup_fast_hopscotch) {
21!
5878
        cache_policy = SIXEL_LUT_POLICY_HOPSCOTCH;
×
5879
        use_cache = 1;
×
5880
    }
5881
    if (cache_policy == SIXEL_LUT_POLICY_AUTO) {
281!
5882
        cache_policy = SIXEL_LUT_POLICY_6BIT;
281✔
5883
    }
113✔
5884
    if (use_cache) {
281✔
5885
        if (cachetable == NULL) {
260!
5886
            status = sixel_quant_cache_prepare(&indextable,
×
5887
                                               &cache_size,
5888
                                               cache_policy,
5889
                                               reqcolor,
5890
                                               allocator);
5891
            if (SIXEL_FAILED(status)) {
×
5892
                quant_trace(stderr,
×
5893
                            "Unable to allocate lookup cache.\n");
5894
                goto end;
×
5895
            }
5896
            allocated_cache = 1;
×
5897
        } else {
5898
            sixel_quant_cache_clear(indextable, cache_policy);
260✔
5899
        }
5900
    }
106✔
5901

5902
    if (use_positional) {
281!
5903
        status = apply_palette_positional(result, data, width, height,
×
5904
                                          depth, palette, reqcolor,
5905
                                          methodForDiffuse, methodForScan,
5906
                                          foptimize_palette, f_lookup,
5907
                                          indextable, complexion, copy,
5908
                                          new_palette, migration_map,
5909
                                          ncolors);
5910
    } else if (use_varerr) {
281!
5911
        status = apply_palette_variable(result, data, width, height,
×
5912
                                        depth, palette, reqcolor,
5913
                                        methodForScan, foptimize_palette,
5914
                                        f_lookup, indextable, complexion,
5915
                                        new_palette, migration_map,
5916
                                        ncolors,
5917
                                        methodForDiffuse,
5918
                                        carry_mode);
5919
    } else {
5920
        status = apply_palette_fixed(result, data, width, height,
394✔
5921
                                     depth, palette, reqcolor,
113✔
5922
                                     methodForScan, foptimize_palette,
113✔
5923
                                     f_lookup, indextable, complexion,
113✔
5924
                                     new_palette, migration_map,
113✔
5925
                                     ncolors, methodForDiffuse,
113✔
5926
                                     carry_mode);
113✔
5927
    }
5928
    if (SIXEL_FAILED(status)) {
281!
5929
        goto end;
×
5930
    }
5931

5932
    if (allocated_cache) {
281!
5933
        sixel_quant_cache_release(indextable,
×
5934
                                  cache_policy,
5935
                                  allocator);
5936
    }
5937

5938
    status = SIXEL_OK;
281✔
5939

5940
end:
168✔
5941
    return status;
281✔
5942
}
5943

5944

5945
void
5946
sixel_quant_free_palette(
256✔
5947
    unsigned char       /* in */ *data,
5948
    sixel_allocator_t   /* in */ *allocator)
5949
{
5950
    sixel_allocator_free(allocator, data);
256✔
5951
}
256✔
5952

5953

5954
#if HAVE_TESTS
5955
static int
5956
test1(void)
×
5957
{
5958
    int nret = EXIT_FAILURE;
×
5959
    sample minval[1] = { 1 };
×
5960
    sample maxval[1] = { 2 };
×
5961
    unsigned int retval;
5962

5963
    retval = largestByLuminosity(minval, maxval, 1);
×
5964
    if (retval != 0) {
×
5965
        goto error;
×
5966
    }
5967
    nret = EXIT_SUCCESS;
×
5968

5969
error:
5970
    return nret;
×
5971
}
5972

5973

5974
int
5975
sixel_quant_tests_main(void)
×
5976
{
5977
    int nret = EXIT_FAILURE;
×
5978
    size_t i;
5979
    typedef int (* testcase)(void);
5980

5981
    static testcase const testcases[] = {
5982
        test1,
5983
    };
5984

5985
    for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) {
×
5986
        nret = testcases[i]();
×
5987
        if (nret != EXIT_SUCCESS) {
×
5988
            goto error;
×
5989
        }
5990
    }
5991

5992
    nret = EXIT_SUCCESS;
×
5993

5994
error:
5995
    return nret;
×
5996
}
5997
#endif  /* HAVE_TESTS */
5998

5999
/* emacs Local Variables:      */
6000
/* emacs mode: c               */
6001
/* emacs tab-width: 4          */
6002
/* emacs indent-tabs-mode: nil */
6003
/* emacs c-basic-offset: 4     */
6004
/* emacs End:                  */
6005
/* vim: set expandtab ts=4 sts=4 sw=4 : */
6006
/* 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