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

saitoha / libsixel / 19172415645

07 Nov 2025 03:06PM UTC coverage: 46.739% (-0.1%) from 46.846%
19172415645

push

github

saitoha
feat: add -F,--final-merge option, implement an over-splitting & ward merging strategy

8060 of 25601 branches covered (31.48%)

169 of 311 new or added lines in 3 files covered. (54.34%)

45 existing lines in 1 file now uncovered.

11453 of 24504 relevant lines covered (46.74%)

2383390.89 hits per line

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

37.85
/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 float env_final_merge_target_factor = 1.81;
82

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

104

105
static float mask_a(int x, int y, int c);
106
static float mask_x(int x, int y, int c);
107
static void diffuse_none(unsigned char *data, int width, int height,
108
                         int x, int y, int depth, int error, int direction);
109
static void diffuse_fs(unsigned char *data, int width, int height,
110
                       int x, int y, int depth, int error, int direction);
111
static void diffuse_atkinson(unsigned char *data, int width, int height,
112
                             int x, int y, int depth, int error,
113
                             int direction);
114
static void diffuse_jajuni(unsigned char *data, int width, int height,
115
                           int x, int y, int depth, int error,
116
                           int direction);
117
static void diffuse_stucki(unsigned char *data, int width, int height,
118
                           int x, int y, int depth, int error,
119
                           int direction);
120
static void diffuse_burkes(unsigned char *data, int width, int height,
121
                           int x, int y, int depth, int error,
122
                           int direction);
123
static void diffuse_sierra1(unsigned char *data, int width, int height,
124
                            int x, int y, int depth, int error,
125
                            int direction);
126
static void diffuse_sierra2(unsigned char *data, int width, int height,
127
                            int x, int y, int depth, int error,
128
                            int direction);
129
static void diffuse_sierra3(unsigned char *data, int width, int height,
130
                            int x, int y, int depth, int error,
131
                            int direction);
132
static void diffuse_none_carry(int32_t *carry_curr, int32_t *carry_next,
133
                               int32_t *carry_far, int width, int height,
134
                               int depth, int x, int y, int32_t error,
135
                               int direction, int channel);
136
static void diffuse_fs_carry(int32_t *carry_curr, int32_t *carry_next,
137
                             int32_t *carry_far, int width, int height,
138
                             int depth, int x, int y, int32_t error,
139
                             int direction, int channel);
140
static void diffuse_atkinson_carry(int32_t *carry_curr, int32_t *carry_next,
141
                                   int32_t *carry_far, int width,
142
                                   int height, int depth, int x, int y,
143
                                   int32_t error, int direction,
144
                                   int channel);
145
static void diffuse_jajuni_carry(int32_t *carry_curr, int32_t *carry_next,
146
                                 int32_t *carry_far, int width, int height,
147
                                 int depth, int x, int y, int32_t error,
148
                                 int direction, int channel);
149
static void diffuse_stucki_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_burkes_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_sierra1_carry(int32_t *carry_curr, int32_t *carry_next,
158
                                  int32_t *carry_far, int width, int height,
159
                                  int depth, int x, int y, int32_t error,
160
                                  int direction, int channel);
161
static void diffuse_sierra2_carry(int32_t *carry_curr, int32_t *carry_next,
162
                                  int32_t *carry_far, int width, int height,
163
                                  int depth, int x, int y, int32_t error,
164
                                  int direction, int channel);
165
static void diffuse_sierra3_carry(int32_t *carry_curr, int32_t *carry_next,
166
                                  int32_t *carry_far, int width, int height,
167
                                  int depth, int x, int y, int32_t error,
168
                                  int direction, int channel);
169

170
static const int (*
171
lso2_table(void))[7]
×
172
{
173
#include "lso2.h"
174
    return var_coefs;
175
}
176

177
#define VARERR_SCALE_SHIFT 12
178
#define VARERR_SCALE       (1 << VARERR_SCALE_SHIFT)
179
#define VARERR_ROUND       (1 << (VARERR_SCALE_SHIFT - 1))
180
#define VARERR_MAX_VALUE   (255 * VARERR_SCALE)
181

182
#if HAVE_DEBUG
183
#define quant_trace fprintf
184
#else
185
static inline void quant_trace(FILE *f, ...) { (void) f; }
186
#endif
187

188
/*****************************************************************************
189
 *
190
 * quantization
191
 *
192
 *****************************************************************************/
193

194
typedef struct box* boxVector;
195
struct box {
196
    unsigned int ind;
197
    unsigned int colors;
198
    unsigned int sum;
199
};
200

201
typedef unsigned long sample;
202
typedef sample * tuple;
203

204
static unsigned char
NEW
205
sixel_quant_reversible_value(unsigned int sample)
×
206
{
UNCOV
207
    if (sample > 255U) {
×
UNCOV
208
        sample = 255U;
×
209
    }
210

NEW
211
    return sixel_safe_tones[sample];
×
212
}
213

214
static void
215
sixel_quant_reversible_pixel(unsigned char const *src,
×
216
                             unsigned int depth,
217
                             unsigned char *dst)
218
{
219
    unsigned int plane;
220

221
    for (plane = 0U; plane < depth; ++plane) {
×
222
        dst[plane] = sixel_quant_reversible_value(src[plane]);
×
223
    }
224
}
×
225

226
static void
227
sixel_quant_reversible_tuple(sample *tuple,
×
228
                             unsigned int depth)
229
{
230
    unsigned int plane;
231
    unsigned int sample_value;
232

233
    for (plane = 0U; plane < depth; ++plane) {
×
234
        sample_value = (unsigned int)tuple[plane];
×
235
        tuple[plane] =
×
236
            (sample)sixel_quant_reversible_value(sample_value);
×
237
    }
238
}
×
239

240
static void
241
sixel_quant_reversible_palette(unsigned char *palette,
×
242
                               unsigned int colors,
243
                               unsigned int depth)
244
{
245
    unsigned int index;
246
    unsigned int plane;
247
    unsigned int sample_value;
248
    size_t offset;
249

250
    for (index = 0U; index < colors; ++index) {
×
251
        for (plane = 0U; plane < depth; ++plane) {
×
252
            offset = (size_t)index * (size_t)depth + (size_t)plane;
×
253
            sample_value = (unsigned int)palette[offset];
×
254
            palette[offset] =
×
255
                sixel_quant_reversible_value(sample_value);
×
256
        }
257
    }
258
}
×
259

260
struct tupleint {
261
    /* An ordered pair of a tuple value and an integer, such as you
262
       would find in a tuple table or tuple hash.
263
       Note that this is a variable length structure.
264
    */
265
    unsigned int value;
266
    sample tuple[1];
267
    /* This is actually a variable size array -- its size is the
268
       depth of the tuple in question.  Some compilers do not let us
269
       declare a variable length array.
270
    */
271
};
272
typedef struct tupleint ** tupletable;
273

274
typedef struct {
275
    unsigned int size;
276
    tupletable table;
277
} tupletable2;
278

279
static unsigned int compareplanePlane;
280
static tupletable2 const *force_palette_source;
281
    /* This is a parameter to compareplane().  We use this global variable
282
       so that compareplane() can be called by qsort(), to compare two
283
       tuples.  qsort() doesn't pass any arguments except the two tuples.
284
    */
285
static int
286
compareplane(const void * const arg1,
12,089,356✔
287
             const void * const arg2)
288
{
289
    int lhs, rhs;
290

291
    typedef const struct tupleint * const * const sortarg;
292
    sortarg comparandPP  = (sortarg) arg1;
12,089,356✔
293
    sortarg comparatorPP = (sortarg) arg2;
12,089,356✔
294
    lhs = (int)(*comparandPP)->tuple[compareplanePlane];
12,089,356✔
295
    rhs = (int)(*comparatorPP)->tuple[compareplanePlane];
12,089,356✔
296

297
    return lhs - rhs;
12,089,356✔
298
}
299

300

301
static int
302
sumcompare(const void * const b1, const void * const b2)
27,883,574✔
303
{
304
    return (int)((boxVector)b2)->sum - (int)((boxVector)b1)->sum;
27,883,574✔
305
}
306

307

308
static SIXELSTATUS
309
alloctupletable(
520✔
310
    tupletable          /* out */ *result,
311
    unsigned int const  /* in */  depth,
312
    unsigned int const  /* in */  size,
313
    sixel_allocator_t   /* in */  *allocator)
314
{
315
    SIXELSTATUS status = SIXEL_FALSE;
520✔
316
    enum { message_buffer_size = 256 };
317
    char message[message_buffer_size];
318
    int nwrite;
319
    unsigned int mainTableSize;
320
    unsigned int tupleIntSize;
321
    unsigned int allocSize;
322
    void * pool;
323
    tupletable tbl;
324
    unsigned int i;
325

326
    if (UINT_MAX / sizeof(struct tupleint) < size) {
520!
327
        nwrite = sixel_compat_snprintf(
×
328
            message,
329
            sizeof(message),
330
            "size %u is too big for arithmetic",
331
            size);
332
        if (nwrite > 0) {
×
333
            sixel_helper_set_additional_message(message);
×
334
        }
335
        status = SIXEL_RUNTIME_ERROR;
×
336
        goto end;
×
337
    }
338

339
    mainTableSize = size * sizeof(struct tupleint *);
520✔
340
    tupleIntSize = sizeof(struct tupleint) - sizeof(sample)
520✔
341
        + depth * sizeof(sample);
236✔
342

343
    /* To save the enormous amount of time it could take to allocate
344
       each individual tuple, we do a trick here and allocate everything
345
       as a single malloc block and suballocate internally.
346
    */
347
    if ((UINT_MAX - mainTableSize) / tupleIntSize < size) {
520!
348
        nwrite = sixel_compat_snprintf(
×
349
            message,
350
            sizeof(message),
351
            "size %u is too big for arithmetic",
352
            size);
353
        if (nwrite > 0) {
×
354
            sixel_helper_set_additional_message(message);
×
355
        }
356
        status = SIXEL_RUNTIME_ERROR;
×
357
        goto end;
×
358
    }
359

360
    allocSize = mainTableSize + size * tupleIntSize;
520✔
361

362
    pool = sixel_allocator_malloc(allocator, allocSize);
520✔
363
    if (pool == NULL) {
520!
364
        sixel_compat_snprintf(
×
365
            message,
366
            sizeof(message),
367
            "unable to allocate %u bytes for a %u-entry tuple table",
368
            allocSize,
369
            size);
370
        sixel_helper_set_additional_message(message);
×
371
        status = SIXEL_BAD_ALLOCATION;
×
372
        goto end;
×
373
    }
374
    tbl = (tupletable) pool;
520✔
375

376
    for (i = 0; i < size; ++i)
311,411✔
377
        tbl[i] = (struct tupleint *)
310,891✔
378
            ((char*)pool + mainTableSize + i * tupleIntSize);
310,891✔
379

380
    *result = tbl;
520✔
381

382
    status = SIXEL_OK;
520✔
383

384
end:
284✔
385
    return status;
520✔
386
}
387

388

389
/*
390
** Here is the fun part, the median-cut colormap generator.  This is based
391
** on Paul Heckbert's paper "Color Image Quantization for Frame Buffer
392
** Display", SIGGRAPH '82 Proceedings, page 297.
393
*/
394

395
static tupletable2
396
newColorMap(unsigned int const newcolors, unsigned int const depth, sixel_allocator_t *allocator)
69✔
397
{
398
    SIXELSTATUS status = SIXEL_FALSE;
69✔
399
    tupletable2 colormap;
400
    unsigned int i;
401

402
    colormap.size = 0;
69✔
403
    status = alloctupletable(&colormap.table, depth, newcolors, allocator);
69✔
404
    if (SIXEL_FAILED(status)) {
69!
405
        goto end;
×
406
    }
407
    if (colormap.table) {
92!
408
        for (i = 0; i < newcolors; ++i) {
13,833✔
409
            unsigned int plane;
410
            for (plane = 0; plane < depth; ++plane)
55,056✔
411
                colormap.table[i]->tuple[plane] = 0;
41,292✔
412
        }
4,588✔
413
        colormap.size = newcolors;
69✔
414
    }
23✔
415

416
end:
417
    return colormap;
69✔
418
}
419

420

421
static boxVector
422
newBoxVector(
69✔
423
    unsigned int const  /* in */ colors,
424
    unsigned int const  /* in */ sum,
425
    unsigned int const  /* in */ newcolors,
426
    sixel_allocator_t   /* in */ *allocator)
427
{
428
    boxVector bv;
429

430
    bv = (boxVector)sixel_allocator_malloc(allocator,
92✔
431
                                           sizeof(struct box) * (size_t)newcolors);
69✔
432
    if (bv == NULL) {
69!
433
        quant_trace(stderr, "out of memory allocating box vector table\n");
×
434
        return NULL;
×
435
    }
436

437
    /* Set up the initial box. */
438
    bv[0].ind = 0;
69✔
439
    bv[0].colors = colors;
69✔
440
    bv[0].sum = sum;
69✔
441

442
    return bv;
69✔
443
}
23✔
444

445

446
static void
447
findBoxBoundaries(tupletable2  const colorfreqtable,
24,813✔
448
                  unsigned int const depth,
449
                  unsigned int const boxStart,
450
                  unsigned int const boxSize,
451
                  sample             minval[],
452
                  sample             maxval[])
453
{
454
/*----------------------------------------------------------------------------
455
  Go through the box finding the minimum and maximum of each
456
  component - the boundaries of the box.
457
-----------------------------------------------------------------------------*/
458
    unsigned int plane;
459
    unsigned int i;
460

461
    for (plane = 0; plane < depth; ++plane) {
99,252✔
462
        minval[plane] = colorfreqtable.table[boxStart]->tuple[plane];
74,439✔
463
        maxval[plane] = minval[plane];
74,439✔
464
    }
24,813✔
465

466
    for (i = 1; i < boxSize; ++i) {
2,226,612✔
467
        for (plane = 0; plane < depth; ++plane) {
8,807,196✔
468
            sample const v = colorfreqtable.table[boxStart + i]->tuple[plane];
6,605,397✔
469
            if (v < minval[plane]) minval[plane] = v;
6,605,397✔
470
            if (v > maxval[plane]) maxval[plane] = v;
6,605,397✔
471
        }
2,202,753✔
472
    }
734,251✔
473
}
24,813✔
474

475

476

477
static unsigned int
478
largestByNorm(sample minval[], sample maxval[], unsigned int const depth)
23,427✔
479
{
480

481
    unsigned int largestDimension;
482
    unsigned int plane;
483
    sample largestSpreadSoFar;
484

485
    largestSpreadSoFar = 0;
23,427✔
486
    largestDimension = 0;
23,427✔
487
    for (plane = 0; plane < depth; ++plane) {
93,708✔
488
        sample const spread = maxval[plane]-minval[plane];
70,281✔
489
        if (spread > largestSpreadSoFar) {
70,281✔
490
            largestDimension = plane;
39,144✔
491
            largestSpreadSoFar = spread;
39,144✔
492
        }
12,946✔
493
    }
23,427✔
494
    return largestDimension;
23,427✔
495
}
496

497

498

499
static unsigned int
500
largestByLuminosity(sample minval[], sample maxval[], unsigned int const depth)
1,386✔
501
{
502
/*----------------------------------------------------------------------------
503
   This subroutine presumes that the tuple type is either
504
   BLACKANDWHITE, GRAYSCALE, or RGB (which implies pamP->depth is 1 or 3).
505
   To save time, we don't actually check it.
506
-----------------------------------------------------------------------------*/
507
    unsigned int retval;
508

509
    double lumin_factor[3] = {0.2989, 0.5866, 0.1145};
1,386✔
510

511
    if (depth == 1) {
1,386!
512
        retval = 0;
×
513
    } else {
514
        /* An RGB tuple */
515
        unsigned int largestDimension;
516
        unsigned int plane;
517
        double largestSpreadSoFar;
518

519
        largestSpreadSoFar = 0.0;
1,386✔
520
        largestDimension = 0;
1,386✔
521

522
        for (plane = 0; plane < 3; ++plane) {
5,544✔
523
            double const spread =
4,158✔
524
                lumin_factor[plane] * (maxval[plane]-minval[plane]);
4,158✔
525
            if (spread > largestSpreadSoFar) {
4,158✔
526
                largestDimension = plane;
2,369✔
527
                largestSpreadSoFar = spread;
2,369✔
528
            }
809✔
529
        }
1,386✔
530
        retval = largestDimension;
1,386✔
531
    }
532
    return retval;
1,386✔
533
}
534

535

536

537
static void
UNCOV
538
centerBox(unsigned int const boxStart,
×
539
          unsigned int const boxSize,
540
          tupletable2  const colorfreqtable,
541
          unsigned int const depth,
542
          tuple        const newTuple)
543
{
544

545
    unsigned int plane;
546
    sample minval, maxval;
547
    unsigned int i;
548

UNCOV
549
    for (plane = 0; plane < depth; ++plane) {
×
UNCOV
550
        minval = maxval = colorfreqtable.table[boxStart]->tuple[plane];
×
551

UNCOV
552
        for (i = 1; i < boxSize; ++i) {
×
UNCOV
553
            sample v = colorfreqtable.table[boxStart + i]->tuple[plane];
×
UNCOV
554
            minval = minval < v ? minval: v;
×
UNCOV
555
            maxval = maxval > v ? maxval: v;
×
556
        }
UNCOV
557
        newTuple[plane] = (minval + maxval) / 2;
×
558
    }
UNCOV
559
}
×
560

561

562

563
static void
UNCOV
564
averageColors(unsigned int const boxStart,
×
565
              unsigned int const boxSize,
566
              tupletable2  const colorfreqtable,
567
              unsigned int const depth,
568
              tuple        const newTuple)
569
{
570
    unsigned int plane;
571
    sample sum;
572
    unsigned int i;
573

UNCOV
574
    for (plane = 0; plane < depth; ++plane) {
×
UNCOV
575
        sum = 0;
×
576

UNCOV
577
        for (i = 0; i < boxSize; ++i) {
×
UNCOV
578
            sum += colorfreqtable.table[boxStart + i]->tuple[plane];
×
579
        }
580

UNCOV
581
        newTuple[plane] = sum / boxSize;
×
582
    }
UNCOV
583
}
×
584

585

586

587
static void
UNCOV
588
averagePixels(unsigned int const boxStart,
×
589
              unsigned int const boxSize,
590
              tupletable2 const colorfreqtable,
591
              unsigned int const depth,
592
              tuple const newTuple)
593
{
594

595
    unsigned int n;
596
        /* Number of tuples represented by the box */
597
    unsigned int plane;
598
    unsigned int i;
599

600
    /* Count the tuples in question */
UNCOV
601
    n = 0;  /* initial value */
×
UNCOV
602
    for (i = 0; i < boxSize; ++i) {
×
UNCOV
603
        n += (unsigned int)colorfreqtable.table[boxStart + i]->value;
×
604
    }
605

UNCOV
606
    for (plane = 0; plane < depth; ++plane) {
×
607
        sample sum;
608

UNCOV
609
        sum = 0;
×
610

UNCOV
611
        for (i = 0; i < boxSize; ++i) {
×
UNCOV
612
            sum += colorfreqtable.table[boxStart + i]->tuple[plane]
×
UNCOV
613
                * (unsigned int)colorfreqtable.table[boxStart + i]->value;
×
614
        }
615

UNCOV
616
        newTuple[plane] = sum / n;
×
617
    }
UNCOV
618
}
×
619

620

621

622
static tupletable2
UNCOV
623
colormapFromBv(unsigned int const newcolors,
×
624
               boxVector const bv,
625
               unsigned int const boxes,
626
               tupletable2 const colorfreqtable,
627
               unsigned int const depth,
628
               int const methodForRep,
629
               int const use_reversible,
630
               sixel_allocator_t *allocator)
631
{
632
    /*
633
    ** Ok, we've got enough boxes.  Now choose a representative color for
634
    ** each box.  There are a number of possible ways to make this choice.
635
    ** One would be to choose the center of the box; this ignores any structure
636
    ** within the boxes.  Another method would be to average all the colors in
637
    ** the box - this is the method specified in Heckbert's paper.  A third
638
    ** method is to average all the pixels in the box.
639
    */
640
    tupletable2 colormap;
641
    unsigned int bi;
642

UNCOV
643
    colormap = newColorMap(newcolors, depth, allocator);
×
UNCOV
644
    if (!colormap.size) {
×
645
        return colormap;
×
646
    }
647

UNCOV
648
    for (bi = 0; bi < boxes; ++bi) {
×
UNCOV
649
        switch (methodForRep) {
×
650
        case SIXEL_REP_CENTER_BOX:
UNCOV
651
            centerBox(bv[bi].ind, bv[bi].colors,
×
652
                      colorfreqtable, depth,
UNCOV
653
                      colormap.table[bi]->tuple);
×
UNCOV
654
            break;
×
655
        case SIXEL_REP_AVERAGE_COLORS:
UNCOV
656
            averageColors(bv[bi].ind, bv[bi].colors,
×
657
                          colorfreqtable, depth,
UNCOV
658
                          colormap.table[bi]->tuple);
×
UNCOV
659
            break;
×
660
        case SIXEL_REP_AVERAGE_PIXELS:
UNCOV
661
            averagePixels(bv[bi].ind, bv[bi].colors,
×
662
                          colorfreqtable, depth,
UNCOV
663
                          colormap.table[bi]->tuple);
×
UNCOV
664
            break;
×
665
        default:
666
            quant_trace(stderr, "Internal error: "
×
667
                                "invalid value of methodForRep: %d\n",
668
                        methodForRep);
669
        }
UNCOV
670
        if (use_reversible) {
×
671
            sixel_quant_reversible_tuple(colormap.table[bi]->tuple,
×
672
                                         depth);
673
        }
674
    }
UNCOV
675
    return colormap;
×
676
}
677

678

679
static int
680
force_palette_compare(const void *lhs, const void *rhs)
×
681
{
682
    unsigned int left;
683
    unsigned int right;
684
    unsigned int left_value;
685
    unsigned int right_value;
686

687
    left = *(const unsigned int *)lhs;
×
688
    right = *(const unsigned int *)rhs;
×
689
    left_value = force_palette_source->table[left]->value;
×
690
    right_value = force_palette_source->table[right]->value;
×
691
    if (left_value > right_value) {
×
692
        return -1;
×
693
    }
694
    if (left_value < right_value) {
×
695
        return 1;
×
696
    }
697
    if (left < right) {
×
698
        return -1;
×
699
    }
700
    if (left > right) {
×
701
        return 1;
×
702
    }
703
    return 0;
×
704
}
705

706

707
static SIXELSTATUS
708
force_palette_completion(tupletable2 *colormapP,
×
709
                         unsigned int depth,
710
                         unsigned int reqColors,
711
                         tupletable2 const colorfreqtable,
712
                         sixel_allocator_t *allocator)
713
{
714
    /*
715
     * We enqueue "losers" from the histogram so that we can revive them:
716
     *
717
     *   histogram --> sort by hit count --> append to palette tail
718
     *        ^                             |
719
     *        +-----------------------------+
720
     *
721
     * The ASCII loop shows how discarded colors walk back into the
722
     * palette when the user demands an exact size.
723
     */
724
    SIXELSTATUS status = SIXEL_FALSE;
×
725
    tupletable new_table = NULL;
×
726
    unsigned int *order = NULL;
×
727
    unsigned int current;
728
    unsigned int fill;
729
    unsigned int candidate;
730
    unsigned int plane;
731
    unsigned int source;
732

733
    current = colormapP->size;
×
734
    if (current >= reqColors) {
×
735
        return SIXEL_OK;
×
736
    }
737

738
    status = alloctupletable(&new_table, depth, reqColors, allocator);
×
739
    if (SIXEL_FAILED(status)) {
×
740
        goto end;
×
741
    }
742

743
    if (colorfreqtable.size > 0U) {
×
744
        order = (unsigned int *)sixel_allocator_malloc(
×
745
            allocator, colorfreqtable.size * sizeof(unsigned int));
×
746
        if (order == NULL) {
×
747
            status = SIXEL_BAD_ALLOCATION;
×
748
            goto end;
×
749
        }
750
        for (candidate = 0; candidate < colorfreqtable.size; ++candidate) {
×
751
            order[candidate] = candidate;
×
752
        }
753
        force_palette_source = &colorfreqtable;
×
754
        qsort(order, colorfreqtable.size, sizeof(unsigned int),
×
755
              force_palette_compare);
756
        force_palette_source = NULL;
×
757
    }
758

759
    for (fill = 0; fill < current; ++fill) {
×
760
        new_table[fill]->value = colormapP->table[fill]->value;
×
761
        for (plane = 0; plane < depth; ++plane) {
×
762
            new_table[fill]->tuple[plane] =
×
763
                colormapP->table[fill]->tuple[plane];
×
764
        }
765
    }
766

767
    candidate = 0U;
×
768
    fill = current;
×
769
    if (order != NULL) {
×
770
        while (fill < reqColors && candidate < colorfreqtable.size) {
×
771
            unsigned int index;
772

773
            index = order[candidate];
×
774
            new_table[fill]->value = colorfreqtable.table[index]->value;
×
775
            for (plane = 0; plane < depth; ++plane) {
×
776
                new_table[fill]->tuple[plane] =
×
777
                    colorfreqtable.table[index]->tuple[plane];
×
778
            }
779
            ++fill;
×
780
            ++candidate;
×
781
        }
782
    }
783

784
    if (fill < reqColors && fill == 0U) {
×
785
        new_table[fill]->value = 0U;
×
786
        for (plane = 0; plane < depth; ++plane) {
×
787
            new_table[fill]->tuple[plane] = 0U;
×
788
        }
789
        ++fill;
×
790
    }
791

792
    source = 0U;
×
793
    while (fill < reqColors) {
×
794
        new_table[fill]->value = new_table[source]->value;
×
795
        for (plane = 0; plane < depth; ++plane) {
×
796
            new_table[fill]->tuple[plane] = new_table[source]->tuple[plane];
×
797
        }
798
        ++fill;
×
799
        ++source;
×
800
        if (source >= fill) {
×
801
            source = 0U;
×
802
        }
803
    }
804

805
    sixel_allocator_free(allocator, colormapP->table);
×
806
    colormapP->table = new_table;
×
807
    colormapP->size = reqColors;
×
808
    status = SIXEL_OK;
×
809

810
end:
811
    if (status != SIXEL_OK && new_table != NULL) {
×
812
        sixel_allocator_free(allocator, new_table);
×
813
    }
814
    if (order != NULL) {
×
815
        sixel_allocator_free(allocator, order);
×
816
    }
817
    return status;
×
818
}
819

820

821
static SIXELSTATUS
822
splitBox(boxVector const bv,
24,813✔
823
         unsigned int *const boxesP,
824
         unsigned int const bi,
825
         tupletable2 const colorfreqtable,
826
         unsigned int const depth,
827
         int const methodForLargest)
828
{
829
/*----------------------------------------------------------------------------
830
   Split Box 'bi' in the box vector bv (so that bv contains one more box
831
   than it did as input).  Split it so that each new box represents about
832
   half of the pixels in the distribution given by 'colorfreqtable' for
833
   the colors in the original box, but with distinct colors in each of the
834
   two new boxes.
835

836
   Assume the box contains at least two colors.
837
-----------------------------------------------------------------------------*/
838
    SIXELSTATUS status = SIXEL_FALSE;
24,813✔
839
    unsigned int const boxStart = bv[bi].ind;
24,813✔
840
    unsigned int const boxSize  = bv[bi].colors;
24,813✔
841
    unsigned int const sm       = bv[bi].sum;
24,813✔
842

843
    enum { max_depth= 16 };
844
    sample minval[max_depth];
845
    sample maxval[max_depth];
846

847
    /* assert(max_depth >= depth); */
848

849
    unsigned int largestDimension;
850
    /* number of the plane with the largest spread */
851
    unsigned int medianIndex;
852
    unsigned int lowersum;
853

854
    /* Number of pixels whose value is "less than" the median */
855
    findBoxBoundaries(colorfreqtable, depth, boxStart, boxSize,
33,084✔
856
                      minval, maxval);
8,271✔
857

858
    /* Find the largest dimension, and sort by that component.  I have
859
       included two methods for determining the "largest" dimension;
860
       first by simply comparing the range in RGB space, and second by
861
       transforming into luminosities before the comparison.
862
    */
863
    switch (methodForLargest) {
24,813!
864
    case SIXEL_LARGE_NORM:
15,618✔
865
        largestDimension = largestByNorm(minval, maxval, depth);
23,427✔
866
        break;
23,427✔
867
    case SIXEL_LARGE_LUM:
924✔
868
        largestDimension = largestByLuminosity(minval, maxval, depth);
1,386✔
869
        break;
1,386✔
870
    default:
871
        sixel_helper_set_additional_message(
×
872
            "Internal error: invalid value of methodForLargest.");
873
        status = SIXEL_LOGIC_ERROR;
×
874
        goto end;
×
875
    }
876

877
    /* TODO: I think this sort should go after creating a box,
878
       not before splitting.  Because you need the sort to use
879
       the SIXEL_REP_CENTER_BOX method of choosing a color to
880
       represent the final boxes
881
    */
882

883
    /* Set the gross global variable 'compareplanePlane' as a
884
       parameter to compareplane(), which is called by qsort().
885
    */
886
    compareplanePlane = largestDimension;
24,813✔
887
    qsort((char*) &colorfreqtable.table[boxStart], boxSize,
24,813✔
888
          sizeof(colorfreqtable.table[boxStart]),
889
          compareplane);
890

891
    {
892
        /* Now find the median based on the counts, so that about half
893
           the pixels (not colors, pixels) are in each subdivision.  */
894

895
        unsigned int i;
896

897
        lowersum = colorfreqtable.table[boxStart]->value; /* initial value */
24,813✔
898
        for (i = 1; i < boxSize - 1 && lowersum < sm / 2; ++i) {
1,038,483✔
899
            lowersum += colorfreqtable.table[boxStart + i]->value;
1,013,670✔
900
        }
336,688✔
901
        medianIndex = i;
24,813✔
902
    }
903
    /* Split the box, and sort to bring the biggest boxes to the top.  */
904

905
    bv[bi].colors = medianIndex;
24,813✔
906
    bv[bi].sum = lowersum;
24,813✔
907
    bv[*boxesP].ind = boxStart + medianIndex;
24,813✔
908
    bv[*boxesP].colors = boxSize - medianIndex;
24,813✔
909
    bv[*boxesP].sum = sm - lowersum;
24,813✔
910
    ++(*boxesP);
24,813✔
911
    qsort((char*) bv, *boxesP, sizeof(struct box), sumcompare);
24,813✔
912

913
    status = SIXEL_OK;
24,813✔
914

915
end:
16,542✔
916
    return status;
24,813✔
917
}
918

919

920

921
static unsigned int sixel_final_merge_target(unsigned int reqcolors,
922
                                             int final_merge_mode);
923
static void sixel_merge_clusters_ward(unsigned long *weights,
924
                                      unsigned long *sums,
925
                                      unsigned int depth,
926
                                      int *cluster_count,
927
                                      int target);
928
static SIXELSTATUS sixel_quant_clusters_to_colormap(unsigned long *weights,
929
                                                    unsigned long *sums,
930
                                                    unsigned int depth,
931
                                                    unsigned int cluster_count,
932
                                                    int use_reversible,
933
                                                    tupletable2 *colormapP,
934
                                                    sixel_allocator_t *allocator);
935

936
static SIXELSTATUS
937
mediancut(tupletable2 const colorfreqtable,
69✔
938
          unsigned int const depth,
939
          unsigned int const newcolors,
940
          int const methodForLargest,
941
          int const methodForRep,
942
          int const use_reversible,
943
          int const final_merge_mode,
944
          tupletable2 *const colormapP,
945
          sixel_allocator_t *allocator)
946
{
947
/*----------------------------------------------------------------------------
948
   Compute a set of only 'newcolors' colors that best represent an
949
   image whose pixels are summarized by the histogram
950
   'colorfreqtable'.  Each tuple in that table has depth 'depth'.
951
   colorfreqtable.table[i] tells the number of pixels in the subject image
952
   have a particular color.
953

954
   As a side effect, sort 'colorfreqtable'.
955
-----------------------------------------------------------------------------*/
956
    boxVector bv;
957
    unsigned int bi;
958
    unsigned int boxes;
959
    int multicolorBoxesExist;
960
    unsigned int i;
961
    unsigned int sum;
962
    unsigned int working_colors;
963
    int apply_merge;
964
    unsigned long *cluster_weight;
965
    unsigned long *cluster_sums;
966
    int cluster_total;
967
    unsigned int plane;
968
    unsigned int offset;
969
    unsigned int size;
970
    unsigned long value;
971
    struct tupleint *entry;
972
    SIXELSTATUS merge_status;
973
    SIXELSTATUS status = SIXEL_FALSE;
69✔
974

975
    sum = 0;
69✔
976
    working_colors = newcolors;
69✔
977
    apply_merge = (final_merge_mode == SIXEL_FINAL_MERGE_AUTO
69✔
978
                   || final_merge_mode == SIXEL_FINAL_MERGE_WARD);
69!
979
    bv = NULL;
69✔
980
    cluster_weight = NULL;
69✔
981
    cluster_sums = NULL;
69✔
982
    cluster_total = 0;
69✔
983
    plane = 0U;
69✔
984
    offset = 0U;
69✔
985
    size = 0U;
69✔
986
    value = 0UL;
69✔
987
    entry = NULL;
69✔
988
    merge_status = SIXEL_OK;
69✔
989

990
    for (i = 0; i < colorfreqtable.size; ++i) {
291,168✔
991
        sum += colorfreqtable.table[i]->value;
291,099✔
992
    }
96,889✔
993

994
    if (apply_merge) {
69!
995
        /* Choose an oversplit target so that the merge stage has slack. */
996
        working_colors = sixel_final_merge_target(newcolors,
92✔
997
                                                  final_merge_mode);
23✔
998
        if (working_colors > colorfreqtable.size) {
69!
NEW
999
            working_colors = colorfreqtable.size;
×
1000
        }
1001
        quant_trace(stderr, "overshoot: %d\n", working_colors);
69✔
1002
    }
23✔
1003
    if (working_colors == 0U) {
69!
NEW
1004
        working_colors = 1U;
×
1005
    }
1006

1007
    /* There is at least one box that contains at least 2 colors; ergo,
1008
       there is more splitting we can do.  */
1009
    bv = newBoxVector(colorfreqtable.size, sum, working_colors, allocator);
69✔
1010
    if (bv == NULL) {
69!
1011
        goto end;
×
1012
    }
1013
    boxes = 1;
69✔
1014
    multicolorBoxesExist = (colorfreqtable.size > 1);
69✔
1015

1016
    /* Main loop: split boxes until we have enough. */
1017
    while (boxes < working_colors && multicolorBoxesExist) {
24,882!
1018
        /* Find the first splittable box. */
1019
        for (bi = 0; bi < boxes && bv[bi].colors < 2; ++bi)
442,226!
1020
            ;
1021
        if (bi >= boxes) {
24,813!
1022
            multicolorBoxesExist = 0;
×
1023
        } else {
1024
            status = splitBox(bv, &boxes, bi,
33,084✔
1025
                              colorfreqtable, depth,
8,271✔
1026
                              methodForLargest);
8,271✔
1027
            if (SIXEL_FAILED(status)) {
24,813!
1028
                goto end;
×
1029
            }
1030
        }
1031
    }
1032
    if (apply_merge && boxes > newcolors) {
69!
1033
        /* Capture weight and component sums for each temporary box. */
1034
        cluster_weight = (unsigned long *)sixel_allocator_malloc(
69✔
1035
            allocator, (size_t)boxes * sizeof(unsigned long));
69✔
1036
        cluster_sums = (unsigned long *)sixel_allocator_malloc(
69✔
1037
            allocator, (size_t)boxes * (size_t)depth * sizeof(unsigned long));
69✔
1038
        if (cluster_weight == NULL || cluster_sums == NULL) {
69!
NEW
1039
            status = SIXEL_BAD_ALLOCATION;
×
NEW
1040
            goto end;
×
1041
        }
1042
        for (bi = 0U; bi < boxes; ++bi) {
24,951✔
1043
            offset = bv[bi].ind;
24,882✔
1044
            size = bv[bi].colors;
24,882✔
1045
            cluster_weight[bi] = 0UL;
24,882✔
1046
            for (plane = 0U; plane < depth; ++plane) {
99,528✔
1047
                cluster_sums[(size_t)bi * (size_t)depth + plane] = 0UL;
74,646✔
1048
            }
24,882✔
1049
            for (i = 0U; i < size; ++i) {
315,981✔
1050
                entry = colorfreqtable.table[offset + i];
291,099✔
1051
                value = (unsigned long)entry->value;
291,099✔
1052
                cluster_weight[bi] += value;
291,099✔
1053
                for (plane = 0U; plane < depth; ++plane) {
1,164,396✔
1054
                    cluster_sums[(size_t)bi * (size_t)depth + plane] +=
873,297✔
1055
                        (unsigned long)entry->tuple[plane] * value;
873,297✔
1056
                }
290,667✔
1057
            }
96,889✔
1058
        }
8,294✔
1059
        cluster_total = (int)boxes;
69✔
1060
        /* Merge clusters greedily using Ward's minimum variance rule. */
1061
        sixel_merge_clusters_ward(cluster_weight, cluster_sums, depth,
92✔
1062
                                  &cluster_total, (int)newcolors);
23✔
1063
        if (cluster_total < 1) {
69!
NEW
1064
            cluster_total = 1;
×
1065
        }
1066
        if ((unsigned int)cluster_total > newcolors) {
69!
NEW
1067
            cluster_total = (int)newcolors;
×
1068
        }
1069
        /* Rebuild the palette using the merged cluster statistics. */
1070
        merge_status = sixel_quant_clusters_to_colormap(cluster_weight,
92✔
1071
                                                        cluster_sums,
23✔
1072
                                                        depth,
23✔
1073
                                                        (unsigned int)cluster_total,
23✔
1074
                                                        use_reversible,
23✔
1075
                                                        colormapP,
23✔
1076
                                                        allocator);
23✔
1077
        if (SIXEL_FAILED(merge_status)) {
69!
NEW
1078
            status = merge_status;
×
NEW
1079
            goto end;
×
1080
        }
1081
    } else {
23✔
NEW
1082
        *colormapP = colormapFromBv(newcolors, bv, boxes,
×
1083
                                    colorfreqtable, depth,
1084
                                    methodForRep, use_reversible,
1085
                                    allocator);
1086
    }
1087

1088
    status = SIXEL_OK;
69✔
1089

1090
end:
46✔
1091
    if (bv != NULL) {
69!
1092
        sixel_allocator_free(allocator, bv);
69✔
1093
    }
23✔
1094
    if (cluster_sums != NULL) {
69!
1095
        sixel_allocator_free(allocator, cluster_sums);
69✔
1096
    }
23✔
1097
    if (cluster_weight != NULL) {
69!
1098
        sixel_allocator_free(allocator, cluster_weight);
69✔
1099
    }
23✔
1100
    return status;
69✔
1101
}
1102

1103

1104
/* Determine how many clusters to create before the final merge step. */
1105
static unsigned int
1106
sixel_final_merge_target(unsigned int reqcolors,
69✔
1107
                         int final_merge_mode)
1108
{
1109
    double factor;
1110
    unsigned int scaled;
1111

1112
    if (final_merge_mode != SIXEL_FINAL_MERGE_AUTO
69!
1113
        && final_merge_mode != SIXEL_FINAL_MERGE_WARD) {
23!
NEW
1114
        return reqcolors;
×
1115
    }
1116
    factor = env_final_merge_target_factor;
69✔
1117
    scaled = (unsigned int)((double)reqcolors * factor);
69✔
1118
    if (scaled <= reqcolors) {
69!
NEW
1119
        scaled = reqcolors;
×
1120
    }
1121
    if (scaled < 1U) {
69!
NEW
1122
        scaled = 1U;
×
1123
    }
1124

1125
    return scaled;
69✔
1126
}
23✔
1127

1128

1129
/* Merge clusters to the requested size using Ward linkage. */
1130
static void
1131
sixel_merge_clusters_ward(unsigned long *weights,
69✔
1132
                          unsigned long *sums,
1133
                          unsigned int depth,
1134
                          int *cluster_count,
1135
                          int target)
1136
{
1137
    int n;
1138
    int desired;
1139
    int best_i;
1140
    int best_j;
1141
    int i;
1142
    int j;
1143
    int k;
1144
    int channel;
1145
    double best_cost;
1146
    double wi;
1147
    double wj;
1148
    double distance_sq;
1149
    double delta;
1150
    double mean_i;
1151
    double mean_j;
1152
    double diff;
1153
    size_t offset_i;
1154
    size_t offset_j;
1155
    size_t dst;
1156
    size_t src;
1157

1158
    if (cluster_count == NULL) {
69!
NEW
1159
        return;
×
1160
    }
1161
    n = *cluster_count;
69✔
1162
    desired = target;
69✔
1163
    if (desired < 1) {
69!
NEW
1164
        desired = 1;
×
1165
    }
1166
    while (n > desired) {
11,187✔
1167
        best_i = -1;
11,118✔
1168
        best_j = -1;
11,118✔
1169
        best_cost = 1.0e300;
11,118✔
1170
        for (i = 0; i < n; ++i) {
3,948,603✔
1171
            if (weights[i] == 0UL) {
3,937,485!
NEW
1172
                continue;
×
1173
            }
1174
            wi = (double)weights[i];
3,937,485✔
1175
            offset_i = (size_t)i * (size_t)depth;
3,937,485✔
1176
            for (j = i + 1; j < n; ++j) {
724,512,615✔
1177
                if (weights[j] == 0UL) {
720,575,130!
NEW
1178
                    continue;
×
1179
                }
1180
                wj = (double)weights[j];
720,575,130✔
1181
                offset_j = (size_t)j * (size_t)depth;
720,575,130✔
1182
                distance_sq = 0.0;
720,575,130✔
1183
                for (channel = 0; channel < (int)depth; ++channel) {
2,147,483,647✔
1184
                    mean_i = (double)sums[offset_i + (size_t)channel] / wi;
2,147,483,647✔
1185
                    mean_j = (double)sums[offset_j + (size_t)channel] / wj;
2,147,483,647✔
1186
                    diff = mean_i - mean_j;
2,147,483,647✔
1187
                    distance_sq += diff * diff;
2,147,483,647✔
1188
                }
720,575,130✔
1189
                delta = (wi * wj) / (wi + wj) * distance_sq;
720,575,130✔
1190
                if (delta < best_cost) {
720,575,130✔
1191
                    best_cost = delta;
124,929✔
1192
                    best_i = i;
124,929✔
1193
                    best_j = j;
124,929✔
1194
                }
41,091✔
1195
            }
240,191,710✔
1196
        }
1,312,495✔
1197
        if (best_i < 0 || best_j < 0) {
11,118!
1198
            break;
1199
        }
1200
        weights[best_i] += weights[best_j];
11,118✔
1201
        offset_i = (size_t)best_i * (size_t)depth;
11,118✔
1202
        offset_j = (size_t)best_j * (size_t)depth;
11,118✔
1203
        for (channel = 0; channel < (int)depth; ++channel) {
44,472✔
1204
            sums[offset_i + (size_t)channel] +=
33,354✔
1205
                sums[offset_j + (size_t)channel];
33,354✔
1206
        }
11,118✔
1207
        for (k = best_j; k < n - 1; ++k) {
941,582✔
1208
            weights[k] = weights[k + 1];
930,464✔
1209
            dst = (size_t)k * (size_t)depth;
930,464✔
1210
            src = (size_t)(k + 1) * (size_t)depth;
930,464✔
1211
            for (channel = 0; channel < (int)depth; ++channel) {
3,721,856✔
1212
                sums[dst + (size_t)channel] = sums[src + (size_t)channel];
2,791,392✔
1213
            }
948,216✔
1214
        }
316,072✔
1215
        --n;
11,118✔
1216
    }
1217
    *cluster_count = n;
69✔
1218
}
23✔
1219

1220

1221
/* Translate merged cluster statistics into a tupletable palette. */
1222
static SIXELSTATUS
1223
sixel_quant_clusters_to_colormap(unsigned long *weights,
69✔
1224
                               unsigned long *sums,
1225
                               unsigned int depth,
1226
                               unsigned int cluster_count,
1227
                               int use_reversible,
1228
                               tupletable2 *colormapP,
1229
                               sixel_allocator_t *allocator)
1230
{
1231
    SIXELSTATUS status;
1232
    tupletable2 colormap;
1233
    unsigned int index;
1234
    unsigned int plane;
1235
    double component;
1236
    unsigned long weight;
1237

1238
    status = SIXEL_BAD_ARGUMENT;
69✔
1239
    if (colormapP == NULL) {
69!
NEW
1240
        return status;
×
1241
    }
1242
    colormap = newColorMap(cluster_count, depth, allocator);
69✔
1243
    if (colormap.size == 0U) {
69!
NEW
1244
        return SIXEL_BAD_ALLOCATION;
×
1245
    }
1246
    for (index = 0U; index < cluster_count; ++index) {
13,833✔
1247
        weight = weights[index];
13,764✔
1248
        if (weight == 0UL) {
13,764!
NEW
1249
            weight = 1UL;
×
1250
        }
1251
        colormap.table[index]->value =
13,764✔
1252
            (unsigned int)((weight > (unsigned long)UINT_MAX)
13,764!
1253
                ? UINT_MAX
1254
                : weight);
4,588✔
1255
        for (plane = 0U; plane < depth; ++plane) {
55,056✔
1256
            component = (double)sums[(size_t)index * (size_t)depth + plane];
41,292✔
1257
            component /= (double)weight;
41,292✔
1258
            if (component < 0.0) {
41,292!
NEW
1259
                component = 0.0;
×
1260
            }
1261
            if (component > 255.0) {
41,292!
NEW
1262
                component = 255.0;
×
1263
            }
1264
            colormap.table[index]->tuple[plane] =
41,292✔
1265
                (sample)(component + 0.5);
41,292✔
1266
        }
13,764✔
1267
        if (use_reversible) {
13,764!
NEW
1268
            sixel_quant_reversible_tuple(colormap.table[index]->tuple,
×
1269
                                         depth);
1270
        }
1271
    }
4,588✔
1272
    *colormapP = colormap;
69✔
1273
    status = SIXEL_OK;
69✔
1274

1275
    return status;
69✔
1276
}
23✔
1277

1278

1279
static int histogram_lut_policy = SIXEL_LUT_POLICY_AUTO;
1280

1281
void
1282
sixel_quant_set_lut_policy(int lut_policy)
545✔
1283
{
1284
    int normalized;
1285

1286
    normalized = SIXEL_LUT_POLICY_AUTO;
545✔
1287
    if (lut_policy == SIXEL_LUT_POLICY_5BIT
778!
1288
        || lut_policy == SIXEL_LUT_POLICY_6BIT
545!
1289
        || lut_policy == SIXEL_LUT_POLICY_ROBINHOOD
545!
1290
        || lut_policy == SIXEL_LUT_POLICY_HOPSCOTCH) {
545!
1291
        normalized = lut_policy;
×
1292
    }
1293

1294
    histogram_lut_policy = normalized;
545✔
1295
}
545✔
1296

1297
struct histogram_control {
1298
    unsigned int channel_shift;
1299
    unsigned int channel_bits;
1300
    unsigned int channel_mask;
1301
    int reversible_rounding;         /* nonzero biases quantization upward */
1302
};
1303

1304
static struct histogram_control
1305
histogram_control_make(unsigned int depth);
1306
static struct histogram_control
1307
histogram_control_make_for_policy(unsigned int depth, int lut_policy);
1308
static size_t histogram_dense_size(unsigned int depth,
1309
                                   struct histogram_control const
1310
                                       *control);
1311
static unsigned int histogram_reconstruct(unsigned int quantized,
1312
                                          struct histogram_control const
1313
                                              *control);
1314

1315
static size_t
1316
histogram_dense_size(unsigned int depth,
527✔
1317
                     struct histogram_control const *control)
1318
{
1319
    size_t size;
1320
    unsigned int exponent;
1321
    unsigned int i;
1322

1323
    size = 1U;
527✔
1324
    exponent = depth * control->channel_bits;
527✔
1325
    for (i = 0U; i < exponent; ++i) {
10,013✔
1326
        if (size > SIZE_MAX / 2U) {
9,486!
1327
            size = SIZE_MAX;
×
1328
            break;
×
1329
        }
1330
        size <<= 1U;
9,486✔
1331
    }
4,086✔
1332

1333
    return size;
527✔
1334
}
1335

1336
static struct histogram_control
1337
histogram_control_make_for_policy(unsigned int depth, int lut_policy)
40,078,485✔
1338
{
1339
    struct histogram_control control;
1340

1341
    control.reversible_rounding = 0;
40,078,485✔
1342
    /*
1343
     * The ASCII ladder below shows how each policy selects bucket width.
1344
     *
1345
     *   auto / 6bit RGB : |--6--|
1346
     *   forced 5bit     : |---5---|
1347
     *   robinhood       : |------8------|
1348
     *   alpha fallback  : |---5---|  (avoids 2^(6*4) buckets)
1349
     */
1350
    control.channel_shift = 2U;
40,078,485✔
1351
    if (depth > 3U) {
40,078,485!
1352
        control.channel_shift = 3U;
×
1353
    }
1354
    if (lut_policy == SIXEL_LUT_POLICY_5BIT) {
40,078,485!
1355
        control.channel_shift = 3U;
×
1356
    } else if (lut_policy == SIXEL_LUT_POLICY_6BIT) {
40,078,485✔
1357
        control.channel_shift = 2U;
267✔
1358
        if (depth > 3U) {
267!
1359
            control.channel_shift = 3U;
×
1360
        }
1361
    } else if (lut_policy == SIXEL_LUT_POLICY_ROBINHOOD
40,078,327!
1362
               || lut_policy == SIXEL_LUT_POLICY_HOPSCOTCH) {
40,078,218!
1363
        control.channel_shift = 0U;
×
1364
    }
1365
    control.channel_bits = 8U - control.channel_shift;
40,078,485✔
1366
    control.channel_mask = (1U << control.channel_bits) - 1U;
40,078,485✔
1367

1368
    return control;
40,078,485✔
1369
}
1370

1371
static unsigned int
1372
histogram_reconstruct(unsigned int quantized,
882,339✔
1373
                      struct histogram_control const *control)
1374
{
1375
    unsigned int value;
1376

1377
    value = quantized << control->channel_shift;
882,339✔
1378
    if (quantized == control->channel_mask) {
882,339✔
1379
        value = 255U;
1,203✔
1380
    } else {
517✔
1381
        if (control->channel_shift > 0U) {
881,136!
1382
            value |= (1U << (control->channel_shift - 1U));
881,136✔
1383
        }
294,374✔
1384
    }
1385
    if (value > 255U) {
882,339!
1386
        value = 255U;
×
1387
    }
1388

1389
    return value;
882,339✔
1390
}
1391

1392
static unsigned int
1393
histogram_quantize(unsigned int sample8,
130,951,272✔
1394
                   struct histogram_control const *control)
1395
{
1396
    unsigned int quantized;
1397
    unsigned int shift;
1398
    unsigned int mask;
1399
    unsigned int rounding;
1400

1401
    /*
1402
     * In reversible mode we already rounded once when snapping to
1403
     * sixel_safe_tones[].  If we rounded to the nearest midpoint
1404
     * again, the second pass would fall back to the lower bucket and break
1405
     * the round-trip.  Biasing towards the upper edge keeps the bucket
1406
     * stable after decoding and encoding again.  Non-reversible runs keep
1407
     * symmetric midpoints to avoid nudging colors upwards.
1408
     *
1409
     *        midpoint bias        upper-edge bias
1410
     *   |----*----|----*----|    |----|----*----|
1411
     *   0         8        16    0    8        16
1412
     *
1413
     * The asterisk marks the midpoint captured by a bucket.  Moving that
1414
     * midpoint to the upper edge keeps reversible tones from drifting.
1415
     */
1416
    shift = control->channel_shift;
130,951,272✔
1417
    mask = control->channel_mask;
130,951,272✔
1418
    if (shift == 0U) {
130,951,272!
1419
        quantized = sample8;
×
1420
    } else {
1421
        if (control->reversible_rounding) {
130,951,272!
1422
            rounding = 1U << shift;
×
1423
        } else {
1424
            rounding = 1U << (shift - 1U);
130,951,272✔
1425
        }
1426
        quantized = (sample8 + rounding) >> shift;
130,951,272✔
1427
        if (quantized > mask) {
130,951,272✔
1428
            quantized = mask;
3,133,447✔
1429
        }
1,500,381✔
1430
    }
1431

1432
    return quantized;
130,951,272✔
1433
}
1434

1435
static uint32_t
1436
histogram_pack_color(unsigned char const *data,
43,650,424✔
1437
                     unsigned int const depth,
1438
                     struct histogram_control const *control)
1439
{
1440
    uint32_t packed;
1441
    unsigned int n;
1442
    unsigned int sample8;
1443
    unsigned int bits;
1444

1445
    packed = 0U;
43,650,424✔
1446
    bits = control->channel_bits;
43,650,424✔
1447
    if (control->channel_shift == 0U) {
43,650,424!
1448
        /*
1449
         * The channel shift being zero means each component keeps eight
1450
         * bits.  We therefore pack pixels in RGB order, as illustrated
1451
         * below:
1452
         *
1453
         *      R   G   B
1454
         *     [ ]-[ ]-[ ]
1455
         *      |   |   |
1456
         *      v   v   v
1457
         *     0xRRGGBB
1458
         */
1459
        for (n = 0U; n < depth; ++n) {
×
1460
            packed |= (uint32_t)data[depth - 1U - n] << (n * bits);
×
1461
        }
1462
        return packed;
×
1463
    }
1464

1465
    for (n = 0U; n < depth; ++n) {
174,601,696✔
1466
        sample8 = (unsigned int)data[depth - 1U - n];
130,951,272✔
1467
        packed |= histogram_quantize(sample8, control) << (n * bits);
130,951,272✔
1468
    }
62,349,072✔
1469

1470
    return packed;
43,650,424✔
1471
}
20,783,024✔
1472

1473
static uint32_t
1474
histogram_hash_mix(uint32_t key)
40,077,958✔
1475
{
1476
    uint32_t hash;
1477

1478
    /*
1479
     * Multiplicative mixing with two avalanching rounds keeps nearby
1480
     * colors far apart in the hash domain.  The final tweak avoids the
1481
     * 0xffffffff sentinel used by hopscotch slots.
1482
     */
1483
    hash = key * 0x9e3779b9U;
40,077,958✔
1484
    hash ^= hash >> 16;
40,077,958✔
1485
    hash *= 0x7feb352dU;
40,077,958✔
1486
    hash ^= hash >> 15;
40,077,958✔
1487
    hash *= 0x846ca68bU;
40,077,958✔
1488
    hash ^= hash >> 16;
40,077,958✔
1489
    if (hash == 0xffffffffU) {
40,077,958!
1490
        hash ^= 0x632be59bU;
×
1491
    }
1492

1493
    return hash;
40,077,958✔
1494
}
1495

1496
static unsigned int
1497
computeHash(unsigned char const *data,
40,077,958✔
1498
            unsigned int const depth,
1499
            struct histogram_control const *control)
1500
{
1501
    uint32_t packed;
1502

1503
    packed = histogram_pack_color(data, depth, control);
40,077,958✔
1504

1505
    return histogram_hash_mix(packed);
40,077,958✔
1506
}
1507

1508
#define CUCKOO_BUCKET_SIZE 4U
1509
#define CUCKOO_MAX_KICKS 128U
1510
#define CUCKOO_STASH_SIZE 32U
1511
#define CUCKOO_EMPTY_KEY 0xffffffffU
1512

1513
struct cuckoo_bucket32 {
1514
    uint32_t key[CUCKOO_BUCKET_SIZE];
1515
    uint32_t value[CUCKOO_BUCKET_SIZE];
1516
};
1517

1518
struct cuckoo_table32 {
1519
    struct cuckoo_bucket32 *buckets;
1520
    uint32_t stash_key[CUCKOO_STASH_SIZE];
1521
    uint32_t stash_value[CUCKOO_STASH_SIZE];
1522
    size_t bucket_count;
1523
    size_t bucket_mask;
1524
    size_t stash_count;
1525
    size_t entry_count;
1526
    sixel_allocator_t *allocator;
1527
};
1528

1529
static size_t cuckoo_round_buckets(size_t hint);
1530
static size_t cuckoo_hash_primary(uint32_t key, size_t mask);
1531
static size_t cuckoo_hash_secondary(uint32_t key, size_t mask);
1532
static size_t cuckoo_hash_alternate(uint32_t key,
1533
                                    size_t bucket,
1534
                                    size_t mask);
1535
static uint32_t *cuckoo_bucket_find(struct cuckoo_bucket32 *bucket,
1536
                                    uint32_t key);
1537
static int cuckoo_bucket_insert_direct(struct cuckoo_bucket32 *bucket,
1538
                                       uint32_t key,
1539
                                       uint32_t value);
1540
static SIXELSTATUS cuckoo_table32_init(struct cuckoo_table32 *table,
1541
                                       size_t expected,
1542
                                       sixel_allocator_t *allocator);
1543
static void cuckoo_table32_clear(struct cuckoo_table32 *table);
1544
static void cuckoo_table32_fini(struct cuckoo_table32 *table);
1545
static uint32_t *cuckoo_table32_lookup(struct cuckoo_table32 *table,
1546
                                       uint32_t key);
1547
static SIXELSTATUS cuckoo_table32_insert(struct cuckoo_table32 *table,
1548
                                         uint32_t key,
1549
                                         uint32_t value);
1550
static SIXELSTATUS cuckoo_table32_grow(struct cuckoo_table32 *table);
1551

1552
struct robinhood_slot32 {
1553
    uint32_t key;
1554
    uint32_t color;
1555
    uint32_t value;
1556
    uint16_t distance;
1557
    uint16_t pad;
1558
};
1559

1560
struct robinhood_table32 {
1561
    struct robinhood_slot32 *slots;
1562
    size_t capacity;
1563
    size_t count;
1564
    sixel_allocator_t *allocator;
1565
};
1566

1567
static size_t robinhood_round_capacity(size_t hint);
1568
static SIXELSTATUS robinhood_table32_init(struct robinhood_table32 *table,
1569
                                         size_t expected,
1570
                                         sixel_allocator_t *allocator);
1571
static void robinhood_table32_fini(struct robinhood_table32 *table);
1572
static struct robinhood_slot32 *
1573
robinhood_table32_lookup(struct robinhood_table32 *table,
1574
                         uint32_t key,
1575
                         uint32_t color);
1576
static SIXELSTATUS robinhood_table32_insert(struct robinhood_table32 *table,
1577
                                           uint32_t key,
1578
                                           uint32_t color,
1579
                                           uint32_t value);
1580
static SIXELSTATUS robinhood_table32_grow(struct robinhood_table32 *table);
1581
static struct robinhood_slot32 *
1582
robinhood_table32_place(struct robinhood_table32 *table,
1583
                        struct robinhood_slot32 entry);
1584

1585
#define HOPSCOTCH_EMPTY_KEY 0xffffffffU
1586
#define HOPSCOTCH_DEFAULT_NEIGHBORHOOD 32U
1587
#define HOPSCOTCH_INSERT_RANGE 256U
1588

1589
struct hopscotch_slot32 {
1590
    uint32_t key;
1591
    uint32_t color;
1592
    uint32_t value;
1593
};
1594

1595
struct hopscotch_table32 {
1596
    struct hopscotch_slot32 *slots;
1597
    uint32_t *hopinfo;
1598
    size_t capacity;
1599
    size_t count;
1600
    size_t neighborhood;
1601
    sixel_allocator_t *allocator;
1602
};
1603

1604
static SIXELSTATUS hopscotch_table32_init(struct hopscotch_table32 *table,
1605
                                          size_t expected,
1606
                                          sixel_allocator_t *allocator);
1607
static void hopscotch_table32_fini(struct hopscotch_table32 *table);
1608
static struct hopscotch_slot32 *
1609
hopscotch_table32_lookup(struct hopscotch_table32 *table,
1610
                         uint32_t key,
1611
                         uint32_t color);
1612
static SIXELSTATUS hopscotch_table32_insert(struct hopscotch_table32 *table,
1613
                                            uint32_t key,
1614
                                            uint32_t color,
1615
                                            uint32_t value);
1616
static SIXELSTATUS hopscotch_table32_grow(struct hopscotch_table32 *table);
1617

1618
static struct histogram_control
1619
histogram_control_make(unsigned int depth)
40,078,218✔
1620
{
1621
    struct histogram_control control;
1622

1623
    control = histogram_control_make_for_policy(depth,
59,084,154✔
1624
                                                histogram_lut_policy);
19,005,936✔
1625

1626
    return control;
40,078,218✔
1627
}
1628

1629
static size_t
1630
robinhood_round_capacity(size_t hint)
×
1631
{
1632
    size_t capacity;
1633

1634
    capacity = 16U;
×
1635
    if (hint < capacity) {
×
1636
        return capacity;
×
1637
    }
1638

1639
    capacity = hint - 1U;
×
1640
    capacity |= capacity >> 1;
×
1641
    capacity |= capacity >> 2;
×
1642
    capacity |= capacity >> 4;
×
1643
    capacity |= capacity >> 8;
×
1644
    capacity |= capacity >> 16;
×
1645
#if SIZE_MAX > UINT32_MAX
1646
    capacity |= capacity >> 32;
×
1647
#endif
1648
    if (capacity == SIZE_MAX) {
×
1649
        return SIZE_MAX;
×
1650
    }
1651
    capacity++;
×
1652
    if (capacity < 16U) {
×
1653
        capacity = 16U;
×
1654
    }
1655

1656
    return capacity;
×
1657
}
1658

1659
static SIXELSTATUS
1660
robinhood_table32_init(struct robinhood_table32 *table,
×
1661
                       size_t expected,
1662
                       sixel_allocator_t *allocator)
1663
{
1664
    size_t hint;
1665
    size_t capacity;
1666

1667
    table->slots = NULL;
×
1668
    table->capacity = 0U;
×
1669
    table->count = 0U;
×
1670
    table->allocator = allocator;
×
1671

1672
    if (expected < 16U) {
×
1673
        expected = 16U;
×
1674
    }
1675
    if (expected > SIZE_MAX / 2U) {
×
1676
        hint = SIZE_MAX / 2U;
×
1677
    } else {
1678
        hint = expected * 2U;
×
1679
    }
1680
    capacity = robinhood_round_capacity(hint);
×
1681
    if (capacity == SIZE_MAX && hint != SIZE_MAX) {
×
1682
        return SIXEL_BAD_ALLOCATION;
×
1683
    }
1684

1685
    table->slots = (struct robinhood_slot32 *)
×
1686
        sixel_allocator_calloc(allocator,
×
1687
                               capacity,
1688
                               sizeof(struct robinhood_slot32));
1689
    if (table->slots == NULL) {
×
1690
        table->capacity = 0U;
×
1691
        table->count = 0U;
×
1692
        return SIXEL_BAD_ALLOCATION;
×
1693
    }
1694
    table->capacity = capacity;
×
1695
    table->count = 0U;
×
1696

1697
    return SIXEL_OK;
×
1698
}
1699

1700
static void
1701
robinhood_table32_fini(struct robinhood_table32 *table)
×
1702
{
1703
    if (table->slots != NULL) {
×
1704
        sixel_allocator_free(table->allocator, table->slots);
×
1705
        table->slots = NULL;
×
1706
    }
1707
    table->capacity = 0U;
×
1708
    table->count = 0U;
×
1709
    table->allocator = NULL;
×
1710
}
×
1711

1712
static struct robinhood_slot32 *
1713
robinhood_table32_lookup(struct robinhood_table32 *table,
×
1714
                         uint32_t key,
1715
                         uint32_t color)
1716
{
1717
    size_t mask;
1718
    size_t index;
1719
    uint16_t distance;
1720
    struct robinhood_slot32 *slot;
1721

1722
    if (table->capacity == 0U || table->slots == NULL) {
×
1723
        return NULL;
×
1724
    }
1725

1726
    mask = table->capacity - 1U;
×
1727
    index = (size_t)(key & mask);
×
1728
    distance = 0U;
×
1729

1730
    for (;;) {
1731
        slot = &table->slots[index];
×
1732
        if (slot->value == 0U) {
×
1733
            return NULL;
×
1734
        }
1735
        if (slot->key == key && slot->color == color) {
×
1736
            return slot;
×
1737
        }
1738
        if (slot->distance < distance) {
×
1739
            return NULL;
×
1740
        }
1741
        index = (index + 1U) & mask;
×
1742
        distance++;
×
1743
    }
1744
}
1745

1746
static struct robinhood_slot32 *
1747
robinhood_table32_place(struct robinhood_table32 *table,
×
1748
                        struct robinhood_slot32 entry)
1749
{
1750
    size_t mask;
1751
    size_t index;
1752
    struct robinhood_slot32 *slot;
1753
    struct robinhood_slot32 tmp;
1754

1755
    mask = table->capacity - 1U;
×
1756
    index = (size_t)(entry.key & mask);
×
1757

1758
    for (;;) {
1759
        slot = &table->slots[index];
×
1760
        if (slot->value == 0U) {
×
1761
            *slot = entry;
×
1762
            table->count++;
×
1763
            return slot;
×
1764
        }
1765
        if (slot->key == entry.key && slot->color == entry.color) {
×
1766
            slot->value = entry.value;
×
1767
            return slot;
×
1768
        }
1769
        if (slot->distance < entry.distance) {
×
1770
            tmp = *slot;
×
1771
            *slot = entry;
×
1772
            entry = tmp;
×
1773
        }
1774
        index = (index + 1U) & mask;
×
1775
        entry.distance++;
×
1776
    }
1777
}
1778

1779
static SIXELSTATUS
1780
robinhood_table32_grow(struct robinhood_table32 *table)
×
1781
{
1782
    struct robinhood_slot32 *old_slots;
1783
    size_t old_capacity;
1784
    size_t new_capacity;
1785
    size_t i;
1786

1787
    if (table->allocator == NULL) {
×
1788
        return SIXEL_BAD_ALLOCATION;
×
1789
    }
1790
    if (table->capacity == 0U) {
×
1791
        new_capacity = 16U;
×
1792
    } else {
1793
        if (table->capacity > SIZE_MAX / 2U) {
×
1794
            return SIXEL_BAD_ALLOCATION;
×
1795
        }
1796
        new_capacity = table->capacity << 1U;
×
1797
    }
1798
    new_capacity = robinhood_round_capacity(new_capacity);
×
1799
    if (new_capacity <= table->capacity) {
×
1800
        return SIXEL_BAD_ALLOCATION;
×
1801
    }
1802

1803
    old_slots = table->slots;
×
1804
    old_capacity = table->capacity;
×
1805
    table->slots = (struct robinhood_slot32 *)
×
1806
        sixel_allocator_calloc(table->allocator,
×
1807
                               new_capacity,
1808
                               sizeof(struct robinhood_slot32));
1809
    if (table->slots == NULL) {
×
1810
        table->slots = old_slots;
×
1811
        table->capacity = old_capacity;
×
1812
        return SIXEL_BAD_ALLOCATION;
×
1813
    }
1814
    table->capacity = new_capacity;
×
1815
    table->count = 0U;
×
1816

1817
    for (i = 0U; i < old_capacity; ++i) {
×
1818
        struct robinhood_slot32 entry;
1819

1820
        if (old_slots[i].value == 0U) {
×
1821
            continue;
×
1822
        }
1823
        entry.key = old_slots[i].key;
×
1824
        entry.color = old_slots[i].color;
×
1825
        entry.value = old_slots[i].value;
×
1826
        entry.distance = 0U;
×
1827
        entry.pad = 0U;  /* ensure padding bytes are initialized */
×
1828
        (void)robinhood_table32_place(table, entry);
×
1829
    }
1830

1831
    sixel_allocator_free(table->allocator, old_slots);
×
1832

1833
    return SIXEL_OK;
×
1834
}
1835

1836
static SIXELSTATUS
1837
robinhood_table32_insert(struct robinhood_table32 *table,
×
1838
                         uint32_t key,
1839
                         uint32_t color,
1840
                         uint32_t value)
1841
{
1842
    SIXELSTATUS status;
1843
    struct robinhood_slot32 entry;
1844

1845
    if (table->slots == NULL || table->capacity == 0U) {
×
1846
        return SIXEL_BAD_ARGUMENT;
×
1847
    }
1848
    if (table->count * 2U >= table->capacity) {
×
1849
        status = robinhood_table32_grow(table);
×
1850
        if (SIXEL_FAILED(status)) {
×
1851
            return status;
×
1852
        }
1853
    }
1854

1855
    entry.key = key;
×
1856
    entry.color = color;
×
1857
    entry.value = value;
×
1858
    entry.distance = 0U;
×
1859
    entry.pad = 0U;  /* ensure padding bytes are initialized */
×
1860
    (void)robinhood_table32_place(table, entry);
×
1861

1862
    return SIXEL_OK;
×
1863
}
1864

1865
static SIXELSTATUS
1866
hopscotch_table32_init(struct hopscotch_table32 *table,
×
1867
                       size_t expected,
1868
                       sixel_allocator_t *allocator)
1869
{
1870
    size_t hint;
1871
    size_t capacity;
1872
    size_t i;
1873

1874
    if (table == NULL) {
×
1875
        return SIXEL_BAD_ARGUMENT;
×
1876
    }
1877

1878
    table->slots = NULL;
×
1879
    table->hopinfo = NULL;
×
1880
    table->capacity = 0U;
×
1881
    table->count = 0U;
×
1882
    table->neighborhood = HOPSCOTCH_DEFAULT_NEIGHBORHOOD;
×
1883
    table->allocator = allocator;
×
1884

1885
    if (expected < 16U) {
×
1886
        expected = 16U;
×
1887
    }
1888
    if (expected > SIZE_MAX / 2U) {
×
1889
        hint = SIZE_MAX / 2U;
×
1890
    } else {
1891
        hint = expected * 2U;
×
1892
    }
1893
    capacity = robinhood_round_capacity(hint);
×
1894
    if (capacity == SIZE_MAX && hint != SIZE_MAX) {
×
1895
        return SIXEL_BAD_ALLOCATION;
×
1896
    }
1897
    if (capacity < table->neighborhood) {
×
1898
        capacity = table->neighborhood;
×
1899
    }
1900

1901
    table->slots = (struct hopscotch_slot32 *)
×
1902
        sixel_allocator_malloc(allocator,
×
1903
                               capacity * sizeof(struct hopscotch_slot32));
1904
    if (table->slots == NULL) {
×
1905
        return SIXEL_BAD_ALLOCATION;
×
1906
    }
1907
    table->hopinfo = (uint32_t *)
×
1908
        sixel_allocator_calloc(allocator,
×
1909
                               capacity,
1910
                               sizeof(uint32_t));
1911
    if (table->hopinfo == NULL) {
×
1912
        sixel_allocator_free(allocator, table->slots);
×
1913
        table->slots = NULL;
×
1914
        return SIXEL_BAD_ALLOCATION;
×
1915
    }
1916

1917
    for (i = 0U; i < capacity; ++i) {
×
1918
        table->slots[i].key = HOPSCOTCH_EMPTY_KEY;
×
1919
        table->slots[i].color = 0U;
×
1920
        table->slots[i].value = 0U;
×
1921
    }
1922
    table->capacity = capacity;
×
1923
    table->count = 0U;
×
1924
    if (table->neighborhood > 32U) {
×
1925
        table->neighborhood = 32U;
×
1926
    }
1927
    if (table->neighborhood > table->capacity) {
×
1928
        table->neighborhood = table->capacity;
×
1929
    }
1930

1931
    return SIXEL_OK;
×
1932
}
1933

1934
static void
1935
hopscotch_table32_fini(struct hopscotch_table32 *table)
×
1936
{
1937
    sixel_allocator_t *allocator;
1938

1939
    if (table == NULL) {
×
1940
        return;
×
1941
    }
1942

1943
    allocator = table->allocator;
×
1944
    if (allocator != NULL) {
×
1945
        if (table->slots != NULL) {
×
1946
            sixel_allocator_free(allocator, table->slots);
×
1947
        }
1948
        if (table->hopinfo != NULL) {
×
1949
            sixel_allocator_free(allocator, table->hopinfo);
×
1950
        }
1951
    }
1952
    table->slots = NULL;
×
1953
    table->hopinfo = NULL;
×
1954
    table->capacity = 0U;
×
1955
    table->count = 0U;
×
1956
}
1957

1958
static struct hopscotch_slot32 *
1959
hopscotch_table32_lookup(struct hopscotch_table32 *table,
×
1960
                         uint32_t key,
1961
                         uint32_t color)
1962
{
1963
    size_t index;
1964
    size_t bit;
1965
    size_t candidate;
1966
    uint32_t hop;
1967
    size_t mask;
1968
    size_t neighborhood;
1969

1970
    if (table == NULL || table->slots == NULL || table->hopinfo == NULL) {
×
1971
        return NULL;
×
1972
    }
1973
    if (table->capacity == 0U) {
×
1974
        return NULL;
×
1975
    }
1976

1977
    mask = table->capacity - 1U;
×
1978
    index = (size_t)key & mask;
×
1979
    hop = table->hopinfo[index];
×
1980
    neighborhood = table->neighborhood;
×
1981
    for (bit = 0U; bit < neighborhood; ++bit) {
×
1982
        if ((hop & (1U << bit)) == 0U) {
×
1983
            continue;
×
1984
        }
1985
        candidate = (index + bit) & mask;
×
1986
        if (table->slots[candidate].key == key
×
1987
            && table->slots[candidate].color == color) {
×
1988
            return &table->slots[candidate];
×
1989
        }
1990
    }
1991

1992
    return NULL;
×
1993
}
1994

1995
static SIXELSTATUS
1996
hopscotch_table32_grow(struct hopscotch_table32 *table)
×
1997
{
1998
    SIXELSTATUS status;
1999
    struct hopscotch_table32 tmp;
2000
    size_t i;
2001
    struct hopscotch_slot32 *slot;
2002

2003
    tmp.slots = NULL;
×
2004
    tmp.hopinfo = NULL;
×
2005
    tmp.capacity = 0U;
×
2006
    tmp.count = 0U;
×
2007
    tmp.neighborhood = table->neighborhood;
×
2008
    tmp.allocator = table->allocator;
×
2009

2010
    status = hopscotch_table32_init(&tmp,
×
2011
                                    table->capacity * 2U,
×
2012
                                    table->allocator);
2013
    if (SIXEL_FAILED(status)) {
×
2014
        return status;
×
2015
    }
2016
    if (tmp.neighborhood > table->neighborhood) {
×
2017
        tmp.neighborhood = table->neighborhood;
×
2018
    }
2019

2020
    for (i = 0U; i < table->capacity; ++i) {
×
2021
        slot = &table->slots[i];
×
2022
        if (slot->key == HOPSCOTCH_EMPTY_KEY) {
×
2023
            continue;
×
2024
        }
2025
        status = hopscotch_table32_insert(&tmp,
×
2026
                                          slot->key,
2027
                                          slot->color,
2028
                                          slot->value);
2029
        if (SIXEL_FAILED(status)) {
×
2030
            hopscotch_table32_fini(&tmp);
×
2031
            return status;
×
2032
        }
2033
    }
2034

2035
    hopscotch_table32_fini(table);
×
2036

2037
    table->slots = tmp.slots;
×
2038
    table->hopinfo = tmp.hopinfo;
×
2039
    table->capacity = tmp.capacity;
×
2040
    table->count = tmp.count;
×
2041
    table->neighborhood = tmp.neighborhood;
×
2042
    table->allocator = tmp.allocator;
×
2043

2044
    return SIXEL_OK;
×
2045
}
2046

2047
static SIXELSTATUS
2048
hopscotch_table32_insert(struct hopscotch_table32 *table,
×
2049
                         uint32_t key,
2050
                         uint32_t color,
2051
                         uint32_t value)
2052
{
2053
    SIXELSTATUS status;
2054
    struct hopscotch_slot32 *slot;
2055
    size_t index;
2056
    size_t mask;
2057
    size_t distance;
2058
    size_t attempts;
2059
    size_t free_index;
2060
    size_t neighborhood;
2061
    int relocated;
2062
    size_t offset;
2063
    uint32_t hop;
2064
    size_t bit;
2065
    size_t move_index;
2066
    struct hopscotch_slot32 tmp_slot;
2067

2068
    if (table == NULL || table->slots == NULL || table->hopinfo == NULL) {
×
2069
        return SIXEL_BAD_ARGUMENT;
×
2070
    }
2071
    if (table->capacity == 0U) {
×
2072
        return SIXEL_BAD_ARGUMENT;
×
2073
    }
2074

2075
    slot = hopscotch_table32_lookup(table, key, color);
×
2076
    if (slot != NULL) {
×
2077
        slot->value = value;
×
2078
        return SIXEL_OK;
×
2079
    }
2080

2081
    if (table->count * 2U >= table->capacity) {
×
2082
        status = hopscotch_table32_grow(table);
×
2083
        if (SIXEL_FAILED(status)) {
×
2084
            return status;
×
2085
        }
2086
        return hopscotch_table32_insert(table, key, color, value);
×
2087
    }
2088

2089
    mask = table->capacity - 1U;
×
2090
    neighborhood = table->neighborhood;
×
2091
    index = (size_t)key & mask;
×
2092
    slot = NULL;
×
2093
    free_index = index;
×
2094
    distance = 0U;
×
2095
    for (attempts = 0U; attempts < HOPSCOTCH_INSERT_RANGE; ++attempts) {
×
2096
        free_index = (index + attempts) & mask;
×
2097
        slot = &table->slots[free_index];
×
2098
        if (slot->key == HOPSCOTCH_EMPTY_KEY) {
×
2099
            distance = attempts;
×
2100
            break;
×
2101
        }
2102
    }
2103
    if (slot == NULL || slot->key != HOPSCOTCH_EMPTY_KEY) {
×
2104
        status = hopscotch_table32_grow(table);
×
2105
        if (SIXEL_FAILED(status)) {
×
2106
            return status;
×
2107
        }
2108
        return hopscotch_table32_insert(table, key, color, value);
×
2109
    }
2110

2111
    /*
2112
     * Relocation diagram:
2113
     *
2114
     *   free slot <--- hop window <--- [base bucket]
2115
     *      ^ move resident outward until distance < neighborhood
2116
     */
2117
    while (distance >= neighborhood) {
×
2118
        relocated = 0;
×
2119
        for (offset = neighborhood - 1U; offset > 0U; --offset) {
×
2120
            size_t base;
2121

2122
            base = (free_index + table->capacity - offset) & mask;
×
2123
            hop = table->hopinfo[base];
×
2124
            if (hop == 0U) {
×
2125
                continue;
×
2126
            }
2127
            for (bit = 0U; bit < offset; ++bit) {
×
2128
                if ((hop & (1U << bit)) == 0U) {
×
2129
                    continue;
×
2130
                }
2131
                move_index = (base + bit) & mask;
×
2132
                tmp_slot = table->slots[move_index];
×
2133
                table->slots[free_index] = tmp_slot;
×
2134
                table->slots[move_index].key = HOPSCOTCH_EMPTY_KEY;
×
2135
                table->slots[move_index].color = 0U;
×
2136
                table->slots[move_index].value = 0U;
×
2137
                table->hopinfo[base] &= (uint32_t)~(1U << bit);
×
2138
                table->hopinfo[base] |= (uint32_t)(1U << offset);
×
2139
                free_index = move_index;
×
2140
                if (free_index >= index) {
×
2141
                    distance = free_index - index;
×
2142
                } else {
2143
                    distance = free_index + table->capacity - index;
×
2144
                }
2145
                relocated = 1;
×
2146
                break;
×
2147
            }
2148
            if (relocated) {
×
2149
                break;
×
2150
            }
2151
        }
2152
        if (!relocated) {
×
2153
            status = hopscotch_table32_grow(table);
×
2154
            if (SIXEL_FAILED(status)) {
×
2155
                return status;
×
2156
            }
2157
            return hopscotch_table32_insert(table, key, color, value);
×
2158
        }
2159
    }
2160

2161
    if (distance >= 32U) {
×
2162
        status = hopscotch_table32_grow(table);
×
2163
        if (SIXEL_FAILED(status)) {
×
2164
            return status;
×
2165
        }
2166
        return hopscotch_table32_insert(table, key, color, value);
×
2167
    }
2168

2169
    table->slots[free_index].key = key;
×
2170
    table->slots[free_index].color = color;
×
2171
    table->slots[free_index].value = value;
×
2172
    table->hopinfo[index] |= (uint32_t)(1U << distance);
×
2173
    table->count++;
×
2174

2175
    return SIXEL_OK;
×
2176
}
2177

2178
/*
2179
 * The cuckoo hash backend stores entries in fixed-width buckets.
2180
 *
2181
 *   [bucket 0] -> key0 key1 key2 key3
2182
 *                 val0 val1 val2 val3
2183
 *   [bucket 1] -> ...
2184
 *
2185
 * Each key is compared against the 128-bit lane using SIMD instructions.
2186
 * Two hash functions map a key to its primary and secondary buckets.  When
2187
 * both are full, the eviction loop "kicks" an entry toward its alternate
2188
 * bucket, as illustrated below:
2189
 *
2190
 *   bucket A --kick--> bucket B --kick--> bucket A ...
2191
 *
2192
 * A tiny stash buffers entries when the table momentarily fills up.  This
2193
 * keeps lookups fast while letting us grow the table lazily.
2194
 */
2195
static size_t
2196
cuckoo_round_buckets(size_t hint)
267✔
2197
{
2198
    size_t desired;
2199
    size_t buckets;
2200
    size_t prev;
2201

2202
    if (hint < CUCKOO_BUCKET_SIZE) {
267!
2203
        hint = CUCKOO_BUCKET_SIZE;
×
2204
    }
2205
    if (hint > SIZE_MAX / 2U) {
267!
2206
        hint = SIZE_MAX / 2U;
×
2207
    }
2208
    desired = (hint * 2U + CUCKOO_BUCKET_SIZE - 1U) / CUCKOO_BUCKET_SIZE;
267✔
2209
    if (desired == 0U) {
267!
2210
        desired = 1U;
×
2211
    }
2212

2213
    buckets = 1U;
267✔
2214
    while (buckets < desired) {
4,806✔
2215
        prev = buckets;
4,539✔
2216
        if (buckets > SIZE_MAX / 2U) {
4,539!
2217
            buckets = prev;
×
2218
            break;
×
2219
        }
2220
        buckets <<= 1U;
4,539✔
2221
        if (buckets < prev) {
4,539!
2222
            buckets = prev;
×
2223
            break;
×
2224
        }
2225
    }
2226
    if (buckets == 0U) {
267!
2227
        buckets = 1U;
×
2228
    }
2229

2230
    return buckets;
267✔
2231
}
2232

2233
static size_t
2234
cuckoo_hash_primary(uint32_t key, size_t mask)
43,908,960✔
2235
{
2236
    uint32_t mix;
2237

2238
    mix = key * 0x9e3779b1U;
43,908,960✔
2239
    return (size_t)(mix & (uint32_t)mask);
43,908,960✔
2240
}
2241

2242
static size_t
2243
cuckoo_hash_secondary(uint32_t key, size_t mask)
3,837,993✔
2244
{
2245
    uint32_t mix;
2246

2247
    mix = key ^ 0x85ebca6bU;
3,837,993✔
2248
    mix ^= mix >> 13;
3,837,993✔
2249
    mix *= 0xc2b2ae35U;
3,837,993✔
2250
    return (size_t)(mix & (uint32_t)mask);
3,837,993✔
2251
}
2252

2253
static size_t
2254
cuckoo_hash_alternate(uint32_t key, size_t bucket, size_t mask)
52✔
2255
{
2256
    size_t primary;
2257
    size_t secondary;
2258

2259
    primary = cuckoo_hash_primary(key, mask);
52✔
2260
    secondary = cuckoo_hash_secondary(key, mask);
52✔
2261
    if (primary == bucket) {
52!
2262
        if (secondary == primary) {
52!
2263
            secondary = (secondary + 1U) & mask;
×
2264
        }
2265
        return secondary;
52✔
2266
    }
2267

2268
    return primary;
×
2269
}
10✔
2270

2271
static uint32_t *
2272
cuckoo_bucket_find(struct cuckoo_bucket32 *bucket, uint32_t key)
45,831,374✔
2273
{
2274
    size_t i;
2275

2276
#if defined(SIXEL_USE_SSE2)
2277
    __m128i needle;
2278
    __m128i keys;
2279
    __m128i cmp;
2280
    int mask;
2281

2282
    needle = _mm_set1_epi32((int)key);
2283
    keys = _mm_loadu_si128((const __m128i *)bucket->key);
2284
    cmp = _mm_cmpeq_epi32(needle, keys);
2285
    mask = _mm_movemask_ps(_mm_castsi128_ps(cmp));
2286
    if ((mask & 1) != 0 && bucket->value[0] != 0U) {
2287
        return &bucket->value[0];
2288
    }
2289
    if ((mask & 2) != 0 && bucket->value[1] != 0U) {
2290
        return &bucket->value[1];
2291
    }
2292
    if ((mask & 4) != 0 && bucket->value[2] != 0U) {
2293
        return &bucket->value[2];
2294
    }
2295
    if ((mask & 8) != 0 && bucket->value[3] != 0U) {
2296
        return &bucket->value[3];
2297
    }
2298
#elif defined(SIXEL_USE_NEON)
2299
    uint32x4_t needle;
2300
    uint32x4_t keys;
2301
    uint32x4_t cmp;
2302

2303
    needle = vdupq_n_u32(key);
2304
    keys = vld1q_u32(bucket->key);
2305
    cmp = vceqq_u32(needle, keys);
2306
    if (vgetq_lane_u32(cmp, 0) != 0U && bucket->value[0] != 0U) {
2307
        return &bucket->value[0];
2308
    }
2309
    if (vgetq_lane_u32(cmp, 1) != 0U && bucket->value[1] != 0U) {
2310
        return &bucket->value[1];
2311
    }
2312
    if (vgetq_lane_u32(cmp, 2) != 0U && bucket->value[2] != 0U) {
2313
        return &bucket->value[2];
2314
    }
2315
    if (vgetq_lane_u32(cmp, 3) != 0U && bucket->value[3] != 0U) {
2316
        return &bucket->value[3];
2317
    }
2318
#else
2319
    for (i = 0U; i < CUCKOO_BUCKET_SIZE; ++i) {
77,866,608✔
2320
        if (bucket->value[i] != 0U && bucket->key[i] == key) {
70,197,717✔
2321
            return &bucket->value[i];
38,162,483✔
2322
        }
2323
    }
10,249,102✔
2324
#endif
2325

2326
    return NULL;
7,668,891✔
2327
}
20,846,518✔
2328

2329
static int
2330
cuckoo_bucket_insert_direct(struct cuckoo_bucket32 *bucket,
1,915,527✔
2331
                            uint32_t key,
2332
                            uint32_t value)
2333
{
2334
    size_t i;
2335

2336
    for (i = 0U; i < CUCKOO_BUCKET_SIZE; ++i) {
2,112,289✔
2337
        if (bucket->value[i] == 0U) {
2,112,237✔
2338
            bucket->key[i] = key;
1,915,475✔
2339
            bucket->value[i] = value;
1,915,475✔
2340
            return 1;
1,915,475✔
2341
        }
2342
    }
59,286✔
2343

2344
    return 0;
52✔
2345
}
613,553✔
2346

2347
static SIXELSTATUS
2348
cuckoo_table32_init(struct cuckoo_table32 *table,
264✔
2349
                    size_t expected,
2350
                    sixel_allocator_t *allocator)
2351
{
2352
    size_t buckets;
2353
    size_t i;
2354
    size_t j;
2355

2356
    if (table == NULL || allocator == NULL) {
264!
2357
        return SIXEL_BAD_ARGUMENT;
×
2358
    }
2359

2360
    buckets = cuckoo_round_buckets(expected);
264✔
2361
    if (buckets == 0U
264!
2362
        || buckets > SIZE_MAX / sizeof(struct cuckoo_bucket32)) {
264!
2363
        sixel_helper_set_additional_message(
×
2364
            "unable to size cuckoo bucket array.");
2365
        return SIXEL_BAD_ALLOCATION;
×
2366
    }
2367

2368
    table->buckets = (struct cuckoo_bucket32 *)sixel_allocator_malloc(
264✔
2369
        allocator, buckets * sizeof(struct cuckoo_bucket32));
108✔
2370
    if (table->buckets == NULL) {
264!
2371
        sixel_helper_set_additional_message(
×
2372
            "unable to allocate cuckoo buckets.");
2373
        return SIXEL_BAD_ALLOCATION;
×
2374
    }
2375

2376
    table->bucket_count = buckets;
264✔
2377
    table->bucket_mask = buckets - 1U;
264✔
2378
    table->stash_count = 0U;
264✔
2379
    table->entry_count = 0U;
264✔
2380
    table->allocator = allocator;
264✔
2381
    for (i = 0U; i < buckets; ++i) {
34,603,272✔
2382
        for (j = 0U; j < CUCKOO_BUCKET_SIZE; ++j) {
173,015,040✔
2383
            table->buckets[i].key[j] = CUCKOO_EMPTY_KEY;
138,412,032✔
2384
            table->buckets[i].value[j] = 0U;
138,412,032✔
2385
        }
56,623,104✔
2386
    }
14,155,776✔
2387
    for (i = 0U; i < CUCKOO_STASH_SIZE; ++i) {
8,712✔
2388
        table->stash_key[i] = CUCKOO_EMPTY_KEY;
8,448✔
2389
        table->stash_value[i] = 0U;
8,448✔
2390
    }
3,456✔
2391

2392
    return SIXEL_OK;
264✔
2393
}
108✔
2394

2395
static void
2396
cuckoo_table32_clear(struct cuckoo_table32 *table)
267✔
2397
{
2398
    size_t i;
2399
    size_t j;
2400

2401
    if (table == NULL || table->buckets == NULL) {
267!
2402
        return;
×
2403
    }
2404

2405
    table->stash_count = 0U;
267✔
2406
    table->entry_count = 0U;
267✔
2407
    for (i = 0U; i < table->bucket_count; ++i) {
34,996,491✔
2408
        for (j = 0U; j < CUCKOO_BUCKET_SIZE; ++j) {
174,981,120✔
2409
            table->buckets[i].key[j] = CUCKOO_EMPTY_KEY;
139,984,896✔
2410
            table->buckets[i].value[j] = 0U;
139,984,896✔
2411
        }
57,147,392✔
2412
    }
14,286,848✔
2413
    for (i = 0U; i < CUCKOO_STASH_SIZE; ++i) {
8,811✔
2414
        table->stash_key[i] = CUCKOO_EMPTY_KEY;
8,544✔
2415
        table->stash_value[i] = 0U;
8,544✔
2416
    }
3,488✔
2417
}
109✔
2418

2419
static void
2420
cuckoo_table32_fini(struct cuckoo_table32 *table)
264✔
2421
{
2422
    if (table == NULL || table->allocator == NULL) {
264!
2423
        return;
×
2424
    }
2425
    if (table->buckets != NULL) {
264!
2426
        sixel_allocator_free(table->allocator, table->buckets);
264✔
2427
        table->buckets = NULL;
264✔
2428
    }
108✔
2429
    table->bucket_count = 0U;
264✔
2430
    table->bucket_mask = 0U;
264✔
2431
    table->stash_count = 0U;
264✔
2432
    table->entry_count = 0U;
264✔
2433
}
108✔
2434

2435
static uint32_t *
2436
cuckoo_table32_lookup(struct cuckoo_table32 *table, uint32_t key)
41,993,433✔
2437
{
2438
    size_t index;
2439
    size_t i;
2440
    uint32_t *slot;
2441

2442
    if (table == NULL || table->buckets == NULL) {
41,993,433!
2443
        return NULL;
×
2444
    }
2445

2446
    index = cuckoo_hash_primary(key, table->bucket_mask);
41,993,433✔
2447
    slot = cuckoo_bucket_find(&table->buckets[index], key);
41,993,433✔
2448
    if (slot != NULL) {
41,993,433✔
2449
        return slot;
38,155,492✔
2450
    }
2451

2452
    index = cuckoo_hash_secondary(key, table->bucket_mask);
3,837,941✔
2453
    slot = cuckoo_bucket_find(&table->buckets[index], key);
3,837,941✔
2454
    if (slot != NULL) {
3,837,941✔
2455
        return slot;
6,991✔
2456
    }
2457

2458
    for (i = 0U; i < table->stash_count; ++i) {
3,830,950!
2459
        if (table->stash_value[i] != 0U && table->stash_key[i] == key) {
×
2460
            return &table->stash_value[i];
×
2461
        }
2462
    }
2463

2464
    return NULL;
3,830,950✔
2465
}
19,619,361✔
2466

2467
static SIXELSTATUS
2468
cuckoo_table32_grow(struct cuckoo_table32 *table)
×
2469
{
2470
    struct cuckoo_table32 tmp;
2471
    struct cuckoo_bucket32 *old_buckets;
2472
    size_t old_count;
2473
    size_t i;
2474
    size_t j;
2475
    SIXELSTATUS status;
2476

2477
    if (table == NULL || table->allocator == NULL) {
×
2478
        return SIXEL_BAD_ARGUMENT;
×
2479
    }
2480

2481
    tmp.buckets = NULL;
×
2482
    tmp.bucket_count = 0U;
×
2483
    tmp.bucket_mask = 0U;
×
2484
    tmp.stash_count = 0U;
×
2485
    tmp.entry_count = 0U;
×
2486
    tmp.allocator = table->allocator;
×
2487
    for (i = 0U; i < CUCKOO_STASH_SIZE; ++i) {
×
2488
        tmp.stash_key[i] = CUCKOO_EMPTY_KEY;
×
2489
        tmp.stash_value[i] = 0U;
×
2490
    }
2491

2492
    status = cuckoo_table32_init(&tmp,
×
2493
                                 (table->entry_count + 1U) * 2U,
×
2494
                                 table->allocator);
2495
    if (SIXEL_FAILED(status)) {
×
2496
        return status;
×
2497
    }
2498

2499
    old_buckets = table->buckets;
×
2500
    old_count = table->bucket_count;
×
2501
    for (i = 0U; i < old_count; ++i) {
×
2502
        for (j = 0U; j < CUCKOO_BUCKET_SIZE; ++j) {
×
2503
            if (old_buckets[i].value[j] != 0U) {
×
2504
                status = cuckoo_table32_insert(&tmp,
×
2505
                                               old_buckets[i].key[j],
×
2506
                                               old_buckets[i].value[j]);
×
2507
                if (SIXEL_FAILED(status)) {
×
2508
                    cuckoo_table32_fini(&tmp);
×
2509
                    return status;
×
2510
                }
2511
            }
2512
        }
2513
    }
2514
    for (i = 0U; i < table->stash_count; ++i) {
×
2515
        if (table->stash_value[i] != 0U) {
×
2516
            status = cuckoo_table32_insert(&tmp,
×
2517
                                           table->stash_key[i],
2518
                                           table->stash_value[i]);
2519
            if (SIXEL_FAILED(status)) {
×
2520
                cuckoo_table32_fini(&tmp);
×
2521
                return status;
×
2522
            }
2523
        }
2524
    }
2525

2526
    sixel_allocator_free(table->allocator, old_buckets);
×
2527
    *table = tmp;
×
2528

2529
    return SIXEL_OK;
×
2530
}
2531

2532
static SIXELSTATUS
2533
cuckoo_table32_insert(struct cuckoo_table32 *table,
1,915,475✔
2534
                      uint32_t key,
2535
                      uint32_t value)
2536
{
2537
    uint32_t *slot;
2538
    uint32_t cur_key;
2539
    uint32_t cur_value;
2540
    uint32_t victim_key;
2541
    uint32_t victim_value;
2542
    size_t bucket_index;
2543
    size_t kicks;
2544
    size_t victim_slot;
2545
    struct cuckoo_bucket32 *bucket;
2546
    SIXELSTATUS status;
2547

2548
    if (table == NULL || table->buckets == NULL) {
1,915,475!
2549
        return SIXEL_BAD_ARGUMENT;
×
2550
    }
2551

2552
    slot = cuckoo_table32_lookup(table, key);
1,915,475✔
2553
    if (slot != NULL) {
1,915,475!
2554
        *slot = value;
×
2555
        return SIXEL_OK;
×
2556
    }
2557

2558
    cur_key = key;
1,915,475✔
2559
    cur_value = value;
1,915,475✔
2560
    bucket_index = cuckoo_hash_primary(cur_key, table->bucket_mask);
1,915,475✔
2561
    for (kicks = 0U; kicks < CUCKOO_MAX_KICKS; ++kicks) {
1,915,527!
2562
        bucket = &table->buckets[bucket_index];
1,915,527✔
2563
        if (cuckoo_bucket_insert_direct(bucket, cur_key, cur_value)) {
1,915,527✔
2564
            table->entry_count++;
1,915,475✔
2565
            return SIXEL_OK;
1,915,475✔
2566
        }
2567
        victim_slot = (size_t)((cur_key + kicks) &
52✔
2568
                               (CUCKOO_BUCKET_SIZE - 1U));
2569
        victim_key = bucket->key[victim_slot];
52✔
2570
        victim_value = bucket->value[victim_slot];
52✔
2571
        bucket->key[victim_slot] = cur_key;
52✔
2572
        bucket->value[victim_slot] = cur_value;
52✔
2573
        cur_key = victim_key;
52✔
2574
        cur_value = victim_value;
52✔
2575
        bucket_index = cuckoo_hash_alternate(cur_key,
62✔
2576
                                             bucket_index,
10✔
2577
                                             table->bucket_mask);
10✔
2578
    }
10✔
2579

2580
    if (table->stash_count < CUCKOO_STASH_SIZE) {
×
2581
        table->stash_key[table->stash_count] = cur_key;
×
2582
        table->stash_value[table->stash_count] = cur_value;
×
2583
        table->stash_count++;
×
2584
        table->entry_count++;
×
2585
        return SIXEL_OK;
×
2586
    }
2587

2588
    status = cuckoo_table32_grow(table);
×
2589
    if (SIXEL_FAILED(status)) {
×
2590
        return status;
×
2591
    }
2592

2593
    return cuckoo_table32_insert(table, cur_key, cur_value);
×
2594
}
613,543✔
2595

2596
static SIXELSTATUS
2597
computeHistogram_robinhood(unsigned char const *data,
2598
                           unsigned int length,
2599
                           unsigned long depth,
2600
                           unsigned int step,
2601
                           unsigned int max_sample,
2602
                           tupletable2 * const colorfreqtableP,
2603
                           struct histogram_control const *control,
2604
                           int use_reversible,
2605
                           sixel_allocator_t *allocator);
2606

2607
static SIXELSTATUS
2608
computeHistogram_hopscotch(unsigned char const *data,
2609
                           unsigned int length,
2610
                           unsigned long depth,
2611
                           unsigned int step,
2612
                           unsigned int max_sample,
2613
                           tupletable2 * const colorfreqtableP,
2614
                           struct histogram_control const *control,
2615
                           int use_reversible,
2616
                           sixel_allocator_t *allocator);
2617

2618
static SIXELSTATUS
2619
computeHistogram(unsigned char const    /* in */  *data,
260✔
2620
                 unsigned int           /* in */  length,
2621
                 unsigned long const    /* in */  depth,
2622
                 tupletable2 * const    /* out */ colorfreqtableP,
2623
                 int const              /* in */  qualityMode,
2624
                 int const              /* in */  use_reversible,
2625
                 sixel_allocator_t      /* in */  *allocator)
2626
{
2627
    SIXELSTATUS status = SIXEL_FALSE;
260✔
2628
    typedef uint32_t unit_t;
2629
    unsigned int i, n;
2630
    unit_t *histogram = NULL;
260✔
2631
    unit_t *refmap = NULL;
260✔
2632
    unit_t *ref;
2633
    unsigned int bucket_index;
2634
    unsigned int step;
2635
    unsigned int max_sample;
2636
    size_t hist_size;
2637
    unit_t bucket_value;
2638
    unsigned int component;
2639
    unsigned int reconstructed;
2640
    struct histogram_control control;
2641
    unsigned int depth_u;
2642
    unsigned char reversible_pixel[4];
2643

2644
    switch (qualityMode) {
260!
2645
    case SIXEL_QUALITY_LOW:
120✔
2646
        max_sample = 18383;
227✔
2647
        break;
227✔
2648
    case SIXEL_QUALITY_HIGH:
20✔
2649
        max_sample = 1118383;
30✔
2650
        break;
30✔
2651
    case SIXEL_QUALITY_FULL:
3✔
2652
    default:
2653
        max_sample = 4003079;
3✔
2654
        break;
3✔
2655
    }
2656

2657
    step = length / depth / max_sample * depth;
260✔
2658
    if (step <= 0) {
260✔
2659
        step = depth;
156✔
2660
    }
52✔
2661

2662
    quant_trace(stderr, "making histogram...\n");
260✔
2663

2664
    depth_u = (unsigned int)depth;
260✔
2665
    control = histogram_control_make(depth_u);
260✔
2666
    if (use_reversible) {
260!
2667
        control.reversible_rounding = 1;
×
2668
    }
2669
    if (histogram_lut_policy == SIXEL_LUT_POLICY_ROBINHOOD
260!
2670
        || histogram_lut_policy == SIXEL_LUT_POLICY_HOPSCOTCH) {
260!
2671
        if (histogram_lut_policy == SIXEL_LUT_POLICY_ROBINHOOD) {
×
2672
            status = computeHistogram_robinhood(data,
×
2673
                                                length,
2674
                                                depth,
2675
                                                step,
2676
                                                max_sample,
2677
                                                colorfreqtableP,
2678
                                                &control,
2679
                                                use_reversible,
2680
                                                allocator);
2681
        } else {
2682
            status = computeHistogram_hopscotch(data,
×
2683
                                                length,
2684
                                                depth,
2685
                                                step,
2686
                                                max_sample,
2687
                                                colorfreqtableP,
2688
                                                &control,
2689
                                                use_reversible,
2690
                                                allocator);
2691
        }
2692
        goto end;
×
2693
    }
2694

2695
    hist_size = histogram_dense_size((unsigned int)depth, &control);
260✔
2696
    histogram = (unit_t *)sixel_allocator_calloc(allocator,
378✔
2697
                                                 hist_size,
118✔
2698
                                                 sizeof(unit_t));
2699
    if (histogram == NULL) {
260!
2700
        sixel_helper_set_additional_message(
×
2701
            "unable to allocate memory for histogram.");
2702
        status = SIXEL_BAD_ALLOCATION;
×
2703
        goto end;
×
2704
    }
2705
    ref = refmap = (unit_t *)sixel_allocator_malloc(allocator,
378✔
2706
                                                    hist_size *
118✔
2707
                                                    sizeof(unit_t));
2708
    if (refmap == NULL) {
260!
2709
        sixel_helper_set_additional_message(
×
2710
            "unable to allocate memory for lookup table.");
2711
        status = SIXEL_BAD_ALLOCATION;
×
2712
        goto end;
×
2713
    }
2714

2715
    for (i = 0; i < length; i += step) {
3,572,726✔
2716
        if (use_reversible) {
3,572,466!
2717
            sixel_quant_reversible_pixel(data + i,
×
2718
                                         depth_u,
2719
                                         reversible_pixel);
2720
            bucket_index = histogram_pack_color(reversible_pixel,
×
2721
                                                depth_u,
2722
                                                &control);
2723
        } else {
2724
            bucket_index = histogram_pack_color(data + i,
5,349,672✔
2725
                                                depth_u,
1,777,206✔
2726
                                                &control);
2727
        }
2728
        if (histogram[bucket_index] == 0) {
3,572,466✔
2729
            *ref++ = bucket_index;
294,113✔
2730
        }
98,297✔
2731
        if (histogram[bucket_index] < UINT32_MAX) {
3,572,466!
2732
            histogram[bucket_index]++;
3,572,466✔
2733
        }
1,777,206✔
2734
    }
1,777,206✔
2735

2736
    colorfreqtableP->size = (unsigned int)(ref - refmap);
260✔
2737

2738
    status = alloctupletable(&colorfreqtableP->table,
378✔
2739
                             depth,
118✔
2740
                             (unsigned int)(ref - refmap),
260✔
2741
                             allocator);
118✔
2742
    if (SIXEL_FAILED(status)) {
260!
2743
        goto end;
×
2744
    }
2745
    for (i = 0; i < colorfreqtableP->size; ++i) {
294,373✔
2746
        bucket_value = refmap[i];
294,113✔
2747
        if (histogram[bucket_value] > 0) {
294,113!
2748
            colorfreqtableP->table[i]->value = histogram[bucket_value];
294,113✔
2749
            for (n = 0; n < depth; n++) {
1,176,452✔
2750
                component = (unsigned int)
882,339✔
2751
                    ((bucket_value >> (n * control.channel_bits)) &
1,177,230✔
2752
                     control.channel_mask);
882,339✔
2753
                reconstructed = histogram_reconstruct(component,
882,339✔
2754
                                                      &control);
2755
                if (use_reversible) {
882,339!
2756
                    reconstructed =
×
2757
                        (unsigned int)sixel_quant_reversible_value(
×
2758
                            reconstructed);
2759
                }
2760
                colorfreqtableP->table[i]->tuple[depth - 1 - n]
882,339✔
2761
                    = (sample)reconstructed;
1,177,230✔
2762
            }
294,891✔
2763
        }
98,297✔
2764
    }
98,297✔
2765

2766
    quant_trace(stderr, "%u colors found\n", colorfreqtableP->size);
260✔
2767

2768
    status = SIXEL_OK;
260✔
2769

2770
end:
142✔
2771
    sixel_allocator_free(allocator, refmap);
260✔
2772
    sixel_allocator_free(allocator, histogram);
260✔
2773

2774
    return status;
260✔
2775
}
2776

2777
static SIXELSTATUS
2778
computeHistogram_robinhood(unsigned char const *data,
×
2779
                           unsigned int length,
2780
                           unsigned long depth,
2781
                           unsigned int step,
2782
                           unsigned int max_sample,
2783
                           tupletable2 * const colorfreqtableP,
2784
                           struct histogram_control const *control,
2785
                           int use_reversible,
2786
                           sixel_allocator_t *allocator)
2787
{
2788
    SIXELSTATUS status = SIXEL_FALSE;
×
2789
    struct robinhood_table32 table;
2790
    size_t expected;
2791
    size_t cap_limit;
2792
    size_t index;
2793
    unsigned int depth_u;
2794
    unsigned int i;
2795
    unsigned int n;
2796
    uint32_t bucket_color;
2797
    uint32_t bucket_hash;
2798
    uint32_t entry_color;
2799
    struct robinhood_slot32 *slot;
2800
    unsigned int component;
2801
    unsigned int reconstructed;
2802
    unsigned char reversible_pixel[4];
2803

2804
    /*
2805
     * The ASCII sketch below shows how the sparse table stores samples:
2806
     *
2807
     *   [hash]->(key,value,distance)  Robin Hood probing keeps dense tails.
2808
     */
2809
    table.slots = NULL;
×
2810
    table.capacity = 0U;
×
2811
    table.count = 0U;
×
2812
    table.allocator = allocator;
×
2813
    cap_limit = (size_t)1U << 20;
×
2814
    expected = max_sample;
×
2815
    if (expected < 256U) {
×
2816
        expected = 256U;
×
2817
    }
2818
    if (expected > cap_limit) {
×
2819
        expected = cap_limit;
×
2820
    }
2821

2822
    status = robinhood_table32_init(&table, expected, allocator);
×
2823
    if (SIXEL_FAILED(status)) {
×
2824
        sixel_helper_set_additional_message(
×
2825
            "unable to allocate robinhood histogram.");
2826
        goto end;
×
2827
    }
2828

2829
    depth_u = (unsigned int)depth;
×
2830
    for (i = 0U; i < length; i += step) {
×
2831
        if (use_reversible) {
×
2832
            sixel_quant_reversible_pixel(data + i, depth_u,
×
2833
                                         reversible_pixel);
2834
            bucket_color = histogram_pack_color(reversible_pixel,
×
2835
                                                depth_u, control);
2836
        } else {
2837
            bucket_color = histogram_pack_color(data + i,
×
2838
                                                depth_u, control);
2839
        }
2840
        bucket_hash = histogram_hash_mix(bucket_color);
×
2841
        /*
2842
         * Hash probing uses the mixed key while the slot stores the
2843
         * original quantized RGB value:
2844
         *
2845
         *   hash --> [slot]
2846
         *             |color|count|
2847
         */
2848
        slot = robinhood_table32_lookup(&table,
×
2849
                                        bucket_hash,
2850
                                        bucket_color);
2851
        if (slot == NULL) {
×
2852
            status = robinhood_table32_insert(&table,
×
2853
                                              bucket_hash,
2854
                                              bucket_color,
2855
                                              1U);
2856
            if (SIXEL_FAILED(status)) {
×
2857
                sixel_helper_set_additional_message(
×
2858
                    "unable to grow robinhood histogram.");
2859
                goto end;
×
2860
            }
2861
        } else if (slot->value < UINT32_MAX) {
×
2862
            slot->value++;
×
2863
        }
2864
    }
2865

2866
    if (table.count > UINT_MAX) {
×
2867
        sixel_helper_set_additional_message(
×
2868
            "too many unique colors for histogram.");
2869
        status = SIXEL_BAD_ARGUMENT;
×
2870
        goto end;
×
2871
    }
2872

2873
    colorfreqtableP->size = (unsigned int)table.count;
×
2874
    status = alloctupletable(&colorfreqtableP->table,
×
2875
                             depth_u,
2876
                             (unsigned int)table.count,
×
2877
                             allocator);
2878
    if (SIXEL_FAILED(status)) {
×
2879
        goto end;
×
2880
    }
2881

2882
    index = 0U;
×
2883
    /*
2884
     * Stream slots in the hash traversal order to avoid qsort overhead.
2885
     * This favors throughput over identical palette ordering.
2886
     */
2887
    for (i = 0U; i < table.capacity; ++i) {
×
2888
        slot = &table.slots[i];
×
2889
        if (slot->value == 0U) {
×
2890
            continue;
×
2891
        }
2892
        if (index >= colorfreqtableP->size) {
×
2893
            break;
×
2894
        }
2895
        entry_color = slot->color;
×
2896
        colorfreqtableP->table[index]->value = slot->value;
×
2897
        for (n = 0U; n < depth_u; ++n) {
×
2898
            component = (unsigned int)
×
2899
                ((entry_color >> (n * control->channel_bits))
×
2900
                 & control->channel_mask);
×
2901
            reconstructed = histogram_reconstruct(component, control);
×
2902
            if (use_reversible) {
×
2903
                reconstructed =
×
2904
                    (unsigned int)sixel_quant_reversible_value(
×
2905
                        reconstructed);
2906
            }
2907
            colorfreqtableP->table[index]->tuple[depth_u - 1U - n]
×
2908
                = (sample)reconstructed;
×
2909
        }
2910
        index++;
×
2911
    }
2912

2913
    status = SIXEL_OK;
×
2914

2915
end:
2916
    robinhood_table32_fini(&table);
×
2917

2918
    return status;
×
2919
}
2920

2921
static SIXELSTATUS
2922
computeHistogram_hopscotch(unsigned char const *data,
×
2923
                           unsigned int length,
2924
                           unsigned long depth,
2925
                           unsigned int step,
2926
                           unsigned int max_sample,
2927
                           tupletable2 * const colorfreqtableP,
2928
                           struct histogram_control const *control,
2929
                           int use_reversible,
2930
                           sixel_allocator_t *allocator)
2931
{
2932
    SIXELSTATUS status = SIXEL_FALSE;
×
2933
    struct hopscotch_table32 table;
2934
    size_t expected;
2935
    size_t cap_limit;
2936
    size_t index;
2937
    unsigned int depth_u;
2938
    unsigned int i;
2939
    unsigned int n;
2940
    uint32_t bucket_color;
2941
    uint32_t bucket_hash;
2942
    uint32_t entry_color;
2943
    struct hopscotch_slot32 *slot;
2944
    unsigned int component;
2945
    unsigned int reconstructed;
2946
    unsigned char reversible_pixel[4];
2947

2948
    /*
2949
     * Hopscotch hashing stores the local neighbourhood using the map below:
2950
     *
2951
     *   [home] hopinfo bits ---> |slot+0|slot+1|slot+2| ...
2952
     *                              ^ entries hop within this window.
2953
     */
2954
    table.slots = NULL;
×
2955
    table.hopinfo = NULL;
×
2956
    table.capacity = 0U;
×
2957
    table.count = 0U;
×
2958
    table.neighborhood = HOPSCOTCH_DEFAULT_NEIGHBORHOOD;
×
2959
    table.allocator = allocator;
×
2960
    cap_limit = (size_t)1U << 20;
×
2961
    expected = max_sample;
×
2962
    if (expected < 256U) {
×
2963
        expected = 256U;
×
2964
    }
2965
    if (expected > cap_limit) {
×
2966
        expected = cap_limit;
×
2967
    }
2968

2969
    status = hopscotch_table32_init(&table, expected, allocator);
×
2970
    if (SIXEL_FAILED(status)) {
×
2971
        sixel_helper_set_additional_message(
×
2972
            "unable to allocate hopscotch histogram.");
2973
        goto end;
×
2974
    }
2975

2976
    depth_u = (unsigned int)depth;
×
2977
    for (i = 0U; i < length; i += step) {
×
2978
        if (use_reversible) {
×
2979
            sixel_quant_reversible_pixel(data + i, depth_u,
×
2980
                                         reversible_pixel);
2981
            bucket_color = histogram_pack_color(reversible_pixel,
×
2982
                                                depth_u, control);
2983
        } else {
2984
            bucket_color = histogram_pack_color(data + i,
×
2985
                                                depth_u, control);
2986
        }
2987
        bucket_hash = histogram_hash_mix(bucket_color);
×
2988
        /*
2989
         * Hopscotch buckets mirror the robinhood layout, keeping the
2990
         * quantized color next to the count so we never derive it from
2991
         * the scrambled hash key.
2992
         */
2993
        slot = hopscotch_table32_lookup(&table,
×
2994
                                        bucket_hash,
2995
                                        bucket_color);
2996
        if (slot == NULL) {
×
2997
            status = hopscotch_table32_insert(&table,
×
2998
                                              bucket_hash,
2999
                                              bucket_color,
3000
                                              1U);
3001
            if (SIXEL_FAILED(status)) {
×
3002
                sixel_helper_set_additional_message(
×
3003
                    "unable to grow hopscotch histogram.");
3004
                goto end;
×
3005
            }
3006
        } else if (slot->value < UINT32_MAX) {
×
3007
            slot->value++;
×
3008
        }
3009
    }
3010

3011
    if (table.count > UINT_MAX) {
×
3012
        sixel_helper_set_additional_message(
×
3013
            "too many unique colors for histogram.");
3014
        status = SIXEL_BAD_ARGUMENT;
×
3015
        goto end;
×
3016
    }
3017

3018
    colorfreqtableP->size = (unsigned int)table.count;
×
3019
    status = alloctupletable(&colorfreqtableP->table,
×
3020
                             depth_u,
3021
                             (unsigned int)table.count,
×
3022
                             allocator);
3023
    if (SIXEL_FAILED(status)) {
×
3024
        goto end;
×
3025
    }
3026

3027
    index = 0U;
×
3028
    /*
3029
     * Stream slots in the hash traversal order to avoid qsort overhead.
3030
     * This favors throughput over identical palette ordering.
3031
     */
3032
    for (i = 0U; i < table.capacity; ++i) {
×
3033
        slot = &table.slots[i];
×
3034
        if (slot->key == HOPSCOTCH_EMPTY_KEY || slot->value == 0U) {
×
3035
            continue;
×
3036
        }
3037
        if (index >= colorfreqtableP->size) {
×
3038
            break;
×
3039
        }
3040
        entry_color = slot->color;
×
3041
        colorfreqtableP->table[index]->value = slot->value;
×
3042
        for (n = 0U; n < depth_u; ++n) {
×
3043
            component = (unsigned int)
×
3044
                ((entry_color >> (n * control->channel_bits))
×
3045
                 & control->channel_mask);
×
3046
            reconstructed = histogram_reconstruct(component, control);
×
3047
            if (use_reversible) {
×
3048
                reconstructed =
×
3049
                    (unsigned int)sixel_quant_reversible_value(
×
3050
                        reconstructed);
3051
            }
3052
            colorfreqtableP->table[index]->tuple[depth_u - 1U - n]
×
3053
                = (sample)reconstructed;
×
3054
        }
3055
        index++;
×
3056
    }
3057

3058
    status = SIXEL_OK;
×
3059

3060
end:
3061
    hopscotch_table32_fini(&table);
×
3062

3063
    return status;
×
3064
}
3065

3066
SIXELSTATUS
3067
sixel_quant_cache_prepare(unsigned short **cachetable,
267✔
3068
                          size_t *cachetable_size,
3069
                          int lut_policy,
3070
                          int reqcolor,
3071
                          sixel_allocator_t *allocator)
3072
{
3073
    SIXELSTATUS status;
3074
    struct histogram_control control;
3075
    struct cuckoo_table32 *table;
3076
    size_t expected;
3077
    size_t buckets;
3078
    size_t cap_limit;
3079
    int normalized;
3080

3081
    if (cachetable == NULL || allocator == NULL) {
267!
3082
        return SIXEL_BAD_ARGUMENT;
×
3083
    }
3084

3085
    /*
3086
     * The cache pointer always references the same ladder:
3087
     *
3088
     *   cache -> cuckoo_table32 -> buckets
3089
     *                           -> stash
3090
     */
3091
    normalized = lut_policy;
267✔
3092
    if (normalized == SIXEL_LUT_POLICY_AUTO) {
267!
3093
        normalized = histogram_lut_policy;
267✔
3094
    }
109✔
3095
    if (normalized == SIXEL_LUT_POLICY_AUTO) {
267!
3096
        normalized = SIXEL_LUT_POLICY_6BIT;
267✔
3097
    }
109✔
3098

3099
    control = histogram_control_make_for_policy(3U, normalized);
267✔
3100
    if (control.channel_shift == 0U) {
267!
3101
        expected = (size_t)reqcolor * 64U;
×
3102
        cap_limit = (size_t)1U << 20;
×
3103
        if (expected < 512U) {
×
3104
            expected = 512U;
×
3105
        }
3106
        if (expected > cap_limit) {
×
3107
            expected = cap_limit;
×
3108
        }
3109
    } else {
3110
        expected = histogram_dense_size(3U, &control);
267✔
3111
        if (expected == 0U) {
267!
3112
            expected = CUCKOO_BUCKET_SIZE;
×
3113
        }
3114
    }
3115

3116
    table = (struct cuckoo_table32 *)*cachetable;
267✔
3117
    if (table != NULL) {
267✔
3118
        buckets = cuckoo_round_buckets(expected);
3✔
3119
        if (table->bucket_count < buckets) {
3!
3120
            cuckoo_table32_fini(table);
×
3121
            sixel_allocator_free(allocator, table);
×
3122
            table = NULL;
×
3123
            *cachetable = NULL;
×
3124
        } else {
3125
            cuckoo_table32_clear(table);
3✔
3126
        }
3127
    }
1✔
3128
    if (table == NULL) {
267✔
3129
        table = (struct cuckoo_table32 *)sixel_allocator_malloc(
264✔
3130
            allocator, sizeof(struct cuckoo_table32));
108✔
3131
        if (table == NULL) {
264!
3132
            sixel_helper_set_additional_message(
×
3133
                "unable to allocate cuckoo cache state.");
3134
            return SIXEL_BAD_ALLOCATION;
×
3135
        }
3136
        memset(table, 0, sizeof(struct cuckoo_table32));
264✔
3137
        status = cuckoo_table32_init(table, expected, allocator);
264✔
3138
        if (SIXEL_FAILED(status)) {
264!
3139
            sixel_allocator_free(allocator, table);
×
3140
            sixel_helper_set_additional_message(
×
3141
                "unable to initialize cuckoo cache.");
3142
            return status;
×
3143
        }
3144
        *cachetable = (unsigned short *)table;
264✔
3145
    }
108✔
3146

3147
    if (cachetable_size != NULL) {
267!
3148
        *cachetable_size = table->bucket_count * CUCKOO_BUCKET_SIZE;
267✔
3149
    }
109✔
3150

3151
    return SIXEL_OK;
267✔
3152
}
109✔
3153

3154
void
3155
sixel_quant_cache_clear(unsigned short *cachetable,
264✔
3156
                        int lut_policy)
3157
{
3158
    struct cuckoo_table32 *table;
3159

3160
    (void)lut_policy;
108✔
3161
    if (cachetable == NULL) {
264!
3162
        return;
×
3163
    }
3164

3165
    table = (struct cuckoo_table32 *)cachetable;
264✔
3166
    cuckoo_table32_clear(table);
264✔
3167
}
108✔
3168

3169
void
3170
sixel_quant_cache_release(unsigned short *cachetable,
529✔
3171
                          int lut_policy,
3172
                          sixel_allocator_t *allocator)
3173
{
3174
    struct cuckoo_table32 *table;
3175

3176
    (void)lut_policy;
183✔
3177
    if (cachetable == NULL || allocator == NULL) {
529!
3178
        return;
265✔
3179
    }
3180

3181
    table = (struct cuckoo_table32 *)cachetable;
264✔
3182
    cuckoo_table32_fini(table);
264✔
3183
    sixel_allocator_free(allocator, table);
264✔
3184
}
183✔
3185

3186

3187
static int
3188
computeColorMapFromInput(unsigned char const *data,
260✔
3189
                         unsigned int const length,
3190
                         unsigned int const depth,
3191
                         unsigned int const reqColors,
3192
                         int const methodForLargest,
3193
                         int const methodForRep,
3194
                         int const qualityMode,
3195
                         int const force_palette,
3196
                         int const use_reversible,
3197
                         int const final_merge_mode,
3198
                         tupletable2 * const colormapP,
3199
                         unsigned int *origcolors,
3200
                         sixel_allocator_t *allocator)
3201
{
3202
/*----------------------------------------------------------------------------
3203
   Produce a colormap containing the best colors to represent the
3204
   image stream in file 'ifP'.  Figure it out using the median cut
3205
   technique.
3206

3207
   The colormap will have 'reqcolors' or fewer colors in it, unless
3208
   'allcolors' is true, in which case it will have all the colors that
3209
   are in the input.
3210

3211
   The colormap has the same maxval as the input.
3212

3213
   Put the colormap in newly allocated storage as a tupletable2
3214
   and return its address as *colormapP.  Return the number of colors in
3215
   it as *colorsP and its maxval as *colormapMaxvalP.
3216

3217
   Return the characteristics of the input file as
3218
   *formatP and *freqPamP.  (This information is not really
3219
   relevant to our colormap mission; just a fringe benefit).
3220
-----------------------------------------------------------------------------*/
3221
    SIXELSTATUS status = SIXEL_FALSE;
260✔
3222
    tupletable2 colorfreqtable = {0, NULL};
260✔
3223
    unsigned int i;
3224
    unsigned int n;
3225

3226
    status = computeHistogram(data, length, depth,
378✔
3227
                              &colorfreqtable, qualityMode,
118✔
3228
                              use_reversible, allocator);
118✔
3229
    if (SIXEL_FAILED(status)) {
260!
3230
        goto end;
×
3231
    }
3232
    if (origcolors) {
260!
3233
        *origcolors = colorfreqtable.size;
260✔
3234
    }
118✔
3235

3236
    if (colorfreqtable.size <= reqColors) {
260✔
3237
        quant_trace(stderr,
286✔
3238
                    "Image already has few enough colors (<=%d).  "
3239
                    "Keeping same colors.\n", reqColors);
95✔
3240
        /* *colormapP = colorfreqtable; */
3241
        colormapP->size = colorfreqtable.size;
191✔
3242
        status = alloctupletable(&colormapP->table, depth, colorfreqtable.size, allocator);
191✔
3243
        if (SIXEL_FAILED(status)) {
191!
3244
            goto end;
×
3245
        }
3246
        for (i = 0; i < colorfreqtable.size; ++i) {
3,205✔
3247
            colormapP->table[i]->value = colorfreqtable.table[i]->value;
3,014✔
3248
            for (n = 0; n < depth; ++n) {
12,056✔
3249
                colormapP->table[i]->tuple[n] = colorfreqtable.table[i]->tuple[n];
9,042✔
3250
            }
4,224✔
3251
            if (use_reversible) {
3,014!
3252
                sixel_quant_reversible_tuple(colormapP->table[i]->tuple,
×
3253
                                             depth);
3254
            }
3255
        }
1,408✔
3256
    } else {
95✔
3257
        quant_trace(stderr, "choosing %d colors...\n", reqColors);
69✔
3258
        status = mediancut(colorfreqtable, depth, reqColors,
92✔
3259
                           methodForLargest, methodForRep,
23✔
3260
                           use_reversible, final_merge_mode,
23✔
3261
                           colormapP, allocator);
23✔
3262
        if (SIXEL_FAILED(status)) {
69!
3263
            goto end;
×
3264
        }
3265
        quant_trace(stderr, "%d colors are choosed.\n", colorfreqtable.size);
69✔
3266
    }
3267

3268
    if (force_palette) {
260!
3269
        status = force_palette_completion(colormapP, depth, reqColors,
×
3270
                                          colorfreqtable, allocator);
3271
        if (SIXEL_FAILED(status)) {
×
3272
            goto end;
×
3273
        }
3274
    }
3275

3276
    status = SIXEL_OK;
260✔
3277

3278
end:
142✔
3279
    sixel_allocator_free(allocator, colorfreqtable.table);
260✔
3280
    return status;
260✔
3281
}
3282

3283

3284
/* diffuse error energy to surround pixels (normal strategy) */
3285
static void
3286
error_diffuse_normal(
281,667,771✔
3287
    unsigned char /* in */    *data,      /* base address of pixel buffer */
3288
    int           /* in */    pos,        /* address of the destination pixel */
3289
    int           /* in */    depth,      /* color depth in bytes */
3290
    int           /* in */    error,      /* error energy */
3291
    int           /* in */    numerator,  /* numerator of diffusion coefficient */
3292
    int           /* in */    denominator /* denominator of diffusion coefficient */)
3293
{
3294
    int c;
3295

3296
    data += pos * depth;
281,667,771✔
3297

3298
    c = *data + (error * numerator * 2 / denominator + 1) / 2;
281,667,771✔
3299
    if (c < 0) {
281,667,771✔
3300
        c = 0;
2,567,509✔
3301
    }
811,245✔
3302
    if (c >= 1 << 8) {
281,667,771✔
3303
        c = (1 << 8) - 1;
959,648✔
3304
    }
304,614✔
3305
    *data = (unsigned char)c;
281,667,771✔
3306
}
281,667,771✔
3307

3308
/* error diffusion with fast strategy */
3309
static void
3310
error_diffuse_fast(
169,469,067✔
3311
    unsigned char /* in */    *data,      /* base address of pixel buffer */
3312
    int           /* in */    pos,        /* address of the destination pixel */
3313
    int           /* in */    depth,      /* color depth in bytes */
3314
    int           /* in */    error,      /* error energy */
3315
    int           /* in */    numerator,  /* numerator of diffusion coefficient */
3316
    int           /* in */    denominator /* denominator of diffusion coefficient */)
3317
{
3318
    int c;
3319

3320
    data += pos * depth;
169,469,067✔
3321

3322
    c = *data + error * numerator / denominator;
169,469,067✔
3323
    if (c < 0) {
169,469,067✔
3324
        c = 0;
7,291,990✔
3325
    }
2,721,044✔
3326
    if (c >= 1 << 8) {
169,469,067✔
3327
        c = (1 << 8) - 1;
459,666✔
3328
    }
150,804✔
3329
    *data = (unsigned char)c;
169,469,067✔
3330
}
169,469,067✔
3331

3332

3333
/* error diffusion with precise strategy */
3334
static void
3335
error_diffuse_precise(
6,111,369✔
3336
    unsigned char /* in */    *data,      /* base address of pixel buffer */
3337
    int           /* in */    pos,        /* address of the destination pixel */
3338
    int           /* in */    depth,      /* color depth in bytes */
3339
    int           /* in */    error,      /* error energy */
3340
    int           /* in */    numerator,  /* numerator of diffusion coefficient */
3341
    int           /* in */    denominator /* denominator of diffusion coefficient */)
3342
{
3343
    int c;
3344

3345
    data += pos * depth;
6,111,369✔
3346

3347
    c = (int)(*data + error * numerator / (double)denominator + 0.5);
6,111,369✔
3348
    if (c < 0) {
6,111,369✔
3349
        c = 0;
70,455✔
3350
    }
25,487✔
3351
    if (c >= 1 << 8) {
6,111,369!
3352
        c = (1 << 8) - 1;
×
3353
    }
3354
    *data = (unsigned char)c;
6,111,369✔
3355
}
6,111,369✔
3356

3357

3358
typedef void (*diffuse_fixed_carry_mode)(int32_t *carry_curr,
3359
                                         int32_t *carry_next,
3360
                                         int32_t *carry_far,
3361
                                         int width,
3362
                                         int height,
3363
                                         int depth,
3364
                                         int x,
3365
                                         int y,
3366
                                         int32_t error,
3367
                                         int direction,
3368
                                         int channel);
3369

3370

3371
typedef void (*diffuse_varerr_mode)(unsigned char *data,
3372
                                    int width,
3373
                                    int height,
3374
                                    int x,
3375
                                    int y,
3376
                                    int depth,
3377
                                    int32_t error,
3378
                                    int index,
3379
                                    int direction);
3380

3381
typedef void (*diffuse_varerr_carry_mode)(int32_t *carry_curr,
3382
                                          int32_t *carry_next,
3383
                                          int32_t *carry_far,
3384
                                          int width,
3385
                                          int height,
3386
                                          int depth,
3387
                                          int x,
3388
                                          int y,
3389
                                          int32_t error,
3390
                                          int index,
3391
                                          int direction,
3392
                                          int channel);
3393

3394

3395
static int32_t
3396
diffuse_varerr_term(int32_t error, int weight, int denom)
×
3397
{
3398
    int64_t delta;
3399

3400
    delta = (int64_t)error * (int64_t)weight;
×
3401
    if (delta >= 0) {
×
3402
        delta = (delta + denom / 2) / denom;
×
3403
    } else {
3404
        delta = (delta - denom / 2) / denom;
×
3405
    }
3406

3407
    return (int32_t)delta;
×
3408
}
3409

3410

3411
static int32_t
3412
diffuse_fixed_term(int32_t error, int numerator, int denominator)
×
3413
{
3414
    int64_t delta;
3415

3416
    delta = (int64_t)error * (int64_t)numerator;
×
3417
    if (delta >= 0) {
×
3418
        delta = (delta + denominator / 2) / denominator;
×
3419
    } else {
3420
        delta = (delta - denominator / 2) / denominator;
×
3421
    }
3422

3423
    return (int32_t)delta;
×
3424
}
3425

3426

3427
static void
3428
diffuse_varerr_apply_direct(unsigned char *target, int depth, size_t offset,
×
3429
                            int32_t delta)
3430
{
3431
    int64_t value;
3432
    int result;
3433

3434
    value = (int64_t)target[offset * depth] << VARERR_SCALE_SHIFT;
×
3435
    value += delta;
×
3436
    if (value < 0) {
×
3437
        value = 0;
×
3438
    } else {
3439
        int64_t max_value;
3440

3441
        max_value = VARERR_MAX_VALUE;
×
3442
        if (value > max_value) {
×
3443
            value = max_value;
×
3444
        }
3445
    }
3446

3447
    result = (int)((value + VARERR_ROUND) >> VARERR_SCALE_SHIFT);
×
3448
    if (result < 0) {
×
3449
        result = 0;
×
3450
    }
3451
    if (result > 255) {
×
3452
        result = 255;
×
3453
    }
3454
    target[offset * depth] = (unsigned char)result;
×
3455
}
×
3456

3457

3458
static void
3459
diffuse_lso2(unsigned char *data, int width, int height,
×
3460
             int x, int y, int depth, int32_t error,
3461
             int index, int direction)
3462
{
3463
    const int (*table)[7];
3464
    const int *entry;
3465
    int denom;
3466
    int32_t term_r = 0;
×
3467
    int32_t term_r2 = 0;
×
3468
    int32_t term_dl = 0;
×
3469
    int32_t term_d = 0;
×
3470
    int32_t term_dr = 0;
×
3471
    int32_t term_d2 = 0;
×
3472
    size_t offset;
3473

3474
    if (error == 0) {
×
3475
        return;
×
3476
    }
3477
    if (index < 0) {
×
3478
        index = 0;
×
3479
    }
3480
    if (index > 255) {
×
3481
        index = 255;
×
3482
    }
3483

3484
    table = lso2_table();
×
3485
    entry = table[index];
×
3486
    denom = entry[6];
×
3487
    if (denom == 0) {
×
3488
        return;
×
3489
    }
3490

3491
    term_r = diffuse_varerr_term(error, entry[0], denom);
×
3492
    term_r2 = diffuse_varerr_term(error, entry[1], denom);
×
3493
    term_dl = diffuse_varerr_term(error, entry[2], denom);
×
3494
    term_d = diffuse_varerr_term(error, entry[3], denom);
×
3495
    term_dr = diffuse_varerr_term(error, entry[4], denom);
×
3496
    term_d2 = diffuse_varerr_term(error, entry[5], denom);
×
3497

3498

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

3556

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

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

3584
    table = lso2_table();
×
3585
    entry = table[index];
×
3586
    denom = entry[6];
×
3587
    if (denom == 0) {
×
3588
        return;
×
3589
    }
3590

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

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

3651

3652
static void
3653
scanline_params(int serpentine, int index, int limit,
59,650✔
3654
                int *start, int *end, int *step, int *direction)
3655
{
3656
    if (serpentine && (index & 1)) {
59,650✔
3657
        *start = limit - 1;
675✔
3658
        *end = -1;
675✔
3659
        *step = -1;
675✔
3660
        *direction = -1;
675✔
3661
    } else {
225✔
3662
        *start = 0;
58,975✔
3663
        *end = limit;
58,975✔
3664
        *step = 1;
58,975✔
3665
        *direction = 1;
58,975✔
3666
    }
3667
}
59,650✔
3668

3669

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

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

3709
    serpentine = (methodForScan == SIXEL_SCAN_SERPENTINE);
×
3710

3711
    if (foptimize_palette) {
×
3712
        int x;
3713

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

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

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

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

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

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

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

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

3792
    return SIXEL_OK;
×
3793
}
3794

3795

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

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

3865
    use_carry = (methodForCarry == SIXEL_CARRY_ENABLE);
×
3866
    carry_len = 0;
×
3867

3868
    switch (methodForDiffuse) {
×
3869
    case SIXEL_DIFFUSE_LSO2:
3870
        varerr_diffuse = diffuse_lso2;
×
3871
        varerr_diffuse_carry = diffuse_lso2_carry;
×
3872
        break;
×
3873
    default:
3874
        varerr_diffuse = diffuse_lso2;
×
3875
        varerr_diffuse_carry = diffuse_lso2_carry;
×
3876
        break;
×
3877
    }
3878

3879
    if (use_carry) {
×
3880
        carry_len = (size_t)width * (size_t)depth;
×
3881
        carry_curr = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
3882
        if (carry_curr == NULL) {
×
3883
            status = SIXEL_BAD_ALLOCATION;
×
3884
            goto end;
×
3885
        }
3886
        carry_next = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
3887
        if (carry_next == NULL) {
×
3888
            status = SIXEL_BAD_ALLOCATION;
×
3889
            goto end;
×
3890
        }
3891
        carry_far = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
3892
        if (carry_far == NULL) {
×
3893
            status = SIXEL_BAD_ALLOCATION;
×
3894
            goto end;
×
3895
        }
3896
    }
3897

3898
    serpentine = (methodForScan == SIXEL_SCAN_SERPENTINE);
×
3899

3900
    if (foptimize_palette) {
×
3901
        *ncolors = 0;
×
3902
        memset(new_palette, 0x00,
×
3903
               (size_t)SIXEL_PALETTE_MAX * (size_t)depth);
3904
        memset(migration_map, 0x00,
×
3905
               sizeof(unsigned short) * (size_t)SIXEL_PALETTE_MAX);
3906
    }
3907

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

3948
            color_index = f_lookup(source_pixel, depth, palette,
×
3949
                                   reqcolor, indextable,
3950
                                   complexion);
3951

3952
            if (foptimize_palette) {
×
3953
                if (migration_map[color_index] == 0) {
×
3954
                    output_index = *ncolors;
×
3955
                    for (n = 0; n < depth; ++n) {
×
3956
                        new_palette[output_index * depth + n]
×
3957
                            = palette[color_index * depth + n];
×
3958
                    }
3959
                    ++*ncolors;
×
3960
                    migration_map[color_index] = *ncolors;
×
3961
                } else {
3962
                    output_index = migration_map[color_index] - 1;
×
3963
                }
3964
                result[pos] = output_index;
×
3965
            } else {
3966
                output_index = color_index;
×
3967
                result[pos] = output_index;
×
3968
            }
3969

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

4015
    if (foptimize_palette) {
×
4016
        memcpy(palette, new_palette, (size_t)(*ncolors * depth));
×
4017
    } else {
4018
        *ncolors = reqcolor;
×
4019
    }
4020

4021
    status = SIXEL_OK;
×
4022

4023
end:
4024
    free(carry_next);
×
4025
    free(carry_curr);
×
4026
    free(carry_far);
×
4027
    return status;
×
4028
}
4029

4030

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

4100
    if (depth > max_channels) {
285!
4101
        status = SIXEL_BAD_ARGUMENT;
×
4102
        goto end;
×
4103
    }
4104

4105
    use_carry = (methodForCarry == SIXEL_CARRY_ENABLE);
285✔
4106
    carry_len = 0;
285✔
4107

4108
    if (depth != 3) {
285!
4109
        f_diffuse = diffuse_none;
×
4110
        f_diffuse_carry = diffuse_none_carry;
×
4111
        use_carry = 0;
×
4112
    } else {
4113
        switch (methodForDiffuse) {
285!
4114
        case SIXEL_DIFFUSE_NONE:
86✔
4115
            f_diffuse = diffuse_none;
158✔
4116
            f_diffuse_carry = diffuse_none_carry;
158✔
4117
            break;
158✔
4118
        case SIXEL_DIFFUSE_ATKINSON:
44✔
4119
            f_diffuse = diffuse_atkinson;
67✔
4120
            f_diffuse_carry = diffuse_atkinson_carry;
67✔
4121
            break;
67✔
4122
        case SIXEL_DIFFUSE_FS:
34✔
4123
            f_diffuse = diffuse_fs;
51✔
4124
            f_diffuse_carry = diffuse_fs_carry;
51✔
4125
            break;
51✔
4126
        case SIXEL_DIFFUSE_JAJUNI:
2✔
4127
            f_diffuse = diffuse_jajuni;
3✔
4128
            f_diffuse_carry = diffuse_jajuni_carry;
3✔
4129
            break;
3✔
4130
        case SIXEL_DIFFUSE_STUCKI:
2✔
4131
            f_diffuse = diffuse_stucki;
3✔
4132
            f_diffuse_carry = diffuse_stucki_carry;
3✔
4133
            break;
3✔
4134
        case SIXEL_DIFFUSE_BURKES:
2✔
4135
            f_diffuse = diffuse_burkes;
3✔
4136
            f_diffuse_carry = diffuse_burkes_carry;
3✔
4137
            break;
3✔
4138
        case SIXEL_DIFFUSE_SIERRA1:
4139
            f_diffuse = diffuse_sierra1;
×
4140
            f_diffuse_carry = diffuse_sierra1_carry;
×
4141
            break;
×
4142
        case SIXEL_DIFFUSE_SIERRA2:
4143
            f_diffuse = diffuse_sierra2;
×
4144
            f_diffuse_carry = diffuse_sierra2_carry;
×
4145
            break;
×
4146
        case SIXEL_DIFFUSE_SIERRA3:
4147
            f_diffuse = diffuse_sierra3;
×
4148
            f_diffuse_carry = diffuse_sierra3_carry;
×
4149
            break;
×
4150
        default:
4151
            quant_trace(stderr,
×
4152
                        "Internal error: invalid methodForDiffuse: %d\n",
4153
                        methodForDiffuse);
4154
            f_diffuse = diffuse_none;
×
4155
            f_diffuse_carry = diffuse_none_carry;
×
4156
            break;
×
4157
        }
4158
    }
4159

4160
    if (use_carry) {
285!
4161
        carry_len = (size_t)width * (size_t)depth;
×
4162
        if (carry_len > 0) {
×
4163
            carry_curr = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
4164
            if (carry_curr == NULL) {
×
4165
                status = SIXEL_BAD_ALLOCATION;
×
4166
                goto end;
×
4167
            }
4168
            carry_next = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
4169
            if (carry_next == NULL) {
×
4170
                status = SIXEL_BAD_ALLOCATION;
×
4171
                goto end;
×
4172
            }
4173
            carry_far = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
4174
            if (carry_far == NULL) {
×
4175
                status = SIXEL_BAD_ALLOCATION;
×
4176
                goto end;
×
4177
            }
4178
        } else {
4179
            use_carry = 0;
×
4180
        }
4181
    }
4182

4183
    serpentine = (methodForScan == SIXEL_SCAN_SERPENTINE);
285✔
4184

4185
    if (foptimize_palette) {
285✔
4186
        *ncolors = 0;
206✔
4187
        memset(new_palette, 0x00,
206✔
4188
               (size_t)SIXEL_PALETTE_MAX * (size_t)depth);
130✔
4189
        memset(migration_map, 0x00,
206✔
4190
               sizeof(unsigned short) * (size_t)SIXEL_PALETTE_MAX);
4191
    } else {
76✔
4192
        *ncolors = reqcolor;
79✔
4193
    }
4194

4195
    for (y = 0; y < height; ++y) {
59,935✔
4196
        scanline_params(serpentine, y, width,
59,650✔
4197
                        &start, &end, &step, &direction);
4198
        for (x = start; x != end; x += step) {
43,528,028✔
4199
            pos = y * width + x;
43,468,378✔
4200
            base = (size_t)pos * (size_t)depth;
43,468,378✔
4201
            carry_base = (size_t)x * (size_t)depth;
43,468,378✔
4202
            if (use_carry) {
43,468,378!
4203
                for (n = 0; n < depth; ++n) {
×
4204
                    accum = ((int64_t)data[base + n]
×
4205
                             << VARERR_SCALE_SHIFT)
×
4206
                           + carry_curr[carry_base + (size_t)n];
×
4207
                    if (accum < INT32_MIN) {
×
4208
                        accum = INT32_MIN;
×
4209
                    } else if (accum > INT32_MAX) {
×
4210
                        accum = INT32_MAX;
×
4211
                    }
4212
                    clamped = accum;
×
4213
                    if (clamped < 0) {
×
4214
                        clamped = 0;
×
4215
                    } else if (clamped > VARERR_MAX_VALUE) {
×
4216
                        clamped = VARERR_MAX_VALUE;
×
4217
                    }
4218
                    accum_scaled[n] = (int32_t)clamped;
×
4219
                    corrected[n]
4220
                        = (unsigned char)((clamped + VARERR_ROUND)
×
4221
                                          >> VARERR_SCALE_SHIFT);
×
4222
                    data[base + n] = corrected[n];
×
4223
                    carry_curr[carry_base + (size_t)n] = 0;
×
4224
                }
4225
                source_pixel = corrected;
×
4226
            } else {
4227
                source_pixel = data + base;
43,468,378✔
4228
            }
4229

4230
            color_index = f_lookup(source_pixel, depth, palette,
63,604,336✔
4231
                                   reqcolor, indextable,
20,135,958✔
4232
                                   complexion);
20,135,958✔
4233

4234
            if (foptimize_palette) {
43,468,378✔
4235
                if (migration_map[color_index] == 0) {
31,366,878✔
4236
                    output_index = *ncolors;
16,650✔
4237
                    for (n = 0; n < depth; ++n) {
66,600✔
4238
                        new_palette[output_index * depth + n]
49,950✔
4239
                            = palette[color_index * depth + n];
67,068✔
4240
                    }
17,118✔
4241
                    ++*ncolors;
16,650✔
4242
                    migration_map[color_index] = *ncolors;
16,650✔
4243
                } else {
5,706✔
4244
                    output_index = migration_map[color_index] - 1;
31,350,228✔
4245
                }
4246
                result[pos] = output_index;
31,366,878✔
4247
            } else {
14,296,138✔
4248
                output_index = color_index;
12,101,500✔
4249
                result[pos] = output_index;
12,101,500✔
4250
            }
4251

4252
            for (n = 0; n < depth; ++n) {
173,873,512✔
4253
                if (foptimize_palette) {
130,405,134✔
4254
                    palette_value = new_palette[output_index * depth + n];
94,100,634✔
4255
                } else {
42,888,414✔
4256
                    palette_value = palette[color_index * depth + n];
36,304,500✔
4257
                }
4258
                if (use_carry) {
130,405,134!
4259
                    target_scaled = (int32_t)palette_value
×
4260
                                  << VARERR_SCALE_SHIFT;
4261
                    error_scaled = accum_scaled[n] - target_scaled;
×
4262
                    f_diffuse_carry(carry_curr, carry_next, carry_far,
×
4263
                                    width, height, depth,
4264
                                    x, y, error_scaled, direction, n);
4265
                } else {
4266
                    offset = (int)source_pixel[n] - palette_value;
130,405,134✔
4267
                    f_diffuse(data + n, width, height, x, y,
190,813,008✔
4268
                              depth, offset, direction);
60,407,874✔
4269
                }
4270
            }
60,407,874✔
4271
        }
20,135,958✔
4272
        if (use_carry) {
59,650!
4273
            tmp = carry_curr;
×
4274
            carry_curr = carry_next;
×
4275
            carry_next = carry_far;
×
4276
            carry_far = tmp;
×
4277
            if (carry_len > 0) {
×
4278
                memset(carry_far, 0x00, carry_len * sizeof(int32_t));
×
4279
            }
4280
        }
4281
    }
26,750✔
4282

4283
    if (foptimize_palette) {
285✔
4284
        memcpy(palette, new_palette, (size_t)(*ncolors * depth));
206✔
4285
    }
76✔
4286

4287
    status = SIXEL_OK;
285✔
4288

4289
end:
170✔
4290
    free(carry_far);
285✔
4291
    free(carry_next);
285✔
4292
    free(carry_curr);
285✔
4293
    return status;
285✔
4294
}
4295

4296

4297
static void
4298
diffuse_none(unsigned char *data, int width, int height,
31,038,318✔
4299
             int x, int y, int depth, int error, int direction)
4300
{
4301
    /* unused */ (void) data;
27,259,002✔
4302
    /* unused */ (void) width;
27,259,002✔
4303
    /* unused */ (void) height;
27,259,002✔
4304
    /* unused */ (void) x;
27,259,002✔
4305
    /* unused */ (void) y;
27,259,002✔
4306
    /* unused */ (void) depth;
27,259,002✔
4307
    /* unused */ (void) error;
27,259,002✔
4308
    /* unused */ (void) direction;
27,259,002✔
4309
}
31,038,318✔
4310

4311

4312
static void
4313
diffuse_none_carry(int32_t *carry_curr, int32_t *carry_next,
×
4314
                   int32_t *carry_far, int width, int height,
4315
                   int depth, int x, int y, int32_t error,
4316
                   int direction, int channel)
4317
{
4318
    /* unused */ (void) carry_curr;
4319
    /* unused */ (void) carry_next;
4320
    /* unused */ (void) carry_far;
4321
    /* unused */ (void) width;
4322
    /* unused */ (void) height;
4323
    /* unused */ (void) depth;
4324
    /* unused */ (void) x;
4325
    /* unused */ (void) y;
4326
    /* unused */ (void) error;
4327
    /* unused */ (void) direction;
4328
    /* unused */ (void) channel;
4329
}
×
4330

4331

4332
static void
4333
diffuse_fs(unsigned char *data, int width, int height,
70,430,256✔
4334
           int x, int y, int depth, int error, int direction)
4335
{
4336
    /* Floyd Steinberg Method
4337
     *          curr    7/16
4338
     *  3/16    5/16    1/16
4339
     */
4340
    int pos;
4341
    int forward;
4342

4343
    pos = y * width + x;
70,430,256✔
4344
    forward = direction >= 0;
70,430,256✔
4345

4346
    if (forward) {
70,430,256✔
4347
        if (x < width - 1) {
69,215,256✔
4348
            error_diffuse_normal(data, pos + 1, depth, error, 7, 16);
69,144,759✔
4349
        }
23,048,253✔
4350
        if (y < height - 1) {
69,215,256✔
4351
            if (x > 0) {
69,121,332✔
4352
                error_diffuse_normal(data,
92,067,972✔
4353
                                     pos + width - 1,
69,050,979✔
4354
                                     depth, error, 3, 16);
23,016,993✔
4355
            }
23,016,993✔
4356
            error_diffuse_normal(data,
92,161,776✔
4357
                                 pos + width,
23,040,444✔
4358
                                 depth, error, 5, 16);
23,040,444✔
4359
            if (x < width - 1) {
69,121,332✔
4360
                error_diffuse_normal(data,
92,067,972✔
4361
                                     pos + width + 1,
69,050,979✔
4362
                                     depth, error, 1, 16);
23,016,993✔
4363
            }
23,016,993✔
4364
        }
23,040,444✔
4365
    } else {
23,071,752✔
4366
        if (x > 0) {
1,215,000✔
4367
            error_diffuse_normal(data, pos - 1, depth, error, 7, 16);
1,212,975✔
4368
        }
404,325✔
4369
        if (y < height - 1) {
1,215,000✔
4370
            if (x < width - 1) {
1,209,600✔
4371
                error_diffuse_normal(data,
1,610,112✔
4372
                                     pos + width + 1,
1,207,584✔
4373
                                     depth, error, 3, 16);
402,528✔
4374
            }
402,528✔
4375
            error_diffuse_normal(data,
1,612,800✔
4376
                                 pos + width,
403,200✔
4377
                                 depth, error, 5, 16);
403,200✔
4378
            if (x > 0) {
1,209,600✔
4379
                error_diffuse_normal(data,
1,610,112✔
4380
                                     pos + width - 1,
1,207,584✔
4381
                                     depth, error, 1, 16);
402,528✔
4382
            }
402,528✔
4383
        }
403,200✔
4384
    }
4385
}
70,430,256✔
4386

4387

4388
static void
4389
diffuse_fs_carry(int32_t *carry_curr, int32_t *carry_next,
×
4390
                 int32_t *carry_far, int width, int height,
4391
                 int depth, int x, int y, int32_t error,
4392
                 int direction, int channel)
4393
{
4394
    /* Floyd Steinberg Method
4395
     *          curr    7/16
4396
     *  3/16    5/48    1/16
4397
     */
4398
    int forward;
4399

4400
    /* unused */ (void) carry_far;
4401
    if (error == 0) {
×
4402
        return;
×
4403
    }
4404

4405
    forward = direction >= 0;
×
4406
    if (forward) {
×
4407
        if (x + 1 < width) {
×
4408
            size_t base;
4409
            int32_t term;
4410

4411
            base = ((size_t)(x + 1) * (size_t)depth)
×
4412
                 + (size_t)channel;
×
4413
            term = diffuse_fixed_term(error, 7, 16);
×
4414
            carry_curr[base] += term;
×
4415
        }
4416
        if (y + 1 < height) {
×
4417
            if (x > 0) {
×
4418
                size_t base;
4419
                int32_t term;
4420

4421
                base = ((size_t)(x - 1) * (size_t)depth)
×
4422
                     + (size_t)channel;
×
4423
                term = diffuse_fixed_term(error, 3, 16);
×
4424
                carry_next[base] += term;
×
4425
            }
4426
            {
4427
                size_t base;
4428
                int32_t term;
4429

4430
                base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
4431
                term = diffuse_fixed_term(error, 5, 16);
×
4432
                carry_next[base] += term;
×
4433
            }
4434
            if (x + 1 < width) {
×
4435
                size_t base;
4436
                int32_t term;
4437

4438
                base = ((size_t)(x + 1) * (size_t)depth)
×
4439
                     + (size_t)channel;
×
4440
                term = diffuse_fixed_term(error, 1, 16);
×
4441
                carry_next[base] += term;
×
4442
            }
4443
        }
4444
    } else {
4445
        if (x - 1 >= 0) {
×
4446
            size_t base;
4447
            int32_t term;
4448

4449
            base = ((size_t)(x - 1) * (size_t)depth)
×
4450
                 + (size_t)channel;
×
4451
            term = diffuse_fixed_term(error, 7, 16);
×
4452
            carry_curr[base] += term;
×
4453
        }
4454
        if (y + 1 < height) {
×
4455
            if (x + 1 < width) {
×
4456
                size_t base;
4457
                int32_t term;
4458

4459
                base = ((size_t)(x + 1) * (size_t)depth)
×
4460
                     + (size_t)channel;
×
4461
                term = diffuse_fixed_term(error, 3, 16);
×
4462
                carry_next[base] += term;
×
4463
            }
4464
            {
4465
                size_t base;
4466
                int32_t term;
4467

4468
                base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
4469
                term = diffuse_fixed_term(error, 5, 16);
×
4470
                carry_next[base] += term;
×
4471
            }
4472
            if (x - 1 >= 0) {
×
4473
                size_t base;
4474
                int32_t term;
4475

4476
                base = ((size_t)(x - 1) * (size_t)depth)
×
4477
                     + (size_t)channel;
×
4478
                term = diffuse_fixed_term(error, 1, 16);
×
4479
                carry_next[base] += term;
×
4480
            }
4481
        }
4482
    }
4483
}
4484

4485

4486
static void
4487
diffuse_atkinson(unsigned char *data, int width, int height,
28,352,460✔
4488
                 int x, int y, int depth, int error, int direction)
4489
{
4490
    /* Atkinson's Method
4491
     *          curr    1/8    1/8
4492
     *   1/8     1/8    1/8
4493
     *           1/8
4494
     */
4495
    int pos;
4496
    int sign;
4497

4498
    pos = y * width + x;
28,352,460✔
4499
    sign = direction >= 0 ? 1 : -1;
28,352,460!
4500

4501
    if (x + sign >= 0 && x + sign < width) {
28,352,460!
4502
        error_diffuse_fast(data, pos + sign, depth, error, 1, 8);
28,296,945✔
4503
    }
9,458,715✔
4504
    if (x + sign * 2 >= 0 && x + sign * 2 < width) {
28,352,460!
4505
        error_diffuse_fast(data, pos + sign * 2, depth, error, 1, 8);
28,241,430✔
4506
    }
9,440,010✔
4507
    if (y < height - 1) {
28,352,460✔
4508
        int row;
4509

4510
        row = pos + width;
28,278,756✔
4511
        if (x - sign >= 0 && x - sign < width) {
28,278,756!
4512
            error_diffuse_fast(data,
37,657,392✔
4513
                               row + (-sign),
9,433,950✔
4514
                               depth, error, 1, 8);
9,433,950✔
4515
        }
9,433,950✔
4516
        error_diffuse_fast(data, row, depth, error, 1, 8);
28,278,756✔
4517
        if (x + sign >= 0 && x + sign < width) {
28,278,756!
4518
            error_diffuse_fast(data,
37,657,392✔
4519
                               row + sign,
9,433,950✔
4520
                               depth, error, 1, 8);
9,433,950✔
4521
        }
9,433,950✔
4522
    }
9,452,586✔
4523
    if (y < height - 2) {
28,352,460✔
4524
        error_diffuse_fast(data, pos + width * 2, depth, error, 1, 8);
28,205,052✔
4525
    }
9,427,752✔
4526
}
28,352,460✔
4527

4528

4529
static void
4530
diffuse_atkinson_carry(int32_t *carry_curr, int32_t *carry_next,
×
4531
                       int32_t *carry_far, int width, int height,
4532
                       int depth, int x, int y, int32_t error,
4533
                       int direction, int channel)
4534
{
4535
    /* Atkinson's Method
4536
     *          curr    1/8    1/8
4537
     *   1/8     1/8    1/8
4538
     *           1/8
4539
     */
4540
    int sign;
4541
    int32_t term;
4542

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

4547
    term = diffuse_fixed_term(error, 1, 8);
×
4548
    sign = direction >= 0 ? 1 : -1;
×
4549
    if (x + sign >= 0 && x + sign < width) {
×
4550
        size_t base;
4551

4552
        base = ((size_t)(x + sign) * (size_t)depth)
×
4553
             + (size_t)channel;
×
4554
        carry_curr[base] += term;
×
4555
    }
4556
    if (x + sign * 2 >= 0 && x + sign * 2 < width) {
×
4557
        size_t base;
4558

4559
        base = ((size_t)(x + sign * 2) * (size_t)depth)
×
4560
             + (size_t)channel;
×
4561
        carry_curr[base] += term;
×
4562
    }
4563
    if (y + 1 < height) {
×
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_next[base] += term;
×
4570
        }
4571
        {
4572
            size_t base;
4573

4574
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
4575
            carry_next[base] += term;
×
4576
        }
4577
        if (x + sign >= 0 && x + sign < width) {
×
4578
            size_t base;
4579

4580
            base = ((size_t)(x + sign) * (size_t)depth)
×
4581
                 + (size_t)channel;
×
4582
            carry_next[base] += term;
×
4583
        }
4584
    }
4585
    if (y + 2 < height) {
×
4586
        size_t base;
4587

4588
        base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
4589
        carry_far[base] += term;
×
4590
    }
4591
}
4592

4593

4594
static void
4595
diffuse_jajuni(unsigned char *data, int width, int height,
396,900✔
4596
               int x, int y, int depth, int error, int direction)
4597
{
4598
    /* Jarvis, Judice & Ninke Method
4599
     *                  curr    7/48    5/48
4600
     *  3/48    5/48    7/48    5/48    3/48
4601
     *  1/48    3/48    5/48    3/48    1/48
4602
     */
4603
    int pos;
4604
    int sign;
4605
    static const int row0_offsets[] = { 1, 2 };
4606
    static const int row0_weights[] = { 7, 5 };
4607
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4608
    static const int row1_weights[] = { 3, 5, 7, 5, 3 };
4609
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
4610
    static const int row2_weights[] = { 1, 3, 5, 3, 1 };
4611
    int i;
4612

4613
    pos = y * width + x;
396,900✔
4614
    sign = direction >= 0 ? 1 : -1;
396,900!
4615

4616
    for (i = 0; i < 2; ++i) {
1,190,700✔
4617
        int neighbor;
4618

4619
        neighbor = x + sign * row0_offsets[i];
793,800✔
4620
        if (neighbor < 0 || neighbor >= width) {
793,800!
4621
            continue;
5,670✔
4622
        }
4623
        error_diffuse_precise(data,
1,050,840✔
4624
                              pos + (neighbor - x),
788,130✔
4625
                              depth, error,
262,710✔
4626
                              row0_weights[i], 48);
788,130✔
4627
    }
262,710✔
4628
    if (y < height - 1) {
396,900✔
4629
        int row;
4630

4631
        row = pos + width;
395,010✔
4632
        for (i = 0; i < 5; ++i) {
2,370,060✔
4633
            int neighbor;
4634

4635
            neighbor = x + sign * row1_offsets[i];
1,975,050✔
4636
            if (neighbor < 0 || neighbor >= width) {
1,975,050✔
4637
                continue;
11,286✔
4638
            }
4639
            error_diffuse_precise(data,
2,618,352✔
4640
                                  row + (neighbor - x),
1,963,764✔
4641
                                  depth, error,
654,588✔
4642
                                  row1_weights[i], 48);
1,963,764✔
4643
        }
654,588✔
4644
    }
131,670✔
4645
    if (y < height - 2) {
396,900✔
4646
        int row;
4647

4648
        row = pos + width * 2;
393,120✔
4649
        for (i = 0; i < 5; ++i) {
2,358,720✔
4650
            int neighbor;
4651

4652
            neighbor = x + sign * row2_offsets[i];
1,965,600✔
4653
            if (neighbor < 0 || neighbor >= width) {
1,965,600✔
4654
                continue;
11,232✔
4655
            }
4656
            error_diffuse_precise(data,
2,605,824✔
4657
                                  row + (neighbor - x),
1,954,368✔
4658
                                  depth, error,
651,456✔
4659
                                  row2_weights[i], 48);
1,954,368✔
4660
        }
651,456✔
4661
    }
131,040✔
4662
}
396,900✔
4663

4664

4665
static void
4666
diffuse_jajuni_carry(int32_t *carry_curr, int32_t *carry_next,
×
4667
                     int32_t *carry_far, int width, int height,
4668
                     int depth, int x, int y, int32_t error,
4669
                     int direction, int channel)
4670
{
4671
    /* Jarvis, Judice & Ninke Method
4672
     *                  curr    7/48    5/48
4673
     *  3/48    5/48    7/48    5/48    3/48
4674
     *  1/48    3/48    5/48    3/48    1/48
4675
     */
4676
    static const int row0_offsets[] = { 1, 2 };
4677
    static const int row0_weights[] = { 7, 5 };
4678
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4679
    static const int row1_weights[] = { 3, 5, 7, 5, 3 };
4680
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
4681
    static const int row2_weights[] = { 1, 3, 5, 3, 1 };
4682
    int sign;
4683
    int i;
4684

4685
    if (error == 0) {
×
4686
        return;
×
4687
    }
4688

4689
    sign = direction >= 0 ? 1 : -1;
×
4690
    for (i = 0; i < 2; ++i) {
×
4691
        int neighbor;
4692
        int32_t term;
4693

4694
        neighbor = x + sign * row0_offsets[i];
×
4695
        if (neighbor < 0 || neighbor >= width) {
×
4696
            continue;
×
4697
        }
4698
        term = diffuse_fixed_term(error, row0_weights[i], 48);
×
4699
        carry_curr[((size_t)neighbor * (size_t)depth)
×
4700
                   + (size_t)channel] += term;
×
4701
    }
4702
    if (y + 1 < height) {
×
4703
        for (i = 0; i < 5; ++i) {
×
4704
            int neighbor;
4705
            int32_t term;
4706

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

4721
            neighbor = x + sign * row2_offsets[i];
×
4722
            if (neighbor < 0 || neighbor >= width) {
×
4723
                continue;
×
4724
            }
4725
            term = diffuse_fixed_term(error, row2_weights[i], 48);
×
4726
            carry_far[((size_t)neighbor * (size_t)depth)
×
4727
                      + (size_t)channel] += term;
×
4728
        }
4729
    }
4730
}
4731

4732

4733
static void
4734
diffuse_stucki(unsigned char *data, int width, int height,
119,700✔
4735
               int x, int y, int depth, int error, int direction)
4736
{
4737
    /* Stucki's Method
4738
     *                  curr    8/48    4/48
4739
     *  2/48    4/48    8/48    4/48    2/48
4740
     *  1/48    2/48    4/48    2/48    1/48
4741
     */
4742
    int pos;
4743
    int sign;
4744
    static const int row0_offsets[] = { 1, 2 };
4745
    static const int row0_num[] = { 1, 1 };
4746
    static const int row0_den[] = { 6, 12 };
4747
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4748
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
4749
    static const int row1_den[] = { 24, 12, 6, 12, 24 };
4750
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
4751
    static const int row2_num[] = { 1, 1, 1, 1, 1 };
4752
    static const int row2_den[] = { 48, 24, 12, 24, 48 };
4753
    int i;
4754

4755
    pos = y * width + x;
119,700✔
4756
    sign = direction >= 0 ? 1 : -1;
119,700!
4757

4758
    for (i = 0; i < 2; ++i) {
359,100✔
4759
        int neighbor;
4760

4761
        neighbor = x + sign * row0_offsets[i];
239,400✔
4762
        if (neighbor < 0 || neighbor >= width) {
239,400!
4763
            continue;
2,700✔
4764
        }
4765
        error_diffuse_precise(data,
315,600✔
4766
                              pos + (neighbor - x),
236,700✔
4767
                              depth, error,
78,900✔
4768
                              row0_num[i], row0_den[i]);
236,700✔
4769
    }
78,900✔
4770
    if (y < height - 1) {
119,700✔
4771
        int row;
4772

4773
        row = pos + width;
118,503✔
4774
        for (i = 0; i < 5; ++i) {
711,018✔
4775
            int neighbor;
4776

4777
            neighbor = x + sign * row1_offsets[i];
592,515✔
4778
            if (neighbor < 0 || neighbor >= width) {
592,515✔
4779
                continue;
5,346✔
4780
            }
4781
            error_diffuse_precise(data,
782,892✔
4782
                                  row + (neighbor - x),
587,169✔
4783
                                  depth, error,
195,723✔
4784
                                  row1_num[i], row1_den[i]);
587,169✔
4785
        }
195,723✔
4786
    }
39,501✔
4787
    if (y < height - 2) {
119,700✔
4788
        int row;
4789

4790
        row = pos + width * 2;
117,306✔
4791
        for (i = 0; i < 5; ++i) {
703,836✔
4792
            int neighbor;
4793

4794
            neighbor = x + sign * row2_offsets[i];
586,530✔
4795
            if (neighbor < 0 || neighbor >= width) {
586,530✔
4796
                continue;
5,292✔
4797
            }
4798
            error_diffuse_precise(data,
774,984✔
4799
                                  row + (neighbor - x),
581,238✔
4800
                                  depth, error,
193,746✔
4801
                                  row2_num[i], row2_den[i]);
581,238✔
4802
        }
193,746✔
4803
    }
39,102✔
4804
}
119,700✔
4805

4806

4807
static void
4808
diffuse_stucki_carry(int32_t *carry_curr, int32_t *carry_next,
×
4809
                     int32_t *carry_far, int width, int height,
4810
                     int depth, int x, int y, int32_t error,
4811
                     int direction, int channel)
4812
{
4813
    /* Stucki's Method
4814
     *                  curr    8/48    4/48
4815
     *  2/48    4/48    8/48    4/48    2/48
4816
     *  1/48    2/48    4/48    2/48    1/48
4817
     */
4818
    static const int row0_offsets[] = { 1, 2 };
4819
    static const int row0_num[] = { 1, 1 };
4820
    static const int row0_den[] = { 6, 12 };
4821
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4822
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
4823
    static const int row1_den[] = { 24, 12, 6, 12, 24 };
4824
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
4825
    static const int row2_num[] = { 1, 1, 1, 1, 1 };
4826
    static const int row2_den[] = { 48, 24, 12, 24, 48 };
4827
    int sign;
4828
    int i;
4829

4830
    if (error == 0) {
×
4831
        return;
×
4832
    }
4833

4834
    sign = direction >= 0 ? 1 : -1;
×
4835
    for (i = 0; i < 2; ++i) {
×
4836
        int neighbor;
4837
        int32_t term;
4838

4839
        neighbor = x + sign * row0_offsets[i];
×
4840
        if (neighbor < 0 || neighbor >= width) {
×
4841
            continue;
×
4842
        }
4843
        term = diffuse_fixed_term(error, row0_num[i], row0_den[i]);
×
4844
        carry_curr[((size_t)neighbor * (size_t)depth)
×
4845
                   + (size_t)channel] += term;
×
4846
    }
4847
    if (y + 1 < height) {
×
4848
        for (i = 0; i < 5; ++i) {
×
4849
            int neighbor;
4850
            int32_t term;
4851

4852
            neighbor = x + sign * row1_offsets[i];
×
4853
            if (neighbor < 0 || neighbor >= width) {
×
4854
                continue;
×
4855
            }
4856
            term = diffuse_fixed_term(error, row1_num[i], row1_den[i]);
×
4857
            carry_next[((size_t)neighbor * (size_t)depth)
×
4858
                       + (size_t)channel] += term;
×
4859
        }
4860
    }
4861
    if (y + 2 < height) {
×
4862
        for (i = 0; i < 5; ++i) {
×
4863
            int neighbor;
4864
            int32_t term;
4865

4866
            neighbor = x + sign * row2_offsets[i];
×
4867
            if (neighbor < 0 || neighbor >= width) {
×
4868
                continue;
×
4869
            }
4870
            term = diffuse_fixed_term(error, row2_num[i], row2_den[i]);
×
4871
            carry_far[((size_t)neighbor * (size_t)depth)
×
4872
                      + (size_t)channel] += term;
×
4873
        }
4874
    }
4875
}
4876

4877

4878
static void
4879
diffuse_burkes(unsigned char *data, int width, int height,
67,500✔
4880
               int x, int y, int depth, int error, int direction)
4881
{
4882
    /* Burkes' Method
4883
     *                  curr    4/16    2/16
4884
     *  1/16    2/16    4/16    2/16    1/16
4885
     */
4886
    int pos;
4887
    int sign;
4888
    static const int row0_offsets[] = { 1, 2 };
4889
    static const int row0_num[] = { 1, 1 };
4890
    static const int row0_den[] = { 4, 8 };
4891
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4892
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
4893
    static const int row1_den[] = { 16, 8, 4, 8, 16 };
4894
    int i;
4895

4896
    pos = y * width + x;
67,500✔
4897
    sign = direction >= 0 ? 1 : -1;
67,500!
4898

4899
    for (i = 0; i < 2; ++i) {
202,500✔
4900
        int neighbor;
4901

4902
        neighbor = x + sign * row0_offsets[i];
135,000✔
4903
        if (neighbor < 0 || neighbor >= width) {
135,000!
4904
            continue;
2,025✔
4905
        }
4906
        error_diffuse_normal(data,
177,300✔
4907
                             pos + (neighbor - x),
132,975✔
4908
                             depth, error,
44,325✔
4909
                             row0_num[i], row0_den[i]);
132,975✔
4910
    }
44,325✔
4911
    if (y < height - 1) {
67,500✔
4912
        int row;
4913

4914
        row = pos + width;
66,600✔
4915
        for (i = 0; i < 5; ++i) {
399,600✔
4916
            int neighbor;
4917

4918
            neighbor = x + sign * row1_offsets[i];
333,000✔
4919
            if (neighbor < 0 || neighbor >= width) {
333,000✔
4920
                continue;
3,996✔
4921
            }
4922
            error_diffuse_normal(data,
438,672✔
4923
                                 row + (neighbor - x),
329,004✔
4924
                                 depth, error,
109,668✔
4925
                                 row1_num[i], row1_den[i]);
329,004✔
4926
        }
109,668✔
4927
    }
22,200✔
4928
}
67,500✔
4929

4930
static void
4931
diffuse_burkes_carry(int32_t *carry_curr, int32_t *carry_next,
×
4932
                     int32_t *carry_far, int width, int height,
4933
                     int depth, int x, int y, int32_t error,
4934
                     int direction, int channel)
4935
{
4936
    /* Burkes' Method
4937
     *                  curr    4/16    2/16
4938
     *  1/16    2/16    4/16    2/16    1/16
4939
     */
4940
    static const int row0_offsets[] = { 1, 2 };
4941
    static const int row0_num[] = { 1, 1 };
4942
    static const int row0_den[] = { 4, 8 };
4943
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4944
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
4945
    static const int row1_den[] = { 16, 8, 4, 8, 16 };
4946
    int sign;
4947
    int i;
4948

4949
    /* unused */ (void) carry_far;
4950

4951
    if (error == 0) {
×
4952
        return;
×
4953
    }
4954

4955
    sign = direction >= 0 ? 1 : -1;
×
4956
    for (i = 0; i < 2; ++i) {
×
4957
        int neighbor;
4958
        int32_t term;
4959

4960
        neighbor = x + sign * row0_offsets[i];
×
4961
        if (neighbor < 0 || neighbor >= width) {
×
4962
            continue;
×
4963
        }
4964
        term = diffuse_fixed_term(error, row0_num[i], row0_den[i]);
×
4965
        carry_curr[((size_t)neighbor * (size_t)depth)
×
4966
                   + (size_t)channel] += term;
×
4967
    }
4968
    if (y + 1 < height) {
×
4969
        for (i = 0; i < 5; ++i) {
×
4970
            int neighbor;
4971
            int32_t term;
4972

4973
            neighbor = x + sign * row1_offsets[i];
×
4974
            if (neighbor < 0 || neighbor >= width) {
×
4975
                continue;
×
4976
            }
4977
            term = diffuse_fixed_term(error, row1_num[i], row1_den[i]);
×
4978
            carry_next[((size_t)neighbor * (size_t)depth)
×
4979
                       + (size_t)channel] += term;
×
4980
        }
4981
    }
4982
}
4983

4984
static void
4985
diffuse_sierra1(unsigned char *data, int width, int height,
×
4986
                int x, int y, int depth, int error, int direction)
4987
{
4988
    /* Sierra Lite Method
4989
     *          curr    2/4
4990
     *  1/4     1/4
4991
     */
4992
    static const int row0_offsets[] = { 1 };
4993
    static const int row0_num[] = { 1 };
4994
    static const int row0_den[] = { 2 };
4995
    static const int row1_offsets[] = { -1, 0 };
4996
    static const int row1_num[] = { 1, 1 };
4997
    static const int row1_den[] = { 4, 4 };
4998
    int pos;
4999
    int sign;
5000
    int i;
5001
    int neighbor;
5002
    int row;
5003

5004
    pos = y * width + x;
×
5005
    sign = direction >= 0 ? 1 : -1;
×
5006

5007
    for (i = 0; i < 1; ++i) {
×
5008
        neighbor = x + sign * row0_offsets[i];
×
5009
        if (neighbor < 0 || neighbor >= width) {
×
5010
            continue;
×
5011
        }
5012
        error_diffuse_normal(data,
×
5013
                             pos + (neighbor - x),
×
5014
                             depth, error,
5015
                             row0_num[i], row0_den[i]);
×
5016
    }
5017
    if (y < height - 1) {
×
5018
        row = pos + width;
×
5019
        for (i = 0; i < 2; ++i) {
×
5020
            neighbor = x + sign * row1_offsets[i];
×
5021
            if (neighbor < 0 || neighbor >= width) {
×
5022
                continue;
×
5023
            }
5024
            error_diffuse_normal(data,
×
5025
                                 row + (neighbor - x),
×
5026
                                 depth, error,
5027
                                 row1_num[i], row1_den[i]);
×
5028
        }
5029
    }
5030
}
×
5031

5032

5033
static void
5034
diffuse_sierra1_carry(int32_t *carry_curr, int32_t *carry_next,
×
5035
                      int32_t *carry_far, int width, int height,
5036
                      int depth, int x, int y, int32_t error,
5037
                      int direction, int channel)
5038
{
5039
    /* Sierra Lite Method
5040
     *          curr    2/4
5041
     *  1/4     1/4
5042
     */
5043
    static const int row0_offsets[] = { 1 };
5044
    static const int row0_num[] = { 1 };
5045
    static const int row0_den[] = { 2 };
5046
    static const int row1_offsets[] = { -1, 0 };
5047
    static const int row1_num[] = { 1, 1 };
5048
    static const int row1_den[] = { 4, 4 };
5049
    int sign;
5050
    int i;
5051
    int neighbor;
5052
    int32_t term;
5053

5054
    /* unused */ (void) carry_far;
5055

5056
    if (error == 0) {
×
5057
        return;
×
5058
    }
5059

5060
    sign = direction >= 0 ? 1 : -1;
×
5061
    for (i = 0; i < 1; ++i) {
×
5062
        neighbor = x + sign * row0_offsets[i];
×
5063
        if (neighbor < 0 || neighbor >= width) {
×
5064
            continue;
×
5065
        }
5066
        term = diffuse_fixed_term(error, row0_num[i], row0_den[i]);
×
5067
        carry_curr[((size_t)neighbor * (size_t)depth)
×
5068
                   + (size_t)channel] += term;
×
5069
    }
5070
    if (y + 1 < height) {
×
5071
        for (i = 0; i < 2; ++i) {
×
5072
            neighbor = x + sign * row1_offsets[i];
×
5073
            if (neighbor < 0 || neighbor >= width) {
×
5074
                continue;
×
5075
            }
5076
            term = diffuse_fixed_term(error, row1_num[i], row1_den[i]);
×
5077
            carry_next[((size_t)neighbor * (size_t)depth)
×
5078
                       + (size_t)channel] += term;
×
5079
        }
5080
    }
5081
}
5082

5083

5084
static void
5085
diffuse_sierra2(unsigned char *data, int width, int height,
×
5086
                int x, int y, int depth, int error, int direction)
5087
{
5088
    /* Sierra Two-row Method
5089
     *                  curr    4/32    3/32
5090
     *  1/32    2/32    3/32    2/32    1/32
5091
     *                  2/32    3/32    2/32
5092
     */
5093
    static const int row0_offsets[] = { 1, 2 };
5094
    static const int row0_num[] = { 4, 3 };
5095
    static const int row0_den[] = { 32, 32 };
5096
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
5097
    static const int row1_num[] = { 1, 2, 3, 2, 1 };
5098
    static const int row1_den[] = { 32, 32, 32, 32, 32 };
5099
    static const int row2_offsets[] = { -1, 0, 1 };
5100
    static const int row2_num[] = { 2, 3, 2 };
5101
    static const int row2_den[] = { 32, 32, 32 };
5102
    int pos;
5103
    int sign;
5104
    int i;
5105
    int neighbor;
5106
    int row;
5107

5108
    pos = y * width + x;
×
5109
    sign = direction >= 0 ? 1 : -1;
×
5110

5111
    for (i = 0; i < 2; ++i) {
×
5112
        neighbor = x + sign * row0_offsets[i];
×
5113
        if (neighbor < 0 || neighbor >= width) {
×
5114
            continue;
×
5115
        }
5116
        error_diffuse_precise(data,
×
5117
                              pos + (neighbor - x),
×
5118
                              depth, error,
5119
                              row0_num[i], row0_den[i]);
×
5120
    }
5121
    if (y < height - 1) {
×
5122
        row = pos + width;
×
5123
        for (i = 0; i < 5; ++i) {
×
5124
            neighbor = x + sign * row1_offsets[i];
×
5125
            if (neighbor < 0 || neighbor >= width) {
×
5126
                continue;
×
5127
            }
5128
            error_diffuse_precise(data,
×
5129
                                  row + (neighbor - x),
×
5130
                                  depth, error,
5131
                                  row1_num[i], row1_den[i]);
×
5132
        }
5133
    }
5134
    if (y < height - 2) {
×
5135
        row = pos + width * 2;
×
5136
        for (i = 0; i < 3; ++i) {
×
5137
            neighbor = x + sign * row2_offsets[i];
×
5138
            if (neighbor < 0 || neighbor >= width) {
×
5139
                continue;
×
5140
            }
5141
            error_diffuse_precise(data,
×
5142
                                  row + (neighbor - x),
×
5143
                                  depth, error,
5144
                                  row2_num[i], row2_den[i]);
×
5145
        }
5146
    }
5147
}
×
5148

5149

5150
static void
5151
diffuse_sierra2_carry(int32_t *carry_curr, int32_t *carry_next,
×
5152
                      int32_t *carry_far, int width, int height,
5153
                      int depth, int x, int y, int32_t error,
5154
                      int direction, int channel)
5155
{
5156
    /* Sierra Two-row Method
5157
     *                  curr    4/32    3/32
5158
     *  1/32    2/32    3/32    2/32    1/32
5159
     *                  2/32    3/32    2/32
5160
     */
5161
    static const int row0_offsets[] = { 1, 2 };
5162
    static const int row0_num[] = { 4, 3 };
5163
    static const int row0_den[] = { 32, 32 };
5164
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
5165
    static const int row1_num[] = { 1, 2, 3, 2, 1 };
5166
    static const int row1_den[] = { 32, 32, 32, 32, 32 };
5167
    static const int row2_offsets[] = { -1, 0, 1 };
5168
    static const int row2_num[] = { 2, 3, 2 };
5169
    static const int row2_den[] = { 32, 32, 32 };
5170
    int sign;
5171
    int i;
5172
    int neighbor;
5173
    int32_t term;
5174

5175
    if (error == 0) {
×
5176
        return;
×
5177
    }
5178

5179
    sign = direction >= 0 ? 1 : -1;
×
5180
    for (i = 0; i < 2; ++i) {
×
5181
        neighbor = x + sign * row0_offsets[i];
×
5182
        if (neighbor < 0 || neighbor >= width) {
×
5183
            continue;
×
5184
        }
5185
        term = diffuse_fixed_term(error, row0_num[i], row0_den[i]);
×
5186
        carry_curr[((size_t)neighbor * (size_t)depth)
×
5187
                   + (size_t)channel] += term;
×
5188
    }
5189
    if (y + 1 < height) {
×
5190
        for (i = 0; i < 5; ++i) {
×
5191
            neighbor = x + sign * row1_offsets[i];
×
5192
            if (neighbor < 0 || neighbor >= width) {
×
5193
                continue;
×
5194
            }
5195
            term = diffuse_fixed_term(error, row1_num[i], row1_den[i]);
×
5196
            carry_next[((size_t)neighbor * (size_t)depth)
×
5197
                       + (size_t)channel] += term;
×
5198
        }
5199
    }
5200
    if (y + 2 < height) {
×
5201
        for (i = 0; i < 3; ++i) {
×
5202
            neighbor = x + sign * row2_offsets[i];
×
5203
            if (neighbor < 0 || neighbor >= width) {
×
5204
                continue;
×
5205
            }
5206
            term = diffuse_fixed_term(error, row2_num[i], row2_den[i]);
×
5207
            carry_far[((size_t)neighbor * (size_t)depth)
×
5208
                      + (size_t)channel] += term;
×
5209
        }
5210
    }
5211
}
5212

5213

5214
static void
5215
diffuse_sierra3(unsigned char *data, int width, int height,
×
5216
                int x, int y, int depth, int error, int direction)
5217
{
5218
    /* Sierra-3 Method
5219
     *                  curr    5/32    3/32
5220
     *  2/32    4/32    5/32    4/32    2/32
5221
     *                  2/32    3/32    2/32
5222
     */
5223
    static const int row0_offsets[] = { 1, 2 };
5224
    static const int row0_num[] = { 5, 3 };
5225
    static const int row0_den[] = { 32, 32 };
5226
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
5227
    static const int row1_num[] = { 2, 4, 5, 4, 2 };
5228
    static const int row1_den[] = { 32, 32, 32, 32, 32 };
5229
    static const int row2_offsets[] = { -1, 0, 1 };
5230
    static const int row2_num[] = { 2, 3, 2 };
5231
    static const int row2_den[] = { 32, 32, 32 };
5232
    int pos;
5233
    int sign;
5234
    int i;
5235
    int neighbor;
5236
    int row;
5237

5238
    pos = y * width + x;
×
5239
    sign = direction >= 0 ? 1 : -1;
×
5240

5241
    for (i = 0; i < 2; ++i) {
×
5242
        neighbor = x + sign * row0_offsets[i];
×
5243
        if (neighbor < 0 || neighbor >= width) {
×
5244
            continue;
×
5245
        }
5246
        error_diffuse_precise(data,
×
5247
                              pos + (neighbor - x),
×
5248
                              depth, error,
5249
                              row0_num[i], row0_den[i]);
×
5250
    }
5251
    if (y < height - 1) {
×
5252
        row = pos + width;
×
5253
        for (i = 0; i < 5; ++i) {
×
5254
            neighbor = x + sign * row1_offsets[i];
×
5255
            if (neighbor < 0 || neighbor >= width) {
×
5256
                continue;
×
5257
            }
5258
            error_diffuse_precise(data,
×
5259
                                  row + (neighbor - x),
×
5260
                                  depth, error,
5261
                                  row1_num[i], row1_den[i]);
×
5262
        }
5263
    }
5264
    if (y < height - 2) {
×
5265
        row = pos + width * 2;
×
5266
        for (i = 0; i < 3; ++i) {
×
5267
            neighbor = x + sign * row2_offsets[i];
×
5268
            if (neighbor < 0 || neighbor >= width) {
×
5269
                continue;
×
5270
            }
5271
            error_diffuse_precise(data,
×
5272
                                  row + (neighbor - x),
×
5273
                                  depth, error,
5274
                                  row2_num[i], row2_den[i]);
×
5275
        }
5276
    }
5277
}
×
5278

5279

5280
static void
5281
diffuse_sierra3_carry(int32_t *carry_curr, int32_t *carry_next,
×
5282
                      int32_t *carry_far, int width, int height,
5283
                      int depth, int x, int y, int32_t error,
5284
                      int direction, int channel)
5285
{
5286
    /* Sierra-3 Method
5287
     *                  curr    5/32    3/32
5288
     *  2/32    4/32    5/32    4/32    2/32
5289
     *                  2/32    3/32    2/32
5290
     */
5291
    static const int row0_offsets[] = { 1, 2 };
5292
    static const int row0_num[] = { 5, 3 };
5293
    static const int row0_den[] = { 32, 32 };
5294
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
5295
    static const int row1_num[] = { 2, 4, 5, 4, 2 };
5296
    static const int row1_den[] = { 32, 32, 32, 32, 32 };
5297
    static const int row2_offsets[] = { -1, 0, 1 };
5298
    static const int row2_num[] = { 2, 3, 2 };
5299
    static const int row2_den[] = { 32, 32, 32 };
5300
    int sign;
5301
    int i;
5302
    int neighbor;
5303
    int32_t term;
5304

5305
    if (error == 0) {
×
5306
        return;
×
5307
    }
5308

5309
    sign = direction >= 0 ? 1 : -1;
×
5310
    for (i = 0; i < 2; ++i) {
×
5311
        neighbor = x + sign * row0_offsets[i];
×
5312
        if (neighbor < 0 || neighbor >= width) {
×
5313
            continue;
×
5314
        }
5315
        term = diffuse_fixed_term(error, row0_num[i], row0_den[i]);
×
5316
        carry_curr[((size_t)neighbor * (size_t)depth)
×
5317
                   + (size_t)channel] += term;
×
5318
    }
5319
    if (y + 1 < height) {
×
5320
        for (i = 0; i < 5; ++i) {
×
5321
            neighbor = x + sign * row1_offsets[i];
×
5322
            if (neighbor < 0 || neighbor >= width) {
×
5323
                continue;
×
5324
            }
5325
            term = diffuse_fixed_term(error, row1_num[i], row1_den[i]);
×
5326
            carry_next[((size_t)neighbor * (size_t)depth)
×
5327
                       + (size_t)channel] += term;
×
5328
        }
5329
    }
5330
    if (y + 2 < height) {
×
5331
        for (i = 0; i < 3; ++i) {
×
5332
            neighbor = x + sign * row2_offsets[i];
×
5333
            if (neighbor < 0 || neighbor >= width) {
×
5334
                continue;
×
5335
            }
5336
            term = diffuse_fixed_term(error, row2_num[i], row2_den[i]);
×
5337
            carry_far[((size_t)neighbor * (size_t)depth)
×
5338
                      + (size_t)channel] += term;
×
5339
        }
5340
    }
5341
}
5342

5343

5344
static float
5345
mask_a (int x, int y, int c)
×
5346
{
5347
    return ((((x + c * 67) + y * 236) * 119) & 255 ) / 128.0 - 1.0;
×
5348
}
5349

5350
static float
5351
mask_x (int x, int y, int c)
×
5352
{
5353
    return ((((x + c * 29) ^ y* 149) * 1234) & 511 ) / 256.0 - 1.0;
×
5354
}
5355

5356
/* lookup closest color from palette with "normal" strategy */
5357
static int
5358
lookup_normal(unsigned char const * const pixel,
849,900✔
5359
              int const depth,
5360
              unsigned char const * const palette,
5361
              int const reqcolor,
5362
              unsigned short * const cachetable,
5363
              int const complexion)
5364
{
5365
    int result;
5366
    int diff;
5367
    int r;
5368
    int i;
5369
    int n;
5370
    int distant;
5371

5372
    result = (-1);
849,900✔
5373
    diff = INT_MAX;
849,900✔
5374

5375
    /* don't use cachetable in 'normal' strategy */
5376
    (void) cachetable;
283,300✔
5377

5378
    for (i = 0; i < reqcolor; i++) {
15,309,900✔
5379
        distant = 0;
14,460,000✔
5380
        r = pixel[0] - palette[i * depth + 0];
14,460,000✔
5381
        distant += r * r * complexion;
14,460,000✔
5382
        for (n = 1; n < depth; ++n) {
43,380,000✔
5383
            r = pixel[n] - palette[i * depth + n];
28,920,000✔
5384
            distant += r * r;
28,920,000✔
5385
        }
9,640,000✔
5386
        if (distant < diff) {
14,460,000✔
5387
            diff = distant;
2,234,570✔
5388
            result = i;
2,234,570✔
5389
        }
744,404✔
5390
    }
4,820,000✔
5391

5392
    return result;
849,900✔
5393
}
5394

5395

5396
/*
5397
 * Shared fast lookup flow:
5398
 *
5399
 *   pixel --> quantize --> cuckoo cache --> palette index
5400
 */
5401
static int
5402
lookup_fast_common(unsigned char const *pixel,
40,077,958✔
5403
                   unsigned char const *palette,
5404
                   int reqcolor,
5405
                   unsigned short *cachetable,
5406
                   int complexion,
5407
                   struct histogram_control control)
5408
{
5409
    int result;
5410
    unsigned int hash;
5411
    int diff;
5412
    int i;
5413
    int distant;
5414
    struct cuckoo_table32 *table;
5415
    uint32_t *slot;
5416
    SIXELSTATUS status;
5417
    unsigned char const *entry;
5418
    unsigned char const *end;
5419
    int pixel0;
5420
    int pixel1;
5421
    int pixel2;
5422
    int delta;
5423

5424
    result = (-1);
40,077,958✔
5425
    diff = INT_MAX;
40,077,958✔
5426
    hash = computeHash(pixel, 3U, &control);
40,077,958✔
5427

5428
    table = (struct cuckoo_table32 *)cachetable;
40,077,958✔
5429
    if (table != NULL) {
40,077,958!
5430
        slot = cuckoo_table32_lookup(table, hash);
40,077,958✔
5431
        if (slot != NULL && *slot != 0U) {
40,077,958!
5432
            return (int)(*slot - 1U);
38,162,483✔
5433
        }
5434
    }
613,543✔
5435

5436
    entry = palette;
1,915,475✔
5437
    end = palette + (size_t)reqcolor * 3;
1,915,475✔
5438
    pixel0 = (int)pixel[0];
1,915,475✔
5439
    pixel1 = (int)pixel[1];
1,915,475✔
5440
    pixel2 = (int)pixel[2];
1,915,475✔
5441
    /*
5442
     * Palette traversal as RGB triplets keeps the stride linear:
5443
     *
5444
     *   i -> [R][G][B]
5445
     *        |  |  |
5446
     *        `--+--'
5447
     *           v
5448
     *         entry
5449
     */
5450
    for (i = 0; entry < end; ++i, entry += 3) {
360,360,099✔
5451
        delta = pixel0 - (int)entry[0];
358,444,624✔
5452
        distant = delta * delta * complexion;
358,444,624✔
5453
        delta = pixel1 - (int)entry[1];
358,444,624✔
5454
        distant += delta * delta;
358,444,624✔
5455
        delta = pixel2 - (int)entry[2];
358,444,624✔
5456
        distant += delta * delta;
358,444,624✔
5457
        if (distant < diff) {
358,444,624✔
5458
            diff = distant;
15,983,557✔
5459
            result = i;
15,983,557✔
5460
        }
5,029,727✔
5461
    }
114,439,102✔
5462

5463
    if (table != NULL && result >= 0) {
1,915,475!
5464
        status = cuckoo_table32_insert(table,
2,529,018✔
5465
                                       hash,
613,543✔
5466
                                       (uint32_t)(result + 1));
1,915,475✔
5467
        if (SIXEL_FAILED(status)) {
1,915,475!
5468
            /* ignore cache update failure */
5469
        }
5470
    }
613,543✔
5471

5472
    return result;
1,915,475✔
5473
}
19,005,818✔
5474

5475
/* lookup closest color from palette with "fast" strategy */
5476
static int
5477
lookup_fast(unsigned char const * const pixel,
40,077,958✔
5478
            int const depth,
5479
            unsigned char const * const palette,
5480
            int const reqcolor,
5481
            unsigned short * const cachetable,
5482
            int const complexion)
5483
{
5484
    struct histogram_control control;
5485

5486
    (void)depth;
19,005,818✔
5487

5488
    control = histogram_control_make(3U);
40,077,958✔
5489

5490
    return lookup_fast_common(pixel,
59,083,776✔
5491
                              palette,
19,005,818✔
5492
                              reqcolor,
19,005,818✔
5493
                              cachetable,
19,005,818✔
5494
                              complexion,
19,005,818✔
5495
                              control);
5496
}
5497

5498
static int
5499
lookup_fast_robinhood(unsigned char const * const pixel,
×
5500
                      int const depth,
5501
                      unsigned char const * const palette,
5502
                      int const reqcolor,
5503
                      unsigned short * const cachetable,
5504
                      int const complexion)
5505
{
5506
    struct histogram_control control;
5507

5508
    (void)depth;
5509

5510
    control = histogram_control_make_for_policy(3U,
×
5511
                                                SIXEL_LUT_POLICY_ROBINHOOD);
5512

5513
    return lookup_fast_common(pixel,
×
5514
                              palette,
5515
                              reqcolor,
5516
                              cachetable,
5517
                              complexion,
5518
                              control);
5519
}
5520

5521
static int
5522
lookup_fast_hopscotch(unsigned char const * const pixel,
×
5523
                      int const depth,
5524
                      unsigned char const * const palette,
5525
                      int const reqcolor,
5526
                      unsigned short * const cachetable,
5527
                      int const complexion)
5528
{
5529
    struct histogram_control control;
5530

5531
    (void)depth;
5532

5533
    control = histogram_control_make_for_policy(3U,
×
5534
                                                SIXEL_LUT_POLICY_HOPSCOTCH);
5535

5536
    return lookup_fast_common(pixel,
×
5537
                              palette,
5538
                              reqcolor,
5539
                              cachetable,
5540
                              complexion,
5541
                              control);
5542
}
5543

5544

5545
static int
5546
lookup_mono_darkbg(unsigned char const * const pixel,
1,730,520✔
5547
                   int const depth,
5548
                   unsigned char const * const palette,
5549
                   int const reqcolor,
5550
                   unsigned short * const cachetable,
5551
                   int const complexion)
5552
{
5553
    int n;
5554
    int distant;
5555

5556
    /* unused */ (void) palette;
576,840✔
5557
    /* unused */ (void) cachetable;
576,840✔
5558
    /* unused */ (void) complexion;
576,840✔
5559

5560
    distant = 0;
1,730,520✔
5561
    for (n = 0; n < depth; ++n) {
6,922,080✔
5562
        distant += pixel[n];
5,191,560✔
5563
    }
1,730,520✔
5564
    return distant >= 128 * reqcolor ? 1: 0;
1,730,520✔
5565
}
5566

5567

5568
static int
5569
lookup_mono_lightbg(unsigned char const * const pixel,
810,000✔
5570
                    int const depth,
5571
                    unsigned char const * const palette,
5572
                    int const reqcolor,
5573
                    unsigned short * const cachetable,
5574
                    int const complexion)
5575
{
5576
    int n;
5577
    int distant;
5578

5579
    /* unused */ (void) palette;
270,000✔
5580
    /* unused */ (void) cachetable;
270,000✔
5581
    /* unused */ (void) complexion;
270,000✔
5582

5583
    distant = 0;
810,000✔
5584
    for (n = 0; n < depth; ++n) {
3,240,000✔
5585
        distant += pixel[n];
2,430,000✔
5586
    }
810,000✔
5587
    return distant < 128 * reqcolor ? 1: 0;
810,000✔
5588
}
5589

5590

5591
static SIXELSTATUS
5592
build_palette_kmeans(unsigned char **result,
×
5593
                     unsigned char const *data,
5594
                     unsigned int length,
5595
                     unsigned int depth,
5596
                     unsigned int reqcolors,
5597
                     unsigned int *ncolors,
5598
                     unsigned int *origcolors,
5599
                     int quality_mode,
5600
                     int force_palette,
5601
                     int final_merge_mode,
5602
                     sixel_allocator_t *allocator)
5603
{
5604
    SIXELSTATUS status;
5605
    unsigned int channels;
5606
    unsigned int pixel_count;
5607
    unsigned int sample_limit;
5608
    unsigned int sample_cap;
5609
    unsigned int valid_seen;
5610
    unsigned int sample_count;
5611
    unsigned int k;
5612
    unsigned int index;
5613
    unsigned int channel;
5614
    unsigned int center_index;
5615
    unsigned int sample_index;
5616
    unsigned int replace;
5617
    unsigned int max_iterations;
5618
    unsigned int iteration;
5619
    unsigned int best_index;
5620
    unsigned int old_cluster;
5621
    unsigned int farthest_index;
5622
    unsigned int fill;
5623
    unsigned int source;
5624
    unsigned int swap_temp;
5625
    unsigned int base;
5626
    unsigned int extra_component;
5627
    unsigned int *membership;
5628
    unsigned int *order;
5629
    unsigned char *samples;
5630
    unsigned char *palette;
5631
    unsigned char *new_palette;
5632
    double *centers;
5633
    double *distance_cache;
5634
    double total_weight;
5635
    double random_point;
5636
    double best_distance;
5637
    double distance;
5638
    double diff;
5639
    double update;
5640
    double farthest_distance;
5641
    unsigned long *counts;
5642
    unsigned long *accum;
5643
    unsigned long *channel_sum;
5644
    unsigned long rand_value;
5645
    int changed;
5646
    int apply_merge;
5647
    unsigned int overshoot;
5648
    unsigned int refine_iterations;
5649
    int cluster_total;
5650

5651
    status = SIXEL_BAD_ARGUMENT;
×
5652
    channels = depth;
×
5653
    pixel_count = 0U;
×
5654
    sample_limit = 50000U;
×
5655
    sample_cap = sample_limit;
×
5656
    valid_seen = 0U;
×
5657
    sample_count = 0U;
×
5658
    k = 0U;
×
5659
    index = 0U;
×
5660
    channel = 0U;
×
5661
    center_index = 0U;
×
5662
    sample_index = 0U;
×
5663
    replace = 0U;
×
5664
    max_iterations = 0U;
×
5665
    iteration = 0U;
×
5666
    best_index = 0U;
×
5667
    old_cluster = 0U;
×
5668
    farthest_index = 0U;
×
5669
    fill = 0U;
×
5670
    source = 0U;
×
5671
    swap_temp = 0U;
×
5672
    base = 0U;
×
5673
    extra_component = 0U;
×
5674
    membership = NULL;
×
5675
    order = NULL;
×
5676
    samples = NULL;
×
5677
    palette = NULL;
×
5678
    new_palette = NULL;
×
5679
    centers = NULL;
×
5680
    distance_cache = NULL;
×
5681
    counts = NULL;
×
5682
    accum = NULL;
×
5683
    channel_sum = NULL;
×
5684
    rand_value = 0UL;
×
5685
    total_weight = 0.0;
×
5686
    random_point = 0.0;
×
5687
    best_distance = 0.0;
×
5688
    distance = 0.0;
×
5689
    diff = 0.0;
×
5690
    update = 0.0;
×
5691
    farthest_distance = 0.0;
×
5692
    changed = 0;
×
NEW
5693
    apply_merge = 0;
×
NEW
5694
    overshoot = 0U;
×
NEW
5695
    refine_iterations = 0U;
×
NEW
5696
    cluster_total = 0;
×
5697

5698
    if (result != NULL) {
×
5699
        *result = NULL;
×
5700
    }
5701
    if (ncolors != NULL) {
×
5702
        *ncolors = 0U;
×
5703
    }
5704
    if (origcolors != NULL) {
×
5705
        *origcolors = 0U;
×
5706
    }
5707

5708
    if (channels != 3U && channels != 4U) {
×
5709
        goto end;
×
5710
    }
5711
    if (channels == 0U) {
×
5712
        goto end;
×
5713
    }
5714

5715
    pixel_count = length / channels;
×
5716
    if (pixel_count == 0U) {
×
5717
        goto end;
×
5718
    }
5719
    if (pixel_count < sample_cap) {
×
5720
        sample_cap = pixel_count;
×
5721
    }
5722
    if (sample_cap == 0U) {
×
5723
        sample_cap = 1U;
×
5724
    }
5725

5726
    samples = (unsigned char *)sixel_allocator_malloc(
×
5727
        allocator, (size_t)sample_cap * 3U);
×
5728
    if (samples == NULL) {
×
5729
        status = SIXEL_BAD_ALLOCATION;
×
5730
        goto end;
×
5731
    }
5732

5733
    /*
5734
     * Reservoir sampling keeps the distribution fair when the image is
5735
     * larger than our budget. Transparent pixels are skipped so that the
5736
     * solver only sees visible colors.
5737
     */
5738
    for (index = 0U; index < pixel_count; ++index) {
×
5739
        base = index * channels;
×
5740
        if (channels == 4U && data[base + 3U] == 0U) {
×
5741
            continue;
×
5742
        }
5743
        ++valid_seen;
×
5744
        if (sample_count < sample_cap) {
×
5745
            for (channel = 0U; channel < 3U; ++channel) {
×
5746
                samples[sample_count * 3U + channel] =
×
5747
                    data[base + channel];
×
5748
            }
5749
            ++sample_count;
×
5750
        } else {
5751
            rand_value = (unsigned long)rand();
×
5752
            replace = (unsigned int)(rand_value % valid_seen);
×
5753
            if (replace < sample_cap) {
×
5754
                for (channel = 0U; channel < 3U; ++channel) {
×
5755
                    samples[replace * 3U + channel] =
×
5756
                        data[base + channel];
×
5757
                }
5758
            }
5759
        }
5760
    }
5761

5762
    if (origcolors != NULL) {
×
5763
        *origcolors = valid_seen;
×
5764
    }
5765
    if (sample_count == 0U) {
×
5766
        goto end;
×
5767
    }
5768

5769
    if (reqcolors == 0U) {
×
5770
        reqcolors = 1U;
×
5771
    }
NEW
5772
    apply_merge = (final_merge_mode == SIXEL_FINAL_MERGE_AUTO
×
NEW
5773
                   || final_merge_mode == SIXEL_FINAL_MERGE_WARD);
×
NEW
5774
    refine_iterations = 2U;
×
NEW
5775
    overshoot = reqcolors;
×
5776
    /* Oversplit so the subsequent Ward merge has room to consolidate. */
NEW
5777
    if (apply_merge) {
×
NEW
5778
        overshoot = sixel_final_merge_target(reqcolors,
×
5779
                                             final_merge_mode);
NEW
5780
        quant_trace(stderr, "overshoot: %d\n", overshoot);
×
5781
    }
NEW
5782
    if (overshoot > sample_count) {
×
NEW
5783
        overshoot = sample_count;
×
5784
    }
NEW
5785
    k = overshoot;
×
5786
    if (k == 0U) {
×
5787
        goto end;
×
5788
    }
5789

5790
    centers = (double *)sixel_allocator_malloc(
×
5791
        allocator, (size_t)k * 3U * sizeof(double));
×
5792
    distance_cache = (double *)sixel_allocator_malloc(
×
5793
        allocator, (size_t)sample_count * sizeof(double));
×
5794
    counts = (unsigned long *)sixel_allocator_malloc(
×
5795
        allocator, (size_t)k * sizeof(unsigned long));
×
5796
    accum = (unsigned long *)sixel_allocator_malloc(
×
5797
        allocator, (size_t)k * 3U * sizeof(unsigned long));
×
5798
    membership = (unsigned int *)sixel_allocator_malloc(
×
5799
        allocator, (size_t)sample_count * sizeof(unsigned int));
×
5800
    if (centers == NULL || distance_cache == NULL || counts == NULL ||
×
5801
            accum == NULL || membership == NULL) {
×
5802
        status = SIXEL_BAD_ALLOCATION;
×
5803
        goto end;
×
5804
    }
5805

5806
    /*
5807
     * Seed the first center uniformly from the sampled set. Subsequent
5808
     * centers use k-means++ to favour distant samples.
5809
     */
5810
    rand_value = (unsigned long)rand();
×
5811
    replace = (unsigned int)(rand_value % sample_count);
×
5812
    for (channel = 0U; channel < 3U; ++channel) {
×
5813
        centers[channel] =
×
5814
            (double)samples[replace * 3U + channel];
×
5815
    }
5816
    for (sample_index = 0U; sample_index < sample_count; ++sample_index) {
×
5817
        distance = 0.0;
×
5818
        for (channel = 0U; channel < 3U; ++channel) {
×
5819
            diff = (double)samples[sample_index * 3U + channel]
×
5820
                - centers[channel];
×
5821
            distance += diff * diff;
×
5822
        }
5823
        distance_cache[sample_index] = distance;
×
5824
    }
5825

5826
    for (center_index = 1U; center_index < k; ++center_index) {
×
5827
        total_weight = 0.0;
×
5828
        for (sample_index = 0U; sample_index < sample_count;
×
5829
                ++sample_index) {
×
5830
            total_weight += distance_cache[sample_index];
×
5831
        }
5832
        random_point = 0.0;
×
5833
        if (total_weight > 0.0) {
×
5834
            random_point =
×
5835
                ((double)rand() / ((double)RAND_MAX + 1.0)) *
×
5836
                total_weight;
5837
        }
5838
        sample_index = 0U;
×
5839
        while (sample_index + 1U < sample_count &&
×
5840
               random_point > distance_cache[sample_index]) {
×
5841
            random_point -= distance_cache[sample_index];
×
5842
            ++sample_index;
×
5843
        }
5844
        for (channel = 0U; channel < 3U; ++channel) {
×
5845
            centers[center_index * 3U + channel] =
×
5846
                (double)samples[sample_index * 3U + channel];
×
5847
        }
5848
        for (index = 0U; index < sample_count; ++index) {
×
5849
            distance = 0.0;
×
5850
            for (channel = 0U; channel < 3U; ++channel) {
×
5851
                diff = (double)samples[index * 3U + channel]
×
5852
                    - centers[center_index * 3U + channel];
×
5853
                distance += diff * diff;
×
5854
            }
5855
            if (distance < distance_cache[index]) {
×
5856
                distance_cache[index] = distance;
×
5857
            }
5858
        }
5859
    }
5860

5861
    switch (quality_mode) {
×
5862
    case SIXEL_QUALITY_LOW:
5863
        max_iterations = 6U;
×
5864
        break;
×
5865
    case SIXEL_QUALITY_HIGH:
5866
        max_iterations = 24U;
×
5867
        break;
×
5868
    case SIXEL_QUALITY_FULL:
5869
        max_iterations = 48U;
×
5870
        break;
×
5871
    case SIXEL_QUALITY_HIGHCOLOR:
5872
        max_iterations = 24U;
×
5873
        break;
×
5874
    case SIXEL_QUALITY_AUTO:
×
5875
    default:
5876
        max_iterations = 12U;
×
5877
        break;
×
5878
    }
5879
    if (max_iterations == 0U) {
×
5880
        max_iterations = 1U;
×
5881
    }
5882
    if (max_iterations > 20U) {
×
5883
        /*
5884
         * The requirements cap the Lloyd refinement at twenty passes to
5885
         * keep runtime bounded even for demanding quality presets.
5886
         */
5887
        max_iterations = 20U;
×
5888
    }
5889

5890
    /*
5891
     * Lloyd refinement assigns samples to their nearest center and moves
5892
     * each center to the mean of its cluster. Empty clusters are reseeded
5893
     * using the farthest sample to improve stability.
5894
     */
5895
    for (iteration = 0U; iteration < max_iterations; ++iteration) {
×
5896
        for (index = 0U; index < k; ++index) {
×
5897
            counts[index] = 0UL;
×
5898
        }
5899
        for (index = 0U; index < k * 3U; ++index) {
×
5900
            accum[index] = 0UL;
×
5901
        }
5902
        for (sample_index = 0U; sample_index < sample_count;
×
5903
                ++sample_index) {
×
5904
            best_index = 0U;
×
5905
            distance = 0.0;
×
5906
            for (channel = 0U; channel < 3U; ++channel) {
×
5907
                diff = (double)samples[sample_index * 3U + channel]
×
5908
                    - centers[channel];
×
5909
                distance += diff * diff;
×
5910
            }
5911
            best_distance = distance;
×
5912
            for (center_index = 1U; center_index < k;
×
5913
                    ++center_index) {
×
5914
                distance = 0.0;
×
5915
                for (channel = 0U; channel < 3U; ++channel) {
×
5916
                    diff = (double)samples[sample_index * 3U + channel]
×
5917
                        - centers[center_index * 3U + channel];
×
5918
                    distance += diff * diff;
×
5919
                }
5920
                if (distance < best_distance) {
×
5921
                    best_distance = distance;
×
5922
                    best_index = center_index;
×
5923
                }
5924
            }
5925
            membership[sample_index] = best_index;
×
5926
            distance_cache[sample_index] = best_distance;
×
5927
            counts[best_index] += 1UL;
×
5928
            channel_sum = accum + (size_t)best_index * 3U;
×
5929
            for (channel = 0U; channel < 3U; ++channel) {
×
5930
                channel_sum[channel] +=
×
5931
                    (unsigned long)samples[sample_index * 3U + channel];
×
5932
            }
5933
        }
5934
        for (center_index = 0U; center_index < k; ++center_index) {
×
5935
            if (counts[center_index] != 0UL) {
×
5936
                continue;
×
5937
            }
5938
            farthest_distance = -1.0;
×
5939
            farthest_index = 0U;
×
5940
            for (sample_index = 0U; sample_index < sample_count;
×
5941
                    ++sample_index) {
×
5942
                if (distance_cache[sample_index] > farthest_distance) {
×
5943
                    farthest_distance = distance_cache[sample_index];
×
5944
                    farthest_index = sample_index;
×
5945
                }
5946
            }
5947
            old_cluster = membership[farthest_index];
×
5948
            if (counts[old_cluster] > 0UL) {
×
5949
                counts[old_cluster] -= 1UL;
×
5950
                channel_sum = accum + (size_t)old_cluster * 3U;
×
5951
                for (channel = 0U; channel < 3U; ++channel) {
×
5952
                    extra_component =
×
5953
                        (unsigned int)samples[farthest_index * 3U + channel];
×
5954
                    if (channel_sum[channel] >=
×
5955
                            (unsigned long)extra_component) {
×
5956
                        channel_sum[channel] -=
×
5957
                            (unsigned long)extra_component;
×
5958
                    } else {
5959
                        channel_sum[channel] = 0UL;
×
5960
                    }
5961
                }
5962
            }
5963
            membership[farthest_index] = center_index;
×
5964
            counts[center_index] = 1UL;
×
5965
            channel_sum = accum + (size_t)center_index * 3U;
×
5966
            for (channel = 0U; channel < 3U; ++channel) {
×
5967
                channel_sum[channel] =
×
5968
                    (unsigned long)samples[farthest_index * 3U + channel];
×
5969
            }
5970
            distance_cache[farthest_index] = 0.0;
×
5971
        }
5972
        changed = 0;
×
5973
        for (center_index = 0U; center_index < k; ++center_index) {
×
5974
            if (counts[center_index] == 0UL) {
×
5975
                continue;
×
5976
            }
5977
            channel_sum = accum + (size_t)center_index * 3U;
×
5978
            for (channel = 0U; channel < 3U; ++channel) {
×
5979
                update = (double)channel_sum[channel]
×
5980
                    / (double)counts[center_index];
×
5981
                diff = centers[center_index * 3U + channel] - update;
×
5982
                if (diff < 0.0) {
×
5983
                    diff = -diff;
×
5984
                }
5985
                if (diff > 0.5) {
×
5986
                    changed = 1;
×
5987
                }
5988
                centers[center_index * 3U + channel] = update;
×
5989
            }
5990
        }
5991
        if (!changed) {
×
5992
            break;
×
5993
        }
5994
    }
5995

NEW
5996
    if (apply_merge && k > reqcolors) {
×
5997
        /* Merge the provisional clusters and polish with a few Lloyd steps. */
NEW
5998
        cluster_total = (int)k;
×
NEW
5999
        sixel_merge_clusters_ward(counts, accum, 3U,
×
6000
                                  &cluster_total, (int)reqcolors);
NEW
6001
        if (cluster_total < 1) {
×
NEW
6002
            cluster_total = 1;
×
6003
        }
NEW
6004
        if ((unsigned int)cluster_total > reqcolors) {
×
NEW
6005
            cluster_total = (int)reqcolors;
×
6006
        }
NEW
6007
        k = (unsigned int)cluster_total;
×
NEW
6008
        if (k == 0U) {
×
NEW
6009
            k = 1U;
×
6010
        }
NEW
6011
        for (center_index = 0U; center_index < k; ++center_index) {
×
NEW
6012
            if (counts[center_index] == 0UL) {
×
NEW
6013
                counts[center_index] = 1UL;
×
6014
            }
NEW
6015
            channel_sum = accum + (size_t)center_index * 3U;
×
NEW
6016
            for (channel = 0U; channel < 3U; ++channel) {
×
NEW
6017
                centers[center_index * 3U + channel] =
×
NEW
6018
                    (double)channel_sum[channel]
×
NEW
6019
                    / (double)counts[center_index];
×
6020
            }
6021
        }
NEW
6022
        for (iteration = 0U; iteration < refine_iterations; ++iteration) {
×
NEW
6023
            for (index = 0U; index < k; ++index) {
×
NEW
6024
                counts[index] = 0UL;
×
6025
            }
NEW
6026
            for (index = 0U; index < k * 3U; ++index) {
×
NEW
6027
                accum[index] = 0UL;
×
6028
            }
NEW
6029
            for (sample_index = 0U; sample_index < sample_count;
×
NEW
6030
                    ++sample_index) {
×
NEW
6031
                best_index = 0U;
×
NEW
6032
                best_distance = 0.0;
×
NEW
6033
                for (channel = 0U; channel < 3U; ++channel) {
×
NEW
6034
                    diff = (double)samples[sample_index * 3U + channel]
×
NEW
6035
                        - centers[channel];
×
NEW
6036
                    best_distance += diff * diff;
×
6037
                }
NEW
6038
                for (center_index = 1U; center_index < k;
×
NEW
6039
                        ++center_index) {
×
NEW
6040
                    distance = 0.0;
×
NEW
6041
                    for (channel = 0U; channel < 3U; ++channel) {
×
NEW
6042
                        diff = (double)samples[sample_index * 3U + channel]
×
NEW
6043
                            - centers[center_index * 3U + channel];
×
NEW
6044
                        distance += diff * diff;
×
6045
                    }
NEW
6046
                    if (distance < best_distance) {
×
NEW
6047
                        best_distance = distance;
×
NEW
6048
                        best_index = center_index;
×
6049
                    }
6050
                }
NEW
6051
                membership[sample_index] = best_index;
×
NEW
6052
                distance_cache[sample_index] = best_distance;
×
NEW
6053
                counts[best_index] += 1UL;
×
NEW
6054
                channel_sum = accum + (size_t)best_index * 3U;
×
NEW
6055
                for (channel = 0U; channel < 3U; ++channel) {
×
NEW
6056
                    channel_sum[channel] +=
×
NEW
6057
                        (unsigned long)samples[sample_index * 3U + channel];
×
6058
                }
6059
            }
NEW
6060
            for (center_index = 0U; center_index < k; ++center_index) {
×
NEW
6061
                if (counts[center_index] != 0UL) {
×
NEW
6062
                    continue;
×
6063
                }
NEW
6064
                farthest_distance = -1.0;
×
NEW
6065
                farthest_index = 0U;
×
NEW
6066
                for (sample_index = 0U; sample_index < sample_count;
×
NEW
6067
                        ++sample_index) {
×
NEW
6068
                    if (distance_cache[sample_index] > farthest_distance) {
×
NEW
6069
                        farthest_distance = distance_cache[sample_index];
×
NEW
6070
                        farthest_index = sample_index;
×
6071
                    }
6072
                }
NEW
6073
                old_cluster = membership[farthest_index];
×
NEW
6074
                if (counts[old_cluster] > 0UL) {
×
NEW
6075
                    counts[old_cluster] -= 1UL;
×
NEW
6076
                    channel_sum = accum + (size_t)old_cluster * 3U;
×
NEW
6077
                    for (channel = 0U; channel < 3U; ++channel) {
×
NEW
6078
                        extra_component =
×
NEW
6079
                            (unsigned int)samples[farthest_index * 3U + channel];
×
NEW
6080
                        if (channel_sum[channel] >=
×
NEW
6081
                                (unsigned long)extra_component) {
×
NEW
6082
                            channel_sum[channel] -=
×
NEW
6083
                                (unsigned long)extra_component;
×
6084
                        } else {
NEW
6085
                            channel_sum[channel] = 0UL;
×
6086
                        }
6087
                    }
6088
                }
NEW
6089
                membership[farthest_index] = center_index;
×
NEW
6090
                counts[center_index] = 1UL;
×
NEW
6091
                channel_sum = accum + (size_t)center_index * 3U;
×
NEW
6092
                for (channel = 0U; channel < 3U; ++channel) {
×
NEW
6093
                    channel_sum[channel] =
×
NEW
6094
                        (unsigned long)samples[farthest_index * 3U + channel];
×
6095
                }
NEW
6096
                distance_cache[farthest_index] = 0.0;
×
6097
            }
NEW
6098
            changed = 0;
×
NEW
6099
            for (center_index = 0U; center_index < k; ++center_index) {
×
NEW
6100
                if (counts[center_index] == 0UL) {
×
NEW
6101
                    continue;
×
6102
                }
NEW
6103
                channel_sum = accum + (size_t)center_index * 3U;
×
NEW
6104
                for (channel = 0U; channel < 3U; ++channel) {
×
NEW
6105
                    update = (double)channel_sum[channel]
×
NEW
6106
                        / (double)counts[center_index];
×
NEW
6107
                    diff = centers[center_index * 3U + channel] - update;
×
NEW
6108
                    if (diff < 0.0) {
×
NEW
6109
                        diff = -diff;
×
6110
                    }
NEW
6111
                    if (diff > 0.5) {
×
NEW
6112
                        changed = 1;
×
6113
                    }
NEW
6114
                    centers[center_index * 3U + channel] = update;
×
6115
                }
6116
            }
NEW
6117
            if (!changed) {
×
NEW
6118
                break;
×
6119
            }
6120
        }
6121
    }
6122

6123
    /*
6124
     * Convert the floating point centers back into the byte palette that
6125
     * callers expect.
6126
     */
6127
    palette = (unsigned char *)sixel_allocator_malloc(
×
6128
        allocator, (size_t)k * 3U);
×
6129
    if (palette == NULL) {
×
6130
        status = SIXEL_BAD_ALLOCATION;
×
6131
        goto end;
×
6132
    }
6133
    for (center_index = 0U; center_index < k; ++center_index) {
×
6134
        for (channel = 0U; channel < 3U; ++channel) {
×
6135
            update = centers[center_index * 3U + channel];
×
6136
            if (update < 0.0) {
×
6137
                update = 0.0;
×
6138
            }
6139
            if (update > 255.0) {
×
6140
                update = 255.0;
×
6141
            }
6142
            palette[center_index * 3U + channel] =
×
6143
                (unsigned char)(update + 0.5);
×
6144
        }
6145
    }
6146

6147
    if (force_palette && k < reqcolors) {
×
6148
        /*
6149
         * Populate the tail of the palette by repeating the most frequent
6150
         * clusters so the caller still receives the requested palette size.
6151
         */
6152
        new_palette = (unsigned char *)sixel_allocator_malloc(
×
6153
            allocator, (size_t)reqcolors * 3U);
×
6154
        if (new_palette == NULL) {
×
6155
            status = SIXEL_BAD_ALLOCATION;
×
6156
            goto end;
×
6157
        }
6158
        for (index = 0U; index < k * 3U; ++index) {
×
6159
            new_palette[index] = palette[index];
×
6160
        }
6161
        order = (unsigned int *)sixel_allocator_malloc(
×
6162
            allocator, (size_t)k * sizeof(unsigned int));
×
6163
        if (order == NULL) {
×
6164
            status = SIXEL_BAD_ALLOCATION;
×
6165
            goto end;
×
6166
        }
6167
        for (index = 0U; index < k; ++index) {
×
6168
            order[index] = index;
×
6169
        }
6170
        for (index = 0U; index < k; ++index) {
×
6171
            for (center_index = index + 1U; center_index < k;
×
6172
                    ++center_index) {
×
6173
                if (counts[order[center_index]] >
×
6174
                        counts[order[index]]) {
×
6175
                    swap_temp = order[index];
×
6176
                    order[index] = order[center_index];
×
6177
                    order[center_index] = swap_temp;
×
6178
                }
6179
            }
6180
        }
6181
        fill = k;
×
6182
        source = 0U;
×
6183
        while (fill < reqcolors && k > 0U) {
×
6184
            center_index = order[source];
×
6185
            for (channel = 0U; channel < 3U; ++channel) {
×
6186
                new_palette[fill * 3U + channel] =
×
6187
                    palette[center_index * 3U + channel];
×
6188
            }
6189
            ++fill;
×
6190
            ++source;
×
6191
            if (source >= k) {
×
6192
                source = 0U;
×
6193
            }
6194
        }
6195
        sixel_allocator_free(allocator, palette);
×
6196
        palette = new_palette;
×
6197
        new_palette = NULL;
×
6198
        k = reqcolors;
×
6199
    }
6200

6201
    status = SIXEL_OK;
×
6202
    if (result != NULL) {
×
6203
        *result = palette;
×
6204
    } else {
6205
        palette = NULL;
×
6206
    }
6207
    if (ncolors != NULL) {
×
6208
        *ncolors = k;
×
6209
    }
6210

6211
end:
6212
    if (status != SIXEL_OK && palette != NULL) {
×
6213
        sixel_allocator_free(allocator, palette);
×
6214
    }
6215
    if (new_palette != NULL) {
×
6216
        sixel_allocator_free(allocator, new_palette);
×
6217
    }
6218
    if (order != NULL) {
×
6219
        sixel_allocator_free(allocator, order);
×
6220
    }
6221
    if (membership != NULL) {
×
6222
        sixel_allocator_free(allocator, membership);
×
6223
    }
6224
    if (accum != NULL) {
×
6225
        sixel_allocator_free(allocator, accum);
×
6226
    }
6227
    if (counts != NULL) {
×
6228
        sixel_allocator_free(allocator, counts);
×
6229
    }
6230
    if (distance_cache != NULL) {
×
6231
        sixel_allocator_free(allocator, distance_cache);
×
6232
    }
6233
    if (centers != NULL) {
×
6234
        sixel_allocator_free(allocator, centers);
×
6235
    }
6236
    if (samples != NULL) {
×
6237
        sixel_allocator_free(allocator, samples);
×
6238
    }
6239
    return status;
×
6240
}
6241

6242

6243
/* choose colors using median-cut method */
6244
SIXELSTATUS
6245
sixel_quant_make_palette(
260✔
6246
    unsigned char          /* out */ **result,
6247
    unsigned char const    /* in */  *data,
6248
    unsigned int           /* in */  length,
6249
    int                    /* in */  pixelformat,
6250
    unsigned int           /* in */  reqcolors,
6251
    unsigned int           /* in */  *ncolors,
6252
    unsigned int           /* in */  *origcolors,
6253
    int                    /* in */  methodForLargest,
6254
    int                    /* in */  methodForRep,
6255
    int                    /* in */  qualityMode,
6256
    int                    /* in */  force_palette,
6257
    int                    /* in */  use_reversible,
6258
    int                    /* in */  quantize_model,
6259
    int                    /* in */  final_merge_mode,
6260
    sixel_allocator_t      /* in */  *allocator)
6261
{
6262
    SIXELSTATUS status = SIXEL_FALSE;
260✔
6263
    unsigned int i;
6264
    unsigned int n;
6265
    int ret;
6266
    tupletable2 colormap;
6267
    unsigned int depth;
6268
    int result_depth;
6269

6270
    result_depth = sixel_helper_compute_depth(pixelformat);
260✔
6271
    if (result_depth <= 0) {
260!
6272
        *result = NULL;
×
6273
        goto end;
×
6274
    }
6275

6276
    depth = (unsigned int)result_depth;
260✔
6277

6278
    if (quantize_model == SIXEL_QUANTIZE_MODEL_KMEANS) {
260!
6279
        status = build_palette_kmeans(result,
×
6280
                                      data,
6281
                                      length,
6282
                                      depth,
6283
                                      reqcolors,
6284
                                      ncolors,
6285
                                      origcolors,
6286
                                      qualityMode,
6287
                                      force_palette,
6288
                                      final_merge_mode,
6289
                                      allocator);
6290
        if (SIXEL_SUCCEEDED(status)) {
×
6291
            if (use_reversible) {
×
6292
                sixel_quant_reversible_palette(*result,
×
6293
                                               *ncolors,
6294
                                               depth);
6295
            }
6296
            return status;
×
6297
        }
6298
    }
6299

6300
    ret = computeColorMapFromInput(data, length, depth,
378✔
6301
                                   reqcolors, methodForLargest,
118✔
6302
                                   methodForRep, qualityMode,
118✔
6303
                                   force_palette, use_reversible,
118✔
6304
                                   final_merge_mode,
118✔
6305
                                   &colormap, origcolors, allocator);
118✔
6306
    if (ret != 0) {
260!
6307
        *result = NULL;
×
6308
        goto end;
×
6309
    }
6310
    *ncolors = colormap.size;
260✔
6311
    quant_trace(stderr, "tupletable size: %d\n", *ncolors);
260✔
6312
    *result = (unsigned char *)sixel_allocator_malloc(allocator, *ncolors * depth);
260✔
6313
    for (i = 0; i < *ncolors; i++) {
17,038✔
6314
        for (n = 0; n < depth; ++n) {
67,112✔
6315
            (*result)[i * depth + n] = colormap.table[i]->tuple[n];
50,334✔
6316
        }
17,988✔
6317
    }
5,996✔
6318

6319
    if (use_reversible) {
260!
6320
        sixel_quant_reversible_palette(*result, *ncolors, depth);
×
6321
    }
6322

6323
    sixel_allocator_free(allocator, colormap.table);
260✔
6324

6325
    status = SIXEL_OK;
260✔
6326

6327
end:
142✔
6328
    return status;
260✔
6329
}
118✔
6330

6331

6332
/* apply color palette into specified pixel buffers */
6333
SIXELSTATUS
6334
sixel_quant_apply_palette(
285✔
6335
    sixel_index_t     /* out */ *result,
6336
    unsigned char     /* in */  *data,
6337
    int               /* in */  width,
6338
    int               /* in */  height,
6339
    int               /* in */  depth,
6340
    unsigned char     /* in */  *palette,
6341
    int               /* in */  reqcolor,
6342
    int               /* in */  methodForDiffuse,
6343
    int               /* in */  methodForScan,
6344
    int               /* in */  methodForCarry,
6345
    int               /* in */  foptimize,
6346
    int               /* in */  foptimize_palette,
6347
    int               /* in */  complexion,
6348
    unsigned short    /* in */  *cachetable,
6349
    int               /* in */  *ncolors,
6350
    sixel_allocator_t /* in */  *allocator)
6351
{
170✔
6352
#if _MSC_VER
6353
    enum { max_depth = 4 };
6354
#else
6355
    const size_t max_depth = 4;
285✔
6356
#endif
6357
    unsigned char copy[max_depth];
170✔
6358
    SIXELSTATUS status = SIXEL_FALSE;
285✔
6359
    int sum1, sum2;
6360
    int n;
6361
    unsigned short *indextable;
6362
    size_t cache_size;
6363
    int allocated_cache;
6364
    int use_cache;
6365
    int cache_policy;
6366
    unsigned char new_palette[SIXEL_PALETTE_MAX * 4];
6367
    unsigned short migration_map[SIXEL_PALETTE_MAX];
6368
    int (*f_lookup)(unsigned char const * const pixel,
6369
                    int const depth,
6370
                    unsigned char const * const palette,
6371
                    int const reqcolor,
6372
                    unsigned short * const cachetable,
6373
                    int const complexion);
6374
    int use_varerr;
6375
    int use_positional;
6376
    int carry_mode;
6377

6378
    /* check bad reqcolor */
6379
    if (reqcolor < 1) {
285!
6380
        status = SIXEL_BAD_ARGUMENT;
×
6381
        sixel_helper_set_additional_message(
×
6382
            "sixel_quant_apply_palette: "
6383
            "a bad argument is detected, reqcolor < 0.");
6384
        goto end;
×
6385
    }
6386

6387
    /* NOTE: diffuse_jajuni, diffuse_stucki, and diffuse_burkes reference at
6388
     * minimum the position pos + width * 1 - 2, so width must be at least 2
6389
     * to avoid underflow.
6390
     * On the other hand, diffuse_fs and diffuse_atkinson
6391
     * reference pos + width * 1 - 1, but since these functions are only called
6392
     * when width >= 1, they do not cause underflow.
6393
     */
6394
    use_varerr = (depth == 3
400✔
6395
                  && methodForDiffuse == SIXEL_DIFFUSE_LSO2);
285!
6396
    use_positional = (methodForDiffuse == SIXEL_DIFFUSE_A_DITHER
400✔
6397
                      || methodForDiffuse == SIXEL_DIFFUSE_X_DITHER);
285!
6398
    carry_mode = (methodForCarry == SIXEL_CARRY_ENABLE)
285✔
6399
               ? SIXEL_CARRY_ENABLE
6400
               : SIXEL_CARRY_DISABLE;
170!
6401

6402
    f_lookup = NULL;
285✔
6403
    if (reqcolor == 2) {
285✔
6404
        sum1 = 0;
19✔
6405
        sum2 = 0;
19✔
6406
        for (n = 0; n < depth; ++n) {
76✔
6407
            sum1 += palette[n];
57✔
6408
        }
21✔
6409
        for (n = depth; n < depth + depth; ++n) {
76✔
6410
            sum2 += palette[n];
57✔
6411
        }
21✔
6412
        if (sum1 == 0 && sum2 == 255 * 3) {
19!
6413
            f_lookup = lookup_mono_darkbg;
12✔
6414
        } else if (sum1 == 255 * 3 && sum2 == 0) {
11!
6415
            f_lookup = lookup_mono_lightbg;
3✔
6416
        }
1✔
6417
    }
7✔
6418
    if (f_lookup == NULL) {
285✔
6419
        if (foptimize && depth == 3) {
270!
6420
            f_lookup = lookup_fast;
264✔
6421
        } else {
108✔
6422
            f_lookup = lookup_normal;
6✔
6423
        }
6424
    }
110✔
6425

6426
    if (f_lookup == lookup_fast) {
285✔
6427
        if (histogram_lut_policy == SIXEL_LUT_POLICY_ROBINHOOD) {
264!
6428
            f_lookup = lookup_fast_robinhood;
×
6429
        } else if (histogram_lut_policy == SIXEL_LUT_POLICY_HOPSCOTCH) {
264!
6430
            f_lookup = lookup_fast_hopscotch;
×
6431
        }
6432
    }
108✔
6433

6434
    indextable = cachetable;
285✔
6435
    allocated_cache = 0;
285✔
6436
    cache_policy = SIXEL_LUT_POLICY_AUTO;
285✔
6437
    use_cache = 0;
285✔
6438
    if (f_lookup == lookup_fast) {
285✔
6439
        cache_policy = histogram_lut_policy;
264✔
6440
        use_cache = 1;
264✔
6441
    } else if (f_lookup == lookup_fast_robinhood) {
129!
6442
        cache_policy = SIXEL_LUT_POLICY_ROBINHOOD;
×
6443
        use_cache = 1;
×
6444
    } else if (f_lookup == lookup_fast_hopscotch) {
21!
6445
        cache_policy = SIXEL_LUT_POLICY_HOPSCOTCH;
×
6446
        use_cache = 1;
×
6447
    }
6448
    if (cache_policy == SIXEL_LUT_POLICY_AUTO) {
285!
6449
        cache_policy = SIXEL_LUT_POLICY_6BIT;
285✔
6450
    }
115✔
6451
    if (use_cache) {
285✔
6452
        if (cachetable == NULL) {
264!
6453
            status = sixel_quant_cache_prepare(&indextable,
×
6454
                                               &cache_size,
6455
                                               cache_policy,
6456
                                               reqcolor,
6457
                                               allocator);
6458
            if (SIXEL_FAILED(status)) {
×
6459
                quant_trace(stderr,
×
6460
                            "Unable to allocate lookup cache.\n");
6461
                goto end;
×
6462
            }
6463
            allocated_cache = 1;
×
6464
        } else {
6465
            sixel_quant_cache_clear(indextable, cache_policy);
264✔
6466
        }
6467
    }
108✔
6468

6469
    if (use_positional) {
285!
6470
        status = apply_palette_positional(result, data, width, height,
×
6471
                                          depth, palette, reqcolor,
6472
                                          methodForDiffuse, methodForScan,
6473
                                          foptimize_palette, f_lookup,
6474
                                          indextable, complexion, copy,
6475
                                          new_palette, migration_map,
6476
                                          ncolors);
6477
    } else if (use_varerr) {
285!
6478
        status = apply_palette_variable(result, data, width, height,
×
6479
                                        depth, palette, reqcolor,
6480
                                        methodForScan, foptimize_palette,
6481
                                        f_lookup, indextable, complexion,
6482
                                        new_palette, migration_map,
6483
                                        ncolors,
6484
                                        methodForDiffuse,
6485
                                        carry_mode);
6486
    } else {
6487
        status = apply_palette_fixed(result, data, width, height,
400✔
6488
                                     depth, palette, reqcolor,
115✔
6489
                                     methodForScan, foptimize_palette,
115✔
6490
                                     f_lookup, indextable, complexion,
115✔
6491
                                     new_palette, migration_map,
115✔
6492
                                     ncolors, methodForDiffuse,
115✔
6493
                                     carry_mode);
115✔
6494
    }
6495
    if (SIXEL_FAILED(status)) {
285!
6496
        goto end;
×
6497
    }
6498

6499
    if (allocated_cache) {
285!
6500
        sixel_quant_cache_release(indextable,
×
6501
                                  cache_policy,
6502
                                  allocator);
6503
    }
6504

6505
    status = SIXEL_OK;
285✔
6506

6507
end:
170✔
6508
    return status;
285✔
6509
}
6510

6511

6512
void
6513
sixel_quant_free_palette(
260✔
6514
    unsigned char       /* in */ *data,
6515
    sixel_allocator_t   /* in */ *allocator)
6516
{
6517
    sixel_allocator_free(allocator, data);
260✔
6518
}
260✔
6519

6520

6521
#if HAVE_TESTS
6522
static int
6523
test1(void)
×
6524
{
6525
    int nret = EXIT_FAILURE;
×
6526
    sample minval[1] = { 1 };
×
6527
    sample maxval[1] = { 2 };
×
6528
    unsigned int retval;
6529

6530
    retval = largestByLuminosity(minval, maxval, 1);
×
6531
    if (retval != 0) {
×
6532
        goto error;
×
6533
    }
6534
    nret = EXIT_SUCCESS;
×
6535

6536
error:
6537
    return nret;
×
6538
}
6539

6540

6541
int
6542
sixel_quant_tests_main(void)
×
6543
{
6544
    int nret = EXIT_FAILURE;
×
6545
    size_t i;
6546
    typedef int (* testcase)(void);
6547

6548
    static testcase const testcases[] = {
6549
        test1,
6550
    };
6551

6552
    for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) {
×
6553
        nret = testcases[i]();
×
6554
        if (nret != EXIT_SUCCESS) {
×
6555
            goto error;
×
6556
        }
6557
    }
6558

6559
    nret = EXIT_SUCCESS;
×
6560

6561
error:
6562
    return nret;
×
6563
}
6564
#endif  /* HAVE_TESTS */
6565

6566
/* emacs Local Variables:      */
6567
/* emacs mode: c               */
6568
/* emacs tab-width: 4          */
6569
/* emacs indent-tabs-mode: nil */
6570
/* emacs c-basic-offset: 4     */
6571
/* emacs End:                  */
6572
/* vim: set expandtab ts=4 sts=4 sw=4 : */
6573
/* 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

© 2025 Coveralls, Inc