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

saitoha / libsixel / 19017176680

02 Nov 2025 07:29PM UTC coverage: 44.655% (+0.09%) from 44.562%
19017176680

push

github

saitoha
perf: fixed slowdown in LUT palette application using Robin Hood and Hopscotch methods when the color count is low (p < 32)

6953 of 23348 branches covered (29.78%)

21 of 50 new or added lines in 1 file covered. (42.0%)

2 existing lines in 1 file now uncovered.

9984 of 22358 relevant lines covered (44.66%)

1090980.06 hits per line

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

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

106
static float mask_a(int x, int y, int c);
107
static float mask_x(int x, int y, int c);
108
static void diffuse_none(unsigned char *data, int width, int height,
109
                         int x, int y, int depth, int error, int direction);
110
static void diffuse_fs(unsigned char *data, int width, int height,
111
                       int x, int y, int depth, int error, int direction);
112
static void diffuse_atkinson(unsigned char *data, int width, int height,
113
                             int x, int y, int depth, int error,
114
                             int direction);
115
static void diffuse_jajuni(unsigned char *data, int width, int height,
116
                           int x, int y, int depth, int error,
117
                           int direction);
118
static void diffuse_stucki(unsigned char *data, int width, int height,
119
                           int x, int y, int depth, int error,
120
                           int direction);
121
static void diffuse_burkes(unsigned char *data, int width, int height,
122
                           int x, int y, int depth, int error,
123
                           int direction);
124
static void diffuse_sierra1(unsigned char *data, int width, int height,
125
                            int x, int y, int depth, int error,
126
                            int direction);
127
static void diffuse_sierra2(unsigned char *data, int width, int height,
128
                            int x, int y, int depth, int error,
129
                            int direction);
130
static void diffuse_sierra3(unsigned char *data, int width, int height,
131
                            int x, int y, int depth, int error,
132
                            int direction);
133
static void diffuse_lso1(unsigned char *data, int width, int height,
134
                         int x, int y, int depth, int error, int direction);
135
static void diffuse_none_carry(int32_t *carry_curr, int32_t *carry_next,
136
                               int32_t *carry_far, int width, int height,
137
                               int depth, int x, int y, int32_t error,
138
                               int direction, int channel);
139
static void diffuse_fs_carry(int32_t *carry_curr, int32_t *carry_next,
140
                             int32_t *carry_far, int width, int height,
141
                             int depth, int x, int y, int32_t error,
142
                             int direction, int channel);
143
static void diffuse_atkinson_carry(int32_t *carry_curr, int32_t *carry_next,
144
                                   int32_t *carry_far, int width,
145
                                   int height, int depth, int x, int y,
146
                                   int32_t error, int direction,
147
                                   int channel);
148
static void diffuse_jajuni_carry(int32_t *carry_curr, int32_t *carry_next,
149
                                 int32_t *carry_far, int width, int height,
150
                                 int depth, int x, int y, int32_t error,
151
                                 int direction, int channel);
152
static void diffuse_stucki_carry(int32_t *carry_curr, int32_t *carry_next,
153
                                 int32_t *carry_far, int width, int height,
154
                                 int depth, int x, int y, int32_t error,
155
                                 int direction, int channel);
156
static void diffuse_burkes_carry(int32_t *carry_curr, int32_t *carry_next,
157
                                 int32_t *carry_far, int width, int height,
158
                                 int depth, int x, int y, int32_t error,
159
                                 int direction, int channel);
160
static void diffuse_sierra1_carry(int32_t *carry_curr, int32_t *carry_next,
161
                                  int32_t *carry_far, int width, int height,
162
                                  int depth, int x, int y, int32_t error,
163
                                  int direction, int channel);
164
static void diffuse_sierra2_carry(int32_t *carry_curr, int32_t *carry_next,
165
                                  int32_t *carry_far, int width, int height,
166
                                  int depth, int x, int y, int32_t error,
167
                                  int direction, int channel);
168
static void diffuse_sierra3_carry(int32_t *carry_curr, int32_t *carry_next,
169
                                  int32_t *carry_far, int width, int height,
170
                                  int depth, int x, int y, int32_t error,
171
                                  int direction, int channel);
172
static void diffuse_lso1_carry(int32_t *carry_curr, int32_t *carry_next,
173
                               int32_t *carry_far, int width, int height,
174
                               int depth, int x, int y, int32_t error,
175
                               int direction, int channel);
176

177
static const int (*
178
lso2_table(void))[7]
×
179
{
180
#include "lso2.h"
181
    return var_coefs;
182
}
183

184
static const int (*
185
lso3_table(void))[7]
×
186
{
187
/* Auto-generated by gen_varcoefs.awk */
188
#include "lso3.h"
189
    return var_coefs;
190
}
191

192

193
#define VARERR_SCALE_SHIFT 12
194
#define VARERR_SCALE       (1 << VARERR_SCALE_SHIFT)
195
#define VARERR_ROUND       (1 << (VARERR_SCALE_SHIFT - 1))
196
#define VARERR_MAX_VALUE   (255 * VARERR_SCALE)
197

198
#if HAVE_DEBUG
199
#define quant_trace fprintf
200
#else
201
static inline void quant_trace(FILE *f, ...) { (void) f; }
202
#endif
203

204
/*****************************************************************************
205
 *
206
 * quantization
207
 *
208
 *****************************************************************************/
209

210
typedef struct box* boxVector;
211
struct box {
212
    unsigned int ind;
213
    unsigned int colors;
214
    unsigned int sum;
215
};
216

217
typedef unsigned long sample;
218
typedef sample * tuple;
219

220
struct tupleint {
221
    /* An ordered pair of a tuple value and an integer, such as you
222
       would find in a tuple table or tuple hash.
223
       Note that this is a variable length structure.
224
    */
225
    unsigned int value;
226
    sample tuple[1];
227
    /* This is actually a variable size array -- its size is the
228
       depth of the tuple in question.  Some compilers do not let us
229
       declare a variable length array.
230
    */
231
};
232
typedef struct tupleint ** tupletable;
233

234
typedef struct {
235
    unsigned int size;
236
    tupletable table;
237
} tupletable2;
238

239
static unsigned int compareplanePlane;
240
static tupletable2 const *force_palette_source;
241
    /* This is a parameter to compareplane().  We use this global variable
242
       so that compareplane() can be called by qsort(), to compare two
243
       tuples.  qsort() doesn't pass any arguments except the two tuples.
244
    */
245
static int
246
compareplane(const void * const arg1,
10,960,527✔
247
             const void * const arg2)
248
{
249
    int lhs, rhs;
250

251
    typedef const struct tupleint * const * const sortarg;
252
    sortarg comparandPP  = (sortarg) arg1;
10,960,527✔
253
    sortarg comparatorPP = (sortarg) arg2;
10,960,527✔
254
    lhs = (int)(*comparandPP)->tuple[compareplanePlane];
10,960,527✔
255
    rhs = (int)(*comparatorPP)->tuple[compareplanePlane];
10,960,527✔
256

257
    return lhs - rhs;
10,960,527✔
258
}
259

260

261
static int
262
sumcompare(const void * const b1, const void * const b2)
7,829,364✔
263
{
264
    return (int)((boxVector)b2)->sum - (int)((boxVector)b1)->sum;
7,829,364✔
265
}
266

267

268
static SIXELSTATUS
269
alloctupletable(
512✔
270
    tupletable          /* out */ *result,
271
    unsigned int const  /* in */  depth,
272
    unsigned int const  /* in */  size,
273
    sixel_allocator_t   /* in */  *allocator)
274
{
275
    SIXELSTATUS status = SIXEL_FALSE;
512✔
276
    enum { message_buffer_size = 256 };
277
    char message[message_buffer_size];
278
    int nwrite;
279
    unsigned int mainTableSize;
280
    unsigned int tupleIntSize;
281
    unsigned int allocSize;
282
    void * pool;
283
    tupletable tbl;
284
    unsigned int i;
285

286
    if (UINT_MAX / sizeof(struct tupleint) < size) {
512!
287
        nwrite = sixel_compat_snprintf(
×
288
            message,
289
            sizeof(message),
290
            "size %u is too big for arithmetic",
291
            size);
292
        if (nwrite > 0) {
×
293
            sixel_helper_set_additional_message(message);
×
294
        }
295
        status = SIXEL_RUNTIME_ERROR;
×
296
        goto end;
×
297
    }
298

299
    mainTableSize = size * sizeof(struct tupleint *);
512✔
300
    tupleIntSize = sizeof(struct tupleint) - sizeof(sample)
512✔
301
        + depth * sizeof(sample);
232✔
302

303
    /* To save the enormous amount of time it could take to allocate
304
       each individual tuple, we do a trick here and allocate everything
305
       as a single malloc block and suballocate internally.
306
    */
307
    if ((UINT_MAX - mainTableSize) / tupleIntSize < size) {
512!
308
        nwrite = sixel_compat_snprintf(
×
309
            message,
310
            sizeof(message),
311
            "size %u is too big for arithmetic",
312
            size);
313
        if (nwrite > 0) {
×
314
            sixel_helper_set_additional_message(message);
×
315
        }
316
        status = SIXEL_RUNTIME_ERROR;
×
317
        goto end;
×
318
    }
319

320
    allocSize = mainTableSize + size * tupleIntSize;
512✔
321

322
    pool = sixel_allocator_malloc(allocator, allocSize);
512✔
323
    if (pool == NULL) {
512!
324
        sixel_compat_snprintf(
×
325
            message,
326
            sizeof(message),
327
            "unable to allocate %u bytes for a %u-entry tuple table",
328
            allocSize,
329
            size);
330
        sixel_helper_set_additional_message(message);
×
331
        status = SIXEL_BAD_ALLOCATION;
×
332
        goto end;
×
333
    }
334
    tbl = (tupletable) pool;
512✔
335

336
    for (i = 0; i < size; ++i)
303,893✔
337
        tbl[i] = (struct tupleint *)
303,381✔
338
            ((char*)pool + mainTableSize + i * tupleIntSize);
303,381✔
339

340
    *result = tbl;
512✔
341

342
    status = SIXEL_OK;
512✔
343

344
end:
280✔
345
    return status;
512✔
346
}
347

348

349
/*
350
** Here is the fun part, the median-cut colormap generator.  This is based
351
** on Paul Heckbert's paper "Color Image Quantization for Frame Buffer
352
** Display", SIGGRAPH '82 Proceedings, page 297.
353
*/
354

355
static tupletable2
356
newColorMap(unsigned int const newcolors, unsigned int const depth, sixel_allocator_t *allocator)
66✔
357
{
358
    SIXELSTATUS status = SIXEL_FALSE;
66✔
359
    tupletable2 colormap;
360
    unsigned int i;
361

362
    colormap.size = 0;
66✔
363
    status = alloctupletable(&colormap.table, depth, newcolors, allocator);
66✔
364
    if (SIXEL_FAILED(status)) {
66!
365
        goto end;
×
366
    }
367
    if (colormap.table) {
88!
368
        for (i = 0; i < newcolors; ++i) {
13,062✔
369
            unsigned int plane;
370
            for (plane = 0; plane < depth; ++plane)
51,984✔
371
                colormap.table[i]->tuple[plane] = 0;
38,988✔
372
        }
4,332✔
373
        colormap.size = newcolors;
66✔
374
    }
22✔
375

376
end:
377
    return colormap;
66✔
378
}
379

380

381
static boxVector
382
newBoxVector(
66✔
383
    unsigned int const  /* in */ colors,
384
    unsigned int const  /* in */ sum,
385
    unsigned int const  /* in */ newcolors,
386
    sixel_allocator_t   /* in */ *allocator)
387
{
388
    boxVector bv;
389

390
    bv = (boxVector)sixel_allocator_malloc(allocator,
88✔
391
                                           sizeof(struct box) * (size_t)newcolors);
66✔
392
    if (bv == NULL) {
66!
393
        quant_trace(stderr, "out of memory allocating box vector table\n");
×
394
        return NULL;
×
395
    }
396

397
    /* Set up the initial box. */
398
    bv[0].ind = 0;
66✔
399
    bv[0].colors = colors;
66✔
400
    bv[0].sum = sum;
66✔
401

402
    return bv;
66✔
403
}
22✔
404

405

406
static void
407
findBoxBoundaries(tupletable2  const colorfreqtable,
12,930✔
408
                  unsigned int const depth,
409
                  unsigned int const boxStart,
410
                  unsigned int const boxSize,
411
                  sample             minval[],
412
                  sample             maxval[])
413
{
414
/*----------------------------------------------------------------------------
415
  Go through the box finding the minimum and maximum of each
416
  component - the boundaries of the box.
417
-----------------------------------------------------------------------------*/
418
    unsigned int plane;
419
    unsigned int i;
420

421
    for (plane = 0; plane < depth; ++plane) {
51,720✔
422
        minval[plane] = colorfreqtable.table[boxStart]->tuple[plane];
38,790✔
423
        maxval[plane] = minval[plane];
38,790✔
424
    }
12,930✔
425

426
    for (i = 1; i < boxSize; ++i) {
1,908,590✔
427
        for (plane = 0; plane < depth; ++plane) {
7,582,640✔
428
            sample const v = colorfreqtable.table[boxStart + i]->tuple[plane];
5,686,980✔
429
            if (v < minval[plane]) minval[plane] = v;
5,686,980✔
430
            if (v > maxval[plane]) maxval[plane] = v;
5,686,980✔
431
        }
1,897,638✔
432
    }
632,546✔
433
}
12,930✔
434

435

436

437
static unsigned int
438
largestByNorm(sample minval[], sample maxval[], unsigned int const depth)
12,165✔
439
{
440

441
    unsigned int largestDimension;
442
    unsigned int plane;
443
    sample largestSpreadSoFar;
444

445
    largestSpreadSoFar = 0;
12,165✔
446
    largestDimension = 0;
12,165✔
447
    for (plane = 0; plane < depth; ++plane) {
48,660✔
448
        sample const spread = maxval[plane]-minval[plane];
36,495✔
449
        if (spread > largestSpreadSoFar) {
36,495✔
450
            largestDimension = plane;
21,318✔
451
            largestSpreadSoFar = spread;
21,318✔
452
        }
7,038✔
453
    }
12,165✔
454
    return largestDimension;
12,165✔
455
}
456

457

458

459
static unsigned int
460
largestByLuminosity(sample minval[], sample maxval[], unsigned int const depth)
765✔
461
{
462
/*----------------------------------------------------------------------------
463
   This subroutine presumes that the tuple type is either
464
   BLACKANDWHITE, GRAYSCALE, or RGB (which implies pamP->depth is 1 or 3).
465
   To save time, we don't actually check it.
466
-----------------------------------------------------------------------------*/
467
    unsigned int retval;
468

469
    double lumin_factor[3] = {0.2989, 0.5866, 0.1145};
765✔
470

471
    if (depth == 1) {
765!
472
        retval = 0;
×
473
    } else {
474
        /* An RGB tuple */
475
        unsigned int largestDimension;
476
        unsigned int plane;
477
        double largestSpreadSoFar;
478

479
        largestSpreadSoFar = 0.0;
765✔
480
        largestDimension = 0;
765✔
481

482
        for (plane = 0; plane < 3; ++plane) {
3,060✔
483
            double const spread =
2,295✔
484
                lumin_factor[plane] * (maxval[plane]-minval[plane]);
2,295✔
485
            if (spread > largestSpreadSoFar) {
2,295✔
486
                largestDimension = plane;
1,331✔
487
                largestSpreadSoFar = spread;
1,331✔
488
            }
451✔
489
        }
765✔
490
        retval = largestDimension;
765✔
491
    }
492
    return retval;
765✔
493
}
494

495

496

497
static void
498
centerBox(unsigned int const boxStart,
10,692✔
499
          unsigned int const boxSize,
500
          tupletable2  const colorfreqtable,
501
          unsigned int const depth,
502
          tuple        const newTuple)
503
{
504

505
    unsigned int plane;
506
    sample minval, maxval;
507
    unsigned int i;
508

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

512
        for (i = 1; i < boxSize; ++i) {
732,150✔
513
            sample v = colorfreqtable.table[boxStart + i]->tuple[plane];
700,074✔
514
            minval = minval < v ? minval: v;
700,074✔
515
            maxval = maxval > v ? maxval: v;
700,074✔
516
        }
233,040✔
517
        newTuple[plane] = (minval + maxval) / 2;
32,076✔
518
    }
10,692✔
519
}
10,692✔
520

521

522

523
static void
524
averageColors(unsigned int const boxStart,
768✔
525
              unsigned int const boxSize,
526
              tupletable2  const colorfreqtable,
527
              unsigned int const depth,
528
              tuple        const newTuple)
529
{
530
    unsigned int plane;
531
    sample sum;
532
    unsigned int i;
533

534
    for (plane = 0; plane < depth; ++plane) {
3,072✔
535
        sum = 0;
2,304✔
536

537
        for (i = 0; i < boxSize; ++i) {
43,962✔
538
            sum += colorfreqtable.table[boxStart + i]->tuple[plane];
41,658✔
539
        }
13,878✔
540

541
        newTuple[plane] = sum / boxSize;
2,304✔
542
    }
768✔
543
}
768✔
544

545

546

547
static void
548
averagePixels(unsigned int const boxStart,
1,536✔
549
              unsigned int const boxSize,
550
              tupletable2 const colorfreqtable,
551
              unsigned int const depth,
552
              tuple const newTuple)
553
{
554

555
    unsigned int n;
556
        /* Number of tuples represented by the box */
557
    unsigned int plane;
558
    unsigned int i;
559

560
    /* Count the tuples in question */
561
    n = 0;  /* initial value */
1,536✔
562
    for (i = 0; i < boxSize; ++i) {
28,453✔
563
        n += (unsigned int)colorfreqtable.table[boxStart + i]->value;
26,917✔
564
    }
8,937✔
565

566
    for (plane = 0; plane < depth; ++plane) {
6,144✔
567
        sample sum;
568

569
        sum = 0;
4,608✔
570

571
        for (i = 0; i < boxSize; ++i) {
85,359✔
572
            sum += colorfreqtable.table[boxStart + i]->tuple[plane]
107,562✔
573
                * (unsigned int)colorfreqtable.table[boxStart + i]->value;
80,751✔
574
        }
26,811✔
575

576
        newTuple[plane] = sum / n;
4,608✔
577
    }
1,536✔
578
}
1,536✔
579

580

581

582
static tupletable2
583
colormapFromBv(unsigned int const newcolors,
66✔
584
               boxVector const bv,
585
               unsigned int const boxes,
586
               tupletable2 const colorfreqtable,
587
               unsigned int const depth,
588
               int const methodForRep,
589
               sixel_allocator_t *allocator)
590
{
591
    /*
592
    ** Ok, we've got enough boxes.  Now choose a representative color for
593
    ** each box.  There are a number of possible ways to make this choice.
594
    ** One would be to choose the center of the box; this ignores any structure
595
    ** within the boxes.  Another method would be to average all the colors in
596
    ** the box - this is the method specified in Heckbert's paper.  A third
597
    ** method is to average all the pixels in the box.
598
    */
599
    tupletable2 colormap;
600
    unsigned int bi;
601

602
    colormap = newColorMap(newcolors, depth, allocator);
66✔
603
    if (!colormap.size) {
66!
604
        return colormap;
×
605
    }
606

607
    for (bi = 0; bi < boxes; ++bi) {
13,062✔
608
        switch (methodForRep) {
12,996!
609
        case SIXEL_REP_CENTER_BOX:
7,128✔
610
            centerBox(bv[bi].ind, bv[bi].colors,
14,256✔
611
                      colorfreqtable, depth,
3,564✔
612
                      colormap.table[bi]->tuple);
10,692✔
613
            break;
10,692✔
614
        case SIXEL_REP_AVERAGE_COLORS:
512✔
615
            averageColors(bv[bi].ind, bv[bi].colors,
1,024✔
616
                          colorfreqtable, depth,
256✔
617
                          colormap.table[bi]->tuple);
768✔
618
            break;
768✔
619
        case SIXEL_REP_AVERAGE_PIXELS:
1,024✔
620
            averagePixels(bv[bi].ind, bv[bi].colors,
2,048✔
621
                          colorfreqtable, depth,
512✔
622
                          colormap.table[bi]->tuple);
1,536✔
623
            break;
1,536✔
624
        default:
625
            quant_trace(stderr, "Internal error: "
×
626
                                "invalid value of methodForRep: %d\n",
627
                        methodForRep);
628
        }
629
    }
4,332✔
630
    return colormap;
66✔
631
}
22✔
632

633

634
static int
635
force_palette_compare(const void *lhs, const void *rhs)
×
636
{
637
    unsigned int left;
638
    unsigned int right;
639
    unsigned int left_value;
640
    unsigned int right_value;
641

642
    left = *(const unsigned int *)lhs;
×
643
    right = *(const unsigned int *)rhs;
×
644
    left_value = force_palette_source->table[left]->value;
×
645
    right_value = force_palette_source->table[right]->value;
×
646
    if (left_value > right_value) {
×
647
        return -1;
×
648
    }
649
    if (left_value < right_value) {
×
650
        return 1;
×
651
    }
652
    if (left < right) {
×
653
        return -1;
×
654
    }
655
    if (left > right) {
×
656
        return 1;
×
657
    }
658
    return 0;
×
659
}
660

661

662
static SIXELSTATUS
663
force_palette_completion(tupletable2 *colormapP,
×
664
                         unsigned int depth,
665
                         unsigned int reqColors,
666
                         tupletable2 const colorfreqtable,
667
                         sixel_allocator_t *allocator)
668
{
669
    /*
670
     * We enqueue "losers" from the histogram so that we can revive them:
671
     *
672
     *   histogram --> sort by hit count --> append to palette tail
673
     *        ^                             |
674
     *        +-----------------------------+
675
     *
676
     * The ASCII loop shows how discarded colors walk back into the
677
     * palette when the user demands an exact size.
678
     */
679
    SIXELSTATUS status = SIXEL_FALSE;
×
680
    tupletable new_table = NULL;
×
681
    unsigned int *order = NULL;
×
682
    unsigned int current;
683
    unsigned int fill;
684
    unsigned int candidate;
685
    unsigned int plane;
686
    unsigned int source;
687

688
    current = colormapP->size;
×
689
    if (current >= reqColors) {
×
690
        return SIXEL_OK;
×
691
    }
692

693
    status = alloctupletable(&new_table, depth, reqColors, allocator);
×
694
    if (SIXEL_FAILED(status)) {
×
695
        goto end;
×
696
    }
697

698
    if (colorfreqtable.size > 0U) {
×
699
        order = (unsigned int *)sixel_allocator_malloc(
×
700
            allocator, colorfreqtable.size * sizeof(unsigned int));
×
701
        if (order == NULL) {
×
702
            status = SIXEL_BAD_ALLOCATION;
×
703
            goto end;
×
704
        }
705
        for (candidate = 0; candidate < colorfreqtable.size; ++candidate) {
×
706
            order[candidate] = candidate;
×
707
        }
708
        force_palette_source = &colorfreqtable;
×
709
        qsort(order, colorfreqtable.size, sizeof(unsigned int),
×
710
              force_palette_compare);
711
        force_palette_source = NULL;
×
712
    }
713

714
    for (fill = 0; fill < current; ++fill) {
×
715
        new_table[fill]->value = colormapP->table[fill]->value;
×
716
        for (plane = 0; plane < depth; ++plane) {
×
717
            new_table[fill]->tuple[plane] =
×
718
                colormapP->table[fill]->tuple[plane];
×
719
        }
720
    }
721

722
    candidate = 0U;
×
723
    fill = current;
×
724
    if (order != NULL) {
×
725
        while (fill < reqColors && candidate < colorfreqtable.size) {
×
726
            unsigned int index;
727

728
            index = order[candidate];
×
729
            new_table[fill]->value = colorfreqtable.table[index]->value;
×
730
            for (plane = 0; plane < depth; ++plane) {
×
731
                new_table[fill]->tuple[plane] =
×
732
                    colorfreqtable.table[index]->tuple[plane];
×
733
            }
734
            ++fill;
×
735
            ++candidate;
×
736
        }
737
    }
738

739
    if (fill < reqColors && fill == 0U) {
×
740
        new_table[fill]->value = 0U;
×
741
        for (plane = 0; plane < depth; ++plane) {
×
742
            new_table[fill]->tuple[plane] = 0U;
×
743
        }
744
        ++fill;
×
745
    }
746

747
    source = 0U;
×
748
    while (fill < reqColors) {
×
749
        new_table[fill]->value = new_table[source]->value;
×
750
        for (plane = 0; plane < depth; ++plane) {
×
751
            new_table[fill]->tuple[plane] = new_table[source]->tuple[plane];
×
752
        }
753
        ++fill;
×
754
        ++source;
×
755
        if (source >= fill) {
×
756
            source = 0U;
×
757
        }
758
    }
759

760
    sixel_allocator_free(allocator, colormapP->table);
×
761
    colormapP->table = new_table;
×
762
    colormapP->size = reqColors;
×
763
    status = SIXEL_OK;
×
764

765
end:
766
    if (status != SIXEL_OK && new_table != NULL) {
×
767
        sixel_allocator_free(allocator, new_table);
×
768
    }
769
    if (order != NULL) {
×
770
        sixel_allocator_free(allocator, order);
×
771
    }
772
    return status;
×
773
}
774

775

776
static SIXELSTATUS
777
splitBox(boxVector const bv,
12,930✔
778
         unsigned int *const boxesP,
779
         unsigned int const bi,
780
         tupletable2 const colorfreqtable,
781
         unsigned int const depth,
782
         int const methodForLargest)
783
{
784
/*----------------------------------------------------------------------------
785
   Split Box 'bi' in the box vector bv (so that bv contains one more box
786
   than it did as input).  Split it so that each new box represents about
787
   half of the pixels in the distribution given by 'colorfreqtable' for
788
   the colors in the original box, but with distinct colors in each of the
789
   two new boxes.
790

791
   Assume the box contains at least two colors.
792
-----------------------------------------------------------------------------*/
793
    SIXELSTATUS status = SIXEL_FALSE;
12,930✔
794
    unsigned int const boxStart = bv[bi].ind;
12,930✔
795
    unsigned int const boxSize  = bv[bi].colors;
12,930✔
796
    unsigned int const sm       = bv[bi].sum;
12,930✔
797

798
    enum { max_depth= 16 };
799
    sample minval[max_depth];
800
    sample maxval[max_depth];
801

802
    /* assert(max_depth >= depth); */
803

804
    unsigned int largestDimension;
805
        /* number of the plane with the largest spread */
806
    unsigned int medianIndex;
807
    unsigned int lowersum;
808
        /* Number of pixels whose value is "less than" the median */
809

810
    findBoxBoundaries(colorfreqtable, depth, boxStart, boxSize,
17,240✔
811
                      minval, maxval);
4,310✔
812

813
    /* Find the largest dimension, and sort by that component.  I have
814
       included two methods for determining the "largest" dimension;
815
       first by simply comparing the range in RGB space, and second by
816
       transforming into luminosities before the comparison.
817
    */
818
    switch (methodForLargest) {
12,930!
819
    case SIXEL_LARGE_NORM:
8,110✔
820
        largestDimension = largestByNorm(minval, maxval, depth);
12,165✔
821
        break;
12,165✔
822
    case SIXEL_LARGE_LUM:
510✔
823
        largestDimension = largestByLuminosity(minval, maxval, depth);
765✔
824
        break;
765✔
825
    default:
826
        sixel_helper_set_additional_message(
×
827
            "Internal error: invalid value of methodForLargest.");
828
        status = SIXEL_LOGIC_ERROR;
×
829
        goto end;
×
830
    }
831

832
    /* TODO: I think this sort should go after creating a box,
833
       not before splitting.  Because you need the sort to use
834
       the SIXEL_REP_CENTER_BOX method of choosing a color to
835
       represent the final boxes
836
    */
837

838
    /* Set the gross global variable 'compareplanePlane' as a
839
       parameter to compareplane(), which is called by qsort().
840
    */
841
    compareplanePlane = largestDimension;
12,930✔
842
    qsort((char*) &colorfreqtable.table[boxStart], boxSize,
12,930✔
843
          sizeof(colorfreqtable.table[boxStart]),
844
          compareplane);
845

846
    {
847
        /* Now find the median based on the counts, so that about half
848
           the pixels (not colors, pixels) are in each subdivision.  */
849

850
        unsigned int i;
851

852
        lowersum = colorfreqtable.table[boxStart]->value; /* initial value */
12,930✔
853
        for (i = 1; i < boxSize - 1 && lowersum < sm / 2; ++i) {
873,221✔
854
            lowersum += colorfreqtable.table[boxStart + i]->value;
860,291✔
855
        }
286,433✔
856
        medianIndex = i;
12,930✔
857
    }
858
    /* Split the box, and sort to bring the biggest boxes to the top.  */
859

860
    bv[bi].colors = medianIndex;
12,930✔
861
    bv[bi].sum = lowersum;
12,930✔
862
    bv[*boxesP].ind = boxStart + medianIndex;
12,930✔
863
    bv[*boxesP].colors = boxSize - medianIndex;
12,930✔
864
    bv[*boxesP].sum = sm - lowersum;
12,930✔
865
    ++(*boxesP);
12,930✔
866
    qsort((char*) bv, *boxesP, sizeof(struct box), sumcompare);
12,930✔
867

868
    status = SIXEL_OK;
12,930✔
869

870
end:
8,620✔
871
    return status;
12,930✔
872
}
873

874

875

876
static SIXELSTATUS
877
mediancut(tupletable2 const colorfreqtable,
66✔
878
          unsigned int const depth,
879
          unsigned int const newcolors,
880
          int const methodForLargest,
881
          int const methodForRep,
882
          tupletable2 *const colormapP,
883
          sixel_allocator_t *allocator)
884
{
885
/*----------------------------------------------------------------------------
886
   Compute a set of only 'newcolors' colors that best represent an
887
   image whose pixels are summarized by the histogram
888
   'colorfreqtable'.  Each tuple in that table has depth 'depth'.
889
   colorfreqtable.table[i] tells the number of pixels in the subject image
890
   have a particular color.
891

892
   As a side effect, sort 'colorfreqtable'.
893
-----------------------------------------------------------------------------*/
894
    boxVector bv;
895
    unsigned int bi;
896
    unsigned int boxes;
897
    int multicolorBoxesExist;
898
    unsigned int i;
899
    unsigned int sum;
900
    SIXELSTATUS status = SIXEL_FALSE;
66✔
901

902
    sum = 0;
66✔
903

904
    for (i = 0; i < colorfreqtable.size; ++i) {
284,919✔
905
        sum += colorfreqtable.table[i]->value;
284,853✔
906
    }
94,807✔
907

908
    /* There is at least one box that contains at least 2 colors; ergo,
909
       there is more splitting we can do.  */
910
    bv = newBoxVector(colorfreqtable.size, sum, newcolors, allocator);
66✔
911
    if (bv == NULL) {
66!
912
        goto end;
×
913
    }
914
    boxes = 1;
66✔
915
    multicolorBoxesExist = (colorfreqtable.size > 1);
66✔
916

917
    /* Main loop: split boxes until we have enough. */
918
    while (boxes < newcolors && multicolorBoxesExist) {
12,996!
919
        /* Find the first splittable box. */
920
        for (bi = 0; bi < boxes && bv[bi].colors < 2; ++bi)
65,222!
921
            ;
922
        if (bi >= boxes) {
12,930!
923
            multicolorBoxesExist = 0;
×
924
        } else {
925
            status = splitBox(bv, &boxes, bi,
17,240✔
926
                              colorfreqtable, depth,
4,310✔
927
                              methodForLargest);
4,310✔
928
            if (SIXEL_FAILED(status)) {
12,930!
929
                goto end;
×
930
            }
931
        }
932
    }
933
    *colormapP = colormapFromBv(newcolors, bv, boxes,
88✔
934
                                colorfreqtable, depth,
22✔
935
                                methodForRep, allocator);
22✔
936

937
    sixel_allocator_free(allocator, bv);
66✔
938

939
    status = SIXEL_OK;
66✔
940

941
end:
44✔
942
    return status;
66✔
943
}
944

945

946
static int histogram_lut_policy = SIXEL_LUT_POLICY_AUTO;
947

948
void
949
sixel_quant_set_lut_policy(int lut_policy)
537✔
950
{
951
    int normalized;
952

953
    normalized = SIXEL_LUT_POLICY_AUTO;
537✔
954
    if (lut_policy == SIXEL_LUT_POLICY_5BIT
766!
955
        || lut_policy == SIXEL_LUT_POLICY_6BIT
537!
956
        || lut_policy == SIXEL_LUT_POLICY_ROBINHOOD
537!
957
        || lut_policy == SIXEL_LUT_POLICY_HOPSCOTCH) {
537!
958
        normalized = lut_policy;
×
959
    }
960

961
    histogram_lut_policy = normalized;
537✔
962
}
537✔
963

964
struct histogram_control {
965
    unsigned int channel_shift;
966
    unsigned int channel_bits;
967
    unsigned int channel_mask;
968
};
969

970
static struct histogram_control
971
histogram_control_make(unsigned int depth);
972
static struct histogram_control
973
histogram_control_make_for_policy(unsigned int depth, int lut_policy);
974
static size_t histogram_dense_size(unsigned int depth,
975
                                   struct histogram_control const
976
                                       *control);
977
static unsigned int histogram_reconstruct(unsigned int quantized,
978
                                          struct histogram_control const
979
                                              *control);
980

981
static size_t
982
histogram_dense_size(unsigned int depth,
519✔
983
                     struct histogram_control const *control)
984
{
985
    size_t size;
986
    unsigned int exponent;
987
    unsigned int i;
988

989
    size = 1U;
519✔
990
    exponent = depth * control->channel_bits;
519✔
991
    for (i = 0U; i < exponent; ++i) {
9,861✔
992
        if (size > SIZE_MAX / 2U) {
9,342!
993
            size = SIZE_MAX;
×
994
            break;
×
995
        }
996
        size <<= 1U;
9,342✔
997
    }
4,014✔
998

999
    return size;
519✔
1000
}
1001

1002
static struct histogram_control
1003
histogram_control_make_for_policy(unsigned int depth, int lut_policy)
23,025,805✔
1004
{
1005
    struct histogram_control control;
1006

1007
    /*
1008
     * The ASCII ladder below shows how each policy selects bucket width.
1009
     *
1010
     *   auto / 6bit RGB : |--6--|
1011
     *   forced 5bit     : |---5---|
1012
     *   robinhood       : |------8------|
1013
     *   alpha fallback  : |---5---|  (avoids 2^(6*4) buckets)
1014
     */
1015
    control.channel_shift = 2U;
23,025,805✔
1016
    if (depth > 3U) {
23,025,805!
1017
        control.channel_shift = 3U;
×
1018
    }
1019
    if (lut_policy == SIXEL_LUT_POLICY_5BIT) {
23,025,805!
1020
        control.channel_shift = 3U;
×
1021
    } else if (lut_policy == SIXEL_LUT_POLICY_6BIT) {
23,025,805✔
1022
        control.channel_shift = 2U;
263✔
1023
        if (depth > 3U) {
263!
1024
            control.channel_shift = 3U;
×
1025
        }
1026
    } else if (lut_policy == SIXEL_LUT_POLICY_ROBINHOOD
23,025,649!
1027
               || lut_policy == SIXEL_LUT_POLICY_HOPSCOTCH) {
23,025,542!
1028
        control.channel_shift = 0U;
×
1029
    }
1030
    control.channel_bits = 8U - control.channel_shift;
23,025,805✔
1031
    control.channel_mask = (1U << control.channel_bits) - 1U;
23,025,805✔
1032

1033
    return control;
23,025,805✔
1034
}
1035

1036
static unsigned int
1037
histogram_reconstruct(unsigned int quantized,
862,857✔
1038
                      struct histogram_control const *control)
1039
{
1040
    unsigned int value;
1041

1042
    value = quantized << control->channel_shift;
862,857✔
1043
    if (quantized == control->channel_mask) {
862,857✔
1044
        value = 255U;
424✔
1045
    } else {
226✔
1046
        if (control->channel_shift > 0U) {
862,433!
1047
            value |= (1U << (control->channel_shift - 1U));
862,433✔
1048
        }
287,675✔
1049
    }
1050
    if (value > 255U) {
862,857!
1051
        value = 255U;
×
1052
    }
1053

1054
    return value;
862,857✔
1055
}
1056

1057
static unsigned int
1058
histogram_quantize(unsigned int sample8,
79,571,784✔
1059
                   struct histogram_control const *control)
1060
{
1061
    unsigned int quantized;
1062
    unsigned int shift;
1063
    unsigned int mask;
1064
    unsigned int rounding;
1065

1066
    /*
1067
     * We want each bucket to capture its center value instead of the lower
1068
     * edge.  The ASCII sketch below shows how rounding keeps the midpoint:
1069
     *
1070
     *   0---1---2---3        sample8 + round
1071
     *   |   |   |   |  ==>  ----------------  -> bucket index
1072
     *   0   1   2   3              2^shift
1073
     */
1074
    shift = control->channel_shift;
79,571,784✔
1075
    mask = control->channel_mask;
79,571,784✔
1076
    if (shift == 0U) {
79,571,784!
1077
        quantized = sample8;
×
1078
    } else {
1079
        rounding = 1U << (shift - 1U);
79,571,784✔
1080
        quantized = (sample8 + rounding) >> shift;
79,571,784✔
1081
        if (quantized > mask) {
79,571,784✔
1082
            quantized = mask;
1,028,383✔
1083
        }
350,179✔
1084
    }
1085

1086
    return quantized;
79,571,784✔
1087
}
1088

1089
static uint32_t
1090
histogram_pack_color(unsigned char const *data,
26,523,928✔
1091
                     unsigned int const depth,
1092
                     struct histogram_control const *control)
1093
{
1094
    uint32_t packed;
1095
    unsigned int n;
1096
    unsigned int sample8;
1097
    unsigned int bits;
1098

1099
    packed = 0U;
26,523,928✔
1100
    bits = control->channel_bits;
26,523,928✔
1101
    if (control->channel_shift == 0U) {
26,523,928!
1102
        /*
1103
         * The channel shift being zero means each component keeps eight
1104
         * bits.  We therefore pack pixels in RGB order, as illustrated
1105
         * below:
1106
         *
1107
         *      R   G   B
1108
         *     [ ]-[ ]-[ ]
1109
         *      |   |   |
1110
         *      v   v   v
1111
         *     0xRRGGBB
1112
         */
1113
        for (n = 0U; n < depth; ++n) {
×
NEW
1114
            packed |= (uint32_t)data[depth - 1U - n] << (n * bits);
×
1115
        }
NEW
1116
        return packed;
×
1117
    }
1118

1119
    for (n = 0U; n < depth; ++n) {
106,095,712✔
1120
        sample8 = (unsigned int)data[depth - 1U - n];
79,571,784✔
1121
        packed |= histogram_quantize(sample8, control) << (n * bits);
79,571,784✔
1122
    }
36,659,328✔
1123

1124
    return packed;
26,523,928✔
1125
}
12,219,776✔
1126

1127
static uint32_t
1128
histogram_hash_mix(uint32_t key)
23,025,286✔
1129
{
1130
    uint32_t hash;
1131

1132
    /*
1133
     * Multiplicative mixing with two avalanching rounds keeps nearby
1134
     * colors far apart in the hash domain.  The final tweak avoids the
1135
     * 0xffffffff sentinel used by hopscotch slots.
1136
     */
1137
    hash = key * 0x9e3779b9U;
23,025,286✔
1138
    hash ^= hash >> 16;
23,025,286✔
1139
    hash *= 0x7feb352dU;
23,025,286✔
1140
    hash ^= hash >> 15;
23,025,286✔
1141
    hash *= 0x846ca68bU;
23,025,286✔
1142
    hash ^= hash >> 16;
23,025,286✔
1143
    if (hash == 0xffffffffU) {
23,025,286!
NEW
1144
        hash ^= 0x632be59bU;
×
1145
    }
1146

1147
    return hash;
23,025,286✔
1148
}
1149

1150
static unsigned int
1151
computeHash(unsigned char const *data,
23,025,286✔
1152
            unsigned int const depth,
1153
            struct histogram_control const *control)
1154
{
1155
    uint32_t packed;
1156

1157
    packed = histogram_pack_color(data, depth, control);
23,025,286✔
1158

1159
    return histogram_hash_mix(packed);
23,025,286✔
1160
}
1161

1162
#define CUCKOO_BUCKET_SIZE 4U
1163
#define CUCKOO_MAX_KICKS 128U
1164
#define CUCKOO_STASH_SIZE 32U
1165
#define CUCKOO_EMPTY_KEY 0xffffffffU
1166

1167
struct cuckoo_bucket32 {
1168
    uint32_t key[CUCKOO_BUCKET_SIZE];
1169
    uint32_t value[CUCKOO_BUCKET_SIZE];
1170
};
1171

1172
struct cuckoo_table32 {
1173
    struct cuckoo_bucket32 *buckets;
1174
    uint32_t stash_key[CUCKOO_STASH_SIZE];
1175
    uint32_t stash_value[CUCKOO_STASH_SIZE];
1176
    size_t bucket_count;
1177
    size_t bucket_mask;
1178
    size_t stash_count;
1179
    size_t entry_count;
1180
    sixel_allocator_t *allocator;
1181
};
1182

1183
static size_t cuckoo_round_buckets(size_t hint);
1184
static size_t cuckoo_hash_primary(uint32_t key, size_t mask);
1185
static size_t cuckoo_hash_secondary(uint32_t key, size_t mask);
1186
static size_t cuckoo_hash_alternate(uint32_t key,
1187
                                    size_t bucket,
1188
                                    size_t mask);
1189
static uint32_t *cuckoo_bucket_find(struct cuckoo_bucket32 *bucket,
1190
                                    uint32_t key);
1191
static int cuckoo_bucket_insert_direct(struct cuckoo_bucket32 *bucket,
1192
                                       uint32_t key,
1193
                                       uint32_t value);
1194
static SIXELSTATUS cuckoo_table32_init(struct cuckoo_table32 *table,
1195
                                       size_t expected,
1196
                                       sixel_allocator_t *allocator);
1197
static void cuckoo_table32_clear(struct cuckoo_table32 *table);
1198
static void cuckoo_table32_fini(struct cuckoo_table32 *table);
1199
static uint32_t *cuckoo_table32_lookup(struct cuckoo_table32 *table,
1200
                                       uint32_t key);
1201
static SIXELSTATUS cuckoo_table32_insert(struct cuckoo_table32 *table,
1202
                                         uint32_t key,
1203
                                         uint32_t value);
1204
static SIXELSTATUS cuckoo_table32_grow(struct cuckoo_table32 *table);
1205

1206
struct robinhood_slot32 {
1207
    uint32_t key;
1208
    uint32_t color;
1209
    uint32_t value;
1210
    uint16_t distance;
1211
    uint16_t pad;
1212
};
1213

1214
struct robinhood_table32 {
1215
    struct robinhood_slot32 *slots;
1216
    size_t capacity;
1217
    size_t count;
1218
    sixel_allocator_t *allocator;
1219
};
1220

1221
static size_t robinhood_round_capacity(size_t hint);
1222
static SIXELSTATUS robinhood_table32_init(struct robinhood_table32 *table,
1223
                                         size_t expected,
1224
                                         sixel_allocator_t *allocator);
1225
static void robinhood_table32_fini(struct robinhood_table32 *table);
1226
static struct robinhood_slot32 *
1227
robinhood_table32_lookup(struct robinhood_table32 *table,
1228
                         uint32_t key,
1229
                         uint32_t color);
1230
static SIXELSTATUS robinhood_table32_insert(struct robinhood_table32 *table,
1231
                                           uint32_t key,
1232
                                           uint32_t color,
1233
                                           uint32_t value);
1234
static SIXELSTATUS robinhood_table32_grow(struct robinhood_table32 *table);
1235
static struct robinhood_slot32 *
1236
robinhood_table32_place(struct robinhood_table32 *table,
1237
                        struct robinhood_slot32 entry);
1238

1239
#define HOPSCOTCH_EMPTY_KEY 0xffffffffU
1240
#define HOPSCOTCH_DEFAULT_NEIGHBORHOOD 32U
1241
#define HOPSCOTCH_INSERT_RANGE 256U
1242

1243
struct hopscotch_slot32 {
1244
    uint32_t key;
1245
    uint32_t color;
1246
    uint32_t value;
1247
};
1248

1249
struct hopscotch_table32 {
1250
    struct hopscotch_slot32 *slots;
1251
    uint32_t *hopinfo;
1252
    size_t capacity;
1253
    size_t count;
1254
    size_t neighborhood;
1255
    sixel_allocator_t *allocator;
1256
};
1257

1258
static SIXELSTATUS hopscotch_table32_init(struct hopscotch_table32 *table,
1259
                                          size_t expected,
1260
                                          sixel_allocator_t *allocator);
1261
static void hopscotch_table32_fini(struct hopscotch_table32 *table);
1262
static struct hopscotch_slot32 *
1263
hopscotch_table32_lookup(struct hopscotch_table32 *table,
1264
                         uint32_t key,
1265
                         uint32_t color);
1266
static SIXELSTATUS hopscotch_table32_insert(struct hopscotch_table32 *table,
1267
                                            uint32_t key,
1268
                                            uint32_t color,
1269
                                            uint32_t value);
1270
static SIXELSTATUS hopscotch_table32_grow(struct hopscotch_table32 *table);
1271

1272
static struct histogram_control
1273
histogram_control_make(unsigned int depth)
23,025,542✔
1274
{
1275
    struct histogram_control control;
1276

1277
    control = histogram_control_make_for_policy(depth,
33,505,140✔
1278
                                                histogram_lut_policy);
10,479,598✔
1279

1280
    return control;
23,025,542✔
1281
}
1282

1283
static size_t
1284
robinhood_round_capacity(size_t hint)
×
1285
{
1286
    size_t capacity;
1287

1288
    capacity = 16U;
×
1289
    if (hint < capacity) {
×
1290
        return capacity;
×
1291
    }
1292

1293
    capacity = hint - 1U;
×
1294
    capacity |= capacity >> 1;
×
1295
    capacity |= capacity >> 2;
×
1296
    capacity |= capacity >> 4;
×
1297
    capacity |= capacity >> 8;
×
1298
    capacity |= capacity >> 16;
×
1299
#if SIZE_MAX > UINT32_MAX
1300
    capacity |= capacity >> 32;
×
1301
#endif
1302
    if (capacity == SIZE_MAX) {
×
1303
        return SIZE_MAX;
×
1304
    }
1305
    capacity++;
×
1306
    if (capacity < 16U) {
×
1307
        capacity = 16U;
×
1308
    }
1309

1310
    return capacity;
×
1311
}
1312

1313
static SIXELSTATUS
1314
robinhood_table32_init(struct robinhood_table32 *table,
×
1315
                       size_t expected,
1316
                       sixel_allocator_t *allocator)
1317
{
1318
    size_t hint;
1319
    size_t capacity;
1320

1321
    table->slots = NULL;
×
1322
    table->capacity = 0U;
×
1323
    table->count = 0U;
×
1324
    table->allocator = allocator;
×
1325

1326
    if (expected < 16U) {
×
1327
        expected = 16U;
×
1328
    }
1329
    if (expected > SIZE_MAX / 2U) {
×
1330
        hint = SIZE_MAX / 2U;
×
1331
    } else {
1332
        hint = expected * 2U;
×
1333
    }
1334
    capacity = robinhood_round_capacity(hint);
×
1335
    if (capacity == SIZE_MAX && hint != SIZE_MAX) {
×
1336
        return SIXEL_BAD_ALLOCATION;
×
1337
    }
1338

1339
    table->slots = (struct robinhood_slot32 *)
×
1340
        sixel_allocator_calloc(allocator,
×
1341
                               capacity,
1342
                               sizeof(struct robinhood_slot32));
1343
    if (table->slots == NULL) {
×
1344
        table->capacity = 0U;
×
1345
        table->count = 0U;
×
1346
        return SIXEL_BAD_ALLOCATION;
×
1347
    }
1348
    table->capacity = capacity;
×
1349
    table->count = 0U;
×
1350

1351
    return SIXEL_OK;
×
1352
}
1353

1354
static void
1355
robinhood_table32_fini(struct robinhood_table32 *table)
×
1356
{
1357
    if (table->slots != NULL) {
×
1358
        sixel_allocator_free(table->allocator, table->slots);
×
1359
        table->slots = NULL;
×
1360
    }
1361
    table->capacity = 0U;
×
1362
    table->count = 0U;
×
1363
    table->allocator = NULL;
×
1364
}
×
1365

1366
static struct robinhood_slot32 *
NEW
1367
robinhood_table32_lookup(struct robinhood_table32 *table,
×
1368
                         uint32_t key,
1369
                         uint32_t color)
1370
{
1371
    size_t mask;
1372
    size_t index;
1373
    uint16_t distance;
1374
    struct robinhood_slot32 *slot;
1375

1376
    if (table->capacity == 0U || table->slots == NULL) {
×
1377
        return NULL;
×
1378
    }
1379

1380
    mask = table->capacity - 1U;
×
1381
    index = (size_t)(key & mask);
×
1382
    distance = 0U;
×
1383

1384
    for (;;) {
1385
        slot = &table->slots[index];
×
1386
        if (slot->value == 0U) {
×
1387
            return NULL;
×
1388
        }
NEW
1389
        if (slot->key == key && slot->color == color) {
×
1390
            return slot;
×
1391
        }
1392
        if (slot->distance < distance) {
×
1393
            return NULL;
×
1394
        }
1395
        index = (index + 1U) & mask;
×
1396
        distance++;
×
1397
    }
1398
}
1399

1400
static struct robinhood_slot32 *
1401
robinhood_table32_place(struct robinhood_table32 *table,
×
1402
                        struct robinhood_slot32 entry)
1403
{
1404
    size_t mask;
1405
    size_t index;
1406
    struct robinhood_slot32 *slot;
1407
    struct robinhood_slot32 tmp;
1408

1409
    mask = table->capacity - 1U;
×
1410
    index = (size_t)(entry.key & mask);
×
1411

1412
    for (;;) {
1413
        slot = &table->slots[index];
×
1414
        if (slot->value == 0U) {
×
1415
            *slot = entry;
×
1416
            table->count++;
×
1417
            return slot;
×
1418
        }
NEW
1419
        if (slot->key == entry.key && slot->color == entry.color) {
×
1420
            slot->value = entry.value;
×
1421
            return slot;
×
1422
        }
1423
        if (slot->distance < entry.distance) {
×
1424
            tmp = *slot;
×
1425
            *slot = entry;
×
1426
            entry = tmp;
×
1427
        }
1428
        index = (index + 1U) & mask;
×
1429
        entry.distance++;
×
1430
    }
1431
}
1432

1433
static SIXELSTATUS
1434
robinhood_table32_grow(struct robinhood_table32 *table)
×
1435
{
1436
    struct robinhood_slot32 *old_slots;
1437
    size_t old_capacity;
1438
    size_t new_capacity;
1439
    size_t i;
1440

1441
    if (table->allocator == NULL) {
×
1442
        return SIXEL_BAD_ALLOCATION;
×
1443
    }
1444
    if (table->capacity == 0U) {
×
1445
        new_capacity = 16U;
×
1446
    } else {
1447
        if (table->capacity > SIZE_MAX / 2U) {
×
1448
            return SIXEL_BAD_ALLOCATION;
×
1449
        }
1450
        new_capacity = table->capacity << 1U;
×
1451
    }
1452
    new_capacity = robinhood_round_capacity(new_capacity);
×
1453
    if (new_capacity <= table->capacity) {
×
1454
        return SIXEL_BAD_ALLOCATION;
×
1455
    }
1456

1457
    old_slots = table->slots;
×
1458
    old_capacity = table->capacity;
×
1459
    table->slots = (struct robinhood_slot32 *)
×
1460
        sixel_allocator_calloc(table->allocator,
×
1461
                               new_capacity,
1462
                               sizeof(struct robinhood_slot32));
1463
    if (table->slots == NULL) {
×
1464
        table->slots = old_slots;
×
1465
        table->capacity = old_capacity;
×
1466
        return SIXEL_BAD_ALLOCATION;
×
1467
    }
1468
    table->capacity = new_capacity;
×
1469
    table->count = 0U;
×
1470

1471
    for (i = 0U; i < old_capacity; ++i) {
×
1472
        struct robinhood_slot32 entry;
1473

1474
        if (old_slots[i].value == 0U) {
×
1475
            continue;
×
1476
        }
1477
        entry.key = old_slots[i].key;
×
NEW
1478
        entry.color = old_slots[i].color;
×
1479
        entry.value = old_slots[i].value;
×
1480
        entry.distance = 0U;
×
1481
        entry.pad = 0U;  /* ensure padding bytes are initialized */
×
1482
        (void)robinhood_table32_place(table, entry);
×
1483
    }
1484

1485
    sixel_allocator_free(table->allocator, old_slots);
×
1486

1487
    return SIXEL_OK;
×
1488
}
1489

1490
static SIXELSTATUS
1491
robinhood_table32_insert(struct robinhood_table32 *table,
×
1492
                         uint32_t key,
1493
                         uint32_t color,
1494
                         uint32_t value)
1495
{
1496
    SIXELSTATUS status;
1497
    struct robinhood_slot32 entry;
1498

1499
    if (table->slots == NULL || table->capacity == 0U) {
×
1500
        return SIXEL_BAD_ARGUMENT;
×
1501
    }
1502
    if (table->count * 2U >= table->capacity) {
×
1503
        status = robinhood_table32_grow(table);
×
1504
        if (SIXEL_FAILED(status)) {
×
1505
            return status;
×
1506
        }
1507
    }
1508

1509
    entry.key = key;
×
NEW
1510
    entry.color = color;
×
1511
    entry.value = value;
×
1512
    entry.distance = 0U;
×
1513
    entry.pad = 0U;  /* ensure padding bytes are initialized */
×
1514
    (void)robinhood_table32_place(table, entry);
×
1515

1516
    return SIXEL_OK;
×
1517
}
1518

1519
static SIXELSTATUS
1520
hopscotch_table32_init(struct hopscotch_table32 *table,
×
1521
                       size_t expected,
1522
                       sixel_allocator_t *allocator)
1523
{
1524
    size_t hint;
1525
    size_t capacity;
1526
    size_t i;
1527

1528
    if (table == NULL) {
×
1529
        return SIXEL_BAD_ARGUMENT;
×
1530
    }
1531

1532
    table->slots = NULL;
×
1533
    table->hopinfo = NULL;
×
1534
    table->capacity = 0U;
×
1535
    table->count = 0U;
×
1536
    table->neighborhood = HOPSCOTCH_DEFAULT_NEIGHBORHOOD;
×
1537
    table->allocator = allocator;
×
1538

1539
    if (expected < 16U) {
×
1540
        expected = 16U;
×
1541
    }
1542
    if (expected > SIZE_MAX / 2U) {
×
1543
        hint = SIZE_MAX / 2U;
×
1544
    } else {
1545
        hint = expected * 2U;
×
1546
    }
1547
    capacity = robinhood_round_capacity(hint);
×
1548
    if (capacity == SIZE_MAX && hint != SIZE_MAX) {
×
1549
        return SIXEL_BAD_ALLOCATION;
×
1550
    }
1551
    if (capacity < table->neighborhood) {
×
1552
        capacity = table->neighborhood;
×
1553
    }
1554

1555
    table->slots = (struct hopscotch_slot32 *)
×
1556
        sixel_allocator_malloc(allocator,
×
1557
                               capacity * sizeof(struct hopscotch_slot32));
1558
    if (table->slots == NULL) {
×
1559
        return SIXEL_BAD_ALLOCATION;
×
1560
    }
1561
    table->hopinfo = (uint32_t *)
×
1562
        sixel_allocator_calloc(allocator,
×
1563
                               capacity,
1564
                               sizeof(uint32_t));
1565
    if (table->hopinfo == NULL) {
×
1566
        sixel_allocator_free(allocator, table->slots);
×
1567
        table->slots = NULL;
×
1568
        return SIXEL_BAD_ALLOCATION;
×
1569
    }
1570

1571
    for (i = 0U; i < capacity; ++i) {
×
1572
        table->slots[i].key = HOPSCOTCH_EMPTY_KEY;
×
NEW
1573
        table->slots[i].color = 0U;
×
UNCOV
1574
        table->slots[i].value = 0U;
×
1575
    }
1576
    table->capacity = capacity;
×
1577
    table->count = 0U;
×
1578
    if (table->neighborhood > 32U) {
×
1579
        table->neighborhood = 32U;
×
1580
    }
1581
    if (table->neighborhood > table->capacity) {
×
1582
        table->neighborhood = table->capacity;
×
1583
    }
1584

1585
    return SIXEL_OK;
×
1586
}
1587

1588
static void
1589
hopscotch_table32_fini(struct hopscotch_table32 *table)
×
1590
{
1591
    sixel_allocator_t *allocator;
1592

1593
    if (table == NULL) {
×
1594
        return;
×
1595
    }
1596

1597
    allocator = table->allocator;
×
1598
    if (allocator != NULL) {
×
1599
        if (table->slots != NULL) {
×
1600
            sixel_allocator_free(allocator, table->slots);
×
1601
        }
1602
        if (table->hopinfo != NULL) {
×
1603
            sixel_allocator_free(allocator, table->hopinfo);
×
1604
        }
1605
    }
1606
    table->slots = NULL;
×
1607
    table->hopinfo = NULL;
×
1608
    table->capacity = 0U;
×
1609
    table->count = 0U;
×
1610
}
1611

1612
static struct hopscotch_slot32 *
NEW
1613
hopscotch_table32_lookup(struct hopscotch_table32 *table,
×
1614
                         uint32_t key,
1615
                         uint32_t color)
1616
{
1617
    size_t index;
1618
    size_t bit;
1619
    size_t candidate;
1620
    uint32_t hop;
1621
    size_t mask;
1622
    size_t neighborhood;
1623

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

1631
    mask = table->capacity - 1U;
×
1632
    index = (size_t)key & mask;
×
1633
    hop = table->hopinfo[index];
×
1634
    neighborhood = table->neighborhood;
×
1635
    for (bit = 0U; bit < neighborhood; ++bit) {
×
1636
        if ((hop & (1U << bit)) == 0U) {
×
1637
            continue;
×
1638
        }
1639
        candidate = (index + bit) & mask;
×
NEW
1640
        if (table->slots[candidate].key == key
×
NEW
1641
            && table->slots[candidate].color == color) {
×
UNCOV
1642
            return &table->slots[candidate];
×
1643
        }
1644
    }
1645

1646
    return NULL;
×
1647
}
1648

1649
static SIXELSTATUS
1650
hopscotch_table32_grow(struct hopscotch_table32 *table)
×
1651
{
1652
    SIXELSTATUS status;
1653
    struct hopscotch_table32 tmp;
1654
    size_t i;
1655
    struct hopscotch_slot32 *slot;
1656

1657
    tmp.slots = NULL;
×
1658
    tmp.hopinfo = NULL;
×
1659
    tmp.capacity = 0U;
×
1660
    tmp.count = 0U;
×
1661
    tmp.neighborhood = table->neighborhood;
×
1662
    tmp.allocator = table->allocator;
×
1663

1664
    status = hopscotch_table32_init(&tmp,
×
1665
                                    table->capacity * 2U,
×
1666
                                    table->allocator);
1667
    if (SIXEL_FAILED(status)) {
×
1668
        return status;
×
1669
    }
1670
    if (tmp.neighborhood > table->neighborhood) {
×
1671
        tmp.neighborhood = table->neighborhood;
×
1672
    }
1673

1674
    for (i = 0U; i < table->capacity; ++i) {
×
1675
        slot = &table->slots[i];
×
1676
        if (slot->key == HOPSCOTCH_EMPTY_KEY) {
×
1677
            continue;
×
1678
        }
1679
        status = hopscotch_table32_insert(&tmp,
×
1680
                                          slot->key,
1681
                                          slot->color,
1682
                                          slot->value);
1683
        if (SIXEL_FAILED(status)) {
×
1684
            hopscotch_table32_fini(&tmp);
×
1685
            return status;
×
1686
        }
1687
    }
1688

1689
    hopscotch_table32_fini(table);
×
1690

1691
    table->slots = tmp.slots;
×
1692
    table->hopinfo = tmp.hopinfo;
×
1693
    table->capacity = tmp.capacity;
×
1694
    table->count = tmp.count;
×
1695
    table->neighborhood = tmp.neighborhood;
×
1696
    table->allocator = tmp.allocator;
×
1697

1698
    return SIXEL_OK;
×
1699
}
1700

1701
static SIXELSTATUS
1702
hopscotch_table32_insert(struct hopscotch_table32 *table,
×
1703
                         uint32_t key,
1704
                         uint32_t color,
1705
                         uint32_t value)
1706
{
1707
    SIXELSTATUS status;
1708
    struct hopscotch_slot32 *slot;
1709
    size_t index;
1710
    size_t mask;
1711
    size_t distance;
1712
    size_t attempts;
1713
    size_t free_index;
1714
    size_t neighborhood;
1715
    int relocated;
1716
    size_t offset;
1717
    uint32_t hop;
1718
    size_t bit;
1719
    size_t move_index;
1720
    struct hopscotch_slot32 tmp_slot;
1721

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

NEW
1729
    slot = hopscotch_table32_lookup(table, key, color);
×
1730
    if (slot != NULL) {
×
1731
        slot->value = value;
×
1732
        return SIXEL_OK;
×
1733
    }
1734

1735
    if (table->count * 2U >= table->capacity) {
×
1736
        status = hopscotch_table32_grow(table);
×
1737
        if (SIXEL_FAILED(status)) {
×
1738
            return status;
×
1739
        }
NEW
1740
        return hopscotch_table32_insert(table, key, color, value);
×
1741
    }
1742

1743
    mask = table->capacity - 1U;
×
1744
    neighborhood = table->neighborhood;
×
1745
    index = (size_t)key & mask;
×
1746
    slot = NULL;
×
1747
    free_index = index;
×
1748
    distance = 0U;
×
1749
    for (attempts = 0U; attempts < HOPSCOTCH_INSERT_RANGE; ++attempts) {
×
1750
        free_index = (index + attempts) & mask;
×
1751
        slot = &table->slots[free_index];
×
1752
        if (slot->key == HOPSCOTCH_EMPTY_KEY) {
×
1753
            distance = attempts;
×
1754
            break;
×
1755
        }
1756
    }
1757
    if (slot == NULL || slot->key != HOPSCOTCH_EMPTY_KEY) {
×
1758
        status = hopscotch_table32_grow(table);
×
1759
        if (SIXEL_FAILED(status)) {
×
1760
            return status;
×
1761
        }
NEW
1762
        return hopscotch_table32_insert(table, key, color, value);
×
1763
    }
1764

1765
    /*
1766
     * Relocation diagram:
1767
     *
1768
     *   free slot <--- hop window <--- [base bucket]
1769
     *      ^ move resident outward until distance < neighborhood
1770
     */
1771
    while (distance >= neighborhood) {
×
1772
        relocated = 0;
×
1773
        for (offset = neighborhood - 1U; offset > 0U; --offset) {
×
1774
            size_t base;
1775

1776
            base = (free_index + table->capacity - offset) & mask;
×
1777
            hop = table->hopinfo[base];
×
1778
            if (hop == 0U) {
×
1779
                continue;
×
1780
            }
1781
            for (bit = 0U; bit < offset; ++bit) {
×
1782
                if ((hop & (1U << bit)) == 0U) {
×
1783
                    continue;
×
1784
                }
1785
                move_index = (base + bit) & mask;
×
1786
                tmp_slot = table->slots[move_index];
×
1787
                table->slots[free_index] = tmp_slot;
×
1788
                table->slots[move_index].key = HOPSCOTCH_EMPTY_KEY;
×
NEW
1789
                table->slots[move_index].color = 0U;
×
1790
                table->slots[move_index].value = 0U;
×
1791
                table->hopinfo[base] &= (uint32_t)~(1U << bit);
×
1792
                table->hopinfo[base] |= (uint32_t)(1U << offset);
×
1793
                free_index = move_index;
×
1794
                if (free_index >= index) {
×
1795
                    distance = free_index - index;
×
1796
                } else {
1797
                    distance = free_index + table->capacity - index;
×
1798
                }
1799
                relocated = 1;
×
1800
                break;
×
1801
            }
1802
            if (relocated) {
×
1803
                break;
×
1804
            }
1805
        }
1806
        if (!relocated) {
×
1807
            status = hopscotch_table32_grow(table);
×
1808
            if (SIXEL_FAILED(status)) {
×
1809
                return status;
×
1810
            }
NEW
1811
            return hopscotch_table32_insert(table, key, color, value);
×
1812
        }
1813
    }
1814

1815
    if (distance >= 32U) {
×
1816
        status = hopscotch_table32_grow(table);
×
1817
        if (SIXEL_FAILED(status)) {
×
1818
            return status;
×
1819
        }
NEW
1820
        return hopscotch_table32_insert(table, key, color, value);
×
1821
    }
1822

1823
    table->slots[free_index].key = key;
×
NEW
1824
    table->slots[free_index].color = color;
×
1825
    table->slots[free_index].value = value;
×
1826
    table->hopinfo[index] |= (uint32_t)(1U << distance);
×
1827
    table->count++;
×
1828

1829
    return SIXEL_OK;
×
1830
}
1831

1832
/*
1833
 * The cuckoo hash backend stores entries in fixed-width buckets.
1834
 *
1835
 *   [bucket 0] -> key0 key1 key2 key3
1836
 *                 val0 val1 val2 val3
1837
 *   [bucket 1] -> ...
1838
 *
1839
 * Each key is compared against the 128-bit lane using SIMD instructions.
1840
 * Two hash functions map a key to its primary and secondary buckets.  When
1841
 * both are full, the eviction loop "kicks" an entry toward its alternate
1842
 * bucket, as illustrated below:
1843
 *
1844
 *   bucket A --kick--> bucket B --kick--> bucket A ...
1845
 *
1846
 * A tiny stash buffers entries when the table momentarily fills up.  This
1847
 * keeps lookups fast while letting us grow the table lazily.
1848
 */
1849
static size_t
1850
cuckoo_round_buckets(size_t hint)
263✔
1851
{
1852
    size_t desired;
1853
    size_t buckets;
1854
    size_t prev;
1855

1856
    if (hint < CUCKOO_BUCKET_SIZE) {
263!
1857
        hint = CUCKOO_BUCKET_SIZE;
×
1858
    }
1859
    if (hint > SIZE_MAX / 2U) {
263!
1860
        hint = SIZE_MAX / 2U;
×
1861
    }
1862
    desired = (hint * 2U + CUCKOO_BUCKET_SIZE - 1U) / CUCKOO_BUCKET_SIZE;
263✔
1863
    if (desired == 0U) {
263!
1864
        desired = 1U;
×
1865
    }
1866

1867
    buckets = 1U;
263✔
1868
    while (buckets < desired) {
4,734✔
1869
        prev = buckets;
4,471✔
1870
        if (buckets > SIZE_MAX / 2U) {
4,471!
1871
            buckets = prev;
×
1872
            break;
×
1873
        }
1874
        buckets <<= 1U;
4,471✔
1875
        if (buckets < prev) {
4,471!
1876
            buckets = prev;
×
1877
            break;
×
1878
        }
1879
    }
1880
    if (buckets == 0U) {
263!
1881
        buckets = 1U;
×
1882
    }
1883

1884
    return buckets;
263✔
1885
}
1886

1887
static size_t
1888
cuckoo_hash_primary(uint32_t key, size_t mask)
26,230,484✔
1889
{
1890
    uint32_t mix;
1891

1892
    mix = key * 0x9e3779b1U;
26,230,484✔
1893
    return (size_t)(mix & (uint32_t)mask);
26,230,484✔
1894
}
1895

1896
static size_t
1897
cuckoo_hash_secondary(uint32_t key, size_t mask)
3,205,422✔
1898
{
1899
    uint32_t mix;
1900

1901
    mix = key ^ 0x85ebca6bU;
3,205,422✔
1902
    mix ^= mix >> 13;
3,205,422✔
1903
    mix *= 0xc2b2ae35U;
3,205,422✔
1904
    return (size_t)(mix & (uint32_t)mask);
3,205,422✔
1905
}
1906

1907
static size_t
1908
cuckoo_hash_alternate(uint32_t key, size_t bucket, size_t mask)
14✔
1909
{
1910
    size_t primary;
1911
    size_t secondary;
1912

1913
    primary = cuckoo_hash_primary(key, mask);
14✔
1914
    secondary = cuckoo_hash_secondary(key, mask);
14✔
1915
    if (primary == bucket) {
14!
1916
        if (secondary == primary) {
14!
1917
            secondary = (secondary + 1U) & mask;
×
1918
        }
1919
        return secondary;
14✔
1920
    }
1921

1922
    return primary;
×
1923
}
2✔
1924

1925
static uint32_t *
1926
cuckoo_bucket_find(struct cuckoo_bucket32 *bucket, uint32_t key)
27,833,286✔
1927
{
1928
    size_t i;
1929

1930
#if defined(SIXEL_USE_SSE2)
1931
    __m128i needle;
1932
    __m128i keys;
1933
    __m128i cmp;
1934
    int mask;
1935

1936
    needle = _mm_set1_epi32((int)key);
1937
    keys = _mm_loadu_si128((const __m128i *)bucket->key);
1938
    cmp = _mm_cmpeq_epi32(needle, keys);
1939
    mask = _mm_movemask_ps(_mm_castsi128_ps(cmp));
1940
    if ((mask & 1) != 0 && bucket->value[0] != 0U) {
1941
        return &bucket->value[0];
1942
    }
1943
    if ((mask & 2) != 0 && bucket->value[1] != 0U) {
1944
        return &bucket->value[1];
1945
    }
1946
    if ((mask & 4) != 0 && bucket->value[2] != 0U) {
1947
        return &bucket->value[2];
1948
    }
1949
    if ((mask & 8) != 0 && bucket->value[3] != 0U) {
1950
        return &bucket->value[3];
1951
    }
1952
#elif defined(SIXEL_USE_NEON)
1953
    uint32x4_t needle;
1954
    uint32x4_t keys;
1955
    uint32x4_t cmp;
1956

1957
    needle = vdupq_n_u32(key);
1958
    keys = vld1q_u32(bucket->key);
1959
    cmp = vceqq_u32(needle, keys);
1960
    if (vgetq_lane_u32(cmp, 0) != 0U && bucket->value[0] != 0U) {
1961
        return &bucket->value[0];
1962
    }
1963
    if (vgetq_lane_u32(cmp, 1) != 0U && bucket->value[1] != 0U) {
1964
        return &bucket->value[1];
1965
    }
1966
    if (vgetq_lane_u32(cmp, 2) != 0U && bucket->value[2] != 0U) {
1967
        return &bucket->value[2];
1968
    }
1969
    if (vgetq_lane_u32(cmp, 3) != 0U && bucket->value[3] != 0U) {
1970
        return &bucket->value[3];
1971
    }
1972
#else
1973
    for (i = 0U; i < CUCKOO_BUCKET_SIZE; ++i) {
54,347,076✔
1974
        if (bucket->value[i] != 0U && bucket->key[i] == key) {
47,936,484✔
1975
            return &bucket->value[i];
21,422,694✔
1976
        }
1977
    }
8,572,072✔
1978
#endif
1979

1980
    return NULL;
6,410,592✔
1981
}
12,032,884✔
1982

1983
static int
1984
cuckoo_bucket_insert_direct(struct cuckoo_bucket32 *bucket,
1,602,606✔
1985
                            uint32_t key,
1986
                            uint32_t value)
1987
{
1988
    size_t i;
1989

1990
    for (i = 0U; i < CUCKOO_BUCKET_SIZE; ++i) {
1,738,657✔
1991
        if (bucket->value[i] == 0U) {
1,738,643✔
1992
            bucket->key[i] = key;
1,602,592✔
1993
            bucket->value[i] = value;
1,602,592✔
1994
            return 1;
1,602,592✔
1995
        }
1996
    }
41,657✔
1997

1998
    return 0;
14✔
1999
}
517,780✔
2000

2001
static SIXELSTATUS
2002
cuckoo_table32_init(struct cuckoo_table32 *table,
260✔
2003
                    size_t expected,
2004
                    sixel_allocator_t *allocator)
2005
{
2006
    size_t buckets;
2007
    size_t i;
2008
    size_t j;
2009

2010
    if (table == NULL || allocator == NULL) {
260!
2011
        return SIXEL_BAD_ARGUMENT;
×
2012
    }
2013

2014
    buckets = cuckoo_round_buckets(expected);
260✔
2015
    if (buckets == 0U
260!
2016
        || buckets > SIZE_MAX / sizeof(struct cuckoo_bucket32)) {
260!
2017
        sixel_helper_set_additional_message(
×
2018
            "unable to size cuckoo bucket array.");
2019
        return SIXEL_BAD_ALLOCATION;
×
2020
    }
2021

2022
    table->buckets = (struct cuckoo_bucket32 *)sixel_allocator_malloc(
260✔
2023
        allocator, buckets * sizeof(struct cuckoo_bucket32));
106✔
2024
    if (table->buckets == NULL) {
260!
2025
        sixel_helper_set_additional_message(
×
2026
            "unable to allocate cuckoo buckets.");
2027
        return SIXEL_BAD_ALLOCATION;
×
2028
    }
2029

2030
    table->bucket_count = buckets;
260✔
2031
    table->bucket_mask = buckets - 1U;
260✔
2032
    table->stash_count = 0U;
260✔
2033
    table->entry_count = 0U;
260✔
2034
    table->allocator = allocator;
260✔
2035
    for (i = 0U; i < buckets; ++i) {
34,078,980✔
2036
        for (j = 0U; j < CUCKOO_BUCKET_SIZE; ++j) {
170,393,600✔
2037
            table->buckets[i].key[j] = CUCKOO_EMPTY_KEY;
136,314,880✔
2038
            table->buckets[i].value[j] = 0U;
136,314,880✔
2039
        }
55,574,528✔
2040
    }
13,893,632✔
2041
    for (i = 0U; i < CUCKOO_STASH_SIZE; ++i) {
8,580✔
2042
        table->stash_key[i] = CUCKOO_EMPTY_KEY;
8,320✔
2043
        table->stash_value[i] = 0U;
8,320✔
2044
    }
3,392✔
2045

2046
    return SIXEL_OK;
260✔
2047
}
106✔
2048

2049
static void
2050
cuckoo_table32_clear(struct cuckoo_table32 *table)
263✔
2051
{
2052
    size_t i;
2053
    size_t j;
2054

2055
    if (table == NULL || table->buckets == NULL) {
263!
2056
        return;
×
2057
    }
2058

2059
    table->stash_count = 0U;
263✔
2060
    table->entry_count = 0U;
263✔
2061
    for (i = 0U; i < table->bucket_count; ++i) {
34,472,199✔
2062
        for (j = 0U; j < CUCKOO_BUCKET_SIZE; ++j) {
172,359,680✔
2063
            table->buckets[i].key[j] = CUCKOO_EMPTY_KEY;
137,887,744✔
2064
            table->buckets[i].value[j] = 0U;
137,887,744✔
2065
        }
56,098,816✔
2066
    }
14,024,704✔
2067
    for (i = 0U; i < CUCKOO_STASH_SIZE; ++i) {
8,679✔
2068
        table->stash_key[i] = CUCKOO_EMPTY_KEY;
8,416✔
2069
        table->stash_value[i] = 0U;
8,416✔
2070
    }
3,424✔
2071
}
107✔
2072

2073
static void
2074
cuckoo_table32_fini(struct cuckoo_table32 *table)
260✔
2075
{
2076
    if (table == NULL || table->allocator == NULL) {
260!
2077
        return;
×
2078
    }
2079
    if (table->buckets != NULL) {
260!
2080
        sixel_allocator_free(table->allocator, table->buckets);
260✔
2081
        table->buckets = NULL;
260✔
2082
    }
106✔
2083
    table->bucket_count = 0U;
260✔
2084
    table->bucket_mask = 0U;
260✔
2085
    table->stash_count = 0U;
260✔
2086
    table->entry_count = 0U;
260✔
2087
}
106✔
2088

2089
static uint32_t *
2090
cuckoo_table32_lookup(struct cuckoo_table32 *table, uint32_t key)
24,627,878✔
2091
{
2092
    size_t index;
2093
    size_t i;
2094
    uint32_t *slot;
2095

2096
    if (table == NULL || table->buckets == NULL) {
24,627,878!
2097
        return NULL;
×
2098
    }
2099

2100
    index = cuckoo_hash_primary(key, table->bucket_mask);
24,627,878✔
2101
    slot = cuckoo_bucket_find(&table->buckets[index], key);
24,627,878✔
2102
    if (slot != NULL) {
24,627,878✔
2103
        return slot;
21,422,470✔
2104
    }
2105

2106
    index = cuckoo_hash_secondary(key, table->bucket_mask);
3,205,408✔
2107
    slot = cuckoo_bucket_find(&table->buckets[index], key);
3,205,408✔
2108
    if (slot != NULL) {
3,205,408✔
2109
        return slot;
224✔
2110
    }
2111

2112
    for (i = 0U; i < table->stash_count; ++i) {
3,205,184!
2113
        if (table->stash_value[i] != 0U && table->stash_key[i] == key) {
×
2114
            return &table->stash_value[i];
×
2115
        }
2116
    }
2117

2118
    return NULL;
3,205,184✔
2119
}
10,997,260✔
2120

2121
static SIXELSTATUS
2122
cuckoo_table32_grow(struct cuckoo_table32 *table)
×
2123
{
2124
    struct cuckoo_table32 tmp;
2125
    struct cuckoo_bucket32 *old_buckets;
2126
    size_t old_count;
2127
    size_t i;
2128
    size_t j;
2129
    SIXELSTATUS status;
2130

2131
    if (table == NULL || table->allocator == NULL) {
×
2132
        return SIXEL_BAD_ARGUMENT;
×
2133
    }
2134

2135
    tmp.buckets = NULL;
×
2136
    tmp.bucket_count = 0U;
×
2137
    tmp.bucket_mask = 0U;
×
2138
    tmp.stash_count = 0U;
×
2139
    tmp.entry_count = 0U;
×
2140
    tmp.allocator = table->allocator;
×
2141
    for (i = 0U; i < CUCKOO_STASH_SIZE; ++i) {
×
2142
        tmp.stash_key[i] = CUCKOO_EMPTY_KEY;
×
2143
        tmp.stash_value[i] = 0U;
×
2144
    }
2145

2146
    status = cuckoo_table32_init(&tmp,
×
2147
                                 (table->entry_count + 1U) * 2U,
×
2148
                                 table->allocator);
2149
    if (SIXEL_FAILED(status)) {
×
2150
        return status;
×
2151
    }
2152

2153
    old_buckets = table->buckets;
×
2154
    old_count = table->bucket_count;
×
2155
    for (i = 0U; i < old_count; ++i) {
×
2156
        for (j = 0U; j < CUCKOO_BUCKET_SIZE; ++j) {
×
2157
            if (old_buckets[i].value[j] != 0U) {
×
2158
                status = cuckoo_table32_insert(&tmp,
×
2159
                                               old_buckets[i].key[j],
×
2160
                                               old_buckets[i].value[j]);
×
2161
                if (SIXEL_FAILED(status)) {
×
2162
                    cuckoo_table32_fini(&tmp);
×
2163
                    return status;
×
2164
                }
2165
            }
2166
        }
2167
    }
2168
    for (i = 0U; i < table->stash_count; ++i) {
×
2169
        if (table->stash_value[i] != 0U) {
×
2170
            status = cuckoo_table32_insert(&tmp,
×
2171
                                           table->stash_key[i],
2172
                                           table->stash_value[i]);
2173
            if (SIXEL_FAILED(status)) {
×
2174
                cuckoo_table32_fini(&tmp);
×
2175
                return status;
×
2176
            }
2177
        }
2178
    }
2179

2180
    sixel_allocator_free(table->allocator, old_buckets);
×
2181
    *table = tmp;
×
2182

2183
    return SIXEL_OK;
×
2184
}
2185

2186
static SIXELSTATUS
2187
cuckoo_table32_insert(struct cuckoo_table32 *table,
1,602,592✔
2188
                      uint32_t key,
2189
                      uint32_t value)
2190
{
2191
    uint32_t *slot;
2192
    uint32_t cur_key;
2193
    uint32_t cur_value;
2194
    uint32_t victim_key;
2195
    uint32_t victim_value;
2196
    size_t bucket_index;
2197
    size_t kicks;
2198
    size_t victim_slot;
2199
    struct cuckoo_bucket32 *bucket;
2200
    SIXELSTATUS status;
2201

2202
    if (table == NULL || table->buckets == NULL) {
1,602,592!
2203
        return SIXEL_BAD_ARGUMENT;
×
2204
    }
2205

2206
    slot = cuckoo_table32_lookup(table, key);
1,602,592✔
2207
    if (slot != NULL) {
1,602,592!
2208
        *slot = value;
×
2209
        return SIXEL_OK;
×
2210
    }
2211

2212
    cur_key = key;
1,602,592✔
2213
    cur_value = value;
1,602,592✔
2214
    bucket_index = cuckoo_hash_primary(cur_key, table->bucket_mask);
1,602,592✔
2215
    for (kicks = 0U; kicks < CUCKOO_MAX_KICKS; ++kicks) {
1,602,606!
2216
        bucket = &table->buckets[bucket_index];
1,602,606✔
2217
        if (cuckoo_bucket_insert_direct(bucket, cur_key, cur_value)) {
1,602,606✔
2218
            table->entry_count++;
1,602,592✔
2219
            return SIXEL_OK;
1,602,592✔
2220
        }
2221
        victim_slot = (size_t)((cur_key + kicks) &
14✔
2222
                               (CUCKOO_BUCKET_SIZE - 1U));
2223
        victim_key = bucket->key[victim_slot];
14✔
2224
        victim_value = bucket->value[victim_slot];
14✔
2225
        bucket->key[victim_slot] = cur_key;
14✔
2226
        bucket->value[victim_slot] = cur_value;
14✔
2227
        cur_key = victim_key;
14✔
2228
        cur_value = victim_value;
14✔
2229
        bucket_index = cuckoo_hash_alternate(cur_key,
16✔
2230
                                             bucket_index,
2✔
2231
                                             table->bucket_mask);
2✔
2232
    }
2✔
2233

2234
    if (table->stash_count < CUCKOO_STASH_SIZE) {
×
2235
        table->stash_key[table->stash_count] = cur_key;
×
2236
        table->stash_value[table->stash_count] = cur_value;
×
2237
        table->stash_count++;
×
2238
        table->entry_count++;
×
2239
        return SIXEL_OK;
×
2240
    }
2241

2242
    status = cuckoo_table32_grow(table);
×
2243
    if (SIXEL_FAILED(status)) {
×
2244
        return status;
×
2245
    }
2246

2247
    return cuckoo_table32_insert(table, cur_key, cur_value);
×
2248
}
517,778✔
2249

2250
static SIXELSTATUS
2251
computeHistogram_robinhood(unsigned char const *data,
2252
                           unsigned int length,
2253
                           unsigned long depth,
2254
                           unsigned int step,
2255
                           unsigned int max_sample,
2256
                           tupletable2 * const colorfreqtableP,
2257
                           struct histogram_control const *control,
2258
                           sixel_allocator_t *allocator);
2259

2260
static SIXELSTATUS
2261
computeHistogram_hopscotch(unsigned char const *data,
2262
                           unsigned int length,
2263
                           unsigned long depth,
2264
                           unsigned int step,
2265
                           unsigned int max_sample,
2266
                           tupletable2 * const colorfreqtableP,
2267
                           struct histogram_control const *control,
2268
                           sixel_allocator_t *allocator);
2269

2270
static SIXELSTATUS
2271
computeHistogram(unsigned char const    /* in */  *data,
256✔
2272
                 unsigned int           /* in */  length,
2273
                 unsigned long const    /* in */  depth,
2274
                 tupletable2 * const    /* out */ colorfreqtableP,
2275
                 int const              /* in */  qualityMode,
2276
                 sixel_allocator_t      /* in */  *allocator)
2277
{
2278
    SIXELSTATUS status = SIXEL_FALSE;
256✔
2279
    typedef uint32_t unit_t;
2280
    unsigned int i, n;
2281
    unit_t *histogram = NULL;
256✔
2282
    unit_t *refmap = NULL;
256✔
2283
    unit_t *ref;
2284
    unsigned int bucket_index;
2285
    unsigned int step;
2286
    unsigned int max_sample;
2287
    size_t hist_size;
2288
    unit_t bucket_value;
2289
    unsigned int component;
2290
    unsigned int reconstructed;
2291
    struct histogram_control control;
2292

2293
    switch (qualityMode) {
256!
2294
    case SIXEL_QUALITY_LOW:
118✔
2295
        max_sample = 18383;
223✔
2296
        break;
223✔
2297
    case SIXEL_QUALITY_HIGH:
20✔
2298
        max_sample = 1118383;
30✔
2299
        break;
30✔
2300
    case SIXEL_QUALITY_FULL:
3✔
2301
    default:
2302
        max_sample = 4003079;
3✔
2303
        break;
3✔
2304
    }
2305

2306
    step = length / depth / max_sample * depth;
256✔
2307
    if (step <= 0) {
256✔
2308
        step = depth;
156✔
2309
    }
52✔
2310

2311
    quant_trace(stderr, "making histogram...\n");
256✔
2312

2313
    control = histogram_control_make((unsigned int)depth);
256✔
2314
    if (histogram_lut_policy == SIXEL_LUT_POLICY_ROBINHOOD
256!
2315
        || histogram_lut_policy == SIXEL_LUT_POLICY_HOPSCOTCH) {
256!
2316
        if (histogram_lut_policy == SIXEL_LUT_POLICY_ROBINHOOD) {
×
2317
            status = computeHistogram_robinhood(data,
×
2318
                                                length,
2319
                                                depth,
2320
                                                step,
2321
                                                max_sample,
2322
                                                colorfreqtableP,
2323
                                                &control,
2324
                                                allocator);
2325
        } else {
2326
            status = computeHistogram_hopscotch(data,
×
2327
                                                length,
2328
                                                depth,
2329
                                                step,
2330
                                                max_sample,
2331
                                                colorfreqtableP,
2332
                                                &control,
2333
                                                allocator);
2334
        }
2335
        goto end;
×
2336
    }
2337

2338
    hist_size = histogram_dense_size((unsigned int)depth, &control);
256✔
2339
    histogram = (unit_t *)sixel_allocator_calloc(allocator,
372✔
2340
                                                 hist_size,
116✔
2341
                                                 sizeof(unit_t));
2342
    if (histogram == NULL) {
256!
2343
        sixel_helper_set_additional_message(
×
2344
            "unable to allocate memory for histogram.");
2345
        status = SIXEL_BAD_ALLOCATION;
×
2346
        goto end;
×
2347
    }
2348
    ref = refmap = (unit_t *)sixel_allocator_malloc(allocator,
372✔
2349
                                                    hist_size *
116✔
2350
                                                    sizeof(unit_t));
2351
    if (refmap == NULL) {
256!
2352
        sixel_helper_set_additional_message(
×
2353
            "unable to allocate memory for lookup table.");
2354
        status = SIXEL_BAD_ALLOCATION;
×
2355
        goto end;
×
2356
    }
2357

2358
    for (i = 0; i < length; i += step) {
3,498,898✔
2359
        bucket_index = histogram_pack_color(data + i,
5,238,936✔
2360
                                            (unsigned int)depth,
1,740,294✔
2361
                                            &control);
2362
        if (histogram[bucket_index] == 0) {
3,498,642✔
2363
            *ref++ = bucket_index;
287,619✔
2364
        }
95,967✔
2365
        if (histogram[bucket_index] < UINT32_MAX) {
3,498,642!
2366
            histogram[bucket_index]++;
3,498,642✔
2367
        }
1,740,294✔
2368
    }
1,740,294✔
2369

2370
    colorfreqtableP->size = (unsigned int)(ref - refmap);
256✔
2371

2372
    status = alloctupletable(&colorfreqtableP->table,
372✔
2373
                             depth,
116✔
2374
                             (unsigned int)(ref - refmap),
256✔
2375
                             allocator);
116✔
2376
    if (SIXEL_FAILED(status)) {
256!
2377
        goto end;
×
2378
    }
2379
    for (i = 0; i < colorfreqtableP->size; ++i) {
287,875✔
2380
        bucket_value = refmap[i];
287,619✔
2381
        if (histogram[bucket_value] > 0) {
287,619!
2382
            colorfreqtableP->table[i]->value = histogram[bucket_value];
287,619✔
2383
            for (n = 0; n < depth; n++) {
1,150,476✔
2384
                component = (unsigned int)
862,857✔
2385
                    ((bucket_value >> (n * control.channel_bits)) &
1,150,758✔
2386
                     control.channel_mask);
862,857✔
2387
                reconstructed = histogram_reconstruct(component,
862,857✔
2388
                                                      &control);
2389
                colorfreqtableP->table[i]->tuple[depth - 1 - n]
862,857✔
2390
                    = (sample)reconstructed;
1,150,758✔
2391
            }
287,901✔
2392
        }
95,967✔
2393
    }
95,967✔
2394

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

2397
    status = SIXEL_OK;
256✔
2398

2399
end:
140✔
2400
    sixel_allocator_free(allocator, refmap);
256✔
2401
    sixel_allocator_free(allocator, histogram);
256✔
2402

2403
    return status;
256✔
2404
}
2405

2406
static SIXELSTATUS
2407
computeHistogram_robinhood(unsigned char const *data,
×
2408
                           unsigned int length,
2409
                           unsigned long depth,
2410
                           unsigned int step,
2411
                           unsigned int max_sample,
2412
                           tupletable2 * const colorfreqtableP,
2413
                           struct histogram_control const *control,
2414
                           sixel_allocator_t *allocator)
2415
{
2416
    SIXELSTATUS status = SIXEL_FALSE;
×
2417
    struct robinhood_table32 table;
2418
    size_t expected;
2419
    size_t cap_limit;
2420
    size_t index;
2421
    unsigned int depth_u;
2422
    unsigned int i;
2423
    unsigned int n;
2424
    uint32_t bucket_color;
2425
    uint32_t bucket_hash;
2426
    uint32_t entry_color;
2427
    struct robinhood_slot32 *slot;
2428
    unsigned int component;
2429
    unsigned int reconstructed;
2430

2431
    /*
2432
     * The ASCII sketch below shows how the sparse table stores samples:
2433
     *
2434
     *   [hash]->(key,value,distance)  Robin Hood probing keeps dense tails.
2435
     */
2436
    table.slots = NULL;
×
2437
    table.capacity = 0U;
×
2438
    table.count = 0U;
×
2439
    table.allocator = allocator;
×
2440
    cap_limit = (size_t)1U << 20;
×
2441
    expected = max_sample;
×
2442
    if (expected < 256U) {
×
2443
        expected = 256U;
×
2444
    }
2445
    if (expected > cap_limit) {
×
2446
        expected = cap_limit;
×
2447
    }
2448

2449
    status = robinhood_table32_init(&table, expected, allocator);
×
2450
    if (SIXEL_FAILED(status)) {
×
2451
        sixel_helper_set_additional_message(
×
2452
            "unable to allocate robinhood histogram.");
2453
        goto end;
×
2454
    }
2455

2456
    depth_u = (unsigned int)depth;
×
2457
    for (i = 0U; i < length; i += step) {
×
NEW
2458
        bucket_color = histogram_pack_color(data + i, depth_u, control);
×
NEW
2459
        bucket_hash = histogram_hash_mix(bucket_color);
×
2460
        /*
2461
         * Hash probing uses the mixed key while the slot stores the
2462
         * original quantized RGB value:
2463
         *
2464
         *   hash --> [slot]
2465
         *             |color|count|
2466
         */
NEW
2467
        slot = robinhood_table32_lookup(&table,
×
2468
                                        bucket_hash,
2469
                                        bucket_color);
2470
        if (slot == NULL) {
×
2471
            status = robinhood_table32_insert(&table,
×
2472
                                              bucket_hash,
2473
                                              bucket_color,
2474
                                              1U);
2475
            if (SIXEL_FAILED(status)) {
×
2476
                sixel_helper_set_additional_message(
×
2477
                    "unable to grow robinhood histogram.");
2478
                goto end;
×
2479
            }
2480
        } else if (slot->value < UINT32_MAX) {
×
2481
            slot->value++;
×
2482
        }
2483
    }
2484

2485
    if (table.count > UINT_MAX) {
×
2486
        sixel_helper_set_additional_message(
×
2487
            "too many unique colors for histogram.");
2488
        status = SIXEL_BAD_ARGUMENT;
×
2489
        goto end;
×
2490
    }
2491

2492
    colorfreqtableP->size = (unsigned int)table.count;
×
2493
    status = alloctupletable(&colorfreqtableP->table,
×
2494
                             depth_u,
2495
                             (unsigned int)table.count,
×
2496
                             allocator);
2497
    if (SIXEL_FAILED(status)) {
×
2498
        goto end;
×
2499
    }
2500

2501
    index = 0U;
×
2502
    /*
2503
     * Stream slots in the hash traversal order to avoid qsort overhead.
2504
     * This favors throughput over identical palette ordering.
2505
     */
2506
    for (i = 0U; i < table.capacity; ++i) {
×
2507
        slot = &table.slots[i];
×
2508
        if (slot->value == 0U) {
×
2509
            continue;
×
2510
        }
2511
        if (index >= colorfreqtableP->size) {
×
2512
            break;
×
2513
        }
NEW
2514
        entry_color = slot->color;
×
2515
        colorfreqtableP->table[index]->value = slot->value;
×
2516
        for (n = 0U; n < depth_u; ++n) {
×
2517
            component = (unsigned int)
×
NEW
2518
                ((entry_color >> (n * control->channel_bits))
×
2519
                 & control->channel_mask);
×
2520
            reconstructed = histogram_reconstruct(component, control);
×
2521
            colorfreqtableP->table[index]->tuple[depth_u - 1U - n]
×
2522
                = (sample)reconstructed;
×
2523
        }
2524
        index++;
×
2525
    }
2526

2527
    status = SIXEL_OK;
×
2528

2529
end:
2530
    robinhood_table32_fini(&table);
×
2531

2532
    return status;
×
2533
}
2534

2535
static SIXELSTATUS
2536
computeHistogram_hopscotch(unsigned char const *data,
×
2537
                           unsigned int length,
2538
                           unsigned long depth,
2539
                           unsigned int step,
2540
                           unsigned int max_sample,
2541
                           tupletable2 * const colorfreqtableP,
2542
                           struct histogram_control const *control,
2543
                           sixel_allocator_t *allocator)
2544
{
2545
    SIXELSTATUS status = SIXEL_FALSE;
×
2546
    struct hopscotch_table32 table;
2547
    size_t expected;
2548
    size_t cap_limit;
2549
    size_t index;
2550
    unsigned int depth_u;
2551
    unsigned int i;
2552
    unsigned int n;
2553
    uint32_t bucket_color;
2554
    uint32_t bucket_hash;
2555
    uint32_t entry_color;
2556
    struct hopscotch_slot32 *slot;
2557
    unsigned int component;
2558
    unsigned int reconstructed;
2559

2560
    /*
2561
     * Hopscotch hashing stores the local neighbourhood using the map below:
2562
     *
2563
     *   [home] hopinfo bits ---> |slot+0|slot+1|slot+2| ...
2564
     *                              ^ entries hop within this window.
2565
     */
2566
    table.slots = NULL;
×
2567
    table.hopinfo = NULL;
×
2568
    table.capacity = 0U;
×
2569
    table.count = 0U;
×
2570
    table.neighborhood = HOPSCOTCH_DEFAULT_NEIGHBORHOOD;
×
2571
    table.allocator = allocator;
×
2572
    cap_limit = (size_t)1U << 20;
×
2573
    expected = max_sample;
×
2574
    if (expected < 256U) {
×
2575
        expected = 256U;
×
2576
    }
2577
    if (expected > cap_limit) {
×
2578
        expected = cap_limit;
×
2579
    }
2580

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

2588
    depth_u = (unsigned int)depth;
×
2589
    for (i = 0U; i < length; i += step) {
×
NEW
2590
        bucket_color = histogram_pack_color(data + i, depth_u, control);
×
NEW
2591
        bucket_hash = histogram_hash_mix(bucket_color);
×
2592
        /*
2593
         * Hopscotch buckets mirror the robinhood layout, keeping the
2594
         * quantized color next to the count so we never derive it from
2595
         * the scrambled hash key.
2596
         */
NEW
2597
        slot = hopscotch_table32_lookup(&table,
×
2598
                                        bucket_hash,
2599
                                        bucket_color);
2600
        if (slot == NULL) {
×
2601
            status = hopscotch_table32_insert(&table,
×
2602
                                              bucket_hash,
2603
                                              bucket_color,
2604
                                              1U);
2605
            if (SIXEL_FAILED(status)) {
×
2606
                sixel_helper_set_additional_message(
×
2607
                    "unable to grow hopscotch histogram.");
2608
                goto end;
×
2609
            }
2610
        } else if (slot->value < UINT32_MAX) {
×
2611
            slot->value++;
×
2612
        }
2613
    }
2614

2615
    if (table.count > UINT_MAX) {
×
2616
        sixel_helper_set_additional_message(
×
2617
            "too many unique colors for histogram.");
2618
        status = SIXEL_BAD_ARGUMENT;
×
2619
        goto end;
×
2620
    }
2621

2622
    colorfreqtableP->size = (unsigned int)table.count;
×
2623
    status = alloctupletable(&colorfreqtableP->table,
×
2624
                             depth_u,
2625
                             (unsigned int)table.count,
×
2626
                             allocator);
2627
    if (SIXEL_FAILED(status)) {
×
2628
        goto end;
×
2629
    }
2630

2631
    index = 0U;
×
2632
    /*
2633
     * Stream slots in the hash traversal order to avoid qsort overhead.
2634
     * This favors throughput over identical palette ordering.
2635
     */
2636
    for (i = 0U; i < table.capacity; ++i) {
×
2637
        slot = &table.slots[i];
×
2638
        if (slot->key == HOPSCOTCH_EMPTY_KEY || slot->value == 0U) {
×
2639
            continue;
×
2640
        }
2641
        if (index >= colorfreqtableP->size) {
×
2642
            break;
×
2643
        }
NEW
2644
        entry_color = slot->color;
×
2645
        colorfreqtableP->table[index]->value = slot->value;
×
2646
        for (n = 0U; n < depth_u; ++n) {
×
2647
            component = (unsigned int)
×
NEW
2648
                ((entry_color >> (n * control->channel_bits))
×
2649
                 & control->channel_mask);
×
2650
            reconstructed = histogram_reconstruct(component, control);
×
2651
            colorfreqtableP->table[index]->tuple[depth_u - 1U - n]
×
2652
                = (sample)reconstructed;
×
2653
        }
2654
        index++;
×
2655
    }
2656

2657
    status = SIXEL_OK;
×
2658

2659
end:
2660
    hopscotch_table32_fini(&table);
×
2661

2662
    return status;
×
2663
}
2664

2665
SIXELSTATUS
2666
sixel_quant_cache_prepare(unsigned short **cachetable,
263✔
2667
                          size_t *cachetable_size,
2668
                          int lut_policy,
2669
                          int reqcolor,
2670
                          sixel_allocator_t *allocator)
2671
{
2672
    SIXELSTATUS status;
2673
    struct histogram_control control;
2674
    struct cuckoo_table32 *table;
2675
    size_t expected;
2676
    size_t buckets;
2677
    size_t cap_limit;
2678
    int normalized;
2679

2680
    if (cachetable == NULL || allocator == NULL) {
263!
2681
        return SIXEL_BAD_ARGUMENT;
×
2682
    }
2683

2684
    /*
2685
     * The cache pointer always references the same ladder:
2686
     *
2687
     *   cache -> cuckoo_table32 -> buckets
2688
     *                           -> stash
2689
     */
2690
    normalized = lut_policy;
263✔
2691
    if (normalized == SIXEL_LUT_POLICY_AUTO) {
263!
2692
        normalized = histogram_lut_policy;
263✔
2693
    }
107✔
2694
    if (normalized == SIXEL_LUT_POLICY_AUTO) {
263!
2695
        normalized = SIXEL_LUT_POLICY_6BIT;
263✔
2696
    }
107✔
2697

2698
    control = histogram_control_make_for_policy(3U, normalized);
263✔
2699
    if (control.channel_shift == 0U) {
263!
2700
        expected = (size_t)reqcolor * 64U;
×
2701
        cap_limit = (size_t)1U << 20;
×
2702
        if (expected < 512U) {
×
2703
            expected = 512U;
×
2704
        }
2705
        if (expected > cap_limit) {
×
2706
            expected = cap_limit;
×
2707
        }
2708
    } else {
2709
        expected = histogram_dense_size(3U, &control);
263✔
2710
        if (expected == 0U) {
263!
2711
            expected = CUCKOO_BUCKET_SIZE;
×
2712
        }
2713
    }
2714

2715
    table = (struct cuckoo_table32 *)*cachetable;
263✔
2716
    if (table != NULL) {
263✔
2717
        buckets = cuckoo_round_buckets(expected);
3✔
2718
        if (table->bucket_count < buckets) {
3!
2719
            cuckoo_table32_fini(table);
×
2720
            sixel_allocator_free(allocator, table);
×
2721
            table = NULL;
×
2722
            *cachetable = NULL;
×
2723
        } else {
2724
            cuckoo_table32_clear(table);
3✔
2725
        }
2726
    }
1✔
2727
    if (table == NULL) {
263✔
2728
        table = (struct cuckoo_table32 *)sixel_allocator_malloc(
260✔
2729
            allocator, sizeof(struct cuckoo_table32));
106✔
2730
        if (table == NULL) {
260!
2731
            sixel_helper_set_additional_message(
×
2732
                "unable to allocate cuckoo cache state.");
2733
            return SIXEL_BAD_ALLOCATION;
×
2734
        }
2735
        memset(table, 0, sizeof(struct cuckoo_table32));
260✔
2736
        status = cuckoo_table32_init(table, expected, allocator);
260✔
2737
        if (SIXEL_FAILED(status)) {
260!
2738
            sixel_allocator_free(allocator, table);
×
2739
            sixel_helper_set_additional_message(
×
2740
                "unable to initialize cuckoo cache.");
2741
            return status;
×
2742
        }
2743
        *cachetable = (unsigned short *)table;
260✔
2744
    }
106✔
2745

2746
    if (cachetable_size != NULL) {
263!
2747
        *cachetable_size = table->bucket_count * CUCKOO_BUCKET_SIZE;
263✔
2748
    }
107✔
2749

2750
    return SIXEL_OK;
263✔
2751
}
107✔
2752

2753
void
2754
sixel_quant_cache_clear(unsigned short *cachetable,
260✔
2755
                        int lut_policy)
2756
{
2757
    struct cuckoo_table32 *table;
2758

2759
    (void)lut_policy;
106✔
2760
    if (cachetable == NULL) {
260!
2761
        return;
×
2762
    }
2763

2764
    table = (struct cuckoo_table32 *)cachetable;
260✔
2765
    cuckoo_table32_clear(table);
260✔
2766
}
106✔
2767

2768
void
2769
sixel_quant_cache_release(unsigned short *cachetable,
525✔
2770
                          int lut_policy,
2771
                          sixel_allocator_t *allocator)
2772
{
2773
    struct cuckoo_table32 *table;
2774

2775
    (void)lut_policy;
181✔
2776
    if (cachetable == NULL || allocator == NULL) {
525!
2777
        return;
265✔
2778
    }
2779

2780
    table = (struct cuckoo_table32 *)cachetable;
260✔
2781
    cuckoo_table32_fini(table);
260✔
2782
    sixel_allocator_free(allocator, table);
260✔
2783
}
181✔
2784

2785

2786
static int
2787
computeColorMapFromInput(unsigned char const *data,
256✔
2788
                         unsigned int const length,
2789
                         unsigned int const depth,
2790
                         unsigned int const reqColors,
2791
                         int const methodForLargest,
2792
                         int const methodForRep,
2793
                         int const qualityMode,
2794
                         int const force_palette,
2795
                         tupletable2 * const colormapP,
2796
                         unsigned int *origcolors,
2797
                         sixel_allocator_t *allocator)
2798
{
2799
/*----------------------------------------------------------------------------
2800
   Produce a colormap containing the best colors to represent the
2801
   image stream in file 'ifP'.  Figure it out using the median cut
2802
   technique.
2803

2804
   The colormap will have 'reqcolors' or fewer colors in it, unless
2805
   'allcolors' is true, in which case it will have all the colors that
2806
   are in the input.
2807

2808
   The colormap has the same maxval as the input.
2809

2810
   Put the colormap in newly allocated storage as a tupletable2
2811
   and return its address as *colormapP.  Return the number of colors in
2812
   it as *colorsP and its maxval as *colormapMaxvalP.
2813

2814
   Return the characteristics of the input file as
2815
   *formatP and *freqPamP.  (This information is not really
2816
   relevant to our colormap mission; just a fringe benefit).
2817
-----------------------------------------------------------------------------*/
2818
    SIXELSTATUS status = SIXEL_FALSE;
256✔
2819
    tupletable2 colorfreqtable = {0, NULL};
256✔
2820
    unsigned int i;
2821
    unsigned int n;
2822

2823
    status = computeHistogram(data, length, depth,
372✔
2824
                              &colorfreqtable, qualityMode, allocator);
116✔
2825
    if (SIXEL_FAILED(status)) {
256!
2826
        goto end;
×
2827
    }
2828
    if (origcolors) {
256!
2829
        *origcolors = colorfreqtable.size;
256✔
2830
    }
116✔
2831

2832
    if (colorfreqtable.size <= reqColors) {
256✔
2833
        quant_trace(stderr,
284✔
2834
                    "Image already has few enough colors (<=%d).  "
2835
                    "Keeping same colors.\n", reqColors);
94✔
2836
        /* *colormapP = colorfreqtable; */
2837
        colormapP->size = colorfreqtable.size;
190✔
2838
        status = alloctupletable(&colormapP->table, depth, colorfreqtable.size, allocator);
190✔
2839
        if (SIXEL_FAILED(status)) {
190!
2840
            goto end;
×
2841
        }
2842
        for (i = 0; i < colorfreqtable.size; ++i) {
2,956✔
2843
            colormapP->table[i]->value = colorfreqtable.table[i]->value;
2,766✔
2844
            for (n = 0; n < depth; ++n) {
11,064✔
2845
                colormapP->table[i]->tuple[n] = colorfreqtable.table[i]->tuple[n];
8,298✔
2846
            }
3,480✔
2847
        }
1,160✔
2848
    } else {
94✔
2849
        quant_trace(stderr, "choosing %d colors...\n", reqColors);
66✔
2850
        status = mediancut(colorfreqtable, depth, reqColors,
88✔
2851
                           methodForLargest, methodForRep, colormapP, allocator);
22✔
2852
        if (SIXEL_FAILED(status)) {
66!
2853
            goto end;
×
2854
        }
2855
        quant_trace(stderr, "%d colors are choosed.\n", colorfreqtable.size);
66✔
2856
    }
2857

2858
    if (force_palette) {
256!
2859
        status = force_palette_completion(colormapP, depth, reqColors,
×
2860
                                          colorfreqtable, allocator);
2861
        if (SIXEL_FAILED(status)) {
×
2862
            goto end;
×
2863
        }
2864
    }
2865

2866
    status = SIXEL_OK;
256✔
2867

2868
end:
140✔
2869
    sixel_allocator_free(allocator, colorfreqtable.table);
256✔
2870
    return status;
256✔
2871
}
2872

2873

2874
/* diffuse error energy to surround pixels (normal strategy) */
2875
static void
2876
error_diffuse_normal(
128,311,857✔
2877
    unsigned char /* in */    *data,      /* base address of pixel buffer */
2878
    int           /* in */    pos,        /* address of the destination pixel */
2879
    int           /* in */    depth,      /* color depth in bytes */
2880
    int           /* in */    error,      /* error energy */
2881
    int           /* in */    numerator,  /* numerator of diffusion coefficient */
2882
    int           /* in */    denominator /* denominator of diffusion coefficient */)
2883
{
2884
    int c;
2885

2886
    data += pos * depth;
128,311,857✔
2887

2888
    c = *data + (error * numerator * 2 / denominator + 1) / 2;
128,311,857✔
2889
    if (c < 0) {
128,311,857✔
2890
        c = 0;
2,632,929✔
2891
    }
879,627✔
2892
    if (c >= 1 << 8) {
128,311,857✔
2893
        c = (1 << 8) - 1;
736,047✔
2894
    }
234,601✔
2895
    *data = (unsigned char)c;
128,311,857✔
2896
}
128,311,857✔
2897

2898
/* error diffusion with fast strategy */
2899
static void
2900
error_diffuse_fast(
169,469,067✔
2901
    unsigned char /* in */    *data,      /* base address of pixel buffer */
2902
    int           /* in */    pos,        /* address of the destination pixel */
2903
    int           /* in */    depth,      /* color depth in bytes */
2904
    int           /* in */    error,      /* error energy */
2905
    int           /* in */    numerator,  /* numerator of diffusion coefficient */
2906
    int           /* in */    denominator /* denominator of diffusion coefficient */)
2907
{
2908
    int c;
2909

2910
    data += pos * depth;
169,469,067✔
2911

2912
    c = *data + error * numerator / denominator;
169,469,067✔
2913
    if (c < 0) {
169,469,067✔
2914
        c = 0;
7,485,418✔
2915
    }
2,822,832✔
2916
    if (c >= 1 << 8) {
169,469,067✔
2917
        c = (1 << 8) - 1;
455,515✔
2918
    }
148,827✔
2919
    *data = (unsigned char)c;
169,469,067✔
2920
}
169,469,067✔
2921

2922

2923
/* error diffusion with precise strategy */
2924
static void
2925
error_diffuse_precise(
6,111,369✔
2926
    unsigned char /* in */    *data,      /* base address of pixel buffer */
2927
    int           /* in */    pos,        /* address of the destination pixel */
2928
    int           /* in */    depth,      /* color depth in bytes */
2929
    int           /* in */    error,      /* error energy */
2930
    int           /* in */    numerator,  /* numerator of diffusion coefficient */
2931
    int           /* in */    denominator /* denominator of diffusion coefficient */)
2932
{
2933
    int c;
2934

2935
    data += pos * depth;
6,111,369✔
2936

2937
    c = (int)(*data + error * numerator / (double)denominator + 0.5);
6,111,369✔
2938
    if (c < 0) {
6,111,369✔
2939
        c = 0;
70,495✔
2940
    }
25,489✔
2941
    if (c >= 1 << 8) {
6,111,369!
2942
        c = (1 << 8) - 1;
×
2943
    }
2944
    *data = (unsigned char)c;
6,111,369✔
2945
}
6,111,369✔
2946

2947

2948
typedef void (*diffuse_fixed_carry_mode)(int32_t *carry_curr,
2949
                                         int32_t *carry_next,
2950
                                         int32_t *carry_far,
2951
                                         int width,
2952
                                         int height,
2953
                                         int depth,
2954
                                         int x,
2955
                                         int y,
2956
                                         int32_t error,
2957
                                         int direction,
2958
                                         int channel);
2959

2960

2961
typedef void (*diffuse_varerr_mode)(unsigned char *data,
2962
                                    int width,
2963
                                    int height,
2964
                                    int x,
2965
                                    int y,
2966
                                    int depth,
2967
                                    int32_t error,
2968
                                    int index,
2969
                                    int direction);
2970

2971
typedef void (*diffuse_varerr_carry_mode)(int32_t *carry_curr,
2972
                                          int32_t *carry_next,
2973
                                          int32_t *carry_far,
2974
                                          int width,
2975
                                          int height,
2976
                                          int depth,
2977
                                          int x,
2978
                                          int y,
2979
                                          int32_t error,
2980
                                          int index,
2981
                                          int direction,
2982
                                          int channel);
2983

2984
static int
2985
zhoufang_index_from_byte(unsigned char value)
×
2986
{
2987
    double px;
2988
    double remapped;
2989
    int scale_index;
2990
    double scale;
2991
    double jitter;
2992
    int index;
2993

2994
    px = (double)value / 255.0;
×
2995
    remapped = px;
×
2996
    if (remapped >= 0.5) {
×
2997
        remapped = 1.0 - remapped;
×
2998
    }
2999
    if (remapped < 0.0) {
×
3000
        remapped = 0.0;
×
3001
    }
3002
    if (remapped > 0.5) {
×
3003
        remapped = 0.5;
×
3004
    }
3005

3006
    scale_index = (int)(remapped * 128.0);
×
3007
    if (scale_index < 0) {
×
3008
        scale_index = 0;
×
3009
    }
3010
    if (scale_index > 127) {
×
3011
        scale_index = 127;
×
3012
    }
3013

3014
    scale = zhoufang_threshold_gain[scale_index] / 100.0;
×
3015
    jitter = ((double)(rand() & 127) / 128.0) * scale;
×
3016
    remapped += (0.5 - remapped) * jitter;
×
3017
    if (remapped < 0.0) {
×
3018
        remapped = 0.0;
×
3019
    }
3020
    if (remapped > 0.5) {
×
3021
        remapped = 0.5;
×
3022
    }
3023

3024
    index = (int)(remapped * 255.0 + 0.5);
×
3025
    if (index > 127) {
×
3026
        index = 127;
×
3027
    }
3028
    if (index < 0) {
×
3029
        index = 0;
×
3030
    }
3031

3032
    return index;
×
3033
}
3034

3035

3036
static int32_t
3037
diffuse_varerr_term(int32_t error, int weight, int denom)
×
3038
{
3039
    int64_t delta;
3040

3041
    delta = (int64_t)error * (int64_t)weight;
×
3042
    if (delta >= 0) {
×
3043
        delta = (delta + denom / 2) / denom;
×
3044
    } else {
3045
        delta = (delta - denom / 2) / denom;
×
3046
    }
3047

3048
    return (int32_t)delta;
×
3049
}
3050

3051

3052
static int32_t
3053
diffuse_fixed_term(int32_t error, int numerator, int denominator)
×
3054
{
3055
    int64_t delta;
3056

3057
    delta = (int64_t)error * (int64_t)numerator;
×
3058
    if (delta >= 0) {
×
3059
        delta = (delta + denominator / 2) / denominator;
×
3060
    } else {
3061
        delta = (delta - denominator / 2) / denominator;
×
3062
    }
3063

3064
    return (int32_t)delta;
×
3065
}
3066

3067

3068
static void
3069
diffuse_varerr_apply_direct(unsigned char *target, int depth, size_t offset,
×
3070
                            int32_t delta)
3071
{
3072
    int64_t value;
3073
    int result;
3074

3075
    value = (int64_t)target[offset * depth] << VARERR_SCALE_SHIFT;
×
3076
    value += delta;
×
3077
    if (value < 0) {
×
3078
        value = 0;
×
3079
    } else {
3080
        int64_t max_value;
3081

3082
        max_value = VARERR_MAX_VALUE;
×
3083
        if (value > max_value) {
×
3084
            value = max_value;
×
3085
        }
3086
    }
3087

3088
    result = (int)((value + VARERR_ROUND) >> VARERR_SCALE_SHIFT);
×
3089
    if (result < 0) {
×
3090
        result = 0;
×
3091
    }
3092
    if (result > 255) {
×
3093
        result = 255;
×
3094
    }
3095
    target[offset * depth] = (unsigned char)result;
×
3096
}
×
3097

3098

3099
static void
3100
diffuse_lso2(unsigned char *data, int width, int height,
×
3101
             int x, int y, int depth, int32_t error,
3102
             int index, int direction)
3103
{
3104
    const int (*table)[7];
3105
    const int *entry;
3106
    int denom;
3107
    int32_t term_r = 0;
×
3108
    int32_t term_r2 = 0;
×
3109
    int32_t term_dl = 0;
×
3110
    int32_t term_d = 0;
×
3111
    int32_t term_dr = 0;
×
3112
    int32_t term_d2 = 0;
×
3113
    size_t offset;
3114

3115
    if (error == 0) {
×
3116
        return;
×
3117
    }
3118
    if (index < 0) {
×
3119
        index = 0;
×
3120
    }
3121
    if (index > 255) {
×
3122
        index = 255;
×
3123
    }
3124

3125
    table = lso2_table();
×
3126
    entry = table[index];
×
3127
    denom = entry[6];
×
3128
    if (denom == 0) {
×
3129
        return;
×
3130
    }
3131

3132
    term_r = diffuse_varerr_term(error, entry[0], denom);
×
3133
    term_r2 = diffuse_varerr_term(error, entry[1], denom);
×
3134
    term_dl = diffuse_varerr_term(error, entry[2], denom);
×
3135
    term_d = diffuse_varerr_term(error, entry[3], denom);
×
3136
    term_dr = diffuse_varerr_term(error, entry[4], denom);
×
3137
    term_d2 = diffuse_varerr_term(error, entry[5], denom);
×
3138

3139

3140
    if (direction >= 0) {
×
3141
        if (x + 1 < width) {
×
3142
            offset = (size_t)y * (size_t)width + (size_t)(x + 1);
×
3143
            diffuse_varerr_apply_direct(data, depth, offset, term_r);
×
3144
        }
3145
        if (x + 2 < width) {
×
3146
            offset = (size_t)y * (size_t)width + (size_t)(x + 2);
×
3147
            diffuse_varerr_apply_direct(data, depth, offset, term_r2);
×
3148
        }
3149
        if (y + 1 < height && x - 1 >= 0) {
×
3150
            offset = (size_t)(y + 1) * (size_t)width;
×
3151
            offset += (size_t)(x - 1);
×
3152
            diffuse_varerr_apply_direct(data, depth, offset, term_dl);
×
3153
        }
3154
        if (y + 1 < height) {
×
3155
            offset = (size_t)(y + 1) * (size_t)width + (size_t)x;
×
3156
            diffuse_varerr_apply_direct(data, depth, offset, term_d);
×
3157
        }
3158
        if (y + 1 < height && x + 1 < width) {
×
3159
            offset = (size_t)(y + 1) * (size_t)width;
×
3160
            offset += (size_t)(x + 1);
×
3161
            diffuse_varerr_apply_direct(data, depth, offset, term_dr);
×
3162
        }
3163
        if (y + 2 < height) {
×
3164
            offset = (size_t)(y + 2) * (size_t)width + (size_t)x;
×
3165
            diffuse_varerr_apply_direct(data, depth, offset, term_d2);
×
3166
        }
3167
    } else {
3168
        if (x - 1 >= 0) {
×
3169
            offset = (size_t)y * (size_t)width + (size_t)(x - 1);
×
3170
            diffuse_varerr_apply_direct(data, depth, offset, term_r);
×
3171
        }
3172
        if (x - 2 >= 0) {
×
3173
            offset = (size_t)y * (size_t)width + (size_t)(x - 2);
×
3174
            diffuse_varerr_apply_direct(data, depth, offset, term_r2);
×
3175
        }
3176
        if (y + 1 < height && x + 1 < width) {
×
3177
            offset = (size_t)(y + 1) * (size_t)width;
×
3178
            offset += (size_t)(x + 1);
×
3179
            diffuse_varerr_apply_direct(data, depth, offset, term_dl);
×
3180
        }
3181
        if (y + 1 < height) {
×
3182
            offset = (size_t)(y + 1) * (size_t)width + (size_t)x;
×
3183
            diffuse_varerr_apply_direct(data, depth, offset, term_d);
×
3184
        }
3185
        if (y + 1 < height && x - 1 >= 0) {
×
3186
            offset = (size_t)(y + 1) * (size_t)width;
×
3187
            offset += (size_t)(x - 1);
×
3188
            diffuse_varerr_apply_direct(data, depth, offset, term_dr);
×
3189
        }
3190
        if (y + 2 < height) {
×
3191
            offset = (size_t)(y + 2) * (size_t)width + (size_t)x;
×
3192
            diffuse_varerr_apply_direct(data, depth, offset, term_d2);
×
3193
        }
3194
    }
3195
}
3196

3197

3198
static void
3199
diffuse_lso3(unsigned char *data, int width, int height,
×
3200
             int x, int y, int depth, int32_t error,
3201
             int index, int direction)
3202
{
3203
    const int (*table)[7];
3204
    const int *entry;
3205
    int denom;
3206
    int32_t term_r = 0;
×
3207
    int32_t term_r2 = 0;
×
3208
    int32_t term_dl = 0;
×
3209
    int32_t term_d = 0;
×
3210
    int32_t term_dr = 0;
×
3211
    int32_t term_d2 = 0;
×
3212
    size_t offset;
3213

3214
    if (error == 0) {
×
3215
        return;
×
3216
    }
3217
    if (index < 0) {
×
3218
        index = 0;
×
3219
    }
3220
    if (index > 255) {
×
3221
        index = 255;
×
3222
    }
3223

3224
    table = lso3_table();
×
3225
    entry = table[index];
×
3226
    denom = entry[6];
×
3227
    if (denom == 0) {
×
3228
        return;
×
3229
    }
3230

3231
    term_r = diffuse_varerr_term(error, entry[0], denom);
×
3232
    term_r2 = diffuse_varerr_term(error, entry[1], denom);
×
3233
    term_dl = diffuse_varerr_term(error, entry[2], denom);
×
3234
    term_d = diffuse_varerr_term(error, entry[3], denom);
×
3235
    term_dr = diffuse_varerr_term(error, entry[4], denom);
×
3236
    term_d2 = error - term_r - term_r2 - term_dl - term_d - term_dr;
×
3237

3238
    if (direction >= 0) {
×
3239
        if (x + 1 < width) {
×
3240
            offset = (size_t)y * (size_t)width + (size_t)(x + 1);
×
3241
            diffuse_varerr_apply_direct(data, depth, offset, term_r);
×
3242
        }
3243
        if (x + 2 < width) {
×
3244
            offset = (size_t)y * (size_t)width + (size_t)(x + 2);
×
3245
            diffuse_varerr_apply_direct(data, depth, offset, term_r2);
×
3246
        }
3247
        if (y + 1 < height && x - 1 >= 0) {
×
3248
            offset = (size_t)(y + 1) * (size_t)width;
×
3249
            offset += (size_t)(x - 1);
×
3250
            diffuse_varerr_apply_direct(data, depth, offset, term_dl);
×
3251
        }
3252
        if (y + 1 < height) {
×
3253
            offset = (size_t)(y + 1) * (size_t)width + (size_t)x;
×
3254
            diffuse_varerr_apply_direct(data, depth, offset, term_d);
×
3255
        }
3256
        if (y + 1 < height && x + 1 < width) {
×
3257
            offset = (size_t)(y + 1) * (size_t)width;
×
3258
            offset += (size_t)(x + 1);
×
3259
            diffuse_varerr_apply_direct(data, depth, offset, term_dr);
×
3260
        }
3261
        if (y + 2 < height) {
×
3262
            offset = (size_t)(y + 2) * (size_t)width + (size_t)x;
×
3263
            diffuse_varerr_apply_direct(data, depth, offset, term_d2);
×
3264
        }
3265
    } else {
3266
        if (x - 1 >= 0) {
×
3267
            offset = (size_t)y * (size_t)width + (size_t)(x - 1);
×
3268
            diffuse_varerr_apply_direct(data, depth, offset, term_r);
×
3269
        }
3270
        if (x - 1 >= 0) {
×
3271
            offset = (size_t)y * (size_t)width + (size_t)(x - 2);
×
3272
            diffuse_varerr_apply_direct(data, depth, offset, term_r2);
×
3273
        }
3274
        if (y + 1 < height && x + 1 < width) {
×
3275
            offset = (size_t)(y + 1) * (size_t)width;
×
3276
            offset += (size_t)(x + 1);
×
3277
            diffuse_varerr_apply_direct(data, depth, offset, term_dl);
×
3278
        }
3279
        if (y + 1 < height) {
×
3280
            offset = (size_t)(y + 1) * (size_t)width + (size_t)x;
×
3281
            diffuse_varerr_apply_direct(data, depth, offset, term_d);
×
3282
        }
3283
        if (y + 1 < height && x - 1 >= 0) {
×
3284
            offset = (size_t)(y + 1) * (size_t)width;
×
3285
            offset += (size_t)(x - 1);
×
3286
            diffuse_varerr_apply_direct(data, depth, offset, term_dr);
×
3287
        }
3288
        if (y + 2 < height) {
×
3289
            offset = (size_t)(y + 2) * (size_t)width + (size_t)x;
×
3290
            diffuse_varerr_apply_direct(data, depth, offset, term_d2);
×
3291
        }
3292
    }
3293
}
3294

3295

3296
static void
3297
diffuse_lso2_carry(int32_t *carry_curr, int32_t *carry_next, int32_t *carry_far,
×
3298
                   int width, int height, int depth,
3299
                   int x, int y, int32_t error,
3300
                   int index, int direction, int channel)
3301
{
3302
    const int (*table)[7];
3303
    const int *entry;
3304
    int denom;
3305
    int32_t term_r = 0;
×
3306
    int32_t term_r2 = 0;
×
3307
    int32_t term_dl = 0;
×
3308
    int32_t term_d = 0;
×
3309
    int32_t term_dr = 0;
×
3310
    int32_t term_d2 = 0;
×
3311
    size_t base;
3312

3313
    if (error == 0) {
×
3314
        return;
×
3315
    }
3316
    if (index < 0) {
×
3317
        index = 0;
×
3318
    }
3319
    if (index > 255) {
×
3320
        index = 255;
×
3321
    }
3322

3323
    table = lso2_table();
×
3324
    entry = table[index];
×
3325
    denom = entry[6];
×
3326
    if (denom == 0) {
×
3327
        return;
×
3328
    }
3329

3330
    term_r = diffuse_varerr_term(error, entry[0], denom);
×
3331
    term_r2 = diffuse_varerr_term(error, entry[1], denom);
×
3332
    term_dl = diffuse_varerr_term(error, entry[2], denom);
×
3333
    term_d = diffuse_varerr_term(error, entry[3], denom);
×
3334
    term_dr = diffuse_varerr_term(error, entry[4], denom);
×
3335
    term_d2 = error - term_r - term_r2 - term_dl - term_d - term_dr;
×
3336

3337
    if (direction >= 0) {
×
3338
        if (x + 1 < width) {
×
3339
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
3340
            carry_curr[base] += term_r;
×
3341
        }
3342
        if (x + 2 < width) {
×
3343
            base = ((size_t)(x + 2) * (size_t)depth) + (size_t)channel;
×
3344
            carry_curr[base] += term_r2;
×
3345
        }
3346
        if (y + 1 < height && x - 1 >= 0) {
×
3347
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
3348
            carry_next[base] += term_dl;
×
3349
        }
3350
        if (y + 1 < height) {
×
3351
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
3352
            carry_next[base] += term_d;
×
3353
        }
3354
        if (y + 1 < height && x + 1 < width) {
×
3355
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
3356
            carry_next[base] += term_dr;
×
3357
        }
3358
        if (y + 2 < height) {
×
3359
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
3360
            carry_far[base] += term_d2;
×
3361
        }
3362
    } else {
3363
        if (x - 1 >= 0) {
×
3364
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
3365
            carry_curr[base] += term_r;
×
3366
        }
3367
        if (x - 2 >= 0) {
×
3368
            base = ((size_t)(x - 2) * (size_t)depth) + (size_t)channel;
×
3369
            carry_curr[base] += term_r;
×
3370
        }
3371
        if (y + 1 < height && x + 1 < width) {
×
3372
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
3373
            carry_next[base] += term_dl;
×
3374
        }
3375
        if (y + 1 < height) {
×
3376
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
3377
            carry_next[base] += term_d;
×
3378
        }
3379
        if (y + 1 < height && x - 1 >= 0) {
×
3380
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
3381
            carry_next[base] += term_dr;
×
3382
        }
3383
        if (y + 2 < height) {
×
3384
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
3385
            carry_far[base] += term_d2;
×
3386
        }
3387
    }
3388
}
3389

3390

3391
static void
3392
diffuse_lso3_carry(int32_t *carry_curr, int32_t *carry_next, int32_t *carry_far,
×
3393
                   int width, int height, int depth,
3394
                   int x, int y, int32_t error,
3395
                   int index, int direction, int channel)
3396
{
3397
    const int (*table)[7];
3398
    const int *entry;
3399
    int denom;
3400
    int32_t term_r = 0;
×
3401
    int32_t term_r2 = 0;
×
3402
    int32_t term_dl = 0;
×
3403
    int32_t term_d = 0;
×
3404
    int32_t term_dr = 0;
×
3405
    int32_t term_d2 = 0;
×
3406
    size_t base;
3407

3408
    if (error == 0) {
×
3409
        return;
×
3410
    }
3411
    if (index < 0) {
×
3412
        index = 0;
×
3413
    }
3414
    if (index > 255) {
×
3415
        index = 255;
×
3416
    }
3417

3418
    table = lso3_table();
×
3419
    entry = table[index];
×
3420
    denom = entry[6];
×
3421
    if (denom == 0) {
×
3422
        return;
×
3423
    }
3424

3425
    term_r = diffuse_varerr_term(error, entry[0], denom);
×
3426
    term_r2 = diffuse_varerr_term(error, entry[1], denom);
×
3427
    term_dl = diffuse_varerr_term(error, entry[2], denom);
×
3428
    term_d = diffuse_varerr_term(error, entry[3], denom);
×
3429
    term_dr = diffuse_varerr_term(error, entry[4], denom);
×
3430
    term_d2 = diffuse_varerr_term(error, entry[5], denom);
×
3431

3432
    if (direction >= 0) {
×
3433
        if (x + 1 < width) {
×
3434
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
3435
            carry_curr[base] += term_r;
×
3436
        }
3437
        if (x + 2 < width) {
×
3438
            base = ((size_t)(x + 2) * (size_t)depth) + (size_t)channel;
×
3439
            carry_curr[base] += term_r2;
×
3440
        }
3441
        if (y + 1 < height && x - 1 >= 0) {
×
3442
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
3443
            carry_next[base] += term_dl;
×
3444
        }
3445
        if (y + 1 < height) {
×
3446
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
3447
            carry_next[base] += term_d;
×
3448
        }
3449
        if (y + 1 < height && x + 1 < width) {
×
3450
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
3451
            carry_next[base] += term_dr;
×
3452
        }
3453
        if (y + 2 < height) {
×
3454
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
3455
            carry_far[base] += term_d2;
×
3456
        }
3457
    } else {
3458
        if (x - 1 >= 0) {
×
3459
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
3460
            carry_curr[base] += term_r;
×
3461
        }
3462
        if (x - 2 >= 0) {
×
3463
            base = ((size_t)(x - 2) * (size_t)depth) + (size_t)channel;
×
3464
            carry_curr[base] += term_r2;
×
3465
        }
3466
        if (y + 1 < height && x + 1 < width) {
×
3467
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
3468
            carry_next[base] += term_dl;
×
3469
        }
3470
        if (y + 1 < height) {
×
3471
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
3472
            carry_next[base] += term_d;
×
3473
        }
3474
        if (y + 1 < height && x - 1 >= 0) {
×
3475
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
3476
            carry_next[base] += term_dr;
×
3477
        }
3478
        if (y + 2 < height) {
×
3479
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
3480
            carry_far[base] += term_d2;
×
3481
        }
3482
    }
3483
}
3484

3485

3486
static void
3487
scanline_params(int serpentine, int index, int limit,
53,794✔
3488
                int *start, int *end, int *step, int *direction)
3489
{
3490
    if (serpentine && (index & 1)) {
53,794✔
3491
        *start = limit - 1;
675✔
3492
        *end = -1;
675✔
3493
        *step = -1;
675✔
3494
        *direction = -1;
675✔
3495
    } else {
225✔
3496
        *start = 0;
53,119✔
3497
        *end = limit;
53,119✔
3498
        *step = 1;
53,119✔
3499
        *direction = 1;
53,119✔
3500
    }
3501
}
53,794✔
3502

3503

3504
static SIXELSTATUS
3505
apply_palette_positional(
×
3506
    sixel_index_t *result,
3507
    unsigned char *data,
3508
    int width,
3509
    int height,
3510
    int depth,
3511
    unsigned char *palette,
3512
    int reqcolor,
3513
    int methodForDiffuse,
3514
    int methodForScan,
3515
    int foptimize_palette,
3516
    int (*f_lookup)(const unsigned char *pixel,
3517
                    int depth,
3518
                    const unsigned char *palette,
3519
                    int reqcolor,
3520
                    unsigned short *cachetable,
3521
                    int complexion),
3522
    unsigned short *indextable,
3523
    int complexion,
3524
    unsigned char copy[],
3525
    unsigned char new_palette[],
3526
    unsigned short migration_map[],
3527
    int *ncolors)
3528
{
3529
    int serpentine;
3530
    int y;
3531
    float (*f_mask)(int x, int y, int c);
3532

3533
    switch (methodForDiffuse) {
×
3534
    case SIXEL_DIFFUSE_A_DITHER:
3535
        f_mask = mask_a;
×
3536
        break;
×
3537
    case SIXEL_DIFFUSE_X_DITHER:
×
3538
    default:
3539
        f_mask = mask_x;
×
3540
        break;
×
3541
    }
3542

3543
    serpentine = (methodForScan == SIXEL_SCAN_SERPENTINE);
×
3544

3545
    if (foptimize_palette) {
×
3546
        int x;
3547

3548
        *ncolors = 0;
×
3549
        memset(new_palette, 0x00,
×
3550
               (size_t)SIXEL_PALETTE_MAX * (size_t)depth);
3551
        memset(migration_map, 0x00,
×
3552
               sizeof(unsigned short) * (size_t)SIXEL_PALETTE_MAX);
3553
        for (y = 0; y < height; ++y) {
×
3554
            int start;
3555
            int end;
3556
            int step;
3557
            int direction;
3558

3559
            scanline_params(serpentine, y, width,
×
3560
                            &start, &end, &step, &direction);
3561
            (void)direction;
3562
            for (x = start; x != end; x += step) {
×
3563
                int pos;
3564
                int d;
3565
                int color_index;
3566

3567
                pos = y * width + x;
×
3568
                for (d = 0; d < depth; ++d) {
×
3569
                    int val;
3570

3571
                    val = data[pos * depth + d]
×
3572
                        + f_mask(x, y, d) * 32;
×
3573
                    copy[d] = val < 0 ? 0
×
3574
                               : val > 255 ? 255 : val;
×
3575
                }
3576
                color_index = f_lookup(copy, depth, palette,
×
3577
                                       reqcolor, indextable,
3578
                                       complexion);
3579
                if (migration_map[color_index] == 0) {
×
3580
                    result[pos] = *ncolors;
×
3581
                    for (d = 0; d < depth; ++d) {
×
3582
                        new_palette[*ncolors * depth + d]
×
3583
                            = palette[color_index * depth + d];
×
3584
                    }
3585
                    ++*ncolors;
×
3586
                    migration_map[color_index] = *ncolors;
×
3587
                } else {
3588
                    result[pos] = migration_map[color_index] - 1;
×
3589
                }
3590
            }
3591
        }
3592
        memcpy(palette, new_palette, (size_t)(*ncolors * depth));
×
3593
    } else {
3594
        int x;
3595

3596
        for (y = 0; y < height; ++y) {
×
3597
            int start;
3598
            int end;
3599
            int step;
3600
            int direction;
3601

3602
            scanline_params(serpentine, y, width,
×
3603
                            &start, &end, &step, &direction);
3604
            (void)direction;
3605
            for (x = start; x != end; x += step) {
×
3606
                int pos;
3607
                int d;
3608

3609
                pos = y * width + x;
×
3610
                for (d = 0; d < depth; ++d) {
×
3611
                    int val;
3612

3613
                    val = data[pos * depth + d]
×
3614
                        + f_mask(x, y, d) * 32;
×
3615
                    copy[d] = val < 0 ? 0
×
3616
                               : val > 255 ? 255 : val;
×
3617
                }
3618
                result[pos] = f_lookup(copy, depth, palette,
×
3619
                                       reqcolor, indextable,
3620
                                       complexion);
3621
            }
3622
        }
3623
        *ncolors = reqcolor;
×
3624
    }
3625

3626
    return SIXEL_OK;
×
3627
}
3628

3629

3630
static SIXELSTATUS
3631
apply_palette_variable(
×
3632
    sixel_index_t *result,
3633
    unsigned char *data,
3634
    int width,
3635
    int height,
3636
    int depth,
3637
    unsigned char *palette,
3638
    int reqcolor,
3639
    int methodForScan,
3640
    int foptimize_palette,
3641
    int (*f_lookup)(const unsigned char *pixel,
3642
                    int depth,
3643
                    const unsigned char *palette,
3644
                    int reqcolor,
3645
                    unsigned short *cachetable,
3646
                    int complexion),
3647
    unsigned short *indextable,
3648
    int complexion,
3649
    unsigned char new_palette[],
3650
    unsigned short migration_map[],
3651
    int *ncolors,
3652
    int methodForDiffuse,
3653
    int methodForCarry)
3654
{
3655
    SIXELSTATUS status = SIXEL_FALSE;
×
3656
#if _MSC_VER
3657
    enum { max_channels = 4 };
3658
#else
3659
    const int max_channels = 4;
×
3660
#endif
3661
    int serpentine;
3662
    int y;
3663
    diffuse_varerr_mode varerr_diffuse;
3664
    diffuse_varerr_carry_mode varerr_diffuse_carry;
3665
    int use_carry;
3666
    size_t carry_len;
3667
    int32_t *carry_curr = NULL;
×
3668
    int32_t *carry_next = NULL;
×
3669
    int32_t *carry_far = NULL;
×
3670
    unsigned char corrected[max_channels];
3671
    int32_t sample_scaled[max_channels];
3672
    int32_t accum_scaled[max_channels];
3673
    int start;
3674
    int end;
3675
    int step;
3676
    int direction;
3677
    int x;
3678
    int pos;
3679
    size_t base;
3680
    size_t carry_base;
3681
    const unsigned char *source_pixel;
3682
    int color_index;
3683
    int output_index;
3684
    int n;
3685
    int palette_value;
3686
    int diff;
3687
    int table_index;
3688
    int64_t accum;
3689
    int64_t clamped;
3690
    int32_t target_scaled;
3691
    int32_t error_scaled;
3692
    int32_t *tmp;
3693

3694
    if (depth > max_channels) {
×
3695
        status = SIXEL_BAD_ARGUMENT;
×
3696
        goto end;
×
3697
    }
3698

3699
    use_carry = (methodForCarry == SIXEL_CARRY_ENABLE);
×
3700
    carry_len = 0;
×
3701

3702
    switch (methodForDiffuse) {
×
3703
    case SIXEL_DIFFUSE_LSO2:
3704
        varerr_diffuse = diffuse_lso2;
×
3705
        varerr_diffuse_carry = diffuse_lso2_carry;
×
3706
        break;
×
3707
    case SIXEL_DIFFUSE_LSO3:
3708
        varerr_diffuse = diffuse_lso3;
×
3709
        varerr_diffuse_carry = diffuse_lso3_carry;
×
3710
        srand((unsigned int)time(NULL));
×
3711
        break;
×
3712
    default:
3713
        varerr_diffuse = diffuse_lso2;
×
3714
        varerr_diffuse_carry = diffuse_lso2_carry;
×
3715
        break;
×
3716
    }
3717

3718
    if (use_carry) {
×
3719
        carry_len = (size_t)width * (size_t)depth;
×
3720
        carry_curr = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
3721
        if (carry_curr == NULL) {
×
3722
            status = SIXEL_BAD_ALLOCATION;
×
3723
            goto end;
×
3724
        }
3725
        carry_next = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
3726
        if (carry_next == NULL) {
×
3727
            status = SIXEL_BAD_ALLOCATION;
×
3728
            goto end;
×
3729
        }
3730
        carry_far = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
3731
        if (carry_far == NULL) {
×
3732
            status = SIXEL_BAD_ALLOCATION;
×
3733
            goto end;
×
3734
        }
3735
    }
3736

3737
    serpentine = (methodForScan == SIXEL_SCAN_SERPENTINE);
×
3738

3739
    if (foptimize_palette) {
×
3740
        *ncolors = 0;
×
3741
        memset(new_palette, 0x00,
×
3742
               (size_t)SIXEL_PALETTE_MAX * (size_t)depth);
3743
        memset(migration_map, 0x00,
×
3744
               sizeof(unsigned short) * (size_t)SIXEL_PALETTE_MAX);
3745
    }
3746

3747
    for (y = 0; y < height; ++y) {
×
3748
        scanline_params(serpentine, y, width,
×
3749
                        &start, &end, &step, &direction);
3750
        for (x = start; x != end; x += step) {
×
3751
            pos = y * width + x;
×
3752
            base = (size_t)pos * (size_t)depth;
×
3753
            carry_base = (size_t)x * (size_t)depth;
×
3754
            if (use_carry) {
×
3755
                for (n = 0; n < depth; ++n) {
×
3756
                    accum = ((int64_t)data[base + n]
×
3757
                             << VARERR_SCALE_SHIFT)
×
3758
                          + carry_curr[carry_base + (size_t)n];
×
3759
                    if (accum < INT32_MIN) {
×
3760
                        accum = INT32_MIN;
×
3761
                    } else if (accum > INT32_MAX) {
×
3762
                        accum = INT32_MAX;
×
3763
                    }
3764
                    carry_curr[carry_base + (size_t)n] = 0;
×
3765
                    clamped = accum;
×
3766
                    if (clamped < 0) {
×
3767
                        clamped = 0;
×
3768
                    } else if (clamped > VARERR_MAX_VALUE) {
×
3769
                        clamped = VARERR_MAX_VALUE;
×
3770
                    }
3771
                    accum_scaled[n] = (int32_t)clamped;
×
3772
                    corrected[n]
3773
                        = (unsigned char)((clamped + VARERR_ROUND)
×
3774
                                          >> VARERR_SCALE_SHIFT);
×
3775
                }
3776
                source_pixel = corrected;
×
3777
            } else {
3778
                for (n = 0; n < depth; ++n) {
×
3779
                    sample_scaled[n]
3780
                        = (int32_t)data[base + n]
×
3781
                        << VARERR_SCALE_SHIFT;
×
3782
                    corrected[n] = data[base + n];
×
3783
                }
3784
                source_pixel = data + base;
×
3785
            }
3786

3787
            color_index = f_lookup(source_pixel, depth, palette,
×
3788
                                   reqcolor, indextable,
3789
                                   complexion);
3790

3791
            if (foptimize_palette) {
×
3792
                if (migration_map[color_index] == 0) {
×
3793
                    output_index = *ncolors;
×
3794
                    for (n = 0; n < depth; ++n) {
×
3795
                        new_palette[output_index * depth + n]
×
3796
                            = palette[color_index * depth + n];
×
3797
                    }
3798
                    ++*ncolors;
×
3799
                    migration_map[color_index] = *ncolors;
×
3800
                } else {
3801
                    output_index = migration_map[color_index] - 1;
×
3802
                }
3803
                result[pos] = output_index;
×
3804
            } else {
3805
                output_index = color_index;
×
3806
                result[pos] = output_index;
×
3807
            }
3808

3809
            for (n = 0; n < depth; ++n) {
×
3810
                if (foptimize_palette) {
×
3811
                    palette_value = new_palette[output_index * depth + n];
×
3812
                } else {
3813
                    palette_value = palette[color_index * depth + n];
×
3814
                }
3815
                diff = (int)source_pixel[n] - palette_value;
×
3816
                if (diff < 0) {
×
3817
                    diff = -diff;
×
3818
                }
3819
                if (diff > 255) {
×
3820
                    diff = 255;
×
3821
                }
3822
                table_index = diff;
×
3823
                if (methodForDiffuse == SIXEL_DIFFUSE_LSO3) {
×
3824
                    table_index = zhoufang_index_from_byte(
×
3825
                        (unsigned char)source_pixel[n]);
×
3826
                }
3827
                if (use_carry) {
×
3828
                    target_scaled = (int32_t)palette_value
×
3829
                                  << VARERR_SCALE_SHIFT;
3830
                    error_scaled = accum_scaled[n] - target_scaled;
×
3831
                    varerr_diffuse_carry(carry_curr, carry_next, carry_far,
×
3832
                                         width, height, depth,
3833
                                         x, y, error_scaled,
3834
                                         table_index,
3835
                                         direction, n);
3836
                } else {
3837
                    target_scaled = (int32_t)palette_value
×
3838
                                  << VARERR_SCALE_SHIFT;
3839
                    error_scaled = sample_scaled[n] - target_scaled;
×
3840
                    varerr_diffuse(data + n, width, height,
×
3841
                                   x, y, depth, error_scaled,
3842
                                   table_index,
3843
                                   direction);
3844
                }
3845
            }
3846
        }
3847
        if (use_carry) {
×
3848
            tmp = carry_curr;
×
3849
            carry_curr = carry_next;
×
3850
            carry_next = carry_far;
×
3851
            carry_far = tmp;
×
3852
            if (carry_len > 0) {
×
3853
                memset(carry_far, 0x00, carry_len * sizeof(int32_t));
×
3854
            }
3855
        }
3856
    }
3857

3858
    if (foptimize_palette) {
×
3859
        memcpy(palette, new_palette, (size_t)(*ncolors * depth));
×
3860
    } else {
3861
        *ncolors = reqcolor;
×
3862
    }
3863

3864
    status = SIXEL_OK;
×
3865

3866
end:
3867
    free(carry_next);
×
3868
    free(carry_curr);
×
3869
    free(carry_far);
×
3870
    return status;
×
3871
}
3872

3873

3874
static SIXELSTATUS
3875
apply_palette_fixed(
281✔
3876
    sixel_index_t *result,
3877
    unsigned char *data,
3878
    int width,
3879
    int height,
3880
    int depth,
3881
    unsigned char *palette,
3882
    int reqcolor,
3883
    int methodForScan,
3884
    int foptimize_palette,
3885
    int (*f_lookup)(const unsigned char *pixel,
3886
                    int depth,
3887
                    const unsigned char *palette,
3888
                    int reqcolor,
3889
                    unsigned short *cachetable,
3890
                    int complexion),
3891
    unsigned short *indextable,
3892
    int complexion,
3893
    unsigned char new_palette[],
3894
    unsigned short migration_map[],
3895
    int *ncolors,
3896
    int methodForDiffuse,
3897
    int methodForCarry)
3898
{
168✔
3899
#if _MSC_VER
3900
    enum { max_channels = 4 };
3901
#else
3902
    const int max_channels = 4;
281✔
3903
#endif
3904
    SIXELSTATUS status = SIXEL_FALSE;
281✔
3905
    int serpentine;
3906
    int y;
3907
    void (*f_diffuse)(unsigned char *data,
3908
                      int width,
3909
                      int height,
3910
                      int x,
3911
                      int y,
3912
                      int depth,
3913
                      int offset,
3914
                      int direction);
3915
    diffuse_fixed_carry_mode f_diffuse_carry;
3916
    int use_carry;
3917
    size_t carry_len;
3918
    int32_t *carry_curr = NULL;
281✔
3919
    int32_t *carry_next = NULL;
281✔
3920
    int32_t *carry_far = NULL;
281✔
3921
    unsigned char corrected[max_channels];
168✔
3922
    int32_t accum_scaled[max_channels];
168✔
3923
    int start;
3924
    int end;
3925
    int step;
3926
    int direction;
3927
    int x;
3928
    int pos;
3929
    size_t base;
3930
    size_t carry_base;
3931
    const unsigned char *source_pixel;
3932
    int color_index;
3933
    int output_index;
3934
    int n;
3935
    int palette_value;
3936
    int64_t accum;
3937
    int64_t clamped;
3938
    int32_t target_scaled;
3939
    int32_t error_scaled;
3940
    int offset;
3941
    int32_t *tmp;
3942

3943
    if (depth > max_channels) {
281!
3944
        status = SIXEL_BAD_ARGUMENT;
×
3945
        goto end;
×
3946
    }
3947

3948
    use_carry = (methodForCarry == SIXEL_CARRY_ENABLE);
281✔
3949
    carry_len = 0;
281✔
3950

3951
    if (depth != 3) {
281!
3952
        f_diffuse = diffuse_none;
×
3953
        f_diffuse_carry = diffuse_none_carry;
×
3954
        use_carry = 0;
×
3955
    } else {
3956
        switch (methodForDiffuse) {
281!
3957
        case SIXEL_DIFFUSE_NONE:
86✔
3958
            f_diffuse = diffuse_none;
157✔
3959
            f_diffuse_carry = diffuse_none_carry;
157✔
3960
            break;
157✔
3961
        case SIXEL_DIFFUSE_ATKINSON:
44✔
3962
            f_diffuse = diffuse_atkinson;
67✔
3963
            f_diffuse_carry = diffuse_atkinson_carry;
67✔
3964
            break;
67✔
3965
        case SIXEL_DIFFUSE_FS:
32✔
3966
            f_diffuse = diffuse_fs;
48✔
3967
            f_diffuse_carry = diffuse_fs_carry;
48✔
3968
            break;
48✔
3969
        case SIXEL_DIFFUSE_JAJUNI:
2✔
3970
            f_diffuse = diffuse_jajuni;
3✔
3971
            f_diffuse_carry = diffuse_jajuni_carry;
3✔
3972
            break;
3✔
3973
        case SIXEL_DIFFUSE_STUCKI:
2✔
3974
            f_diffuse = diffuse_stucki;
3✔
3975
            f_diffuse_carry = diffuse_stucki_carry;
3✔
3976
            break;
3✔
3977
        case SIXEL_DIFFUSE_BURKES:
2✔
3978
            f_diffuse = diffuse_burkes;
3✔
3979
            f_diffuse_carry = diffuse_burkes_carry;
3✔
3980
            break;
3✔
3981
        case SIXEL_DIFFUSE_SIERRA1:
3982
            f_diffuse = diffuse_sierra1;
×
3983
            f_diffuse_carry = diffuse_sierra1_carry;
×
3984
            break;
×
3985
        case SIXEL_DIFFUSE_SIERRA2:
3986
            f_diffuse = diffuse_sierra2;
×
3987
            f_diffuse_carry = diffuse_sierra2_carry;
×
3988
            break;
×
3989
        case SIXEL_DIFFUSE_SIERRA3:
3990
            f_diffuse = diffuse_sierra3;
×
3991
            f_diffuse_carry = diffuse_sierra3_carry;
×
3992
            break;
×
3993
        case SIXEL_DIFFUSE_LSO1:
3994
            f_diffuse = diffuse_lso1;
×
3995
            f_diffuse_carry = diffuse_lso1_carry;
×
3996
            break;
×
3997
        default:
3998
            quant_trace(stderr,
×
3999
                        "Internal error: invalid methodForDiffuse: %d\n",
4000
                        methodForDiffuse);
4001
            f_diffuse = diffuse_none;
×
4002
            f_diffuse_carry = diffuse_none_carry;
×
4003
            break;
×
4004
        }
4005
    }
4006

4007
    if (use_carry) {
281!
4008
        carry_len = (size_t)width * (size_t)depth;
×
4009
        if (carry_len > 0) {
×
4010
            carry_curr = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
4011
            if (carry_curr == NULL) {
×
4012
                status = SIXEL_BAD_ALLOCATION;
×
4013
                goto end;
×
4014
            }
4015
            carry_next = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
4016
            if (carry_next == NULL) {
×
4017
                status = SIXEL_BAD_ALLOCATION;
×
4018
                goto end;
×
4019
            }
4020
            carry_far = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
4021
            if (carry_far == NULL) {
×
4022
                status = SIXEL_BAD_ALLOCATION;
×
4023
                goto end;
×
4024
            }
4025
        } else {
4026
            use_carry = 0;
×
4027
        }
4028
    }
4029

4030
    serpentine = (methodForScan == SIXEL_SCAN_SERPENTINE);
281✔
4031

4032
    if (foptimize_palette) {
281✔
4033
        *ncolors = 0;
202✔
4034
        memset(new_palette, 0x00,
202✔
4035
               (size_t)SIXEL_PALETTE_MAX * (size_t)depth);
128✔
4036
        memset(migration_map, 0x00,
202✔
4037
               sizeof(unsigned short) * (size_t)SIXEL_PALETTE_MAX);
4038
    } else {
74✔
4039
        *ncolors = reqcolor;
79✔
4040
    }
4041

4042
    for (y = 0; y < height; ++y) {
54,075✔
4043
        scanline_params(serpentine, y, width,
53,794✔
4044
                        &start, &end, &step, &direction);
4045
        for (x = start; x != end; x += step) {
26,469,500✔
4046
            pos = y * width + x;
26,415,706✔
4047
            base = (size_t)pos * (size_t)depth;
26,415,706✔
4048
            carry_base = (size_t)x * (size_t)depth;
26,415,706✔
4049
            if (use_carry) {
26,415,706!
4050
                for (n = 0; n < depth; ++n) {
×
4051
                    accum = ((int64_t)data[base + n]
×
4052
                             << VARERR_SCALE_SHIFT)
×
4053
                           + carry_curr[carry_base + (size_t)n];
×
4054
                    if (accum < INT32_MIN) {
×
4055
                        accum = INT32_MIN;
×
4056
                    } else if (accum > INT32_MAX) {
×
4057
                        accum = INT32_MAX;
×
4058
                    }
4059
                    clamped = accum;
×
4060
                    if (clamped < 0) {
×
4061
                        clamped = 0;
×
4062
                    } else if (clamped > VARERR_MAX_VALUE) {
×
4063
                        clamped = VARERR_MAX_VALUE;
×
4064
                    }
4065
                    accum_scaled[n] = (int32_t)clamped;
×
4066
                    corrected[n]
4067
                        = (unsigned char)((clamped + VARERR_ROUND)
×
4068
                                          >> VARERR_SCALE_SHIFT);
×
4069
                    data[base + n] = corrected[n];
×
4070
                    carry_curr[carry_base + (size_t)n] = 0;
×
4071
                }
4072
                source_pixel = corrected;
×
4073
            } else {
4074
                source_pixel = data + base;
26,415,706✔
4075
            }
4076

4077
            color_index = f_lookup(source_pixel, depth, palette,
38,025,328✔
4078
                                   reqcolor, indextable,
11,609,622✔
4079
                                   complexion);
11,609,622✔
4080

4081
            if (foptimize_palette) {
26,415,706✔
4082
                if (migration_map[color_index] == 0) {
14,314,206✔
4083
                    output_index = *ncolors;
15,636✔
4084
                    for (n = 0; n < depth; ++n) {
62,544✔
4085
                        new_palette[output_index * depth + n]
46,908✔
4086
                            = palette[color_index * depth + n];
62,628✔
4087
                    }
15,720✔
4088
                    ++*ncolors;
15,636✔
4089
                    migration_map[color_index] = *ncolors;
15,636✔
4090
                } else {
5,240✔
4091
                    output_index = migration_map[color_index] - 1;
14,298,570✔
4092
                }
4093
                result[pos] = output_index;
14,314,206✔
4094
            } else {
5,769,802✔
4095
                output_index = color_index;
12,101,500✔
4096
                result[pos] = output_index;
12,101,500✔
4097
            }
4098

4099
            for (n = 0; n < depth; ++n) {
105,662,824✔
4100
                if (foptimize_palette) {
79,247,118✔
4101
                    palette_value = new_palette[output_index * depth + n];
42,942,618✔
4102
                } else {
17,309,406✔
4103
                    palette_value = palette[color_index * depth + n];
36,304,500✔
4104
                }
4105
                if (use_carry) {
79,247,118!
4106
                    target_scaled = (int32_t)palette_value
×
4107
                                  << VARERR_SCALE_SHIFT;
4108
                    error_scaled = accum_scaled[n] - target_scaled;
×
4109
                    f_diffuse_carry(carry_curr, carry_next, carry_far,
×
4110
                                    width, height, depth,
4111
                                    x, y, error_scaled, direction, n);
4112
                } else {
4113
                    offset = (int)source_pixel[n] - palette_value;
79,247,118✔
4114
                    f_diffuse(data + n, width, height, x, y,
114,075,984✔
4115
                              depth, offset, direction);
34,828,866✔
4116
                }
4117
            }
34,828,866✔
4118
        }
11,609,622✔
4119
        if (use_carry) {
53,794!
4120
            tmp = carry_curr;
×
4121
            carry_curr = carry_next;
×
4122
            carry_next = carry_far;
×
4123
            carry_far = tmp;
×
4124
            if (carry_len > 0) {
×
4125
                memset(carry_far, 0x00, carry_len * sizeof(int32_t));
×
4126
            }
4127
        }
4128
    }
23,822✔
4129

4130
    if (foptimize_palette) {
281✔
4131
        memcpy(palette, new_palette, (size_t)(*ncolors * depth));
202✔
4132
    }
74✔
4133

4134
    status = SIXEL_OK;
281✔
4135

4136
end:
168✔
4137
    free(carry_far);
281✔
4138
    free(carry_next);
281✔
4139
    free(carry_curr);
281✔
4140
    return status;
281✔
4141
}
4142

4143

4144
static void
4145
diffuse_none(unsigned char *data, int width, int height,
18,248,814✔
4146
             int x, int y, int depth, int error, int direction)
4147
{
4148
    /* unused */ (void) data;
14,469,498✔
4149
    /* unused */ (void) width;
14,469,498✔
4150
    /* unused */ (void) height;
14,469,498✔
4151
    /* unused */ (void) x;
14,469,498✔
4152
    /* unused */ (void) y;
14,469,498✔
4153
    /* unused */ (void) depth;
14,469,498✔
4154
    /* unused */ (void) error;
14,469,498✔
4155
    /* unused */ (void) direction;
14,469,498✔
4156
}
18,248,814✔
4157

4158

4159
static void
4160
diffuse_none_carry(int32_t *carry_curr, int32_t *carry_next,
×
4161
                   int32_t *carry_far, int width, int height,
4162
                   int depth, int x, int y, int32_t error,
4163
                   int direction, int channel)
4164
{
4165
    /* unused */ (void) carry_curr;
4166
    /* unused */ (void) carry_next;
4167
    /* unused */ (void) carry_far;
4168
    /* unused */ (void) width;
4169
    /* unused */ (void) height;
4170
    /* unused */ (void) depth;
4171
    /* unused */ (void) x;
4172
    /* unused */ (void) y;
4173
    /* unused */ (void) error;
4174
    /* unused */ (void) direction;
4175
    /* unused */ (void) channel;
4176
}
×
4177

4178

4179
static void
4180
diffuse_fs(unsigned char *data, int width, int height,
32,061,744✔
4181
           int x, int y, int depth, int error, int direction)
4182
{
4183
    /* Floyd Steinberg Method
4184
     *          curr    7/16
4185
     *  3/16    5/48    1/16
4186
     */
4187
    int pos;
4188
    int forward;
4189

4190
    pos = y * width + x;
32,061,744✔
4191
    forward = direction >= 0;
32,061,744✔
4192

4193
    if (forward) {
32,061,744✔
4194
        if (x < width - 1) {
30,846,744✔
4195
            error_diffuse_normal(data, pos + 1, depth, error, 7, 16);
30,789,423✔
4196
        }
10,263,141✔
4197
        if (y < height - 1) {
30,846,744✔
4198
            if (x > 0) {
30,779,028✔
4199
                error_diffuse_normal(data,
40,962,456✔
4200
                                     pos + width - 1,
30,721,842✔
4201
                                     depth, error, 3, 16);
10,240,614✔
4202
            }
10,240,614✔
4203
            error_diffuse_normal(data,
41,038,704✔
4204
                                 pos + width,
10,259,676✔
4205
                                 depth, error, 5, 16);
10,259,676✔
4206
            if (x < width - 1) {
30,779,028✔
4207
                error_diffuse_normal(data,
40,962,456✔
4208
                                     pos + width + 1,
30,721,842✔
4209
                                     depth, error, 1, 16);
10,240,614✔
4210
            }
10,240,614✔
4211
        }
10,259,676✔
4212
    } else {
10,282,248✔
4213
        if (x > 0) {
1,215,000✔
4214
            error_diffuse_normal(data, pos - 1, depth, error, 7, 16);
1,212,975✔
4215
        }
404,325✔
4216
        if (y < height - 1) {
1,215,000✔
4217
            if (x < width - 1) {
1,209,600✔
4218
                error_diffuse_normal(data,
1,610,112✔
4219
                                     pos + width + 1,
1,207,584✔
4220
                                     depth, error, 3, 16);
402,528✔
4221
            }
402,528✔
4222
            error_diffuse_normal(data,
1,612,800✔
4223
                                 pos + width,
403,200✔
4224
                                 depth, error, 5, 16);
403,200✔
4225
            if (x > 0) {
1,209,600✔
4226
                error_diffuse_normal(data,
1,610,112✔
4227
                                     pos + width - 1,
1,207,584✔
4228
                                     depth, error, 1, 16);
402,528✔
4229
            }
402,528✔
4230
        }
403,200✔
4231
    }
4232
}
32,061,744✔
4233

4234

4235
static void
4236
diffuse_fs_carry(int32_t *carry_curr, int32_t *carry_next,
×
4237
                 int32_t *carry_far, int width, int height,
4238
                 int depth, int x, int y, int32_t error,
4239
                 int direction, int channel)
4240
{
4241
    /* Floyd Steinberg Method
4242
     *          curr    7/16
4243
     *  3/16    5/48    1/16
4244
     */
4245
    int forward;
4246

4247
    /* unused */ (void) carry_far;
4248
    if (error == 0) {
×
4249
        return;
×
4250
    }
4251

4252
    forward = direction >= 0;
×
4253
    if (forward) {
×
4254
        if (x + 1 < width) {
×
4255
            size_t base;
4256
            int32_t term;
4257

4258
            base = ((size_t)(x + 1) * (size_t)depth)
×
4259
                 + (size_t)channel;
×
4260
            term = diffuse_fixed_term(error, 7, 16);
×
4261
            carry_curr[base] += term;
×
4262
        }
4263
        if (y + 1 < height) {
×
4264
            if (x > 0) {
×
4265
                size_t base;
4266
                int32_t term;
4267

4268
                base = ((size_t)(x - 1) * (size_t)depth)
×
4269
                     + (size_t)channel;
×
4270
                term = diffuse_fixed_term(error, 3, 16);
×
4271
                carry_next[base] += term;
×
4272
            }
4273
            {
4274
                size_t base;
4275
                int32_t term;
4276

4277
                base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
4278
                term = diffuse_fixed_term(error, 5, 16);
×
4279
                carry_next[base] += term;
×
4280
            }
4281
            if (x + 1 < width) {
×
4282
                size_t base;
4283
                int32_t term;
4284

4285
                base = ((size_t)(x + 1) * (size_t)depth)
×
4286
                     + (size_t)channel;
×
4287
                term = diffuse_fixed_term(error, 1, 16);
×
4288
                carry_next[base] += term;
×
4289
            }
4290
        }
4291
    } else {
4292
        if (x - 1 >= 0) {
×
4293
            size_t base;
4294
            int32_t term;
4295

4296
            base = ((size_t)(x - 1) * (size_t)depth)
×
4297
                 + (size_t)channel;
×
4298
            term = diffuse_fixed_term(error, 7, 16);
×
4299
            carry_curr[base] += term;
×
4300
        }
4301
        if (y + 1 < height) {
×
4302
            if (x + 1 < width) {
×
4303
                size_t base;
4304
                int32_t term;
4305

4306
                base = ((size_t)(x + 1) * (size_t)depth)
×
4307
                     + (size_t)channel;
×
4308
                term = diffuse_fixed_term(error, 3, 16);
×
4309
                carry_next[base] += term;
×
4310
            }
4311
            {
4312
                size_t base;
4313
                int32_t term;
4314

4315
                base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
4316
                term = diffuse_fixed_term(error, 5, 16);
×
4317
                carry_next[base] += term;
×
4318
            }
4319
            if (x - 1 >= 0) {
×
4320
                size_t base;
4321
                int32_t term;
4322

4323
                base = ((size_t)(x - 1) * (size_t)depth)
×
4324
                     + (size_t)channel;
×
4325
                term = diffuse_fixed_term(error, 1, 16);
×
4326
                carry_next[base] += term;
×
4327
            }
4328
        }
4329
    }
4330
}
4331

4332

4333
static void
4334
diffuse_atkinson(unsigned char *data, int width, int height,
28,352,460✔
4335
                 int x, int y, int depth, int error, int direction)
4336
{
4337
    /* Atkinson's Method
4338
     *          curr    1/8    1/8
4339
     *   1/8     1/8    1/8
4340
     *           1/8
4341
     */
4342
    int pos;
4343
    int sign;
4344

4345
    pos = y * width + x;
28,352,460✔
4346
    sign = direction >= 0 ? 1 : -1;
28,352,460!
4347

4348
    if (x + sign >= 0 && x + sign < width) {
28,352,460!
4349
        error_diffuse_fast(data, pos + sign, depth, error, 1, 8);
28,296,945✔
4350
    }
9,458,715✔
4351
    if (x + sign * 2 >= 0 && x + sign * 2 < width) {
28,352,460!
4352
        error_diffuse_fast(data, pos + sign * 2, depth, error, 1, 8);
28,241,430✔
4353
    }
9,440,010✔
4354
    if (y < height - 1) {
28,352,460✔
4355
        int row;
4356

4357
        row = pos + width;
28,278,756✔
4358
        if (x - sign >= 0 && x - sign < width) {
28,278,756!
4359
            error_diffuse_fast(data,
37,657,392✔
4360
                               row + (-sign),
9,433,950✔
4361
                               depth, error, 1, 8);
9,433,950✔
4362
        }
9,433,950✔
4363
        error_diffuse_fast(data, row, depth, error, 1, 8);
28,278,756✔
4364
        if (x + sign >= 0 && x + sign < width) {
28,278,756!
4365
            error_diffuse_fast(data,
37,657,392✔
4366
                               row + sign,
9,433,950✔
4367
                               depth, error, 1, 8);
9,433,950✔
4368
        }
9,433,950✔
4369
    }
9,452,586✔
4370
    if (y < height - 2) {
28,352,460✔
4371
        error_diffuse_fast(data, pos + width * 2, depth, error, 1, 8);
28,205,052✔
4372
    }
9,427,752✔
4373
}
28,352,460✔
4374

4375

4376
static void
4377
diffuse_atkinson_carry(int32_t *carry_curr, int32_t *carry_next,
×
4378
                       int32_t *carry_far, int width, int height,
4379
                       int depth, int x, int y, int32_t error,
4380
                       int direction, int channel)
4381
{
4382
    /* Atkinson's Method
4383
     *          curr    1/8    1/8
4384
     *   1/8     1/8    1/8
4385
     *           1/8
4386
     */
4387
    int sign;
4388
    int32_t term;
4389

4390
    if (error == 0) {
×
4391
        return;
×
4392
    }
4393

4394
    term = diffuse_fixed_term(error, 1, 8);
×
4395
    sign = direction >= 0 ? 1 : -1;
×
4396
    if (x + sign >= 0 && x + sign < width) {
×
4397
        size_t base;
4398

4399
        base = ((size_t)(x + sign) * (size_t)depth)
×
4400
             + (size_t)channel;
×
4401
        carry_curr[base] += term;
×
4402
    }
4403
    if (x + sign * 2 >= 0 && x + sign * 2 < width) {
×
4404
        size_t base;
4405

4406
        base = ((size_t)(x + sign * 2) * (size_t)depth)
×
4407
             + (size_t)channel;
×
4408
        carry_curr[base] += term;
×
4409
    }
4410
    if (y + 1 < height) {
×
4411
        if (x - sign >= 0 && x - sign < width) {
×
4412
            size_t base;
4413

4414
            base = ((size_t)(x - sign) * (size_t)depth)
×
4415
                 + (size_t)channel;
×
4416
            carry_next[base] += term;
×
4417
        }
4418
        {
4419
            size_t base;
4420

4421
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
4422
            carry_next[base] += term;
×
4423
        }
4424
        if (x + sign >= 0 && x + sign < width) {
×
4425
            size_t base;
4426

4427
            base = ((size_t)(x + sign) * (size_t)depth)
×
4428
                 + (size_t)channel;
×
4429
            carry_next[base] += term;
×
4430
        }
4431
    }
4432
    if (y + 2 < height) {
×
4433
        size_t base;
4434

4435
        base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
4436
        carry_far[base] += term;
×
4437
    }
4438
}
4439

4440

4441
static void
4442
diffuse_jajuni(unsigned char *data, int width, int height,
396,900✔
4443
               int x, int y, int depth, int error, int direction)
4444
{
4445
    /* Jarvis, Judice & Ninke Method
4446
     *                  curr    7/48    5/48
4447
     *  3/48    5/48    7/48    5/48    3/48
4448
     *  1/48    3/48    5/48    3/48    1/48
4449
     */
4450
    int pos;
4451
    int sign;
4452
    static const int row0_offsets[] = { 1, 2 };
4453
    static const int row0_weights[] = { 7, 5 };
4454
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4455
    static const int row1_weights[] = { 3, 5, 7, 5, 3 };
4456
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
4457
    static const int row2_weights[] = { 1, 3, 5, 3, 1 };
4458
    int i;
4459

4460
    pos = y * width + x;
396,900✔
4461
    sign = direction >= 0 ? 1 : -1;
396,900!
4462

4463
    for (i = 0; i < 2; ++i) {
1,190,700✔
4464
        int neighbor;
4465

4466
        neighbor = x + sign * row0_offsets[i];
793,800✔
4467
        if (neighbor < 0 || neighbor >= width) {
793,800!
4468
            continue;
5,670✔
4469
        }
4470
        error_diffuse_precise(data,
1,050,840✔
4471
                              pos + (neighbor - x),
788,130✔
4472
                              depth, error,
262,710✔
4473
                              row0_weights[i], 48);
788,130✔
4474
    }
262,710✔
4475
    if (y < height - 1) {
396,900✔
4476
        int row;
4477

4478
        row = pos + width;
395,010✔
4479
        for (i = 0; i < 5; ++i) {
2,370,060✔
4480
            int neighbor;
4481

4482
            neighbor = x + sign * row1_offsets[i];
1,975,050✔
4483
            if (neighbor < 0 || neighbor >= width) {
1,975,050✔
4484
                continue;
11,286✔
4485
            }
4486
            error_diffuse_precise(data,
2,618,352✔
4487
                                  row + (neighbor - x),
1,963,764✔
4488
                                  depth, error,
654,588✔
4489
                                  row1_weights[i], 48);
1,963,764✔
4490
        }
654,588✔
4491
    }
131,670✔
4492
    if (y < height - 2) {
396,900✔
4493
        int row;
4494

4495
        row = pos + width * 2;
393,120✔
4496
        for (i = 0; i < 5; ++i) {
2,358,720✔
4497
            int neighbor;
4498

4499
            neighbor = x + sign * row2_offsets[i];
1,965,600✔
4500
            if (neighbor < 0 || neighbor >= width) {
1,965,600✔
4501
                continue;
11,232✔
4502
            }
4503
            error_diffuse_precise(data,
2,605,824✔
4504
                                  row + (neighbor - x),
1,954,368✔
4505
                                  depth, error,
651,456✔
4506
                                  row2_weights[i], 48);
1,954,368✔
4507
        }
651,456✔
4508
    }
131,040✔
4509
}
396,900✔
4510

4511

4512
static void
4513
diffuse_jajuni_carry(int32_t *carry_curr, int32_t *carry_next,
×
4514
                     int32_t *carry_far, int width, int height,
4515
                     int depth, int x, int y, int32_t error,
4516
                     int direction, int channel)
4517
{
4518
    /* Jarvis, Judice & Ninke Method
4519
     *                  curr    7/48    5/48
4520
     *  3/48    5/48    7/48    5/48    3/48
4521
     *  1/48    3/48    5/48    3/48    1/48
4522
     */
4523
    static const int row0_offsets[] = { 1, 2 };
4524
    static const int row0_weights[] = { 7, 5 };
4525
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4526
    static const int row1_weights[] = { 3, 5, 7, 5, 3 };
4527
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
4528
    static const int row2_weights[] = { 1, 3, 5, 3, 1 };
4529
    int sign;
4530
    int i;
4531

4532
    if (error == 0) {
×
4533
        return;
×
4534
    }
4535

4536
    sign = direction >= 0 ? 1 : -1;
×
4537
    for (i = 0; i < 2; ++i) {
×
4538
        int neighbor;
4539
        int32_t term;
4540

4541
        neighbor = x + sign * row0_offsets[i];
×
4542
        if (neighbor < 0 || neighbor >= width) {
×
4543
            continue;
×
4544
        }
4545
        term = diffuse_fixed_term(error, row0_weights[i], 48);
×
4546
        carry_curr[((size_t)neighbor * (size_t)depth)
×
4547
                   + (size_t)channel] += term;
×
4548
    }
4549
    if (y + 1 < height) {
×
4550
        for (i = 0; i < 5; ++i) {
×
4551
            int neighbor;
4552
            int32_t term;
4553

4554
            neighbor = x + sign * row1_offsets[i];
×
4555
            if (neighbor < 0 || neighbor >= width) {
×
4556
                continue;
×
4557
            }
4558
            term = diffuse_fixed_term(error, row1_weights[i], 48);
×
4559
            carry_next[((size_t)neighbor * (size_t)depth)
×
4560
                       + (size_t)channel] += term;
×
4561
        }
4562
    }
4563
    if (y + 2 < height) {
×
4564
        for (i = 0; i < 5; ++i) {
×
4565
            int neighbor;
4566
            int32_t term;
4567

4568
            neighbor = x + sign * row2_offsets[i];
×
4569
            if (neighbor < 0 || neighbor >= width) {
×
4570
                continue;
×
4571
            }
4572
            term = diffuse_fixed_term(error, row2_weights[i], 48);
×
4573
            carry_far[((size_t)neighbor * (size_t)depth)
×
4574
                      + (size_t)channel] += term;
×
4575
        }
4576
    }
4577
}
4578

4579

4580
static void
4581
diffuse_stucki(unsigned char *data, int width, int height,
119,700✔
4582
               int x, int y, int depth, int error, int direction)
4583
{
4584
    /* Stucki's Method
4585
     *                  curr    8/48    4/48
4586
     *  2/48    4/48    8/48    4/48    2/48
4587
     *  1/48    2/48    4/48    2/48    1/48
4588
     */
4589
    int pos;
4590
    int sign;
4591
    static const int row0_offsets[] = { 1, 2 };
4592
    static const int row0_num[] = { 1, 1 };
4593
    static const int row0_den[] = { 6, 12 };
4594
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4595
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
4596
    static const int row1_den[] = { 24, 12, 6, 12, 24 };
4597
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
4598
    static const int row2_num[] = { 1, 1, 1, 1, 1 };
4599
    static const int row2_den[] = { 48, 24, 12, 24, 48 };
4600
    int i;
4601

4602
    pos = y * width + x;
119,700✔
4603
    sign = direction >= 0 ? 1 : -1;
119,700!
4604

4605
    for (i = 0; i < 2; ++i) {
359,100✔
4606
        int neighbor;
4607

4608
        neighbor = x + sign * row0_offsets[i];
239,400✔
4609
        if (neighbor < 0 || neighbor >= width) {
239,400!
4610
            continue;
2,700✔
4611
        }
4612
        error_diffuse_precise(data,
315,600✔
4613
                              pos + (neighbor - x),
236,700✔
4614
                              depth, error,
78,900✔
4615
                              row0_num[i], row0_den[i]);
236,700✔
4616
    }
78,900✔
4617
    if (y < height - 1) {
119,700✔
4618
        int row;
4619

4620
        row = pos + width;
118,503✔
4621
        for (i = 0; i < 5; ++i) {
711,018✔
4622
            int neighbor;
4623

4624
            neighbor = x + sign * row1_offsets[i];
592,515✔
4625
            if (neighbor < 0 || neighbor >= width) {
592,515✔
4626
                continue;
5,346✔
4627
            }
4628
            error_diffuse_precise(data,
782,892✔
4629
                                  row + (neighbor - x),
587,169✔
4630
                                  depth, error,
195,723✔
4631
                                  row1_num[i], row1_den[i]);
587,169✔
4632
        }
195,723✔
4633
    }
39,501✔
4634
    if (y < height - 2) {
119,700✔
4635
        int row;
4636

4637
        row = pos + width * 2;
117,306✔
4638
        for (i = 0; i < 5; ++i) {
703,836✔
4639
            int neighbor;
4640

4641
            neighbor = x + sign * row2_offsets[i];
586,530✔
4642
            if (neighbor < 0 || neighbor >= width) {
586,530✔
4643
                continue;
5,292✔
4644
            }
4645
            error_diffuse_precise(data,
774,984✔
4646
                                  row + (neighbor - x),
581,238✔
4647
                                  depth, error,
193,746✔
4648
                                  row2_num[i], row2_den[i]);
581,238✔
4649
        }
193,746✔
4650
    }
39,102✔
4651
}
119,700✔
4652

4653

4654
static void
4655
diffuse_stucki_carry(int32_t *carry_curr, int32_t *carry_next,
×
4656
                     int32_t *carry_far, int width, int height,
4657
                     int depth, int x, int y, int32_t error,
4658
                     int direction, int channel)
4659
{
4660
    /* Stucki's Method
4661
     *                  curr    8/48    4/48
4662
     *  2/48    4/48    8/48    4/48    2/48
4663
     *  1/48    2/48    4/48    2/48    1/48
4664
     */
4665
    static const int row0_offsets[] = { 1, 2 };
4666
    static const int row0_num[] = { 1, 1 };
4667
    static const int row0_den[] = { 6, 12 };
4668
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4669
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
4670
    static const int row1_den[] = { 24, 12, 6, 12, 24 };
4671
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
4672
    static const int row2_num[] = { 1, 1, 1, 1, 1 };
4673
    static const int row2_den[] = { 48, 24, 12, 24, 48 };
4674
    int sign;
4675
    int i;
4676

4677
    if (error == 0) {
×
4678
        return;
×
4679
    }
4680

4681
    sign = direction >= 0 ? 1 : -1;
×
4682
    for (i = 0; i < 2; ++i) {
×
4683
        int neighbor;
4684
        int32_t term;
4685

4686
        neighbor = x + sign * row0_offsets[i];
×
4687
        if (neighbor < 0 || neighbor >= width) {
×
4688
            continue;
×
4689
        }
4690
        term = diffuse_fixed_term(error, row0_num[i], row0_den[i]);
×
4691
        carry_curr[((size_t)neighbor * (size_t)depth)
×
4692
                   + (size_t)channel] += term;
×
4693
    }
4694
    if (y + 1 < height) {
×
4695
        for (i = 0; i < 5; ++i) {
×
4696
            int neighbor;
4697
            int32_t term;
4698

4699
            neighbor = x + sign * row1_offsets[i];
×
4700
            if (neighbor < 0 || neighbor >= width) {
×
4701
                continue;
×
4702
            }
4703
            term = diffuse_fixed_term(error, row1_num[i], row1_den[i]);
×
4704
            carry_next[((size_t)neighbor * (size_t)depth)
×
4705
                       + (size_t)channel] += term;
×
4706
        }
4707
    }
4708
    if (y + 2 < height) {
×
4709
        for (i = 0; i < 5; ++i) {
×
4710
            int neighbor;
4711
            int32_t term;
4712

4713
            neighbor = x + sign * row2_offsets[i];
×
4714
            if (neighbor < 0 || neighbor >= width) {
×
4715
                continue;
×
4716
            }
4717
            term = diffuse_fixed_term(error, row2_num[i], row2_den[i]);
×
4718
            carry_far[((size_t)neighbor * (size_t)depth)
×
4719
                      + (size_t)channel] += term;
×
4720
        }
4721
    }
4722
}
4723

4724

4725
static void
4726
diffuse_burkes(unsigned char *data, int width, int height,
67,500✔
4727
               int x, int y, int depth, int error, int direction)
4728
{
4729
    /* Burkes' Method
4730
     *                  curr    4/16    2/16
4731
     *  1/16    2/16    4/16    2/16    1/16
4732
     */
4733
    int pos;
4734
    int sign;
4735
    static const int row0_offsets[] = { 1, 2 };
4736
    static const int row0_num[] = { 1, 1 };
4737
    static const int row0_den[] = { 4, 8 };
4738
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4739
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
4740
    static const int row1_den[] = { 16, 8, 4, 8, 16 };
4741
    int i;
4742

4743
    pos = y * width + x;
67,500✔
4744
    sign = direction >= 0 ? 1 : -1;
67,500!
4745

4746
    for (i = 0; i < 2; ++i) {
202,500✔
4747
        int neighbor;
4748

4749
        neighbor = x + sign * row0_offsets[i];
135,000✔
4750
        if (neighbor < 0 || neighbor >= width) {
135,000!
4751
            continue;
2,025✔
4752
        }
4753
        error_diffuse_normal(data,
177,300✔
4754
                             pos + (neighbor - x),
132,975✔
4755
                             depth, error,
44,325✔
4756
                             row0_num[i], row0_den[i]);
132,975✔
4757
    }
44,325✔
4758
    if (y < height - 1) {
67,500✔
4759
        int row;
4760

4761
        row = pos + width;
66,600✔
4762
        for (i = 0; i < 5; ++i) {
399,600✔
4763
            int neighbor;
4764

4765
            neighbor = x + sign * row1_offsets[i];
333,000✔
4766
            if (neighbor < 0 || neighbor >= width) {
333,000✔
4767
                continue;
3,996✔
4768
            }
4769
            error_diffuse_normal(data,
438,672✔
4770
                                 row + (neighbor - x),
329,004✔
4771
                                 depth, error,
109,668✔
4772
                                 row1_num[i], row1_den[i]);
329,004✔
4773
        }
109,668✔
4774
    }
22,200✔
4775
}
67,500✔
4776

4777
static void
4778
diffuse_burkes_carry(int32_t *carry_curr, int32_t *carry_next,
×
4779
                     int32_t *carry_far, int width, int height,
4780
                     int depth, int x, int y, int32_t error,
4781
                     int direction, int channel)
4782
{
4783
    /* Burkes' Method
4784
     *                  curr    4/16    2/16
4785
     *  1/16    2/16    4/16    2/16    1/16
4786
     */
4787
    static const int row0_offsets[] = { 1, 2 };
4788
    static const int row0_num[] = { 1, 1 };
4789
    static const int row0_den[] = { 4, 8 };
4790
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4791
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
4792
    static const int row1_den[] = { 16, 8, 4, 8, 16 };
4793
    int sign;
4794
    int i;
4795

4796
    /* unused */ (void) carry_far;
4797

4798
    if (error == 0) {
×
4799
        return;
×
4800
    }
4801

4802
    sign = direction >= 0 ? 1 : -1;
×
4803
    for (i = 0; i < 2; ++i) {
×
4804
        int neighbor;
4805
        int32_t term;
4806

4807
        neighbor = x + sign * row0_offsets[i];
×
4808
        if (neighbor < 0 || neighbor >= width) {
×
4809
            continue;
×
4810
        }
4811
        term = diffuse_fixed_term(error, row0_num[i], row0_den[i]);
×
4812
        carry_curr[((size_t)neighbor * (size_t)depth)
×
4813
                   + (size_t)channel] += term;
×
4814
    }
4815
    if (y + 1 < height) {
×
4816
        for (i = 0; i < 5; ++i) {
×
4817
            int neighbor;
4818
            int32_t term;
4819

4820
            neighbor = x + sign * row1_offsets[i];
×
4821
            if (neighbor < 0 || neighbor >= width) {
×
4822
                continue;
×
4823
            }
4824
            term = diffuse_fixed_term(error, row1_num[i], row1_den[i]);
×
4825
            carry_next[((size_t)neighbor * (size_t)depth)
×
4826
                       + (size_t)channel] += term;
×
4827
        }
4828
    }
4829
}
4830

4831
static void
4832
diffuse_sierra1(unsigned char *data, int width, int height,
×
4833
                int x, int y, int depth, int error, int direction)
4834
{
4835
    /* Sierra Lite Method
4836
     *          curr    2/4
4837
     *  1/4     1/4
4838
     */
4839
    static const int row0_offsets[] = { 1 };
4840
    static const int row0_num[] = { 1 };
4841
    static const int row0_den[] = { 2 };
4842
    static const int row1_offsets[] = { -1, 0 };
4843
    static const int row1_num[] = { 1, 1 };
4844
    static const int row1_den[] = { 4, 4 };
4845
    int pos;
4846
    int sign;
4847
    int i;
4848
    int neighbor;
4849
    int row;
4850

4851
    pos = y * width + x;
×
4852
    sign = direction >= 0 ? 1 : -1;
×
4853

4854
    for (i = 0; i < 1; ++i) {
×
4855
        neighbor = x + sign * row0_offsets[i];
×
4856
        if (neighbor < 0 || neighbor >= width) {
×
4857
            continue;
×
4858
        }
4859
        error_diffuse_normal(data,
×
4860
                             pos + (neighbor - x),
×
4861
                             depth, error,
4862
                             row0_num[i], row0_den[i]);
×
4863
    }
4864
    if (y < height - 1) {
×
4865
        row = pos + width;
×
4866
        for (i = 0; i < 2; ++i) {
×
4867
            neighbor = x + sign * row1_offsets[i];
×
4868
            if (neighbor < 0 || neighbor >= width) {
×
4869
                continue;
×
4870
            }
4871
            error_diffuse_normal(data,
×
4872
                                 row + (neighbor - x),
×
4873
                                 depth, error,
4874
                                 row1_num[i], row1_den[i]);
×
4875
        }
4876
    }
4877
}
×
4878

4879

4880
static void
4881
diffuse_sierra1_carry(int32_t *carry_curr, int32_t *carry_next,
×
4882
                      int32_t *carry_far, int width, int height,
4883
                      int depth, int x, int y, int32_t error,
4884
                      int direction, int channel)
4885
{
4886
    /* Sierra Lite Method
4887
     *          curr    2/4
4888
     *  1/4     1/4
4889
     */
4890
    static const int row0_offsets[] = { 1 };
4891
    static const int row0_num[] = { 1 };
4892
    static const int row0_den[] = { 2 };
4893
    static const int row1_offsets[] = { -1, 0 };
4894
    static const int row1_num[] = { 1, 1 };
4895
    static const int row1_den[] = { 4, 4 };
4896
    int sign;
4897
    int i;
4898
    int neighbor;
4899
    int32_t term;
4900

4901
    /* unused */ (void) carry_far;
4902

4903
    if (error == 0) {
×
4904
        return;
×
4905
    }
4906

4907
    sign = direction >= 0 ? 1 : -1;
×
4908
    for (i = 0; i < 1; ++i) {
×
4909
        neighbor = x + sign * row0_offsets[i];
×
4910
        if (neighbor < 0 || neighbor >= width) {
×
4911
            continue;
×
4912
        }
4913
        term = diffuse_fixed_term(error, row0_num[i], row0_den[i]);
×
4914
        carry_curr[((size_t)neighbor * (size_t)depth)
×
4915
                   + (size_t)channel] += term;
×
4916
    }
4917
    if (y + 1 < height) {
×
4918
        for (i = 0; i < 2; ++i) {
×
4919
            neighbor = x + sign * row1_offsets[i];
×
4920
            if (neighbor < 0 || neighbor >= width) {
×
4921
                continue;
×
4922
            }
4923
            term = diffuse_fixed_term(error, row1_num[i], row1_den[i]);
×
4924
            carry_next[((size_t)neighbor * (size_t)depth)
×
4925
                       + (size_t)channel] += term;
×
4926
        }
4927
    }
4928
}
4929

4930

4931
static void
4932
diffuse_sierra2(unsigned char *data, int width, int height,
×
4933
                int x, int y, int depth, int error, int direction)
4934
{
4935
    /* Sierra Two-row Method
4936
     *                  curr    4/32    3/32
4937
     *  1/32    2/32    3/32    2/32    1/32
4938
     *                  2/32    3/32    2/32
4939
     */
4940
    static const int row0_offsets[] = { 1, 2 };
4941
    static const int row0_num[] = { 4, 3 };
4942
    static const int row0_den[] = { 32, 32 };
4943
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4944
    static const int row1_num[] = { 1, 2, 3, 2, 1 };
4945
    static const int row1_den[] = { 32, 32, 32, 32, 32 };
4946
    static const int row2_offsets[] = { -1, 0, 1 };
4947
    static const int row2_num[] = { 2, 3, 2 };
4948
    static const int row2_den[] = { 32, 32, 32 };
4949
    int pos;
4950
    int sign;
4951
    int i;
4952
    int neighbor;
4953
    int row;
4954

4955
    pos = y * width + x;
×
4956
    sign = direction >= 0 ? 1 : -1;
×
4957

4958
    for (i = 0; i < 2; ++i) {
×
4959
        neighbor = x + sign * row0_offsets[i];
×
4960
        if (neighbor < 0 || neighbor >= width) {
×
4961
            continue;
×
4962
        }
4963
        error_diffuse_precise(data,
×
4964
                              pos + (neighbor - x),
×
4965
                              depth, error,
4966
                              row0_num[i], row0_den[i]);
×
4967
    }
4968
    if (y < height - 1) {
×
4969
        row = pos + width;
×
4970
        for (i = 0; i < 5; ++i) {
×
4971
            neighbor = x + sign * row1_offsets[i];
×
4972
            if (neighbor < 0 || neighbor >= width) {
×
4973
                continue;
×
4974
            }
4975
            error_diffuse_precise(data,
×
4976
                                  row + (neighbor - x),
×
4977
                                  depth, error,
4978
                                  row1_num[i], row1_den[i]);
×
4979
        }
4980
    }
4981
    if (y < height - 2) {
×
4982
        row = pos + width * 2;
×
4983
        for (i = 0; i < 3; ++i) {
×
4984
            neighbor = x + sign * row2_offsets[i];
×
4985
            if (neighbor < 0 || neighbor >= width) {
×
4986
                continue;
×
4987
            }
4988
            error_diffuse_precise(data,
×
4989
                                  row + (neighbor - x),
×
4990
                                  depth, error,
4991
                                  row2_num[i], row2_den[i]);
×
4992
        }
4993
    }
4994
}
×
4995

4996

4997
static void
4998
diffuse_sierra2_carry(int32_t *carry_curr, int32_t *carry_next,
×
4999
                      int32_t *carry_far, int width, int height,
5000
                      int depth, int x, int y, int32_t error,
5001
                      int direction, int channel)
5002
{
5003
    /* Sierra Two-row Method
5004
     *                  curr    4/32    3/32
5005
     *  1/32    2/32    3/32    2/32    1/32
5006
     *                  2/32    3/32    2/32
5007
     */
5008
    static const int row0_offsets[] = { 1, 2 };
5009
    static const int row0_num[] = { 4, 3 };
5010
    static const int row0_den[] = { 32, 32 };
5011
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
5012
    static const int row1_num[] = { 1, 2, 3, 2, 1 };
5013
    static const int row1_den[] = { 32, 32, 32, 32, 32 };
5014
    static const int row2_offsets[] = { -1, 0, 1 };
5015
    static const int row2_num[] = { 2, 3, 2 };
5016
    static const int row2_den[] = { 32, 32, 32 };
5017
    int sign;
5018
    int i;
5019
    int neighbor;
5020
    int32_t term;
5021

5022
    if (error == 0) {
×
5023
        return;
×
5024
    }
5025

5026
    sign = direction >= 0 ? 1 : -1;
×
5027
    for (i = 0; i < 2; ++i) {
×
5028
        neighbor = x + sign * row0_offsets[i];
×
5029
        if (neighbor < 0 || neighbor >= width) {
×
5030
            continue;
×
5031
        }
5032
        term = diffuse_fixed_term(error, row0_num[i], row0_den[i]);
×
5033
        carry_curr[((size_t)neighbor * (size_t)depth)
×
5034
                   + (size_t)channel] += term;
×
5035
    }
5036
    if (y + 1 < height) {
×
5037
        for (i = 0; i < 5; ++i) {
×
5038
            neighbor = x + sign * row1_offsets[i];
×
5039
            if (neighbor < 0 || neighbor >= width) {
×
5040
                continue;
×
5041
            }
5042
            term = diffuse_fixed_term(error, row1_num[i], row1_den[i]);
×
5043
            carry_next[((size_t)neighbor * (size_t)depth)
×
5044
                       + (size_t)channel] += term;
×
5045
        }
5046
    }
5047
    if (y + 2 < height) {
×
5048
        for (i = 0; i < 3; ++i) {
×
5049
            neighbor = x + sign * row2_offsets[i];
×
5050
            if (neighbor < 0 || neighbor >= width) {
×
5051
                continue;
×
5052
            }
5053
            term = diffuse_fixed_term(error, row2_num[i], row2_den[i]);
×
5054
            carry_far[((size_t)neighbor * (size_t)depth)
×
5055
                      + (size_t)channel] += term;
×
5056
        }
5057
    }
5058
}
5059

5060

5061
static void
5062
diffuse_sierra3(unsigned char *data, int width, int height,
×
5063
                int x, int y, int depth, int error, int direction)
5064
{
5065
    /* Sierra-3 Method
5066
     *                  curr    5/32    3/32
5067
     *  2/32    4/32    5/32    4/32    2/32
5068
     *                  2/32    3/32    2/32
5069
     */
5070
    static const int row0_offsets[] = { 1, 2 };
5071
    static const int row0_num[] = { 5, 3 };
5072
    static const int row0_den[] = { 32, 32 };
5073
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
5074
    static const int row1_num[] = { 2, 4, 5, 4, 2 };
5075
    static const int row1_den[] = { 32, 32, 32, 32, 32 };
5076
    static const int row2_offsets[] = { -1, 0, 1 };
5077
    static const int row2_num[] = { 2, 3, 2 };
5078
    static const int row2_den[] = { 32, 32, 32 };
5079
    int pos;
5080
    int sign;
5081
    int i;
5082
    int neighbor;
5083
    int row;
5084

5085
    pos = y * width + x;
×
5086
    sign = direction >= 0 ? 1 : -1;
×
5087

5088
    for (i = 0; i < 2; ++i) {
×
5089
        neighbor = x + sign * row0_offsets[i];
×
5090
        if (neighbor < 0 || neighbor >= width) {
×
5091
            continue;
×
5092
        }
5093
        error_diffuse_precise(data,
×
5094
                              pos + (neighbor - x),
×
5095
                              depth, error,
5096
                              row0_num[i], row0_den[i]);
×
5097
    }
5098
    if (y < height - 1) {
×
5099
        row = pos + width;
×
5100
        for (i = 0; i < 5; ++i) {
×
5101
            neighbor = x + sign * row1_offsets[i];
×
5102
            if (neighbor < 0 || neighbor >= width) {
×
5103
                continue;
×
5104
            }
5105
            error_diffuse_precise(data,
×
5106
                                  row + (neighbor - x),
×
5107
                                  depth, error,
5108
                                  row1_num[i], row1_den[i]);
×
5109
        }
5110
    }
5111
    if (y < height - 2) {
×
5112
        row = pos + width * 2;
×
5113
        for (i = 0; i < 3; ++i) {
×
5114
            neighbor = x + sign * row2_offsets[i];
×
5115
            if (neighbor < 0 || neighbor >= width) {
×
5116
                continue;
×
5117
            }
5118
            error_diffuse_precise(data,
×
5119
                                  row + (neighbor - x),
×
5120
                                  depth, error,
5121
                                  row2_num[i], row2_den[i]);
×
5122
        }
5123
    }
5124
}
×
5125

5126

5127
static void
5128
diffuse_sierra3_carry(int32_t *carry_curr, int32_t *carry_next,
×
5129
                      int32_t *carry_far, int width, int height,
5130
                      int depth, int x, int y, int32_t error,
5131
                      int direction, int channel)
5132
{
5133
    /* Sierra-3 Method
5134
     *                  curr    5/32    3/32
5135
     *  2/32    4/32    5/32    4/32    2/32
5136
     *                  2/32    3/32    2/32
5137
     */
5138
    static const int row0_offsets[] = { 1, 2 };
5139
    static const int row0_num[] = { 5, 3 };
5140
    static const int row0_den[] = { 32, 32 };
5141
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
5142
    static const int row1_num[] = { 2, 4, 5, 4, 2 };
5143
    static const int row1_den[] = { 32, 32, 32, 32, 32 };
5144
    static const int row2_offsets[] = { -1, 0, 1 };
5145
    static const int row2_num[] = { 2, 3, 2 };
5146
    static const int row2_den[] = { 32, 32, 32 };
5147
    int sign;
5148
    int i;
5149
    int neighbor;
5150
    int32_t term;
5151

5152
    if (error == 0) {
×
5153
        return;
×
5154
    }
5155

5156
    sign = direction >= 0 ? 1 : -1;
×
5157
    for (i = 0; i < 2; ++i) {
×
5158
        neighbor = x + sign * row0_offsets[i];
×
5159
        if (neighbor < 0 || neighbor >= width) {
×
5160
            continue;
×
5161
        }
5162
        term = diffuse_fixed_term(error, row0_num[i], row0_den[i]);
×
5163
        carry_curr[((size_t)neighbor * (size_t)depth)
×
5164
                   + (size_t)channel] += term;
×
5165
    }
5166
    if (y + 1 < height) {
×
5167
        for (i = 0; i < 5; ++i) {
×
5168
            neighbor = x + sign * row1_offsets[i];
×
5169
            if (neighbor < 0 || neighbor >= width) {
×
5170
                continue;
×
5171
            }
5172
            term = diffuse_fixed_term(error, row1_num[i], row1_den[i]);
×
5173
            carry_next[((size_t)neighbor * (size_t)depth)
×
5174
                       + (size_t)channel] += term;
×
5175
        }
5176
    }
5177
    if (y + 2 < height) {
×
5178
        for (i = 0; i < 3; ++i) {
×
5179
            neighbor = x + sign * row2_offsets[i];
×
5180
            if (neighbor < 0 || neighbor >= width) {
×
5181
                continue;
×
5182
            }
5183
            term = diffuse_fixed_term(error, row2_num[i], row2_den[i]);
×
5184
            carry_far[((size_t)neighbor * (size_t)depth)
×
5185
                      + (size_t)channel] += term;
×
5186
        }
5187
    }
5188
}
5189

5190

5191
static void
5192
diffuse_lso1(unsigned char *data, int width, int height,
×
5193
             int x, int y, int depth, int error, int direction)
5194
{
5195
    int pos;
5196
    int sign;
5197

5198
    pos = y * width + x;
×
5199
    sign = direction >= 0 ? 1 : -1;
×
5200

5201
    /* lso1 (libsixel original) method:
5202
     *
5203
     * libsixel-specific error diffusion (dithering) to improve sixel
5204
     * compression; by steering error propagation so out-of-palette
5205
     * intermediate colors render as horizontal bands rather than grainy
5206
     * noise, we increase RLE more effective.
5207
     *
5208
     *          curr
5209
     *   1/8    4/8    1/8
5210
     *          2/8
5211
     */
5212
    if (y < height - 1) {
×
5213
        int row;
5214

5215
        row = pos + width;
×
5216
        if (x - sign >= 0 && x - sign < width) {
×
5217
            error_diffuse_fast(data,
×
5218
                               row + (-sign),
5219
                               depth, error, 1, 8);
5220
        }
5221
        error_diffuse_fast(data, row, depth, error, 4, 8);
×
5222
        if (x + sign >= 0 && x + sign < width) {
×
5223
            error_diffuse_fast(data,
×
5224
                               row + sign,
5225
                               depth, error, 1, 8);
5226
        }
5227
    }
5228
    if (y < height - 2) {
×
5229
        error_diffuse_fast(data, pos + width * 2, depth, error, 2, 8);
×
5230
    }
5231
}
×
5232

5233

5234
static void
5235
diffuse_lso1_carry(int32_t *carry_curr, int32_t *carry_next,
×
5236
                   int32_t *carry_far, int width, int height,
5237
                   int depth, int x, int y, int32_t error,
5238
                   int direction, int channel)
5239
{
5240
    int sign;
5241
    int32_t edge_term;
5242
    int32_t center_term;
5243
    int32_t far_term;
5244

5245
    /* unused */ (void) carry_curr;
5246
    if (error == 0) {
×
5247
        return;
×
5248
    }
5249

5250
    sign = direction >= 0 ? 1 : -1;
×
5251
    edge_term = diffuse_fixed_term(error, 1, 8);
×
5252
    center_term = diffuse_fixed_term(error, 4, 8);
×
5253
    far_term = diffuse_fixed_term(error, 2, 8);
×
5254

5255
    if (y + 1 < height) {
×
5256
        if (x - sign >= 0 && x - sign < width) {
×
5257
            size_t base;
5258

5259
            base = ((size_t)(x - sign) * (size_t)depth)
×
5260
                 + (size_t)channel;
×
5261
            carry_next[base] += edge_term;
×
5262
        }
5263
        {
5264
            size_t base;
5265

5266
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
5267
            carry_next[base] += center_term;
×
5268
        }
5269
        if (x + sign >= 0 && x + sign < width) {
×
5270
            size_t base;
5271

5272
            base = ((size_t)(x + sign) * (size_t)depth)
×
5273
                 + (size_t)channel;
×
5274
            carry_next[base] += edge_term;
×
5275
        }
5276
    }
5277
    if (y + 2 < height) {
×
5278
        size_t base;
5279

5280
        base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
5281
        carry_far[base] += far_term;
×
5282
    }
5283
}
5284

5285
static float
5286
mask_a (int x, int y, int c)
×
5287
{
5288
    return ((((x + c * 67) + y * 236) * 119) & 255 ) / 128.0 - 1.0;
×
5289
}
5290

5291
static float
5292
mask_x (int x, int y, int c)
×
5293
{
5294
    return ((((x + c * 29) ^ y* 149) * 1234) & 511 ) / 256.0 - 1.0;
×
5295
}
5296

5297
/* lookup closest color from palette with "normal" strategy */
5298
static int
5299
lookup_normal(unsigned char const * const pixel,
849,900✔
5300
              int const depth,
5301
              unsigned char const * const palette,
5302
              int const reqcolor,
5303
              unsigned short * const cachetable,
5304
              int const complexion)
5305
{
5306
    int result;
5307
    int diff;
5308
    int r;
5309
    int i;
5310
    int n;
5311
    int distant;
5312

5313
    result = (-1);
849,900✔
5314
    diff = INT_MAX;
849,900✔
5315

5316
    /* don't use cachetable in 'normal' strategy */
5317
    (void) cachetable;
283,300✔
5318

5319
    for (i = 0; i < reqcolor; i++) {
15,309,900✔
5320
        distant = 0;
14,460,000✔
5321
        r = pixel[0] - palette[i * depth + 0];
14,460,000✔
5322
        distant += r * r * complexion;
14,460,000✔
5323
        for (n = 1; n < depth; ++n) {
43,380,000✔
5324
            r = pixel[n] - palette[i * depth + n];
28,920,000✔
5325
            distant += r * r;
28,920,000✔
5326
        }
9,640,000✔
5327
        if (distant < diff) {
14,460,000✔
5328
            diff = distant;
2,211,216✔
5329
            result = i;
2,211,216✔
5330
        }
736,152✔
5331
    }
4,820,000✔
5332

5333
    return result;
849,900✔
5334
}
5335

5336

5337
/*
5338
 * Shared fast lookup flow:
5339
 *
5340
 *   pixel --> quantize --> cuckoo cache --> palette index
5341
 */
5342
static int
5343
lookup_fast_common(unsigned char const *pixel,
23,025,286✔
5344
                   unsigned char const *palette,
5345
                   int reqcolor,
5346
                   unsigned short *cachetable,
5347
                   int complexion,
5348
                   struct histogram_control control)
5349
{
5350
    int result;
5351
    unsigned int hash;
5352
    int diff;
5353
    int i;
5354
    int distant;
5355
    struct cuckoo_table32 *table;
5356
    uint32_t *slot;
5357
    SIXELSTATUS status;
5358
    unsigned char const *entry;
5359
    unsigned char const *end;
5360
    int pixel0;
5361
    int pixel1;
5362
    int pixel2;
5363
    int delta;
5364

5365
    result = (-1);
23,025,286✔
5366
    diff = INT_MAX;
23,025,286✔
5367
    hash = computeHash(pixel, 3U, &control);
23,025,286✔
5368

5369
    table = (struct cuckoo_table32 *)cachetable;
23,025,286✔
5370
    if (table != NULL) {
23,025,286!
5371
        slot = cuckoo_table32_lookup(table, hash);
23,025,286✔
5372
        if (slot != NULL && *slot != 0U) {
23,025,286!
5373
            return (int)(*slot - 1U);
21,422,694✔
5374
        }
5375
    }
517,778✔
5376

5377
    entry = palette;
1,602,592✔
5378
    end = palette + (size_t)reqcolor * 3;
1,602,592✔
5379
    pixel0 = (int)pixel[0];
1,602,592✔
5380
    pixel1 = (int)pixel[1];
1,602,592✔
5381
    pixel2 = (int)pixel[2];
1,602,592✔
5382
    /*
5383
     * Palette traversal as RGB triplets keeps the stride linear:
5384
     *
5385
     *   i -> [R][G][B]
5386
     *        |  |  |
5387
     *        `--+--'
5388
     *           v
5389
     *         entry
5390
     */
5391
    for (i = 0; entry < end; ++i, entry += 3) {
280,320,144✔
5392
        delta = pixel0 - (int)entry[0];
278,717,552✔
5393
        distant = delta * delta * complexion;
278,717,552✔
5394
        delta = pixel1 - (int)entry[1];
278,717,552✔
5395
        distant += delta * delta;
278,717,552✔
5396
        delta = pixel2 - (int)entry[2];
278,717,552✔
5397
        distant += delta * delta;
278,717,552✔
5398
        if (distant < diff) {
278,717,552✔
5399
            diff = distant;
12,059,504✔
5400
            result = i;
12,059,504✔
5401
        }
3,817,818✔
5402
    }
90,105,810✔
5403

5404
    if (table != NULL && result >= 0) {
1,602,592!
5405
        status = cuckoo_table32_insert(table,
2,120,370✔
5406
                                       hash,
517,778✔
5407
                                       (uint32_t)(result + 1));
1,602,592✔
5408
        if (SIXEL_FAILED(status)) {
1,602,592!
5409
            /* ignore cache update failure */
5410
        }
5411
    }
517,778✔
5412

5413
    return result;
1,602,592✔
5414
}
10,479,482✔
5415

5416
/* lookup closest color from palette with "fast" strategy */
5417
static int
5418
lookup_fast(unsigned char const * const pixel,
23,025,286✔
5419
            int const depth,
5420
            unsigned char const * const palette,
5421
            int const reqcolor,
5422
            unsigned short * const cachetable,
5423
            int const complexion)
5424
{
5425
    struct histogram_control control;
5426

5427
    (void)depth;
10,479,482✔
5428

5429
    control = histogram_control_make(3U);
23,025,286✔
5430

5431
    return lookup_fast_common(pixel,
33,504,768✔
5432
                              palette,
10,479,482✔
5433
                              reqcolor,
10,479,482✔
5434
                              cachetable,
10,479,482✔
5435
                              complexion,
10,479,482✔
5436
                              control);
5437
}
5438

5439
static int
5440
lookup_fast_robinhood(unsigned char const * const pixel,
×
5441
                      int const depth,
5442
                      unsigned char const * const palette,
5443
                      int const reqcolor,
5444
                      unsigned short * const cachetable,
5445
                      int const complexion)
5446
{
5447
    struct histogram_control control;
5448

5449
    (void)depth;
5450

5451
    control = histogram_control_make_for_policy(3U,
×
5452
                                                SIXEL_LUT_POLICY_ROBINHOOD);
5453

5454
    return lookup_fast_common(pixel,
×
5455
                              palette,
5456
                              reqcolor,
5457
                              cachetable,
5458
                              complexion,
5459
                              control);
5460
}
5461

5462
static int
5463
lookup_fast_hopscotch(unsigned char const * const pixel,
×
5464
                      int const depth,
5465
                      unsigned char const * const palette,
5466
                      int const reqcolor,
5467
                      unsigned short * const cachetable,
5468
                      int const complexion)
5469
{
5470
    struct histogram_control control;
5471

5472
    (void)depth;
5473

5474
    control = histogram_control_make_for_policy(3U,
×
5475
                                                SIXEL_LUT_POLICY_HOPSCOTCH);
5476

5477
    return lookup_fast_common(pixel,
×
5478
                              palette,
5479
                              reqcolor,
5480
                              cachetable,
5481
                              complexion,
5482
                              control);
5483
}
5484

5485

5486
static int
5487
lookup_mono_darkbg(unsigned char const * const pixel,
1,730,520✔
5488
                   int const depth,
5489
                   unsigned char const * const palette,
5490
                   int const reqcolor,
5491
                   unsigned short * const cachetable,
5492
                   int const complexion)
5493
{
5494
    int n;
5495
    int distant;
5496

5497
    /* unused */ (void) palette;
576,840✔
5498
    /* unused */ (void) cachetable;
576,840✔
5499
    /* unused */ (void) complexion;
576,840✔
5500

5501
    distant = 0;
1,730,520✔
5502
    for (n = 0; n < depth; ++n) {
6,922,080✔
5503
        distant += pixel[n];
5,191,560✔
5504
    }
1,730,520✔
5505
    return distant >= 128 * reqcolor ? 1: 0;
1,730,520✔
5506
}
5507

5508

5509
static int
5510
lookup_mono_lightbg(unsigned char const * const pixel,
810,000✔
5511
                    int const depth,
5512
                    unsigned char const * const palette,
5513
                    int const reqcolor,
5514
                    unsigned short * const cachetable,
5515
                    int const complexion)
5516
{
5517
    int n;
5518
    int distant;
5519

5520
    /* unused */ (void) palette;
270,000✔
5521
    /* unused */ (void) cachetable;
270,000✔
5522
    /* unused */ (void) complexion;
270,000✔
5523

5524
    distant = 0;
810,000✔
5525
    for (n = 0; n < depth; ++n) {
3,240,000✔
5526
        distant += pixel[n];
2,430,000✔
5527
    }
810,000✔
5528
    return distant < 128 * reqcolor ? 1: 0;
810,000✔
5529
}
5530

5531

5532
/* choose colors using median-cut method */
5533
SIXELSTATUS
5534
sixel_quant_make_palette(
256✔
5535
    unsigned char          /* out */ **result,
5536
    unsigned char const    /* in */  *data,
5537
    unsigned int           /* in */  length,
5538
    int                    /* in */  pixelformat,
5539
    unsigned int           /* in */  reqcolors,
5540
    unsigned int           /* in */  *ncolors,
5541
    unsigned int           /* in */  *origcolors,
5542
    int                    /* in */  methodForLargest,
5543
    int                    /* in */  methodForRep,
5544
    int                    /* in */  qualityMode,
5545
    int                    /* in */  force_palette,
5546
    sixel_allocator_t      /* in */  *allocator)
5547
{
5548
    SIXELSTATUS status = SIXEL_FALSE;
256✔
5549
    unsigned int i;
5550
    unsigned int n;
5551
    int ret;
5552
    tupletable2 colormap;
5553
    unsigned int depth;
5554
    int result_depth;
5555

5556
    result_depth = sixel_helper_compute_depth(pixelformat);
256✔
5557
    if (result_depth <= 0) {
256!
5558
        *result = NULL;
×
5559
        goto end;
×
5560
    }
5561

5562
    depth = (unsigned int)result_depth;
256✔
5563

5564
    ret = computeColorMapFromInput(data, length, depth,
372✔
5565
                                   reqcolors, methodForLargest,
116✔
5566
                                   methodForRep, qualityMode,
116✔
5567
                                   force_palette, &colormap,
116✔
5568
                                   origcolors, allocator);
116✔
5569
    if (ret != 0) {
256!
5570
        *result = NULL;
×
5571
        goto end;
×
5572
    }
5573
    *ncolors = colormap.size;
256✔
5574
    quant_trace(stderr, "tupletable size: %d\n", *ncolors);
256✔
5575
    *result = (unsigned char *)sixel_allocator_malloc(allocator, *ncolors * depth);
256✔
5576
    for (i = 0; i < *ncolors; i++) {
16,018✔
5577
        for (n = 0; n < depth; ++n) {
63,048✔
5578
            (*result)[i * depth + n] = colormap.table[i]->tuple[n];
47,286✔
5579
        }
16,476✔
5580
    }
5,492✔
5581

5582
    sixel_allocator_free(allocator, colormap.table);
256✔
5583

5584
    status = SIXEL_OK;
256✔
5585

5586
end:
140✔
5587
    return status;
256✔
5588
}
5589

5590

5591
/* apply color palette into specified pixel buffers */
5592
SIXELSTATUS
5593
sixel_quant_apply_palette(
281✔
5594
    sixel_index_t     /* out */ *result,
5595
    unsigned char     /* in */  *data,
5596
    int               /* in */  width,
5597
    int               /* in */  height,
5598
    int               /* in */  depth,
5599
    unsigned char     /* in */  *palette,
5600
    int               /* in */  reqcolor,
5601
    int               /* in */  methodForDiffuse,
5602
    int               /* in */  methodForScan,
5603
    int               /* in */  methodForCarry,
5604
    int               /* in */  foptimize,
5605
    int               /* in */  foptimize_palette,
5606
    int               /* in */  complexion,
5607
    unsigned short    /* in */  *cachetable,
5608
    int               /* in */  *ncolors,
5609
    sixel_allocator_t /* in */  *allocator)
5610
{
168✔
5611
#if _MSC_VER
5612
    enum { max_depth = 4 };
5613
#else
5614
    const size_t max_depth = 4;
281✔
5615
#endif
5616
    unsigned char copy[max_depth];
168✔
5617
    SIXELSTATUS status = SIXEL_FALSE;
281✔
5618
    int sum1, sum2;
5619
    int n;
5620
    unsigned short *indextable;
5621
    size_t cache_size;
5622
    int allocated_cache;
5623
    int use_cache;
5624
    int cache_policy;
5625
    unsigned char new_palette[SIXEL_PALETTE_MAX * 4];
5626
    unsigned short migration_map[SIXEL_PALETTE_MAX];
5627
    int (*f_lookup)(unsigned char const * const pixel,
5628
                    int const depth,
5629
                    unsigned char const * const palette,
5630
                    int const reqcolor,
5631
                    unsigned short * const cachetable,
5632
                    int const complexion);
5633
    int use_varerr;
5634
    int use_positional;
5635
    int carry_mode;
5636

5637
    /* check bad reqcolor */
5638
    if (reqcolor < 1) {
281!
5639
        status = SIXEL_BAD_ARGUMENT;
×
5640
        sixel_helper_set_additional_message(
×
5641
            "sixel_quant_apply_palette: "
5642
            "a bad argument is detected, reqcolor < 0.");
5643
        goto end;
×
5644
    }
5645

5646
    /* NOTE: diffuse_jajuni, diffuse_stucki, and diffuse_burkes reference at
5647
     * minimum the position pos + width * 1 - 2, so width must be at least 2
5648
     * to avoid underflow.
5649
     * On the other hand, diffuse_fs and diffuse_atkinson
5650
     * reference pos + width * 1 - 1, but since these functions are only called
5651
     * when width >= 1, they do not cause underflow.
5652
     */
5653
    use_varerr = (depth == 3
394✔
5654
                  && (methodForDiffuse == SIXEL_DIFFUSE_LSO2
562!
5655
                      || methodForDiffuse == SIXEL_DIFFUSE_LSO3));
281!
5656
    use_positional = (methodForDiffuse == SIXEL_DIFFUSE_A_DITHER
394✔
5657
                      || methodForDiffuse == SIXEL_DIFFUSE_X_DITHER);
281!
5658
    carry_mode = (methodForCarry == SIXEL_CARRY_ENABLE)
281✔
5659
               ? SIXEL_CARRY_ENABLE
5660
               : SIXEL_CARRY_DISABLE;
168!
5661

5662
    f_lookup = NULL;
281✔
5663
    if (reqcolor == 2) {
281✔
5664
        sum1 = 0;
19✔
5665
        sum2 = 0;
19✔
5666
        for (n = 0; n < depth; ++n) {
76✔
5667
            sum1 += palette[n];
57✔
5668
        }
21✔
5669
        for (n = depth; n < depth + depth; ++n) {
76✔
5670
            sum2 += palette[n];
57✔
5671
        }
21✔
5672
        if (sum1 == 0 && sum2 == 255 * 3) {
19!
5673
            f_lookup = lookup_mono_darkbg;
12✔
5674
        } else if (sum1 == 255 * 3 && sum2 == 0) {
11!
5675
            f_lookup = lookup_mono_lightbg;
3✔
5676
        }
1✔
5677
    }
7✔
5678
    if (f_lookup == NULL) {
281✔
5679
        if (foptimize && depth == 3) {
266!
5680
            f_lookup = lookup_fast;
260✔
5681
        } else {
106✔
5682
            f_lookup = lookup_normal;
6✔
5683
        }
5684
    }
108✔
5685

5686
    if (f_lookup == lookup_fast) {
281✔
5687
        if (histogram_lut_policy == SIXEL_LUT_POLICY_ROBINHOOD) {
260!
5688
            f_lookup = lookup_fast_robinhood;
×
5689
        } else if (histogram_lut_policy == SIXEL_LUT_POLICY_HOPSCOTCH) {
260!
5690
            f_lookup = lookup_fast_hopscotch;
×
5691
        }
5692
    }
106✔
5693

5694
    indextable = cachetable;
281✔
5695
    allocated_cache = 0;
281✔
5696
    cache_policy = SIXEL_LUT_POLICY_AUTO;
281✔
5697
    use_cache = 0;
281✔
5698
    if (f_lookup == lookup_fast) {
281✔
5699
        cache_policy = histogram_lut_policy;
260✔
5700
        use_cache = 1;
260✔
5701
    } else if (f_lookup == lookup_fast_robinhood) {
127!
5702
        cache_policy = SIXEL_LUT_POLICY_ROBINHOOD;
×
5703
        use_cache = 1;
×
5704
    } else if (f_lookup == lookup_fast_hopscotch) {
21!
5705
        cache_policy = SIXEL_LUT_POLICY_HOPSCOTCH;
×
5706
        use_cache = 1;
×
5707
    }
5708
    if (cache_policy == SIXEL_LUT_POLICY_AUTO) {
281!
5709
        cache_policy = SIXEL_LUT_POLICY_6BIT;
281✔
5710
    }
113✔
5711
    if (use_cache) {
281✔
5712
        if (cachetable == NULL) {
260!
5713
            status = sixel_quant_cache_prepare(&indextable,
×
5714
                                               &cache_size,
5715
                                               cache_policy,
5716
                                               reqcolor,
5717
                                               allocator);
5718
            if (SIXEL_FAILED(status)) {
×
5719
                quant_trace(stderr,
×
5720
                            "Unable to allocate lookup cache.\n");
5721
                goto end;
×
5722
            }
5723
            allocated_cache = 1;
×
5724
        } else {
5725
            sixel_quant_cache_clear(indextable, cache_policy);
260✔
5726
        }
5727
    }
106✔
5728

5729
    if (use_positional) {
281!
5730
        status = apply_palette_positional(result, data, width, height,
×
5731
                                          depth, palette, reqcolor,
5732
                                          methodForDiffuse, methodForScan,
5733
                                          foptimize_palette, f_lookup,
5734
                                          indextable, complexion, copy,
5735
                                          new_palette, migration_map,
5736
                                          ncolors);
5737
    } else if (use_varerr) {
281!
5738
        status = apply_palette_variable(result, data, width, height,
×
5739
                                        depth, palette, reqcolor,
5740
                                        methodForScan, foptimize_palette,
5741
                                        f_lookup, indextable, complexion,
5742
                                        new_palette, migration_map,
5743
                                        ncolors,
5744
                                        methodForDiffuse,
5745
                                        carry_mode);
5746
    } else {
5747
        status = apply_palette_fixed(result, data, width, height,
394✔
5748
                                     depth, palette, reqcolor,
113✔
5749
                                     methodForScan, foptimize_palette,
113✔
5750
                                     f_lookup, indextable, complexion,
113✔
5751
                                     new_palette, migration_map,
113✔
5752
                                     ncolors, methodForDiffuse,
113✔
5753
                                     carry_mode);
113✔
5754
    }
5755
    if (SIXEL_FAILED(status)) {
281!
5756
        goto end;
×
5757
    }
5758

5759
    if (allocated_cache) {
281!
5760
        sixel_quant_cache_release(indextable,
×
5761
                                  cache_policy,
5762
                                  allocator);
5763
    }
5764

5765
    status = SIXEL_OK;
281✔
5766

5767
end:
168✔
5768
    return status;
281✔
5769
}
5770

5771

5772
void
5773
sixel_quant_free_palette(
256✔
5774
    unsigned char       /* in */ *data,
5775
    sixel_allocator_t   /* in */ *allocator)
5776
{
5777
    sixel_allocator_free(allocator, data);
256✔
5778
}
256✔
5779

5780

5781
#if HAVE_TESTS
5782
static int
5783
test1(void)
×
5784
{
5785
    int nret = EXIT_FAILURE;
×
5786
    sample minval[1] = { 1 };
×
5787
    sample maxval[1] = { 2 };
×
5788
    unsigned int retval;
5789

5790
    retval = largestByLuminosity(minval, maxval, 1);
×
5791
    if (retval != 0) {
×
5792
        goto error;
×
5793
    }
5794
    nret = EXIT_SUCCESS;
×
5795

5796
error:
5797
    return nret;
×
5798
}
5799

5800

5801
int
5802
sixel_quant_tests_main(void)
×
5803
{
5804
    int nret = EXIT_FAILURE;
×
5805
    size_t i;
5806
    typedef int (* testcase)(void);
5807

5808
    static testcase const testcases[] = {
5809
        test1,
5810
    };
5811

5812
    for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) {
×
5813
        nret = testcases[i]();
×
5814
        if (nret != EXIT_SUCCESS) {
×
5815
            goto error;
×
5816
        }
5817
    }
5818

5819
    nret = EXIT_SUCCESS;
×
5820

5821
error:
5822
    return nret;
×
5823
}
5824
#endif  /* HAVE_TESTS */
5825

5826
/* emacs Local Variables:      */
5827
/* emacs mode: c               */
5828
/* emacs tab-width: 4          */
5829
/* emacs indent-tabs-mode: nil */
5830
/* emacs c-basic-offset: 4     */
5831
/* emacs End:                  */
5832
/* vim: set expandtab ts=4 sts=4 sw=4 : */
5833
/* EOF */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc