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

saitoha / libsixel / 18801138724

25 Oct 2025 09:14AM UTC coverage: 50.934% (-1.1%) from 52.018%
18801138724

push

github

saitoha
quant: add sierra1,sierra2,sierra3 method for diffusion

5683 of 16336 branches covered (34.79%)

8 of 357 new or added lines in 3 files covered. (2.24%)

8265 of 16227 relevant lines covered (50.93%)

1182650.55 hits per line

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

38.37
/src/quant.c
1
/*
2
 *
3
 * mediancut algorithm implementation is imported from pnmcolormap.c
4
 * in netpbm library.
5
 * http://netpbm.sourceforge.net/
6
 *
7
 * *******************************************************************************
8
 *                  original license block of pnmcolormap.c
9
 * *******************************************************************************
10
 *
11
 *   Derived from ppmquant, originally by Jef Poskanzer.
12
 *
13
 *   Copyright (C) 1989, 1991 by Jef Poskanzer.
14
 *   Copyright (C) 2001 by Bryan Henderson.
15
 *
16
 *   Permission to use, copy, modify, and distribute this software and its
17
 *   documentation for any purpose and without fee is hereby granted, provided
18
 *   that the above copyright notice appear in all copies and that both that
19
 *   copyright notice and this permission notice appear in supporting
20
 *   documentation.  This software is provided "as is" without express or
21
 *   implied warranty.
22
 *
23
 * ******************************************************************************
24
 *
25
 * Copyright (c) 2014-2018 Hayaki Saito
26
 *
27
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
28
 * this software and associated documentation files (the "Software"), to deal in
29
 * the Software without restriction, including without limitation the rights to
30
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
31
 * the Software, and to permit persons to whom the Software is furnished to do so,
32
 * subject to the following conditions:
33
 *
34
 * The above copyright notice and this permission notice shall be included in all
35
 * copies or substantial portions of the Software.
36
 *
37
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
38
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
39
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
40
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
41
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
42
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
43
 *
44
 *
45
 */
46

47
#include "config.h"
48

49
/* STDC_HEADERS */
50
#include <stdlib.h>
51
#include <stdio.h>
52
#include <time.h>
53

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

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

78
#include "quant.h"
79

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

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

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

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

191

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

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

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

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

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

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

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

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

250
    typedef const struct tupleint * const * const sortarg;
251
    sortarg comparandPP  = (sortarg) arg1;
6,571,383✔
252
    sortarg comparatorPP = (sortarg) arg2;
6,571,383✔
253
    lhs = (int)(*comparandPP)->tuple[compareplanePlane];
6,571,383✔
254
    rhs = (int)(*comparatorPP)->tuple[compareplanePlane];
6,571,383✔
255

256
    return lhs - rhs;
6,571,383✔
257
}
258

259

260
static int
261
sumcompare(const void * const b1, const void * const b2)
4,522,638✔
262
{
263
    return (int)((boxVector)b2)->sum - (int)((boxVector)b1)->sum;
4,522,638✔
264
}
265

266

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

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

296
    mainTableSize = size * sizeof(struct tupleint *);
470✔
297
    tupleIntSize = sizeof(struct tupleint) - sizeof(sample)
470✔
298
        + depth * sizeof(sample);
218✔
299

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

315
    allocSize = mainTableSize + size * tupleIntSize;
470✔
316

317
    pool = sixel_allocator_malloc(allocator, allocSize);
470✔
318
    if (pool == NULL) {
470!
319
        sprintf(message,
×
320
                "unable to allocate %u bytes for a %u-entry "
321
                "tuple table",
322
                 allocSize, size);
323
        sixel_helper_set_additional_message(message);
×
324
        status = SIXEL_BAD_ALLOCATION;
×
325
        goto end;
×
326
    }
327
    tbl = (tupletable) pool;
470✔
328

329
    for (i = 0; i < size; ++i)
201,263✔
330
        tbl[i] = (struct tupleint *)
200,793✔
331
            ((char*)pool + mainTableSize + i * tupleIntSize);
200,793✔
332

333
    *result = tbl;
470✔
334

335
    status = SIXEL_OK;
470✔
336

337
end:
252✔
338
    return status;
470✔
339
}
340

341

342
/*
343
** Here is the fun part, the median-cut colormap generator.  This is based
344
** on Paul Heckbert's paper "Color Image Quantization for Frame Buffer
345
** Display", SIGGRAPH '82 Proceedings, page 297.
346
*/
347

348
static tupletable2
349
newColorMap(unsigned int const newcolors, unsigned int const depth, sixel_allocator_t *allocator)
45✔
350
{
351
    SIXELSTATUS status = SIXEL_FALSE;
45✔
352
    tupletable2 colormap;
353
    unsigned int i;
354

355
    colormap.size = 0;
45✔
356
    status = alloctupletable(&colormap.table, depth, newcolors, allocator);
45✔
357
    if (SIXEL_FAILED(status)) {
45!
358
        goto end;
×
359
    }
360
    if (colormap.table) {
60!
361
        for (i = 0; i < newcolors; ++i) {
7,665✔
362
            unsigned int plane;
363
            for (plane = 0; plane < depth; ++plane)
30,480✔
364
                colormap.table[i]->tuple[plane] = 0;
22,860✔
365
        }
2,540✔
366
        colormap.size = newcolors;
45✔
367
    }
15✔
368

369
end:
370
    return colormap;
45✔
371
}
372

373

374
static boxVector
375
newBoxVector(
45✔
376
    unsigned int const  /* in */ colors,
377
    unsigned int const  /* in */ sum,
378
    unsigned int const  /* in */ newcolors,
379
    sixel_allocator_t   /* in */ *allocator)
380
{
381
    boxVector bv;
382

383
    bv = (boxVector)sixel_allocator_malloc(allocator,
60✔
384
                                           sizeof(struct box) * (size_t)newcolors);
45✔
385
    if (bv == NULL) {
45!
386
        quant_trace(stderr, "out of memory allocating box vector table\n");
×
387
        return NULL;
×
388
    }
389

390
    /* Set up the initial box. */
391
    bv[0].ind = 0;
45✔
392
    bv[0].colors = colors;
45✔
393
    bv[0].sum = sum;
45✔
394

395
    return bv;
45✔
396
}
15✔
397

398

399
static void
400
findBoxBoundaries(tupletable2  const colorfreqtable,
7,575✔
401
                  unsigned int const depth,
402
                  unsigned int const boxStart,
403
                  unsigned int const boxSize,
404
                  sample             minval[],
405
                  sample             maxval[])
406
{
407
/*----------------------------------------------------------------------------
408
  Go through the box finding the minimum and maximum of each
409
  component - the boundaries of the box.
410
-----------------------------------------------------------------------------*/
411
    unsigned int plane;
412
    unsigned int i;
413

414
    for (plane = 0; plane < depth; ++plane) {
30,300✔
415
        minval[plane] = colorfreqtable.table[boxStart]->tuple[plane];
22,725✔
416
        maxval[plane] = minval[plane];
22,725✔
417
    }
7,575✔
418

419
    for (i = 1; i < boxSize; ++i) {
1,131,595✔
420
        for (plane = 0; plane < depth; ++plane) {
4,496,080✔
421
            sample const v = colorfreqtable.table[boxStart + i]->tuple[plane];
3,372,060✔
422
            if (v < minval[plane]) minval[plane] = v;
3,372,060✔
423
            if (v > maxval[plane]) maxval[plane] = v;
3,372,060✔
424
        }
1,124,790✔
425
    }
374,930✔
426
}
7,575✔
427

428

429

430
static unsigned int
431
largestByNorm(sample minval[], sample maxval[], unsigned int const depth)
6,810✔
432
{
433

434
    unsigned int largestDimension;
435
    unsigned int plane;
436
    sample largestSpreadSoFar;
437

438
    largestSpreadSoFar = 0;
6,810✔
439
    largestDimension = 0;
6,810✔
440
    for (plane = 0; plane < depth; ++plane) {
27,240✔
441
        sample const spread = maxval[plane]-minval[plane];
20,430✔
442
        if (spread > largestSpreadSoFar) {
20,430✔
443
            largestDimension = plane;
11,992✔
444
            largestSpreadSoFar = spread;
11,992✔
445
        }
3,972✔
446
    }
6,810✔
447
    return largestDimension;
6,810✔
448
}
449

450

451

452
static unsigned int
453
largestByLuminosity(sample minval[], sample maxval[], unsigned int const depth)
765✔
454
{
455
/*----------------------------------------------------------------------------
456
   This subroutine presumes that the tuple type is either
457
   BLACKANDWHITE, GRAYSCALE, or RGB (which implies pamP->depth is 1 or 3).
458
   To save time, we don't actually check it.
459
-----------------------------------------------------------------------------*/
460
    unsigned int retval;
461

462
    double lumin_factor[3] = {0.2989, 0.5866, 0.1145};
765✔
463

464
    if (depth == 1) {
765!
465
        retval = 0;
×
466
    } else {
467
        /* An RGB tuple */
468
        unsigned int largestDimension;
469
        unsigned int plane;
470
        double largestSpreadSoFar;
471

472
        largestSpreadSoFar = 0.0;
765✔
473
        largestDimension = 0;
765✔
474

475
        for (plane = 0; plane < 3; ++plane) {
3,060✔
476
            double const spread =
2,295✔
477
                lumin_factor[plane] * (maxval[plane]-minval[plane]);
2,295✔
478
            if (spread > largestSpreadSoFar) {
2,295✔
479
                largestDimension = plane;
1,331✔
480
                largestSpreadSoFar = spread;
1,331✔
481
            }
451✔
482
        }
765✔
483
        retval = largestDimension;
765✔
484
    }
485
    return retval;
765✔
486
}
487

488

489

490
static void
491
centerBox(unsigned int const boxStart,
6,084✔
492
          unsigned int const boxSize,
493
          tupletable2  const colorfreqtable,
494
          unsigned int const depth,
495
          tuple        const newTuple)
496
{
497

498
    unsigned int plane;
499
    sample minval, maxval;
500
    unsigned int i;
501

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

505
        for (i = 1; i < boxSize; ++i) {
482,172✔
506
            sample v = colorfreqtable.table[boxStart + i]->tuple[plane];
463,920✔
507
            minval = minval < v ? minval: v;
463,920✔
508
            maxval = maxval > v ? maxval: v;
463,920✔
509
        }
154,362✔
510
        newTuple[plane] = (minval + maxval) / 2;
18,252✔
511
    }
6,084✔
512
}
6,084✔
513

514

515

516
static void
517
averageColors(unsigned int const boxStart,
768✔
518
              unsigned int const boxSize,
519
              tupletable2  const colorfreqtable,
520
              unsigned int const depth,
521
              tuple        const newTuple)
522
{
523
    unsigned int plane;
524
    sample sum;
525
    unsigned int i;
526

527
    for (plane = 0; plane < depth; ++plane) {
3,072✔
528
        sum = 0;
2,304✔
529

530
        for (i = 0; i < boxSize; ++i) {
43,962✔
531
            sum += colorfreqtable.table[boxStart + i]->tuple[plane];
41,658✔
532
        }
13,878✔
533

534
        newTuple[plane] = sum / boxSize;
2,304✔
535
    }
768✔
536
}
768✔
537

538

539

540
static void
541
averagePixels(unsigned int const boxStart,
768✔
542
              unsigned int const boxSize,
543
              tupletable2 const colorfreqtable,
544
              unsigned int const depth,
545
              tuple const newTuple)
546
{
547

548
    unsigned int n;
549
        /* Number of tuples represented by the box */
550
    unsigned int plane;
551
    unsigned int i;
552

553
    /* Count the tuples in question */
554
    n = 0;  /* initial value */
768✔
555
    for (i = 0; i < boxSize; ++i) {
13,799✔
556
        n += (unsigned int)colorfreqtable.table[boxStart + i]->value;
13,031✔
557
    }
4,311✔
558

559
    for (plane = 0; plane < depth; ++plane) {
3,072✔
560
        sample sum;
561

562
        sum = 0;
2,304✔
563

564
        for (i = 0; i < boxSize; ++i) {
41,397✔
565
            sum += colorfreqtable.table[boxStart + i]->tuple[plane]
52,026✔
566
                * (unsigned int)colorfreqtable.table[boxStart + i]->value;
39,093✔
567
        }
12,933✔
568

569
        newTuple[plane] = sum / n;
2,304✔
570
    }
768✔
571
}
768✔
572

573

574

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

595
    colormap = newColorMap(newcolors, depth, allocator);
45✔
596
    if (!colormap.size) {
45!
597
        return colormap;
×
598
    }
599

600
    for (bi = 0; bi < boxes; ++bi) {
7,665✔
601
        switch (methodForRep) {
7,620!
602
        case SIXEL_REP_CENTER_BOX:
4,056✔
603
            centerBox(bv[bi].ind, bv[bi].colors,
8,112✔
604
                      colorfreqtable, depth,
2,028✔
605
                      colormap.table[bi]->tuple);
6,084✔
606
            break;
6,084✔
607
        case SIXEL_REP_AVERAGE_COLORS:
512✔
608
            averageColors(bv[bi].ind, bv[bi].colors,
1,024✔
609
                          colorfreqtable, depth,
256✔
610
                          colormap.table[bi]->tuple);
768✔
611
            break;
768✔
612
        case SIXEL_REP_AVERAGE_PIXELS:
512✔
613
            averagePixels(bv[bi].ind, bv[bi].colors,
1,024✔
614
                          colorfreqtable, depth,
256✔
615
                          colormap.table[bi]->tuple);
768✔
616
            break;
768✔
617
        default:
618
            quant_trace(stderr, "Internal error: "
×
619
                                "invalid value of methodForRep: %d\n",
620
                        methodForRep);
621
        }
622
    }
2,540✔
623
    return colormap;
45✔
624
}
15✔
625

626

627
static int
628
force_palette_compare(const void *lhs, const void *rhs)
×
629
{
630
    unsigned int left;
631
    unsigned int right;
632
    unsigned int left_value;
633
    unsigned int right_value;
634

635
    left = *(const unsigned int *)lhs;
×
636
    right = *(const unsigned int *)rhs;
×
637
    left_value = force_palette_source->table[left]->value;
×
638
    right_value = force_palette_source->table[right]->value;
×
639
    if (left_value > right_value) {
×
640
        return -1;
×
641
    }
642
    if (left_value < right_value) {
×
643
        return 1;
×
644
    }
645
    if (left < right) {
×
646
        return -1;
×
647
    }
648
    if (left > right) {
×
649
        return 1;
×
650
    }
651
    return 0;
×
652
}
653

654

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

681
    current = colormapP->size;
×
682
    if (current >= reqColors) {
×
683
        return SIXEL_OK;
×
684
    }
685

686
    status = alloctupletable(&new_table, depth, reqColors, allocator);
×
687
    if (SIXEL_FAILED(status)) {
×
688
        goto end;
×
689
    }
690

691
    if (colorfreqtable.size > 0U) {
×
692
        order = (unsigned int *)sixel_allocator_malloc(
×
693
            allocator, colorfreqtable.size * sizeof(unsigned int));
×
694
        if (order == NULL) {
×
695
            status = SIXEL_BAD_ALLOCATION;
×
696
            goto end;
×
697
        }
698
        for (candidate = 0; candidate < colorfreqtable.size; ++candidate) {
×
699
            order[candidate] = candidate;
×
700
        }
701
        force_palette_source = &colorfreqtable;
×
702
        qsort(order, colorfreqtable.size, sizeof(unsigned int),
×
703
              force_palette_compare);
704
        force_palette_source = NULL;
×
705
    }
706

707
    for (fill = 0; fill < current; ++fill) {
×
708
        new_table[fill]->value = colormapP->table[fill]->value;
×
709
        for (plane = 0; plane < depth; ++plane) {
×
710
            new_table[fill]->tuple[plane] =
×
711
                colormapP->table[fill]->tuple[plane];
×
712
        }
713
    }
714

715
    candidate = 0U;
×
716
    fill = current;
×
717
    if (order != NULL) {
×
718
        while (fill < reqColors && candidate < colorfreqtable.size) {
×
719
            unsigned int index;
720

721
            index = order[candidate];
×
722
            new_table[fill]->value = colorfreqtable.table[index]->value;
×
723
            for (plane = 0; plane < depth; ++plane) {
×
724
                new_table[fill]->tuple[plane] =
×
725
                    colorfreqtable.table[index]->tuple[plane];
×
726
            }
727
            ++fill;
×
728
            ++candidate;
×
729
        }
730
    }
731

732
    if (fill < reqColors && fill == 0U) {
×
733
        new_table[fill]->value = 0U;
×
734
        for (plane = 0; plane < depth; ++plane) {
×
735
            new_table[fill]->tuple[plane] = 0U;
×
736
        }
737
        ++fill;
×
738
    }
739

740
    source = 0U;
×
741
    while (fill < reqColors) {
×
742
        new_table[fill]->value = new_table[source]->value;
×
743
        for (plane = 0; plane < depth; ++plane) {
×
744
            new_table[fill]->tuple[plane] = new_table[source]->tuple[plane];
×
745
        }
746
        ++fill;
×
747
        ++source;
×
748
        if (source >= fill) {
×
749
            source = 0U;
×
750
        }
751
    }
752

753
    sixel_allocator_free(allocator, colormapP->table);
×
754
    colormapP->table = new_table;
×
755
    colormapP->size = reqColors;
×
756
    status = SIXEL_OK;
×
757

758
end:
759
    if (status != SIXEL_OK && new_table != NULL) {
×
760
        sixel_allocator_free(allocator, new_table);
×
761
    }
762
    if (order != NULL) {
×
763
        sixel_allocator_free(allocator, order);
×
764
    }
765
    return status;
×
766
}
767

768

769
static SIXELSTATUS
770
splitBox(boxVector const bv,
7,575✔
771
         unsigned int *const boxesP,
772
         unsigned int const bi,
773
         tupletable2 const colorfreqtable,
774
         unsigned int const depth,
775
         int const methodForLargest)
776
{
777
/*----------------------------------------------------------------------------
778
   Split Box 'bi' in the box vector bv (so that bv contains one more box
779
   than it did as input).  Split it so that each new box represents about
780
   half of the pixels in the distribution given by 'colorfreqtable' for
781
   the colors in the original box, but with distinct colors in each of the
782
   two new boxes.
783

784
   Assume the box contains at least two colors.
785
-----------------------------------------------------------------------------*/
786
    SIXELSTATUS status = SIXEL_FALSE;
7,575✔
787
    unsigned int const boxStart = bv[bi].ind;
7,575✔
788
    unsigned int const boxSize  = bv[bi].colors;
7,575✔
789
    unsigned int const sm       = bv[bi].sum;
7,575✔
790

791
    enum { max_depth= 16 };
792
    sample minval[max_depth];
793
    sample maxval[max_depth];
794

795
    /* assert(max_depth >= depth); */
796

797
    unsigned int largestDimension;
798
        /* number of the plane with the largest spread */
799
    unsigned int medianIndex;
800
    unsigned int lowersum;
801
        /* Number of pixels whose value is "less than" the median */
802

803
    findBoxBoundaries(colorfreqtable, depth, boxStart, boxSize,
10,100✔
804
                      minval, maxval);
2,525✔
805

806
    /* Find the largest dimension, and sort by that component.  I have
807
       included two methods for determining the "largest" dimension;
808
       first by simply comparing the range in RGB space, and second by
809
       transforming into luminosities before the comparison.
810
    */
811
    switch (methodForLargest) {
7,575!
812
    case SIXEL_LARGE_NORM:
4,540✔
813
        largestDimension = largestByNorm(minval, maxval, depth);
6,810✔
814
        break;
6,810✔
815
    case SIXEL_LARGE_LUM:
510✔
816
        largestDimension = largestByLuminosity(minval, maxval, depth);
765✔
817
        break;
765✔
818
    default:
819
        sixel_helper_set_additional_message(
×
820
            "Internal error: invalid value of methodForLargest.");
821
        status = SIXEL_LOGIC_ERROR;
×
822
        goto end;
×
823
    }
824

825
    /* TODO: I think this sort should go after creating a box,
826
       not before splitting.  Because you need the sort to use
827
       the SIXEL_REP_CENTER_BOX method of choosing a color to
828
       represent the final boxes
829
    */
830

831
    /* Set the gross global variable 'compareplanePlane' as a
832
       parameter to compareplane(), which is called by qsort().
833
    */
834
    compareplanePlane = largestDimension;
7,575✔
835
    qsort((char*) &colorfreqtable.table[boxStart], boxSize,
7,575✔
836
          sizeof(colorfreqtable.table[boxStart]),
837
          compareplane);
838

839
    {
840
        /* Now find the median based on the counts, so that about half
841
           the pixels (not colors, pixels) are in each subdivision.  */
842

843
        unsigned int i;
844

845
        lowersum = colorfreqtable.table[boxStart]->value; /* initial value */
7,575✔
846
        for (i = 1; i < boxSize - 1 && lowersum < sm / 2; ++i) {
518,432✔
847
            lowersum += colorfreqtable.table[boxStart + i]->value;
510,857✔
848
        }
169,789✔
849
        medianIndex = i;
7,575✔
850
    }
851
    /* Split the box, and sort to bring the biggest boxes to the top.  */
852

853
    bv[bi].colors = medianIndex;
7,575✔
854
    bv[bi].sum = lowersum;
7,575✔
855
    bv[*boxesP].ind = boxStart + medianIndex;
7,575✔
856
    bv[*boxesP].colors = boxSize - medianIndex;
7,575✔
857
    bv[*boxesP].sum = sm - lowersum;
7,575✔
858
    ++(*boxesP);
7,575✔
859
    qsort((char*) bv, *boxesP, sizeof(struct box), sumcompare);
7,575✔
860

861
    status = SIXEL_OK;
7,575✔
862

863
end:
5,050✔
864
    return status;
7,575✔
865
}
866

867

868

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

885
   As a side effect, sort 'colorfreqtable'.
886
-----------------------------------------------------------------------------*/
887
    boxVector bv;
888
    unsigned int bi;
889
    unsigned int boxes;
890
    int multicolorBoxesExist;
891
    unsigned int i;
892
    unsigned int sum;
893
    SIXELSTATUS status = SIXEL_FALSE;
45✔
894

895
    sum = 0;
45✔
896

897
    for (i = 0; i < colorfreqtable.size; ++i) {
187,686✔
898
        sum += colorfreqtable.table[i]->value;
187,641✔
899
    }
62,419✔
900

901
    /* There is at least one box that contains at least 2 colors; ergo,
902
       there is more splitting we can do.  */
903
    bv = newBoxVector(colorfreqtable.size, sum, newcolors, allocator);
45✔
904
    if (bv == NULL) {
45!
905
        goto end;
×
906
    }
907
    boxes = 1;
45✔
908
    multicolorBoxesExist = (colorfreqtable.size > 1);
45✔
909

910
    /* Main loop: split boxes until we have enough. */
911
    while (boxes < newcolors && multicolorBoxesExist) {
7,620!
912
        /* Find the first splittable box. */
913
        for (bi = 0; bi < boxes && bv[bi].colors < 2; ++bi)
40,306!
914
            ;
915
        if (bi >= boxes) {
7,575!
916
            multicolorBoxesExist = 0;
×
917
        } else {
918
            status = splitBox(bv, &boxes, bi,
10,100✔
919
                              colorfreqtable, depth,
2,525✔
920
                              methodForLargest);
2,525✔
921
            if (SIXEL_FAILED(status)) {
7,575!
922
                goto end;
×
923
            }
924
        }
925
    }
926
    *colormapP = colormapFromBv(newcolors, bv, boxes,
60✔
927
                                colorfreqtable, depth,
15✔
928
                                methodForRep, allocator);
15✔
929

930
    sixel_allocator_free(allocator, bv);
45✔
931

932
    status = SIXEL_OK;
45✔
933

934
end:
30✔
935
    return status;
45✔
936
}
937

938

939
static int histogram_lut_policy = SIXEL_LUT_POLICY_AUTO;
940

941
void
942
sixel_quant_set_lut_policy(int lut_policy)
492✔
943
{
944
    int normalized;
945

946
    normalized = SIXEL_LUT_POLICY_AUTO;
492✔
947
    if (lut_policy == SIXEL_LUT_POLICY_5BIT
706!
948
        || lut_policy == SIXEL_LUT_POLICY_6BIT
492!
949
        || lut_policy == SIXEL_LUT_POLICY_ROBINHOOD
492!
950
        || lut_policy == SIXEL_LUT_POLICY_HOPSCOTCH) {
492!
951
        normalized = lut_policy;
×
952
    }
953

954
    histogram_lut_policy = normalized;
492✔
955
}
492✔
956

957
struct histogram_control {
958
    unsigned int channel_shift;
959
    unsigned int channel_bits;
960
    unsigned int channel_mask;
961
};
962

963
static struct histogram_control
964
histogram_control_make(unsigned int depth);
965
static struct histogram_control
966
histogram_control_make_for_policy(unsigned int depth, int lut_policy);
967
static size_t histogram_dense_size(unsigned int depth,
968
                                   struct histogram_control const
969
                                       *control);
970
static unsigned int histogram_reconstruct(unsigned int quantized,
971
                                          struct histogram_control const
972
                                              *control);
973

974
static size_t
975
histogram_dense_size(unsigned int depth,
474✔
976
                     struct histogram_control const *control)
977
{
978
    size_t size;
979
    unsigned int exponent;
980
    unsigned int i;
981

982
    size = 1U;
474✔
983
    exponent = depth * control->channel_bits;
474✔
984
    for (i = 0U; i < exponent; ++i) {
9,006✔
985
        if (size > SIZE_MAX / 2U) {
8,532!
986
            size = SIZE_MAX;
×
987
            break;
×
988
        }
989
        size <<= 1U;
8,532✔
990
    }
3,744✔
991

992
    return size;
474✔
993
}
994

995
static struct histogram_control
996
histogram_control_make_for_policy(unsigned int depth, int lut_policy)
16,545,760✔
997
{
998
    struct histogram_control control;
999

1000
    /*
1001
     * The ASCII ladder below shows how each policy selects bucket width.
1002
     *
1003
     *   auto / 6bit RGB : |--6--|
1004
     *   forced 5bit     : |---5---|
1005
     *   robinhood       : |------8------|
1006
     *   alpha fallback  : |---5---|  (avoids 2^(6*4) buckets)
1007
     */
1008
    control.channel_shift = 2U;
16,545,760✔
1009
    if (depth > 3U) {
16,545,760!
1010
        control.channel_shift = 3U;
×
1011
    }
1012
    if (lut_policy == SIXEL_LUT_POLICY_5BIT) {
16,545,760!
1013
        control.channel_shift = 3U;
×
1014
    } else if (lut_policy == SIXEL_LUT_POLICY_6BIT) {
16,545,760✔
1015
        control.channel_shift = 2U;
239✔
1016
        if (depth > 3U) {
239!
1017
            control.channel_shift = 3U;
×
1018
        }
1019
    } else if (lut_policy == SIXEL_LUT_POLICY_ROBINHOOD
16,545,620!
1020
               || lut_policy == SIXEL_LUT_POLICY_HOPSCOTCH) {
16,545,521!
1021
        control.channel_shift = 0U;
×
1022
    }
1023
    control.channel_bits = 8U - control.channel_shift;
16,545,760✔
1024
    control.channel_mask = (1U << control.channel_bits) - 1U;
16,545,760✔
1025

1026
    return control;
16,545,760✔
1027
}
1028

1029
static unsigned int
1030
histogram_reconstruct(unsigned int quantized,
571,221✔
1031
                      struct histogram_control const *control)
1032
{
1033
    unsigned int value;
1034

1035
    value = quantized << control->channel_shift;
571,221✔
1036
    if (quantized == control->channel_mask) {
571,221✔
1037
        value = 255U;
298✔
1038
    } else {
184✔
1039
        if (control->channel_shift > 0U) {
570,923!
1040
            value |= (1U << (control->channel_shift - 1U));
570,923✔
1041
        }
190,553✔
1042
    }
1043
    if (value > 255U) {
571,221!
1044
        value = 255U;
×
1045
    }
1046

1047
    return value;
571,221✔
1048
}
1049

1050
static unsigned int
1051
histogram_quantize(unsigned int sample8,
58,916,766✔
1052
                   struct histogram_control const *control)
1053
{
1054
    unsigned int quantized;
1055
    unsigned int shift;
1056
    unsigned int mask;
1057
    unsigned int rounding;
1058

1059
    /*
1060
     * We want each bucket to capture its center value instead of the lower
1061
     * edge.  The ASCII sketch below shows how rounding keeps the midpoint:
1062
     *
1063
     *   0---1---2---3        sample8 + round
1064
     *   |   |   |   |  ==>  ----------------  -> bucket index
1065
     *   0   1   2   3              2^shift
1066
     */
1067
    shift = control->channel_shift;
58,916,766✔
1068
    mask = control->channel_mask;
58,916,766✔
1069
    if (shift == 0U) {
58,916,766!
1070
        quantized = sample8;
×
1071
    } else {
1072
        rounding = 1U << (shift - 1U);
58,916,766✔
1073
        quantized = (sample8 + rounding) >> shift;
58,916,766✔
1074
        if (quantized > mask) {
58,916,766✔
1075
            quantized = mask;
968,660✔
1076
        }
334,068✔
1077
    }
1078

1079
    return quantized;
58,916,766✔
1080
}
1081

1082
static unsigned int
1083
computeHash(unsigned char const *data,
19,638,922✔
1084
            unsigned int const depth,
1085
            struct histogram_control const *control)
1086
{
1087
    unsigned int hash;
1088
    unsigned int n;
1089
    unsigned int sample8;
1090
    unsigned int bits;
1091

1092
    hash = 0;
19,638,922✔
1093
    bits = control->channel_bits;
19,638,922✔
1094
    for (n = 0; n < depth; n++) {
78,555,688✔
1095
#if 0
1096
        hash |= (unsigned int)(data[depth - 1 - n] >> 3) << n * 5;
1097
#else
1098
        sample8 = (unsigned int)data[depth - 1 - n];
58,916,766✔
1099
        hash |= histogram_quantize(sample8, control) << (n * bits);
58,916,766✔
1100
#endif
1101
    }
29,774,322✔
1102

1103
    return hash;
19,638,922✔
1104
}
1105

1106
#define CUCKOO_BUCKET_SIZE 4U
1107
#define CUCKOO_MAX_KICKS 128U
1108
#define CUCKOO_STASH_SIZE 32U
1109
#define CUCKOO_EMPTY_KEY 0xffffffffU
1110

1111
struct cuckoo_bucket32 {
1112
    uint32_t key[CUCKOO_BUCKET_SIZE];
1113
    uint32_t value[CUCKOO_BUCKET_SIZE];
1114
};
1115

1116
struct cuckoo_table32 {
1117
    struct cuckoo_bucket32 *buckets;
1118
    uint32_t stash_key[CUCKOO_STASH_SIZE];
1119
    uint32_t stash_value[CUCKOO_STASH_SIZE];
1120
    size_t bucket_count;
1121
    size_t bucket_mask;
1122
    size_t stash_count;
1123
    size_t entry_count;
1124
    sixel_allocator_t *allocator;
1125
};
1126

1127
static size_t cuckoo_round_buckets(size_t hint);
1128
static size_t cuckoo_hash_primary(uint32_t key, size_t mask);
1129
static size_t cuckoo_hash_secondary(uint32_t key, size_t mask);
1130
static size_t cuckoo_hash_alternate(uint32_t key,
1131
                                    size_t bucket,
1132
                                    size_t mask);
1133
static uint32_t *cuckoo_bucket_find(struct cuckoo_bucket32 *bucket,
1134
                                    uint32_t key);
1135
static int cuckoo_bucket_insert_direct(struct cuckoo_bucket32 *bucket,
1136
                                       uint32_t key,
1137
                                       uint32_t value);
1138
static SIXELSTATUS cuckoo_table32_init(struct cuckoo_table32 *table,
1139
                                       size_t expected,
1140
                                       sixel_allocator_t *allocator);
1141
static void cuckoo_table32_clear(struct cuckoo_table32 *table);
1142
static void cuckoo_table32_fini(struct cuckoo_table32 *table);
1143
static uint32_t *cuckoo_table32_lookup(struct cuckoo_table32 *table,
1144
                                       uint32_t key);
1145
static SIXELSTATUS cuckoo_table32_insert(struct cuckoo_table32 *table,
1146
                                         uint32_t key,
1147
                                         uint32_t value);
1148
static SIXELSTATUS cuckoo_table32_grow(struct cuckoo_table32 *table);
1149

1150
struct robinhood_slot32 {
1151
    uint32_t key;
1152
    uint32_t value;
1153
    uint16_t distance;
1154
    uint16_t pad;
1155
};
1156

1157
struct robinhood_table32 {
1158
    struct robinhood_slot32 *slots;
1159
    size_t capacity;
1160
    size_t count;
1161
    sixel_allocator_t *allocator;
1162
};
1163

1164
static size_t robinhood_round_capacity(size_t hint);
1165
static SIXELSTATUS robinhood_table32_init(struct robinhood_table32 *table,
1166
                                         size_t expected,
1167
                                         sixel_allocator_t *allocator);
1168
static void robinhood_table32_fini(struct robinhood_table32 *table);
1169
static struct robinhood_slot32 *
1170
robinhood_table32_lookup(struct robinhood_table32 *table, uint32_t key);
1171
static SIXELSTATUS robinhood_table32_insert(struct robinhood_table32 *table,
1172
                                           uint32_t key,
1173
                                           uint32_t value);
1174
static SIXELSTATUS robinhood_table32_grow(struct robinhood_table32 *table);
1175
static struct robinhood_slot32 *
1176
robinhood_table32_place(struct robinhood_table32 *table,
1177
                        struct robinhood_slot32 entry);
1178

1179
#define HOPSCOTCH_EMPTY_KEY 0xffffffffU
1180
#define HOPSCOTCH_DEFAULT_NEIGHBORHOOD 32U
1181
#define HOPSCOTCH_INSERT_RANGE 256U
1182

1183
struct hopscotch_slot32 {
1184
    uint32_t key;
1185
    uint32_t value;
1186
};
1187

1188
struct hopscotch_table32 {
1189
    struct hopscotch_slot32 *slots;
1190
    uint32_t *hopinfo;
1191
    size_t capacity;
1192
    size_t count;
1193
    size_t neighborhood;
1194
    sixel_allocator_t *allocator;
1195
};
1196

1197
static SIXELSTATUS hopscotch_table32_init(struct hopscotch_table32 *table,
1198
                                          size_t expected,
1199
                                          sixel_allocator_t *allocator);
1200
static void hopscotch_table32_fini(struct hopscotch_table32 *table);
1201
static struct hopscotch_slot32 *
1202
hopscotch_table32_lookup(struct hopscotch_table32 *table, uint32_t key);
1203
static SIXELSTATUS hopscotch_table32_insert(struct hopscotch_table32 *table,
1204
                                            uint32_t key,
1205
                                            uint32_t value);
1206
static SIXELSTATUS hopscotch_table32_grow(struct hopscotch_table32 *table);
1207

1208
static struct histogram_control
1209
histogram_control_make(unsigned int depth)
16,545,521✔
1210
{
1211
    struct histogram_control control;
1212

1213
    control = histogram_control_make_for_policy(depth,
24,865,112✔
1214
                                                histogram_lut_policy);
8,319,591✔
1215

1216
    return control;
16,545,521✔
1217
}
1218

1219
static size_t
1220
robinhood_round_capacity(size_t hint)
×
1221
{
1222
    size_t capacity;
1223

1224
    capacity = 16U;
×
1225
    if (hint < capacity) {
×
1226
        return capacity;
×
1227
    }
1228

1229
    capacity = hint - 1U;
×
1230
    capacity |= capacity >> 1;
×
1231
    capacity |= capacity >> 2;
×
1232
    capacity |= capacity >> 4;
×
1233
    capacity |= capacity >> 8;
×
1234
    capacity |= capacity >> 16;
×
1235
#if SIZE_MAX > UINT32_MAX
1236
    capacity |= capacity >> 32;
×
1237
#endif
1238
    if (capacity == SIZE_MAX) {
×
1239
        return SIZE_MAX;
×
1240
    }
1241
    capacity++;
×
1242
    if (capacity < 16U) {
×
1243
        capacity = 16U;
×
1244
    }
1245

1246
    return capacity;
×
1247
}
1248

1249
static SIXELSTATUS
1250
robinhood_table32_init(struct robinhood_table32 *table,
×
1251
                       size_t expected,
1252
                       sixel_allocator_t *allocator)
1253
{
1254
    size_t hint;
1255
    size_t capacity;
1256

1257
    table->slots = NULL;
×
1258
    table->capacity = 0U;
×
1259
    table->count = 0U;
×
1260
    table->allocator = allocator;
×
1261

1262
    if (expected < 16U) {
×
1263
        expected = 16U;
×
1264
    }
1265
    if (expected > SIZE_MAX / 2U) {
×
1266
        hint = SIZE_MAX / 2U;
×
1267
    } else {
1268
        hint = expected * 2U;
×
1269
    }
1270
    capacity = robinhood_round_capacity(hint);
×
1271
    if (capacity == SIZE_MAX && hint != SIZE_MAX) {
×
1272
        return SIXEL_BAD_ALLOCATION;
×
1273
    }
1274

1275
    table->slots = (struct robinhood_slot32 *)
×
1276
        sixel_allocator_calloc(allocator,
×
1277
                               capacity,
1278
                               sizeof(struct robinhood_slot32));
1279
    if (table->slots == NULL) {
×
1280
        table->capacity = 0U;
×
1281
        table->count = 0U;
×
1282
        return SIXEL_BAD_ALLOCATION;
×
1283
    }
1284
    table->capacity = capacity;
×
1285
    table->count = 0U;
×
1286

1287
    return SIXEL_OK;
×
1288
}
1289

1290
static void
1291
robinhood_table32_fini(struct robinhood_table32 *table)
×
1292
{
1293
    if (table->slots != NULL) {
×
1294
        sixel_allocator_free(table->allocator, table->slots);
×
1295
        table->slots = NULL;
×
1296
    }
1297
    table->capacity = 0U;
×
1298
    table->count = 0U;
×
1299
    table->allocator = NULL;
×
1300
}
×
1301

1302
static struct robinhood_slot32 *
1303
robinhood_table32_lookup(struct robinhood_table32 *table, uint32_t key)
×
1304
{
1305
    size_t mask;
1306
    size_t index;
1307
    uint16_t distance;
1308
    struct robinhood_slot32 *slot;
1309

1310
    if (table->capacity == 0U || table->slots == NULL) {
×
1311
        return NULL;
×
1312
    }
1313

1314
    mask = table->capacity - 1U;
×
1315
    index = (size_t)(key & mask);
×
1316
    distance = 0U;
×
1317

1318
    for (;;) {
1319
        slot = &table->slots[index];
×
1320
        if (slot->value == 0U) {
×
1321
            return NULL;
×
1322
        }
1323
        if (slot->key == key) {
×
1324
            return slot;
×
1325
        }
1326
        if (slot->distance < distance) {
×
1327
            return NULL;
×
1328
        }
1329
        index = (index + 1U) & mask;
×
1330
        distance++;
×
1331
    }
1332
}
1333

1334
static struct robinhood_slot32 *
1335
robinhood_table32_place(struct robinhood_table32 *table,
×
1336
                        struct robinhood_slot32 entry)
1337
{
1338
    size_t mask;
1339
    size_t index;
1340
    struct robinhood_slot32 *slot;
1341
    struct robinhood_slot32 tmp;
1342

1343
    mask = table->capacity - 1U;
×
1344
    index = (size_t)(entry.key & mask);
×
1345

1346
    for (;;) {
1347
        slot = &table->slots[index];
×
1348
        if (slot->value == 0U) {
×
1349
            *slot = entry;
×
1350
            table->count++;
×
1351
            return slot;
×
1352
        }
1353
        if (slot->key == entry.key) {
×
1354
            slot->value = entry.value;
×
1355
            return slot;
×
1356
        }
1357
        if (slot->distance < entry.distance) {
×
1358
            tmp = *slot;
×
1359
            *slot = entry;
×
1360
            entry = tmp;
×
1361
        }
1362
        index = (index + 1U) & mask;
×
1363
        entry.distance++;
×
1364
    }
1365
}
1366

1367
static SIXELSTATUS
1368
robinhood_table32_grow(struct robinhood_table32 *table)
×
1369
{
1370
    struct robinhood_slot32 *old_slots;
1371
    size_t old_capacity;
1372
    size_t new_capacity;
1373
    size_t i;
1374

1375
    if (table->allocator == NULL) {
×
1376
        return SIXEL_BAD_ALLOCATION;
×
1377
    }
1378
    if (table->capacity == 0U) {
×
1379
        new_capacity = 16U;
×
1380
    } else {
1381
        if (table->capacity > SIZE_MAX / 2U) {
×
1382
            return SIXEL_BAD_ALLOCATION;
×
1383
        }
1384
        new_capacity = table->capacity << 1U;
×
1385
    }
1386
    new_capacity = robinhood_round_capacity(new_capacity);
×
1387
    if (new_capacity <= table->capacity) {
×
1388
        return SIXEL_BAD_ALLOCATION;
×
1389
    }
1390

1391
    old_slots = table->slots;
×
1392
    old_capacity = table->capacity;
×
1393
    table->slots = (struct robinhood_slot32 *)
×
1394
        sixel_allocator_calloc(table->allocator,
×
1395
                               new_capacity,
1396
                               sizeof(struct robinhood_slot32));
1397
    if (table->slots == NULL) {
×
1398
        table->slots = old_slots;
×
1399
        table->capacity = old_capacity;
×
1400
        return SIXEL_BAD_ALLOCATION;
×
1401
    }
1402
    table->capacity = new_capacity;
×
1403
    table->count = 0U;
×
1404

1405
    for (i = 0U; i < old_capacity; ++i) {
×
1406
        struct robinhood_slot32 entry;
1407

1408
        if (old_slots[i].value == 0U) {
×
1409
            continue;
×
1410
        }
1411
        entry.key = old_slots[i].key;
×
1412
        entry.value = old_slots[i].value;
×
1413
        entry.distance = 0U;
×
1414
        (void)robinhood_table32_place(table, entry);
×
1415
    }
1416

1417
    sixel_allocator_free(table->allocator, old_slots);
×
1418

1419
    return SIXEL_OK;
×
1420
}
1421

1422
static SIXELSTATUS
1423
robinhood_table32_insert(struct robinhood_table32 *table,
×
1424
                         uint32_t key,
1425
                         uint32_t value)
1426
{
1427
    SIXELSTATUS status;
1428
    struct robinhood_slot32 entry;
1429

1430
    if (table->slots == NULL || table->capacity == 0U) {
×
1431
        return SIXEL_BAD_ARGUMENT;
×
1432
    }
1433
    if (table->count * 2U >= table->capacity) {
×
1434
        status = robinhood_table32_grow(table);
×
1435
        if (SIXEL_FAILED(status)) {
×
1436
            return status;
×
1437
        }
1438
    }
1439

1440
    entry.key = key;
×
1441
    entry.value = value;
×
1442
    entry.distance = 0U;
×
1443
    (void)robinhood_table32_place(table, entry);
×
1444

1445
    return SIXEL_OK;
×
1446
}
1447

1448
static SIXELSTATUS
1449
hopscotch_table32_init(struct hopscotch_table32 *table,
×
1450
                       size_t expected,
1451
                       sixel_allocator_t *allocator)
1452
{
1453
    size_t hint;
1454
    size_t capacity;
1455
    size_t i;
1456

1457
    if (table == NULL) {
×
1458
        return SIXEL_BAD_ARGUMENT;
×
1459
    }
1460

1461
    table->slots = NULL;
×
1462
    table->hopinfo = NULL;
×
1463
    table->capacity = 0U;
×
1464
    table->count = 0U;
×
1465
    table->neighborhood = HOPSCOTCH_DEFAULT_NEIGHBORHOOD;
×
1466
    table->allocator = allocator;
×
1467

1468
    if (expected < 16U) {
×
1469
        expected = 16U;
×
1470
    }
1471
    if (expected > SIZE_MAX / 2U) {
×
1472
        hint = SIZE_MAX / 2U;
×
1473
    } else {
1474
        hint = expected * 2U;
×
1475
    }
1476
    capacity = robinhood_round_capacity(hint);
×
1477
    if (capacity == SIZE_MAX && hint != SIZE_MAX) {
×
1478
        return SIXEL_BAD_ALLOCATION;
×
1479
    }
1480
    if (capacity < table->neighborhood) {
×
1481
        capacity = table->neighborhood;
×
1482
    }
1483

1484
    table->slots = (struct hopscotch_slot32 *)
×
1485
        sixel_allocator_malloc(allocator,
×
1486
                               capacity * sizeof(struct hopscotch_slot32));
1487
    if (table->slots == NULL) {
×
1488
        return SIXEL_BAD_ALLOCATION;
×
1489
    }
1490
    table->hopinfo = (uint32_t *)
×
1491
        sixel_allocator_calloc(allocator,
×
1492
                               capacity,
1493
                               sizeof(uint32_t));
1494
    if (table->hopinfo == NULL) {
×
1495
        sixel_allocator_free(allocator, table->slots);
×
1496
        table->slots = NULL;
×
1497
        return SIXEL_BAD_ALLOCATION;
×
1498
    }
1499

1500
    for (i = 0U; i < capacity; ++i) {
×
1501
        table->slots[i].key = HOPSCOTCH_EMPTY_KEY;
×
1502
        table->slots[i].value = 0U;
×
1503
    }
1504
    table->capacity = capacity;
×
1505
    table->count = 0U;
×
1506
    if (table->neighborhood > 32U) {
×
1507
        table->neighborhood = 32U;
×
1508
    }
1509
    if (table->neighborhood > table->capacity) {
×
1510
        table->neighborhood = table->capacity;
×
1511
    }
1512

1513
    return SIXEL_OK;
×
1514
}
1515

1516
static void
1517
hopscotch_table32_fini(struct hopscotch_table32 *table)
×
1518
{
1519
    sixel_allocator_t *allocator;
1520

1521
    if (table == NULL) {
×
1522
        return;
×
1523
    }
1524

1525
    allocator = table->allocator;
×
1526
    if (allocator != NULL) {
×
1527
        if (table->slots != NULL) {
×
1528
            sixel_allocator_free(allocator, table->slots);
×
1529
        }
1530
        if (table->hopinfo != NULL) {
×
1531
            sixel_allocator_free(allocator, table->hopinfo);
×
1532
        }
1533
    }
1534
    table->slots = NULL;
×
1535
    table->hopinfo = NULL;
×
1536
    table->capacity = 0U;
×
1537
    table->count = 0U;
×
1538
}
1539

1540
static struct hopscotch_slot32 *
1541
hopscotch_table32_lookup(struct hopscotch_table32 *table, uint32_t key)
×
1542
{
1543
    size_t index;
1544
    size_t bit;
1545
    size_t candidate;
1546
    uint32_t hop;
1547
    size_t mask;
1548
    size_t neighborhood;
1549

1550
    if (table == NULL || table->slots == NULL || table->hopinfo == NULL) {
×
1551
        return NULL;
×
1552
    }
1553
    if (table->capacity == 0U) {
×
1554
        return NULL;
×
1555
    }
1556

1557
    mask = table->capacity - 1U;
×
1558
    index = (size_t)key & mask;
×
1559
    hop = table->hopinfo[index];
×
1560
    neighborhood = table->neighborhood;
×
1561
    for (bit = 0U; bit < neighborhood; ++bit) {
×
1562
        if ((hop & (1U << bit)) == 0U) {
×
1563
            continue;
×
1564
        }
1565
        candidate = (index + bit) & mask;
×
1566
        if (table->slots[candidate].key == key) {
×
1567
            return &table->slots[candidate];
×
1568
        }
1569
    }
1570

1571
    return NULL;
×
1572
}
1573

1574
static SIXELSTATUS
1575
hopscotch_table32_grow(struct hopscotch_table32 *table)
×
1576
{
1577
    SIXELSTATUS status;
1578
    struct hopscotch_table32 tmp;
1579
    size_t i;
1580
    struct hopscotch_slot32 *slot;
1581

1582
    tmp.slots = NULL;
×
1583
    tmp.hopinfo = NULL;
×
1584
    tmp.capacity = 0U;
×
1585
    tmp.count = 0U;
×
1586
    tmp.neighborhood = table->neighborhood;
×
1587
    tmp.allocator = table->allocator;
×
1588

1589
    status = hopscotch_table32_init(&tmp,
×
1590
                                    table->capacity * 2U,
×
1591
                                    table->allocator);
1592
    if (SIXEL_FAILED(status)) {
×
1593
        return status;
×
1594
    }
1595
    if (tmp.neighborhood > table->neighborhood) {
×
1596
        tmp.neighborhood = table->neighborhood;
×
1597
    }
1598

1599
    for (i = 0U; i < table->capacity; ++i) {
×
1600
        slot = &table->slots[i];
×
1601
        if (slot->key == HOPSCOTCH_EMPTY_KEY) {
×
1602
            continue;
×
1603
        }
1604
        status = hopscotch_table32_insert(&tmp,
×
1605
                                          slot->key,
1606
                                          slot->value);
1607
        if (SIXEL_FAILED(status)) {
×
1608
            hopscotch_table32_fini(&tmp);
×
1609
            return status;
×
1610
        }
1611
    }
1612

1613
    hopscotch_table32_fini(table);
×
1614

1615
    table->slots = tmp.slots;
×
1616
    table->hopinfo = tmp.hopinfo;
×
1617
    table->capacity = tmp.capacity;
×
1618
    table->count = tmp.count;
×
1619
    table->neighborhood = tmp.neighborhood;
×
1620
    table->allocator = tmp.allocator;
×
1621

1622
    return SIXEL_OK;
×
1623
}
1624

1625
static SIXELSTATUS
1626
hopscotch_table32_insert(struct hopscotch_table32 *table,
×
1627
                         uint32_t key,
1628
                         uint32_t value)
1629
{
1630
    SIXELSTATUS status;
1631
    struct hopscotch_slot32 *slot;
1632
    size_t index;
1633
    size_t mask;
1634
    size_t distance;
1635
    size_t attempts;
1636
    size_t free_index;
1637
    size_t neighborhood;
1638
    int relocated;
1639
    size_t offset;
1640
    uint32_t hop;
1641
    size_t bit;
1642
    size_t move_index;
1643
    struct hopscotch_slot32 tmp_slot;
1644

1645
    if (table == NULL || table->slots == NULL || table->hopinfo == NULL) {
×
1646
        return SIXEL_BAD_ARGUMENT;
×
1647
    }
1648
    if (table->capacity == 0U) {
×
1649
        return SIXEL_BAD_ARGUMENT;
×
1650
    }
1651

1652
    slot = hopscotch_table32_lookup(table, key);
×
1653
    if (slot != NULL) {
×
1654
        slot->value = value;
×
1655
        return SIXEL_OK;
×
1656
    }
1657

1658
    if (table->count * 2U >= table->capacity) {
×
1659
        status = hopscotch_table32_grow(table);
×
1660
        if (SIXEL_FAILED(status)) {
×
1661
            return status;
×
1662
        }
1663
        return hopscotch_table32_insert(table, key, value);
×
1664
    }
1665

1666
    mask = table->capacity - 1U;
×
1667
    neighborhood = table->neighborhood;
×
1668
    index = (size_t)key & mask;
×
1669
    slot = NULL;
×
1670
    free_index = index;
×
1671
    distance = 0U;
×
1672
    for (attempts = 0U; attempts < HOPSCOTCH_INSERT_RANGE; ++attempts) {
×
1673
        free_index = (index + attempts) & mask;
×
1674
        slot = &table->slots[free_index];
×
1675
        if (slot->key == HOPSCOTCH_EMPTY_KEY) {
×
1676
            distance = attempts;
×
1677
            break;
×
1678
        }
1679
    }
1680
    if (slot == NULL || slot->key != HOPSCOTCH_EMPTY_KEY) {
×
1681
        status = hopscotch_table32_grow(table);
×
1682
        if (SIXEL_FAILED(status)) {
×
1683
            return status;
×
1684
        }
1685
        return hopscotch_table32_insert(table, key, value);
×
1686
    }
1687

1688
    /*
1689
     * Relocation diagram:
1690
     *
1691
     *   free slot <--- hop window <--- [base bucket]
1692
     *      ^ move resident outward until distance < neighborhood
1693
     */
1694
    while (distance >= neighborhood) {
×
1695
        relocated = 0;
×
1696
        for (offset = neighborhood - 1U; offset > 0U; --offset) {
×
1697
            size_t base;
1698

1699
            base = (free_index + table->capacity - offset) & mask;
×
1700
            hop = table->hopinfo[base];
×
1701
            if (hop == 0U) {
×
1702
                continue;
×
1703
            }
1704
            for (bit = 0U; bit < offset; ++bit) {
×
1705
                if ((hop & (1U << bit)) == 0U) {
×
1706
                    continue;
×
1707
                }
1708
                move_index = (base + bit) & mask;
×
1709
                tmp_slot = table->slots[move_index];
×
1710
                table->slots[free_index] = tmp_slot;
×
1711
                table->slots[move_index].key = HOPSCOTCH_EMPTY_KEY;
×
1712
                table->slots[move_index].value = 0U;
×
1713
                table->hopinfo[base] &= (uint32_t)~(1U << bit);
×
1714
                table->hopinfo[base] |= (uint32_t)(1U << offset);
×
1715
                free_index = move_index;
×
1716
                if (free_index >= index) {
×
1717
                    distance = free_index - index;
×
1718
                } else {
1719
                    distance = free_index + table->capacity - index;
×
1720
                }
1721
                relocated = 1;
×
1722
                break;
×
1723
            }
1724
            if (relocated) {
×
1725
                break;
×
1726
            }
1727
        }
1728
        if (!relocated) {
×
1729
            status = hopscotch_table32_grow(table);
×
1730
            if (SIXEL_FAILED(status)) {
×
1731
                return status;
×
1732
            }
1733
            return hopscotch_table32_insert(table, key, value);
×
1734
        }
1735
    }
1736

1737
    if (distance >= 32U) {
×
1738
        status = hopscotch_table32_grow(table);
×
1739
        if (SIXEL_FAILED(status)) {
×
1740
            return status;
×
1741
        }
1742
        return hopscotch_table32_insert(table, key, value);
×
1743
    }
1744

1745
    table->slots[free_index].key = key;
×
1746
    table->slots[free_index].value = value;
×
1747
    table->hopinfo[index] |= (uint32_t)(1U << distance);
×
1748
    table->count++;
×
1749

1750
    return SIXEL_OK;
×
1751
}
1752

1753
/*
1754
 * The cuckoo hash backend stores entries in fixed-width buckets.
1755
 *
1756
 *   [bucket 0] -> key0 key1 key2 key3
1757
 *                 val0 val1 val2 val3
1758
 *   [bucket 1] -> ...
1759
 *
1760
 * Each key is compared against the 128-bit lane using SIMD instructions.
1761
 * Two hash functions map a key to its primary and secondary buckets.  When
1762
 * both are full, the eviction loop "kicks" an entry toward its alternate
1763
 * bucket, as illustrated below:
1764
 *
1765
 *   bucket A --kick--> bucket B --kick--> bucket A ...
1766
 *
1767
 * A tiny stash buffers entries when the table momentarily fills up.  This
1768
 * keeps lookups fast while letting us grow the table lazily.
1769
 */
1770
static size_t
1771
cuckoo_round_buckets(size_t hint)
239✔
1772
{
1773
    size_t desired;
1774
    size_t buckets;
1775
    size_t prev;
1776

1777
    if (hint < CUCKOO_BUCKET_SIZE) {
239!
1778
        hint = CUCKOO_BUCKET_SIZE;
×
1779
    }
1780
    if (hint > SIZE_MAX / 2U) {
239!
1781
        hint = SIZE_MAX / 2U;
×
1782
    }
1783
    desired = (hint * 2U + CUCKOO_BUCKET_SIZE - 1U) / CUCKOO_BUCKET_SIZE;
239✔
1784
    if (desired == 0U) {
239!
1785
        desired = 1U;
×
1786
    }
1787

1788
    buckets = 1U;
239✔
1789
    while (buckets < desired) {
4,302✔
1790
        prev = buckets;
4,063✔
1791
        if (buckets > SIZE_MAX / 2U) {
4,063!
1792
            buckets = prev;
×
1793
            break;
×
1794
        }
1795
        buckets <<= 1U;
4,063✔
1796
        if (buckets < prev) {
4,063!
1797
            buckets = prev;
×
1798
            break;
×
1799
        }
1800
    }
1801
    if (buckets == 0U) {
239!
1802
        buckets = 1U;
×
1803
    }
1804

1805
    return buckets;
239✔
1806
}
1807

1808
static size_t
1809
cuckoo_hash_primary(uint32_t key, size_t mask)
18,541,056✔
1810
{
1811
    uint32_t mix;
1812

1813
    mix = key * 0x9e3779b1U;
18,541,056✔
1814
    return (size_t)(mix & (uint32_t)mask);
18,541,056✔
1815
}
1816

1817
static size_t
1818
cuckoo_hash_secondary(uint32_t key, size_t mask)
1,995,770✔
1819
{
1820
    uint32_t mix;
1821

1822
    mix = key ^ 0x85ebca6bU;
1,995,770✔
1823
    mix ^= mix >> 13;
1,995,770✔
1824
    mix *= 0xc2b2ae35U;
1,995,770✔
1825
    return (size_t)(mix & (uint32_t)mask);
1,995,770✔
1826
}
1827

1828
static size_t
1829
cuckoo_hash_alternate(uint32_t key, size_t bucket, size_t mask)
×
1830
{
1831
    size_t primary;
1832
    size_t secondary;
1833

1834
    primary = cuckoo_hash_primary(key, mask);
×
1835
    secondary = cuckoo_hash_secondary(key, mask);
×
1836
    if (primary == bucket) {
×
1837
        if (secondary == primary) {
×
1838
            secondary = (secondary + 1U) & mask;
×
1839
        }
1840
        return secondary;
×
1841
    }
1842

1843
    return primary;
×
1844
}
1845

1846
static uint32_t *
1847
cuckoo_bucket_find(struct cuckoo_bucket32 *bucket, uint32_t key)
19,538,941✔
1848
{
1849
    size_t i;
1850

1851
#if defined(SIXEL_USE_SSE2)
1852
    __m128i needle;
1853
    __m128i keys;
1854
    __m128i cmp;
1855
    int mask;
1856

1857
    needle = _mm_set1_epi32((int)key);
1858
    keys = _mm_loadu_si128((const __m128i *)bucket->key);
1859
    cmp = _mm_cmpeq_epi32(needle, keys);
1860
    mask = _mm_movemask_ps(_mm_castsi128_ps(cmp));
1861
    if ((mask & 1) != 0 && bucket->value[0] != 0U) {
1862
        return &bucket->value[0];
1863
    }
1864
    if ((mask & 2) != 0 && bucket->value[1] != 0U) {
1865
        return &bucket->value[1];
1866
    }
1867
    if ((mask & 4) != 0 && bucket->value[2] != 0U) {
1868
        return &bucket->value[2];
1869
    }
1870
    if ((mask & 8) != 0 && bucket->value[3] != 0U) {
1871
        return &bucket->value[3];
1872
    }
1873
#elif defined(SIXEL_USE_NEON)
1874
    uint32x4_t needle;
1875
    uint32x4_t keys;
1876
    uint32x4_t cmp;
1877

1878
    needle = vdupq_n_u32(key);
1879
    keys = vld1q_u32(bucket->key);
1880
    cmp = vceqq_u32(needle, keys);
1881
    if (vgetq_lane_u32(cmp, 0) != 0U && bucket->value[0] != 0U) {
1882
        return &bucket->value[0];
1883
    }
1884
    if (vgetq_lane_u32(cmp, 1) != 0U && bucket->value[1] != 0U) {
1885
        return &bucket->value[1];
1886
    }
1887
    if (vgetq_lane_u32(cmp, 2) != 0U && bucket->value[2] != 0U) {
1888
        return &bucket->value[2];
1889
    }
1890
    if (vgetq_lane_u32(cmp, 3) != 0U && bucket->value[3] != 0U) {
1891
        return &bucket->value[3];
1892
    }
1893
#else
1894
    for (i = 0U; i < CUCKOO_BUCKET_SIZE; ++i) {
35,635,723✔
1895
        if (bucket->value[i] != 0U && bucket->key[i] == key) {
31,644,183✔
1896
            return &bucket->value[i];
15,547,401✔
1897
        }
1898
    }
5,249,408✔
1899
#endif
1900

1901
    return NULL;
3,991,540✔
1902
}
9,296,267✔
1903

1904
static int
1905
cuckoo_bucket_insert_direct(struct cuckoo_bucket32 *bucket,
997,885✔
1906
                            uint32_t key,
1907
                            uint32_t value)
1908
{
1909
    size_t i;
1910

1911
    for (i = 0U; i < CUCKOO_BUCKET_SIZE; ++i) {
1,029,007!
1912
        if (bucket->value[i] == 0U) {
1,029,007✔
1913
            bucket->key[i] = key;
997,885✔
1914
            bucket->value[i] = value;
997,885✔
1915
            return 1;
997,885✔
1916
        }
1917
    }
9,174✔
1918

1919
    return 0;
×
1920
}
325,595✔
1921

1922
static SIXELSTATUS
1923
cuckoo_table32_init(struct cuckoo_table32 *table,
239✔
1924
                    size_t expected,
1925
                    sixel_allocator_t *allocator)
1926
{
1927
    size_t buckets;
1928
    size_t i;
1929
    size_t j;
1930

1931
    if (table == NULL || allocator == NULL) {
239!
1932
        return SIXEL_BAD_ARGUMENT;
×
1933
    }
1934

1935
    buckets = cuckoo_round_buckets(expected);
239✔
1936
    if (buckets == 0U
239!
1937
        || buckets > SIZE_MAX / sizeof(struct cuckoo_bucket32)) {
239!
1938
        sixel_helper_set_additional_message(
×
1939
            "unable to size cuckoo bucket array.");
1940
        return SIXEL_BAD_ALLOCATION;
×
1941
    }
1942

1943
    table->buckets = (struct cuckoo_bucket32 *)sixel_allocator_malloc(
239✔
1944
        allocator, buckets * sizeof(struct cuckoo_bucket32));
99✔
1945
    if (table->buckets == NULL) {
239!
1946
        sixel_helper_set_additional_message(
×
1947
            "unable to allocate cuckoo buckets.");
1948
        return SIXEL_BAD_ALLOCATION;
×
1949
    }
1950

1951
    table->bucket_count = buckets;
239✔
1952
    table->bucket_mask = buckets - 1U;
239✔
1953
    table->stash_count = 0U;
239✔
1954
    table->entry_count = 0U;
239✔
1955
    table->allocator = allocator;
239✔
1956
    for (i = 0U; i < buckets; ++i) {
31,326,447✔
1957
        for (j = 0U; j < CUCKOO_BUCKET_SIZE; ++j) {
156,631,040✔
1958
            table->buckets[i].key[j] = CUCKOO_EMPTY_KEY;
125,304,832✔
1959
            table->buckets[i].value[j] = 0U;
125,304,832✔
1960
        }
51,904,512✔
1961
    }
12,976,128✔
1962
    for (i = 0U; i < CUCKOO_STASH_SIZE; ++i) {
7,887✔
1963
        table->stash_key[i] = CUCKOO_EMPTY_KEY;
7,648✔
1964
        table->stash_value[i] = 0U;
7,648✔
1965
    }
3,168✔
1966

1967
    return SIXEL_OK;
239✔
1968
}
99✔
1969

1970
static void
1971
cuckoo_table32_clear(struct cuckoo_table32 *table)
236✔
1972
{
1973
    size_t i;
1974
    size_t j;
1975

1976
    if (table == NULL || table->buckets == NULL) {
236!
1977
        return;
×
1978
    }
1979

1980
    table->stash_count = 0U;
236✔
1981
    table->entry_count = 0U;
236✔
1982
    for (i = 0U; i < table->bucket_count; ++i) {
30,933,228✔
1983
        for (j = 0U; j < CUCKOO_BUCKET_SIZE; ++j) {
154,664,960✔
1984
            table->buckets[i].key[j] = CUCKOO_EMPTY_KEY;
123,731,968✔
1985
            table->buckets[i].value[j] = 0U;
123,731,968✔
1986
        }
51,380,224✔
1987
    }
12,845,056✔
1988
    for (i = 0U; i < CUCKOO_STASH_SIZE; ++i) {
7,788✔
1989
        table->stash_key[i] = CUCKOO_EMPTY_KEY;
7,552✔
1990
        table->stash_value[i] = 0U;
7,552✔
1991
    }
3,136✔
1992
}
98✔
1993

1994
static void
1995
cuckoo_table32_fini(struct cuckoo_table32 *table)
239✔
1996
{
1997
    if (table == NULL || table->allocator == NULL) {
239!
1998
        return;
×
1999
    }
2000
    if (table->buckets != NULL) {
239!
2001
        sixel_allocator_free(table->allocator, table->buckets);
239✔
2002
        table->buckets = NULL;
239✔
2003
    }
99✔
2004
    table->bucket_count = 0U;
239✔
2005
    table->bucket_mask = 0U;
239✔
2006
    table->stash_count = 0U;
239✔
2007
    table->entry_count = 0U;
239✔
2008
}
99✔
2009

2010
static uint32_t *
2011
cuckoo_table32_lookup(struct cuckoo_table32 *table, uint32_t key)
17,543,171✔
2012
{
2013
    size_t index;
2014
    size_t i;
2015
    uint32_t *slot;
2016

2017
    if (table == NULL || table->buckets == NULL) {
17,543,171!
2018
        return NULL;
×
2019
    }
2020

2021
    index = cuckoo_hash_primary(key, table->bucket_mask);
17,543,171✔
2022
    slot = cuckoo_bucket_find(&table->buckets[index], key);
17,543,171✔
2023
    if (slot != NULL) {
17,543,171✔
2024
        return slot;
15,547,401✔
2025
    }
2026

2027
    index = cuckoo_hash_secondary(key, table->bucket_mask);
1,995,770✔
2028
    slot = cuckoo_bucket_find(&table->buckets[index], key);
1,995,770✔
2029
    if (slot != NULL) {
1,995,770!
2030
        return slot;
×
2031
    }
2032

2033
    for (i = 0U; i < table->stash_count; ++i) {
1,995,770!
2034
        if (table->stash_value[i] != 0U && table->stash_key[i] == key) {
×
2035
            return &table->stash_value[i];
×
2036
        }
2037
    }
2038

2039
    return NULL;
1,995,770✔
2040
}
8,645,077✔
2041

2042
static SIXELSTATUS
2043
cuckoo_table32_grow(struct cuckoo_table32 *table)
×
2044
{
2045
    struct cuckoo_table32 tmp;
2046
    struct cuckoo_bucket32 *old_buckets;
2047
    size_t old_count;
2048
    size_t i;
2049
    size_t j;
2050
    SIXELSTATUS status;
2051

2052
    if (table == NULL || table->allocator == NULL) {
×
2053
        return SIXEL_BAD_ARGUMENT;
×
2054
    }
2055

2056
    tmp.buckets = NULL;
×
2057
    tmp.bucket_count = 0U;
×
2058
    tmp.bucket_mask = 0U;
×
2059
    tmp.stash_count = 0U;
×
2060
    tmp.entry_count = 0U;
×
2061
    tmp.allocator = table->allocator;
×
2062
    for (i = 0U; i < CUCKOO_STASH_SIZE; ++i) {
×
2063
        tmp.stash_key[i] = CUCKOO_EMPTY_KEY;
×
2064
        tmp.stash_value[i] = 0U;
×
2065
    }
2066

2067
    status = cuckoo_table32_init(&tmp,
×
2068
                                 (table->entry_count + 1U) * 2U,
×
2069
                                 table->allocator);
2070
    if (SIXEL_FAILED(status)) {
×
2071
        return status;
×
2072
    }
2073

2074
    old_buckets = table->buckets;
×
2075
    old_count = table->bucket_count;
×
2076
    for (i = 0U; i < old_count; ++i) {
×
2077
        for (j = 0U; j < CUCKOO_BUCKET_SIZE; ++j) {
×
2078
            if (old_buckets[i].value[j] != 0U) {
×
2079
                status = cuckoo_table32_insert(&tmp,
×
2080
                                               old_buckets[i].key[j],
×
2081
                                               old_buckets[i].value[j]);
×
2082
                if (SIXEL_FAILED(status)) {
×
2083
                    cuckoo_table32_fini(&tmp);
×
2084
                    return status;
×
2085
                }
2086
            }
2087
        }
2088
    }
2089
    for (i = 0U; i < table->stash_count; ++i) {
×
2090
        if (table->stash_value[i] != 0U) {
×
2091
            status = cuckoo_table32_insert(&tmp,
×
2092
                                           table->stash_key[i],
2093
                                           table->stash_value[i]);
2094
            if (SIXEL_FAILED(status)) {
×
2095
                cuckoo_table32_fini(&tmp);
×
2096
                return status;
×
2097
            }
2098
        }
2099
    }
2100

2101
    sixel_allocator_free(table->allocator, old_buckets);
×
2102
    *table = tmp;
×
2103

2104
    return SIXEL_OK;
×
2105
}
2106

2107
static SIXELSTATUS
2108
cuckoo_table32_insert(struct cuckoo_table32 *table,
997,885✔
2109
                      uint32_t key,
2110
                      uint32_t value)
2111
{
2112
    uint32_t *slot;
2113
    uint32_t cur_key;
2114
    uint32_t cur_value;
2115
    uint32_t victim_key;
2116
    uint32_t victim_value;
2117
    size_t bucket_index;
2118
    size_t kicks;
2119
    size_t victim_slot;
2120
    struct cuckoo_bucket32 *bucket;
2121
    SIXELSTATUS status;
2122

2123
    if (table == NULL || table->buckets == NULL) {
997,885!
2124
        return SIXEL_BAD_ARGUMENT;
×
2125
    }
2126

2127
    slot = cuckoo_table32_lookup(table, key);
997,885✔
2128
    if (slot != NULL) {
997,885!
2129
        *slot = value;
×
2130
        return SIXEL_OK;
×
2131
    }
2132

2133
    cur_key = key;
997,885✔
2134
    cur_value = value;
997,885✔
2135
    bucket_index = cuckoo_hash_primary(cur_key, table->bucket_mask);
997,885✔
2136
    for (kicks = 0U; kicks < CUCKOO_MAX_KICKS; ++kicks) {
997,885!
2137
        bucket = &table->buckets[bucket_index];
997,885✔
2138
        if (cuckoo_bucket_insert_direct(bucket, cur_key, cur_value)) {
997,885!
2139
            table->entry_count++;
997,885✔
2140
            return SIXEL_OK;
997,885✔
2141
        }
2142
        victim_slot = (size_t)((cur_key + kicks) &
×
2143
                               (CUCKOO_BUCKET_SIZE - 1U));
2144
        victim_key = bucket->key[victim_slot];
×
2145
        victim_value = bucket->value[victim_slot];
×
2146
        bucket->key[victim_slot] = cur_key;
×
2147
        bucket->value[victim_slot] = cur_value;
×
2148
        cur_key = victim_key;
×
2149
        cur_value = victim_value;
×
2150
        bucket_index = cuckoo_hash_alternate(cur_key,
×
2151
                                             bucket_index,
2152
                                             table->bucket_mask);
2153
    }
2154

2155
    if (table->stash_count < CUCKOO_STASH_SIZE) {
×
2156
        table->stash_key[table->stash_count] = cur_key;
×
2157
        table->stash_value[table->stash_count] = cur_value;
×
2158
        table->stash_count++;
×
2159
        table->entry_count++;
×
2160
        return SIXEL_OK;
×
2161
    }
2162

2163
    status = cuckoo_table32_grow(table);
×
2164
    if (SIXEL_FAILED(status)) {
×
2165
        return status;
×
2166
    }
2167

2168
    return cuckoo_table32_insert(table, cur_key, cur_value);
×
2169
}
325,595✔
2170

2171
static SIXELSTATUS
2172
computeHistogram_robinhood(unsigned char const *data,
2173
                           unsigned int length,
2174
                           unsigned long depth,
2175
                           unsigned int step,
2176
                           unsigned int max_sample,
2177
                           tupletable2 * const colorfreqtableP,
2178
                           struct histogram_control const *control,
2179
                           sixel_allocator_t *allocator);
2180

2181
static SIXELSTATUS
2182
computeHistogram_hopscotch(unsigned char const *data,
2183
                           unsigned int length,
2184
                           unsigned long depth,
2185
                           unsigned int step,
2186
                           unsigned int max_sample,
2187
                           tupletable2 * const colorfreqtableP,
2188
                           struct histogram_control const *control,
2189
                           sixel_allocator_t *allocator);
2190

2191
static SIXELSTATUS
2192
computeHistogram(unsigned char const    /* in */  *data,
235✔
2193
                 unsigned int           /* in */  length,
2194
                 unsigned long const    /* in */  depth,
2195
                 tupletable2 * const    /* out */ colorfreqtableP,
2196
                 int const              /* in */  qualityMode,
2197
                 sixel_allocator_t      /* in */  *allocator)
2198
{
2199
    SIXELSTATUS status = SIXEL_FALSE;
235✔
2200
    typedef uint32_t unit_t;
2201
    unsigned int i, n;
2202
    unit_t *histogram = NULL;
235✔
2203
    unit_t *refmap = NULL;
235✔
2204
    unit_t *ref;
2205
    unsigned int bucket_index;
2206
    unsigned int step;
2207
    unsigned int max_sample;
2208
    size_t hist_size;
2209
    unit_t bucket_value;
2210
    unsigned int component;
2211
    unsigned int reconstructed;
2212
    struct histogram_control control;
2213

2214
    switch (qualityMode) {
235!
2215
    case SIXEL_QUALITY_LOW:
104✔
2216
        max_sample = 18383;
202✔
2217
        break;
202✔
2218
    case SIXEL_QUALITY_HIGH:
20✔
2219
        max_sample = 1118383;
30✔
2220
        break;
30✔
2221
    case SIXEL_QUALITY_FULL:
3✔
2222
    default:
2223
        max_sample = 4003079;
3✔
2224
        break;
3✔
2225
    }
2226

2227
    step = length / depth / max_sample * depth;
235✔
2228
    if (step <= 0) {
235✔
2229
        step = depth;
156✔
2230
    }
52✔
2231

2232
    quant_trace(stderr, "making histogram...\n");
235✔
2233

2234
    control = histogram_control_make((unsigned int)depth);
235✔
2235
    if (histogram_lut_policy == SIXEL_LUT_POLICY_ROBINHOOD
235!
2236
        || histogram_lut_policy == SIXEL_LUT_POLICY_HOPSCOTCH) {
235!
2237
        if (histogram_lut_policy == SIXEL_LUT_POLICY_ROBINHOOD) {
×
2238
            status = computeHistogram_robinhood(data,
×
2239
                                                length,
2240
                                                depth,
2241
                                                step,
2242
                                                max_sample,
2243
                                                colorfreqtableP,
2244
                                                &control,
2245
                                                allocator);
2246
        } else {
2247
            status = computeHistogram_hopscotch(data,
×
2248
                                                length,
2249
                                                depth,
2250
                                                step,
2251
                                                max_sample,
2252
                                                colorfreqtableP,
2253
                                                &control,
2254
                                                allocator);
2255
        }
2256
        goto end;
×
2257
    }
2258

2259
    hist_size = histogram_dense_size((unsigned int)depth, &control);
235✔
2260
    histogram = (unit_t *)sixel_allocator_calloc(allocator,
344✔
2261
                                                 hist_size,
109✔
2262
                                                 sizeof(unit_t));
2263
    if (histogram == NULL) {
235!
2264
        sixel_helper_set_additional_message(
×
2265
            "unable to allocate memory for histogram.");
2266
        status = SIXEL_BAD_ALLOCATION;
×
2267
        goto end;
×
2268
    }
2269
    ref = refmap = (unit_t *)sixel_allocator_malloc(allocator,
344✔
2270
                                                    hist_size *
109✔
2271
                                                    sizeof(unit_t));
2272
    if (refmap == NULL) {
235!
2273
        sixel_helper_set_additional_message(
×
2274
            "unable to allocate memory for lookup table.");
2275
        status = SIXEL_BAD_ALLOCATION;
×
2276
        goto end;
×
2277
    }
2278

2279
    for (i = 0; i < length; i += step) {
3,093,871✔
2280
        bucket_index = computeHash(data + i,
4,698,928✔
2281
                                   (unsigned int)depth,
1,605,292✔
2282
                                   &control);
2283
        if (histogram[bucket_index] == 0) {
3,093,636✔
2284
            *ref++ = bucket_index;
190,407✔
2285
        }
63,579✔
2286
        if (histogram[bucket_index] < UINT32_MAX) {
3,093,636!
2287
            histogram[bucket_index]++;
3,093,636✔
2288
        }
1,605,292✔
2289
    }
1,605,292✔
2290

2291
    colorfreqtableP->size = (unsigned int)(ref - refmap);
235✔
2292

2293
    status = alloctupletable(&colorfreqtableP->table,
344✔
2294
                             depth,
109✔
2295
                             (unsigned int)(ref - refmap),
235✔
2296
                             allocator);
109✔
2297
    if (SIXEL_FAILED(status)) {
235!
2298
        goto end;
×
2299
    }
2300
    for (i = 0; i < colorfreqtableP->size; ++i) {
190,642✔
2301
        bucket_value = refmap[i];
190,407✔
2302
        if (histogram[bucket_value] > 0) {
190,407!
2303
            colorfreqtableP->table[i]->value = histogram[bucket_value];
190,407✔
2304
            for (n = 0; n < depth; n++) {
761,628✔
2305
                component = (unsigned int)
571,221✔
2306
                    ((bucket_value >> (n * control.channel_bits)) &
761,958✔
2307
                     control.channel_mask);
571,221✔
2308
                reconstructed = histogram_reconstruct(component,
571,221✔
2309
                                                      &control);
2310
                colorfreqtableP->table[i]->tuple[depth - 1 - n]
571,221✔
2311
                    = (sample)reconstructed;
761,958✔
2312
            }
190,737✔
2313
        }
63,579✔
2314
    }
63,579✔
2315

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

2318
    status = SIXEL_OK;
235✔
2319

2320
end:
126✔
2321
    sixel_allocator_free(allocator, refmap);
235✔
2322
    sixel_allocator_free(allocator, histogram);
235✔
2323

2324
    return status;
235✔
2325
}
2326

2327
static SIXELSTATUS
2328
computeHistogram_robinhood(unsigned char const *data,
×
2329
                           unsigned int length,
2330
                           unsigned long depth,
2331
                           unsigned int step,
2332
                           unsigned int max_sample,
2333
                           tupletable2 * const colorfreqtableP,
2334
                           struct histogram_control const *control,
2335
                           sixel_allocator_t *allocator)
2336
{
2337
    SIXELSTATUS status = SIXEL_FALSE;
×
2338
    struct robinhood_table32 table;
2339
    size_t expected;
2340
    size_t cap_limit;
2341
    size_t index;
2342
    unsigned int depth_u;
2343
    unsigned int i;
2344
    unsigned int n;
2345
    uint32_t bucket_index;
2346
    uint32_t entry_key;
2347
    struct robinhood_slot32 *slot;
2348
    unsigned int component;
2349
    unsigned int reconstructed;
2350

2351
    /*
2352
     * The ASCII sketch below shows how the sparse table stores samples:
2353
     *
2354
     *   [hash]->(key,value,distance)  Robin Hood probing keeps dense tails.
2355
     */
2356
    table.slots = NULL;
×
2357
    table.capacity = 0U;
×
2358
    table.count = 0U;
×
2359
    table.allocator = allocator;
×
2360
    cap_limit = (size_t)1U << 20;
×
2361
    expected = max_sample;
×
2362
    if (expected < 256U) {
×
2363
        expected = 256U;
×
2364
    }
2365
    if (expected > cap_limit) {
×
2366
        expected = cap_limit;
×
2367
    }
2368

2369
    status = robinhood_table32_init(&table, expected, allocator);
×
2370
    if (SIXEL_FAILED(status)) {
×
2371
        sixel_helper_set_additional_message(
×
2372
            "unable to allocate robinhood histogram.");
2373
        goto end;
×
2374
    }
2375

2376
    depth_u = (unsigned int)depth;
×
2377
    for (i = 0U; i < length; i += step) {
×
2378
        bucket_index = computeHash(data + i, depth_u, control);
×
2379
        slot = robinhood_table32_lookup(&table, bucket_index);
×
2380
        if (slot == NULL) {
×
2381
            status = robinhood_table32_insert(&table,
×
2382
                                              bucket_index,
2383
                                              1U);
2384
            if (SIXEL_FAILED(status)) {
×
2385
                sixel_helper_set_additional_message(
×
2386
                    "unable to grow robinhood histogram.");
2387
                goto end;
×
2388
            }
2389
        } else if (slot->value < UINT32_MAX) {
×
2390
            slot->value++;
×
2391
        }
2392
    }
2393

2394
    if (table.count > UINT_MAX) {
×
2395
        sixel_helper_set_additional_message(
×
2396
            "too many unique colors for histogram.");
2397
        status = SIXEL_BAD_ARGUMENT;
×
2398
        goto end;
×
2399
    }
2400

2401
    colorfreqtableP->size = (unsigned int)table.count;
×
2402
    status = alloctupletable(&colorfreqtableP->table,
×
2403
                             depth_u,
2404
                             (unsigned int)table.count,
×
2405
                             allocator);
2406
    if (SIXEL_FAILED(status)) {
×
2407
        goto end;
×
2408
    }
2409

2410
    index = 0U;
×
2411
    /*
2412
     * Stream slots in the hash traversal order to avoid qsort overhead.
2413
     * This favors throughput over identical palette ordering.
2414
     */
2415
    for (i = 0U; i < table.capacity; ++i) {
×
2416
        slot = &table.slots[i];
×
2417
        if (slot->value == 0U) {
×
2418
            continue;
×
2419
        }
2420
        if (index >= colorfreqtableP->size) {
×
2421
            break;
×
2422
        }
2423
        entry_key = slot->key;
×
2424
        colorfreqtableP->table[index]->value = slot->value;
×
2425
        for (n = 0U; n < depth_u; ++n) {
×
2426
            component = (unsigned int)
×
2427
                ((entry_key >> (n * control->channel_bits))
×
2428
                 & control->channel_mask);
×
2429
            reconstructed = histogram_reconstruct(component, control);
×
2430
            colorfreqtableP->table[index]->tuple[depth_u - 1U - n]
×
2431
                = (sample)reconstructed;
×
2432
        }
2433
        index++;
×
2434
    }
2435

2436
    status = SIXEL_OK;
×
2437

2438
end:
2439
    robinhood_table32_fini(&table);
×
2440

2441
    return status;
×
2442
}
2443

2444
static SIXELSTATUS
2445
computeHistogram_hopscotch(unsigned char const *data,
×
2446
                           unsigned int length,
2447
                           unsigned long depth,
2448
                           unsigned int step,
2449
                           unsigned int max_sample,
2450
                           tupletable2 * const colorfreqtableP,
2451
                           struct histogram_control const *control,
2452
                           sixel_allocator_t *allocator)
2453
{
2454
    SIXELSTATUS status = SIXEL_FALSE;
×
2455
    struct hopscotch_table32 table;
2456
    size_t expected;
2457
    size_t cap_limit;
2458
    size_t index;
2459
    unsigned int depth_u;
2460
    unsigned int i;
2461
    unsigned int n;
2462
    uint32_t bucket_index;
2463
    uint32_t entry_key;
2464
    struct hopscotch_slot32 *slot;
2465
    unsigned int component;
2466
    unsigned int reconstructed;
2467

2468
    /*
2469
     * Hopscotch hashing stores the local neighbourhood using the map below:
2470
     *
2471
     *   [home] hopinfo bits ---> |slot+0|slot+1|slot+2| ...
2472
     *                              ^ entries hop within this window.
2473
     */
2474
    table.slots = NULL;
×
2475
    table.hopinfo = NULL;
×
2476
    table.capacity = 0U;
×
2477
    table.count = 0U;
×
2478
    table.neighborhood = HOPSCOTCH_DEFAULT_NEIGHBORHOOD;
×
2479
    table.allocator = allocator;
×
2480
    cap_limit = (size_t)1U << 20;
×
2481
    expected = max_sample;
×
2482
    if (expected < 256U) {
×
2483
        expected = 256U;
×
2484
    }
2485
    if (expected > cap_limit) {
×
2486
        expected = cap_limit;
×
2487
    }
2488

2489
    status = hopscotch_table32_init(&table, expected, allocator);
×
2490
    if (SIXEL_FAILED(status)) {
×
2491
        sixel_helper_set_additional_message(
×
2492
            "unable to allocate hopscotch histogram.");
2493
        goto end;
×
2494
    }
2495

2496
    depth_u = (unsigned int)depth;
×
2497
    for (i = 0U; i < length; i += step) {
×
2498
        bucket_index = computeHash(data + i, depth_u, control);
×
2499
        slot = hopscotch_table32_lookup(&table, bucket_index);
×
2500
        if (slot == NULL) {
×
2501
            status = hopscotch_table32_insert(&table,
×
2502
                                              bucket_index,
2503
                                              1U);
2504
            if (SIXEL_FAILED(status)) {
×
2505
                sixel_helper_set_additional_message(
×
2506
                    "unable to grow hopscotch histogram.");
2507
                goto end;
×
2508
            }
2509
        } else if (slot->value < UINT32_MAX) {
×
2510
            slot->value++;
×
2511
        }
2512
    }
2513

2514
    if (table.count > UINT_MAX) {
×
2515
        sixel_helper_set_additional_message(
×
2516
            "too many unique colors for histogram.");
2517
        status = SIXEL_BAD_ARGUMENT;
×
2518
        goto end;
×
2519
    }
2520

2521
    colorfreqtableP->size = (unsigned int)table.count;
×
2522
    status = alloctupletable(&colorfreqtableP->table,
×
2523
                             depth_u,
2524
                             (unsigned int)table.count,
×
2525
                             allocator);
2526
    if (SIXEL_FAILED(status)) {
×
2527
        goto end;
×
2528
    }
2529

2530
    index = 0U;
×
2531
    /*
2532
     * Stream slots in the hash traversal order to avoid qsort overhead.
2533
     * This favors throughput over identical palette ordering.
2534
     */
2535
    for (i = 0U; i < table.capacity; ++i) {
×
2536
        slot = &table.slots[i];
×
2537
        if (slot->key == HOPSCOTCH_EMPTY_KEY || slot->value == 0U) {
×
2538
            continue;
×
2539
        }
2540
        if (index >= colorfreqtableP->size) {
×
2541
            break;
×
2542
        }
2543
        entry_key = slot->key;
×
2544
        colorfreqtableP->table[index]->value = slot->value;
×
2545
        for (n = 0U; n < depth_u; ++n) {
×
2546
            component = (unsigned int)
×
2547
                ((entry_key >> (n * control->channel_bits))
×
2548
                 & control->channel_mask);
×
2549
            reconstructed = histogram_reconstruct(component, control);
×
2550
            colorfreqtableP->table[index]->tuple[depth_u - 1U - n]
×
2551
                = (sample)reconstructed;
×
2552
        }
2553
        index++;
×
2554
    }
2555

2556
    status = SIXEL_OK;
×
2557

2558
end:
2559
    hopscotch_table32_fini(&table);
×
2560

2561
    return status;
×
2562
}
2563

2564
SIXELSTATUS
2565
sixel_quant_cache_prepare(unsigned short **cachetable,
239✔
2566
                          size_t *cachetable_size,
2567
                          int lut_policy,
2568
                          int reqcolor,
2569
                          sixel_allocator_t *allocator)
2570
{
2571
    SIXELSTATUS status;
2572
    struct histogram_control control;
2573
    struct cuckoo_table32 *table;
2574
    size_t expected;
2575
    size_t buckets;
2576
    size_t cap_limit;
2577
    int normalized;
2578

2579
    if (cachetable == NULL || allocator == NULL) {
239!
2580
        return SIXEL_BAD_ARGUMENT;
×
2581
    }
2582

2583
    /*
2584
     * The cache pointer always references the same ladder:
2585
     *
2586
     *   cache -> cuckoo_table32 -> buckets
2587
     *                           -> stash
2588
     */
2589
    normalized = lut_policy;
239✔
2590
    if (normalized == SIXEL_LUT_POLICY_AUTO) {
239!
2591
        normalized = histogram_lut_policy;
239✔
2592
    }
99✔
2593
    if (normalized == SIXEL_LUT_POLICY_AUTO) {
239!
2594
        normalized = SIXEL_LUT_POLICY_6BIT;
239✔
2595
    }
99✔
2596

2597
    control = histogram_control_make_for_policy(3U, normalized);
239✔
2598
    if (control.channel_shift == 0U) {
239!
2599
        expected = (size_t)reqcolor * 64U;
×
2600
        cap_limit = (size_t)1U << 20;
×
2601
        if (expected < 512U) {
×
2602
            expected = 512U;
×
2603
        }
2604
        if (expected > cap_limit) {
×
2605
            expected = cap_limit;
×
2606
        }
2607
    } else {
2608
        expected = histogram_dense_size(3U, &control);
239✔
2609
        if (expected == 0U) {
239!
2610
            expected = CUCKOO_BUCKET_SIZE;
×
2611
        }
2612
    }
2613

2614
    table = (struct cuckoo_table32 *)*cachetable;
239✔
2615
    if (table != NULL) {
239!
2616
        buckets = cuckoo_round_buckets(expected);
×
2617
        if (table->bucket_count < buckets) {
×
2618
            cuckoo_table32_fini(table);
×
2619
            sixel_allocator_free(allocator, table);
×
2620
            table = NULL;
×
2621
            *cachetable = NULL;
×
2622
        } else {
2623
            cuckoo_table32_clear(table);
×
2624
        }
2625
    }
2626
    if (table == NULL) {
239!
2627
        table = (struct cuckoo_table32 *)sixel_allocator_malloc(
239✔
2628
            allocator, sizeof(struct cuckoo_table32));
99✔
2629
        if (table == NULL) {
239!
2630
            sixel_helper_set_additional_message(
×
2631
                "unable to allocate cuckoo cache state.");
2632
            return SIXEL_BAD_ALLOCATION;
×
2633
        }
2634
        memset(table, 0, sizeof(struct cuckoo_table32));
239✔
2635
        status = cuckoo_table32_init(table, expected, allocator);
239✔
2636
        if (SIXEL_FAILED(status)) {
239!
2637
            sixel_allocator_free(allocator, table);
×
2638
            sixel_helper_set_additional_message(
×
2639
                "unable to initialize cuckoo cache.");
2640
            return status;
×
2641
        }
2642
        *cachetable = (unsigned short *)table;
239✔
2643
    }
99✔
2644

2645
    if (cachetable_size != NULL) {
239!
2646
        *cachetable_size = table->bucket_count * CUCKOO_BUCKET_SIZE;
239✔
2647
    }
99✔
2648

2649
    return SIXEL_OK;
239✔
2650
}
99✔
2651

2652
void
2653
sixel_quant_cache_clear(unsigned short *cachetable,
236✔
2654
                        int lut_policy)
2655
{
2656
    struct cuckoo_table32 *table;
2657

2658
    (void)lut_policy;
98✔
2659
    if (cachetable == NULL) {
236!
2660
        return;
×
2661
    }
2662

2663
    table = (struct cuckoo_table32 *)cachetable;
236✔
2664
    cuckoo_table32_clear(table);
236✔
2665
}
98✔
2666

2667
void
2668
sixel_quant_cache_release(unsigned short *cachetable,
504✔
2669
                          int lut_policy,
2670
                          sixel_allocator_t *allocator)
2671
{
2672
    struct cuckoo_table32 *table;
2673

2674
    (void)lut_policy;
174✔
2675
    if (cachetable == NULL || allocator == NULL) {
504!
2676
        return;
265✔
2677
    }
2678

2679
    table = (struct cuckoo_table32 *)cachetable;
239✔
2680
    cuckoo_table32_fini(table);
239✔
2681
    sixel_allocator_free(allocator, table);
239✔
2682
}
174✔
2683

2684

2685
static int
2686
computeColorMapFromInput(unsigned char const *data,
235✔
2687
                         unsigned int const length,
2688
                         unsigned int const depth,
2689
                         unsigned int const reqColors,
2690
                         int const methodForLargest,
2691
                         int const methodForRep,
2692
                         int const qualityMode,
2693
                         int const force_palette,
2694
                         tupletable2 * const colormapP,
2695
                         unsigned int *origcolors,
2696
                         sixel_allocator_t *allocator)
2697
{
2698
/*----------------------------------------------------------------------------
2699
   Produce a colormap containing the best colors to represent the
2700
   image stream in file 'ifP'.  Figure it out using the median cut
2701
   technique.
2702

2703
   The colormap will have 'reqcolors' or fewer colors in it, unless
2704
   'allcolors' is true, in which case it will have all the colors that
2705
   are in the input.
2706

2707
   The colormap has the same maxval as the input.
2708

2709
   Put the colormap in newly allocated storage as a tupletable2
2710
   and return its address as *colormapP.  Return the number of colors in
2711
   it as *colorsP and its maxval as *colormapMaxvalP.
2712

2713
   Return the characteristics of the input file as
2714
   *formatP and *freqPamP.  (This information is not really
2715
   relevant to our colormap mission; just a fringe benefit).
2716
-----------------------------------------------------------------------------*/
2717
    SIXELSTATUS status = SIXEL_FALSE;
235✔
2718
    tupletable2 colorfreqtable = {0, NULL};
235✔
2719
    unsigned int i;
2720
    unsigned int n;
2721

2722
    status = computeHistogram(data, length, depth,
344✔
2723
                              &colorfreqtable, qualityMode, allocator);
109✔
2724
    if (SIXEL_FAILED(status)) {
235!
2725
        goto end;
×
2726
    }
2727
    if (origcolors) {
235!
2728
        *origcolors = colorfreqtable.size;
235✔
2729
    }
109✔
2730

2731
    if (colorfreqtable.size <= reqColors) {
235✔
2732
        quant_trace(stderr,
284✔
2733
                    "Image already has few enough colors (<=%d).  "
2734
                    "Keeping same colors.\n", reqColors);
94✔
2735
        /* *colormapP = colorfreqtable; */
2736
        colormapP->size = colorfreqtable.size;
190✔
2737
        status = alloctupletable(&colormapP->table, depth, colorfreqtable.size, allocator);
190✔
2738
        if (SIXEL_FAILED(status)) {
190!
2739
            goto end;
×
2740
        }
2741
        for (i = 0; i < colorfreqtable.size; ++i) {
2,956✔
2742
            colormapP->table[i]->value = colorfreqtable.table[i]->value;
2,766✔
2743
            for (n = 0; n < depth; ++n) {
11,064✔
2744
                colormapP->table[i]->tuple[n] = colorfreqtable.table[i]->tuple[n];
8,298✔
2745
            }
3,480✔
2746
        }
1,160✔
2747
    } else {
94✔
2748
        quant_trace(stderr, "choosing %d colors...\n", reqColors);
45✔
2749
        status = mediancut(colorfreqtable, depth, reqColors,
60✔
2750
                           methodForLargest, methodForRep, colormapP, allocator);
15✔
2751
        if (SIXEL_FAILED(status)) {
45!
2752
            goto end;
×
2753
        }
2754
        quant_trace(stderr, "%d colors are choosed.\n", colorfreqtable.size);
45✔
2755
    }
2756

2757
    if (force_palette) {
235!
2758
        status = force_palette_completion(colormapP, depth, reqColors,
×
2759
                                          colorfreqtable, allocator);
2760
        if (SIXEL_FAILED(status)) {
×
2761
            goto end;
×
2762
        }
2763
    }
2764

2765
    status = SIXEL_OK;
235✔
2766

2767
end:
126✔
2768
    sixel_allocator_free(allocator, colorfreqtable.table);
235✔
2769
    return status;
235✔
2770
}
2771

2772

2773
/* diffuse error energy to surround pixels (normal strategy) */
2774
static void
2775
error_diffuse_normal(
50,778,513✔
2776
    unsigned char /* in */    *data,      /* base address of pixel buffer */
2777
    int           /* in */    pos,        /* address of the destination pixel */
2778
    int           /* in */    depth,      /* color depth in bytes */
2779
    int           /* in */    error,      /* error energy */
2780
    int           /* in */    numerator,  /* numerator of diffusion coefficient */
2781
    int           /* in */    denominator /* denominator of diffusion coefficient */)
2782
{
2783
    int c;
2784

2785
    data += pos * depth;
50,778,513✔
2786

2787
    c = *data + (error * numerator * 2 / denominator + 1) / 2;
50,778,513✔
2788
    if (c < 0) {
50,778,513✔
2789
        c = 0;
2,039,547✔
2790
    }
690,913✔
2791
    if (c >= 1 << 8) {
50,778,513✔
2792
        c = (1 << 8) - 1;
651,403✔
2793
    }
217,243✔
2794
    *data = (unsigned char)c;
50,778,513✔
2795
}
50,778,513✔
2796

2797
/* error diffusion with fast strategy */
2798
static void
2799
error_diffuse_fast(
169,469,067✔
2800
    unsigned char /* in */    *data,      /* base address of pixel buffer */
2801
    int           /* in */    pos,        /* address of the destination pixel */
2802
    int           /* in */    depth,      /* color depth in bytes */
2803
    int           /* in */    error,      /* error energy */
2804
    int           /* in */    numerator,  /* numerator of diffusion coefficient */
2805
    int           /* in */    denominator /* denominator of diffusion coefficient */)
2806
{
2807
    int c;
2808

2809
    data += pos * depth;
169,469,067✔
2810

2811
    c = *data + error * numerator / denominator;
169,469,067✔
2812
    if (c < 0) {
169,469,067✔
2813
        c = 0;
7,485,418✔
2814
    }
2,822,832✔
2815
    if (c >= 1 << 8) {
169,469,067✔
2816
        c = (1 << 8) - 1;
455,515✔
2817
    }
148,827✔
2818
    *data = (unsigned char)c;
169,469,067✔
2819
}
169,469,067✔
2820

2821

2822
/* error diffusion with precise strategy */
2823
static void
2824
error_diffuse_precise(
6,111,369✔
2825
    unsigned char /* in */    *data,      /* base address of pixel buffer */
2826
    int           /* in */    pos,        /* address of the destination pixel */
2827
    int           /* in */    depth,      /* color depth in bytes */
2828
    int           /* in */    error,      /* error energy */
2829
    int           /* in */    numerator,  /* numerator of diffusion coefficient */
2830
    int           /* in */    denominator /* denominator of diffusion coefficient */)
2831
{
2832
    int c;
2833

2834
    data += pos * depth;
6,111,369✔
2835

2836
    c = (int)(*data + error * numerator / (double)denominator + 0.5);
6,111,369✔
2837
    if (c < 0) {
6,111,369✔
2838
        c = 0;
70,495✔
2839
    }
25,489✔
2840
    if (c >= 1 << 8) {
6,111,369!
2841
        c = (1 << 8) - 1;
×
2842
    }
2843
    *data = (unsigned char)c;
6,111,369✔
2844
}
6,111,369✔
2845

2846

2847
typedef void (*diffuse_fixed_carry_mode)(int32_t *carry_curr,
2848
                                         int32_t *carry_next,
2849
                                         int32_t *carry_far,
2850
                                         int width,
2851
                                         int height,
2852
                                         int depth,
2853
                                         int x,
2854
                                         int y,
2855
                                         int32_t error,
2856
                                         int direction,
2857
                                         int channel);
2858

2859

2860
typedef void (*diffuse_varerr_mode)(unsigned char *data,
2861
                                    int width,
2862
                                    int height,
2863
                                    int x,
2864
                                    int y,
2865
                                    int depth,
2866
                                    int32_t error,
2867
                                    int index,
2868
                                    int direction);
2869

2870
typedef void (*diffuse_varerr_carry_mode)(int32_t *carry_curr,
2871
                                          int32_t *carry_next,
2872
                                          int32_t *carry_far,
2873
                                          int width,
2874
                                          int height,
2875
                                          int depth,
2876
                                          int x,
2877
                                          int y,
2878
                                          int32_t error,
2879
                                          int index,
2880
                                          int direction,
2881
                                          int channel);
2882

2883
static int
2884
zhoufang_index_from_byte(unsigned char value)
×
2885
{
2886
    double px;
2887
    double remapped;
2888
    int scale_index;
2889
    double scale;
2890
    double jitter;
2891
    int index;
2892

2893
    px = (double)value / 255.0;
×
2894
    remapped = px;
×
2895
    if (remapped >= 0.5) {
×
2896
        remapped = 1.0 - remapped;
×
2897
    }
2898
    if (remapped < 0.0) {
×
2899
        remapped = 0.0;
×
2900
    }
2901
    if (remapped > 0.5) {
×
2902
        remapped = 0.5;
×
2903
    }
2904

2905
    scale_index = (int)(remapped * 128.0);
×
2906
    if (scale_index < 0) {
×
2907
        scale_index = 0;
×
2908
    }
2909
    if (scale_index > 127) {
×
2910
        scale_index = 127;
×
2911
    }
2912

2913
    scale = zhoufang_threshold_gain[scale_index] / 100.0;
×
2914
    jitter = ((double)(rand() & 127) / 128.0) * scale;
×
2915
    remapped += (0.5 - remapped) * jitter;
×
2916
    if (remapped < 0.0) {
×
2917
        remapped = 0.0;
×
2918
    }
2919
    if (remapped > 0.5) {
×
2920
        remapped = 0.5;
×
2921
    }
2922

2923
    index = (int)(remapped * 255.0 + 0.5);
×
2924
    if (index > 127) {
×
2925
        index = 127;
×
2926
    }
2927
    if (index < 0) {
×
2928
        index = 0;
×
2929
    }
2930

2931
    return index;
×
2932
}
2933

2934

2935
static int32_t
2936
diffuse_varerr_term(int32_t error, int weight, int denom)
×
2937
{
2938
    int64_t delta;
2939

2940
    delta = (int64_t)error * (int64_t)weight;
×
2941
    if (delta >= 0) {
×
2942
        delta = (delta + denom / 2) / denom;
×
2943
    } else {
2944
        delta = (delta - denom / 2) / denom;
×
2945
    }
2946

2947
    return (int32_t)delta;
×
2948
}
2949

2950

2951
static int32_t
2952
diffuse_fixed_term(int32_t error, int numerator, int denominator)
×
2953
{
2954
    int64_t delta;
2955

2956
    delta = (int64_t)error * (int64_t)numerator;
×
2957
    if (delta >= 0) {
×
2958
        delta = (delta + denominator / 2) / denominator;
×
2959
    } else {
2960
        delta = (delta - denominator / 2) / denominator;
×
2961
    }
2962

2963
    return (int32_t)delta;
×
2964
}
2965

2966

2967
static void
2968
diffuse_varerr_apply_direct(unsigned char *target, int depth, size_t offset,
×
2969
                            int32_t delta)
2970
{
2971
    int64_t value;
2972
    int result;
2973

2974
    value = (int64_t)target[offset * depth] << VARERR_SCALE_SHIFT;
×
2975
    value += delta;
×
2976
    if (value < 0) {
×
2977
        value = 0;
×
2978
    } else {
2979
        int64_t max_value;
2980

2981
        max_value = VARERR_MAX_VALUE;
×
2982
        if (value > max_value) {
×
2983
            value = max_value;
×
2984
        }
2985
    }
2986

2987
    result = (int)((value + VARERR_ROUND) >> VARERR_SCALE_SHIFT);
×
2988
    if (result < 0) {
×
2989
        result = 0;
×
2990
    }
2991
    if (result > 255) {
×
2992
        result = 255;
×
2993
    }
2994
    target[offset * depth] = (unsigned char)result;
×
2995
}
×
2996

2997

2998
static void
2999
diffuse_lso2(unsigned char *data, int width, int height,
×
3000
             int x, int y, int depth, int32_t error,
3001
             int index, int direction)
3002
{
3003
    const int (*table)[7];
3004
    const int *entry;
3005
    int denom;
3006
    int32_t term_r = 0;
×
3007
    int32_t term_r2 = 0;
×
3008
    int32_t term_dl = 0;
×
3009
    int32_t term_d = 0;
×
3010
    int32_t term_dr = 0;
×
3011
    int32_t term_d2 = 0;
×
3012
    size_t offset;
3013

3014
    if (error == 0) {
×
3015
        return;
×
3016
    }
3017
    if (index < 0) {
×
3018
        index = 0;
×
3019
    }
3020
    if (index > 255) {
×
3021
        index = 255;
×
3022
    }
3023

3024
    table = lso2_table();
×
3025
    entry = table[index];
×
3026
    denom = entry[6];
×
3027
    if (denom == 0) {
×
3028
        return;
×
3029
    }
3030

3031
    term_r = diffuse_varerr_term(error, entry[0], denom);
×
3032
    term_r2 = diffuse_varerr_term(error, entry[1], denom);
×
3033
    term_dl = diffuse_varerr_term(error, entry[2], denom);
×
3034
    term_d = diffuse_varerr_term(error, entry[3], denom);
×
3035
    term_dr = diffuse_varerr_term(error, entry[4], denom);
×
3036
    term_d2 = diffuse_varerr_term(error, entry[5], denom);
×
3037

3038

3039
    if (direction >= 0) {
×
3040
        if (x + 1 < width) {
×
3041
            offset = (size_t)y * (size_t)width + (size_t)(x + 1);
×
3042
            diffuse_varerr_apply_direct(data, depth, offset, term_r);
×
3043
        }
3044
        if (x + 2 < width) {
×
3045
            offset = (size_t)y * (size_t)width + (size_t)(x + 2);
×
3046
            diffuse_varerr_apply_direct(data, depth, offset, term_r2);
×
3047
        }
3048
        if (y + 1 < height && x - 1 >= 0) {
×
3049
            offset = (size_t)(y + 1) * (size_t)width;
×
3050
            offset += (size_t)(x - 1);
×
3051
            diffuse_varerr_apply_direct(data, depth, offset, term_dl);
×
3052
        }
3053
        if (y + 1 < height) {
×
3054
            offset = (size_t)(y + 1) * (size_t)width + (size_t)x;
×
3055
            diffuse_varerr_apply_direct(data, depth, offset, term_d);
×
3056
        }
3057
        if (y + 1 < height && x + 1 < width) {
×
3058
            offset = (size_t)(y + 1) * (size_t)width;
×
3059
            offset += (size_t)(x + 1);
×
3060
            diffuse_varerr_apply_direct(data, depth, offset, term_dr);
×
3061
        }
3062
        if (y + 2 < height) {
×
3063
            offset = (size_t)(y + 2) * (size_t)width + (size_t)x;
×
3064
            diffuse_varerr_apply_direct(data, depth, offset, term_d2);
×
3065
        }
3066
    } else {
3067
        if (x - 1 >= 0) {
×
3068
            offset = (size_t)y * (size_t)width + (size_t)(x - 1);
×
3069
            diffuse_varerr_apply_direct(data, depth, offset, term_r);
×
3070
        }
3071
        if (x - 2 >= 0) {
×
3072
            offset = (size_t)y * (size_t)width + (size_t)(x - 2);
×
3073
            diffuse_varerr_apply_direct(data, depth, offset, term_r2);
×
3074
        }
3075
        if (y + 1 < height && x + 1 < width) {
×
3076
            offset = (size_t)(y + 1) * (size_t)width;
×
3077
            offset += (size_t)(x + 1);
×
3078
            diffuse_varerr_apply_direct(data, depth, offset, term_dl);
×
3079
        }
3080
        if (y + 1 < height) {
×
3081
            offset = (size_t)(y + 1) * (size_t)width + (size_t)x;
×
3082
            diffuse_varerr_apply_direct(data, depth, offset, term_d);
×
3083
        }
3084
        if (y + 1 < height && x - 1 >= 0) {
×
3085
            offset = (size_t)(y + 1) * (size_t)width;
×
3086
            offset += (size_t)(x - 1);
×
3087
            diffuse_varerr_apply_direct(data, depth, offset, term_dr);
×
3088
        }
3089
        if (y + 2 < height) {
×
3090
            offset = (size_t)(y + 2) * (size_t)width + (size_t)x;
×
3091
            diffuse_varerr_apply_direct(data, depth, offset, term_d2);
×
3092
        }
3093
    }
3094
}
3095

3096

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

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

3123
    table = lso3_table();
×
3124
    entry = table[index];
×
3125
    denom = entry[6];
×
3126
    if (denom == 0) {
×
3127
        return;
×
3128
    }
3129

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

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

3194

3195
static void
3196
diffuse_lso2_carry(int32_t *carry_curr, int32_t *carry_next, int32_t *carry_far,
×
3197
                   int width, int height, int depth,
3198
                   int x, int y, int32_t error,
3199
                   int index, int direction, int channel)
3200
{
3201
    const int (*table)[7];
3202
    const int *entry;
3203
    int denom;
3204
    int32_t term_r = 0;
×
3205
    int32_t term_r2 = 0;
×
3206
    int32_t term_dl = 0;
×
3207
    int32_t term_d = 0;
×
3208
    int32_t term_dr = 0;
×
3209
    int32_t term_d2 = 0;
×
3210
    size_t base;
3211

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

3222
    table = lso2_table();
×
3223
    entry = table[index];
×
3224
    denom = entry[6];
×
3225
    if (denom == 0) {
×
3226
        return;
×
3227
    }
3228

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

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

3289

3290
static void
3291
diffuse_lso3_carry(int32_t *carry_curr, int32_t *carry_next, int32_t *carry_far,
×
3292
                   int width, int height, int depth,
3293
                   int x, int y, int32_t error,
3294
                   int index, int direction, int channel)
3295
{
3296
    const int (*table)[7];
3297
    const int *entry;
3298
    int denom;
3299
    int32_t term_r = 0;
×
3300
    int32_t term_r2 = 0;
×
3301
    int32_t term_dl = 0;
×
3302
    int32_t term_d = 0;
×
3303
    int32_t term_dr = 0;
×
3304
    int32_t term_d2 = 0;
×
3305
    size_t base;
3306

3307
    if (error == 0) {
×
3308
        return;
×
3309
    }
3310
    if (index < 0) {
×
3311
        index = 0;
×
3312
    }
3313
    if (index > 255) {
×
3314
        index = 255;
×
3315
    }
3316

3317
    table = lso3_table();
×
3318
    entry = table[index];
×
3319
    denom = entry[6];
×
3320
    if (denom == 0) {
×
3321
        return;
×
3322
    }
3323

3324
    term_r = diffuse_varerr_term(error, entry[0], denom);
×
3325
    term_r2 = diffuse_varerr_term(error, entry[1], denom);
×
3326
    term_dl = diffuse_varerr_term(error, entry[2], denom);
×
3327
    term_d = diffuse_varerr_term(error, entry[3], denom);
×
3328
    term_dr = diffuse_varerr_term(error, entry[4], denom);
×
3329
    term_d2 = diffuse_varerr_term(error, entry[5], denom);
×
3330

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

3384

3385
static void
3386
scanline_params(int serpentine, int index, int limit,
42,994✔
3387
                int *start, int *end, int *step, int *direction)
3388
{
3389
    if (serpentine && (index & 1)) {
42,994!
3390
        *start = limit - 1;
×
3391
        *end = -1;
×
3392
        *step = -1;
×
3393
        *direction = -1;
×
3394
    } else {
3395
        *start = 0;
42,994✔
3396
        *end = limit;
42,994✔
3397
        *step = 1;
42,994✔
3398
        *direction = 1;
42,994✔
3399
    }
3400
}
42,994✔
3401

3402

3403
static SIXELSTATUS
3404
apply_palette_positional(
×
3405
    sixel_index_t *result,
3406
    unsigned char *data,
3407
    int width,
3408
    int height,
3409
    int depth,
3410
    unsigned char *palette,
3411
    int reqcolor,
3412
    int methodForDiffuse,
3413
    int methodForScan,
3414
    int foptimize_palette,
3415
    int (*f_lookup)(const unsigned char *pixel,
3416
                    int depth,
3417
                    const unsigned char *palette,
3418
                    int reqcolor,
3419
                    unsigned short *cachetable,
3420
                    int complexion),
3421
    unsigned short *indextable,
3422
    int complexion,
3423
    unsigned char copy[],
3424
    unsigned char new_palette[],
3425
    unsigned short migration_map[],
3426
    int *ncolors)
3427
{
3428
    int serpentine;
3429
    int y;
3430
    float (*f_mask)(int x, int y, int c);
3431

3432
    switch (methodForDiffuse) {
×
3433
    case SIXEL_DIFFUSE_A_DITHER:
3434
        f_mask = mask_a;
×
3435
        break;
×
3436
    case SIXEL_DIFFUSE_X_DITHER:
×
3437
    default:
3438
        f_mask = mask_x;
×
3439
        break;
×
3440
    }
3441

3442
    serpentine = (methodForScan == SIXEL_SCAN_SERPENTINE);
×
3443

3444
    if (foptimize_palette) {
×
3445
        int x;
3446

3447
        *ncolors = 0;
×
3448
        memset(new_palette, 0x00,
×
3449
               (size_t)SIXEL_PALETTE_MAX * (size_t)depth);
3450
        memset(migration_map, 0x00,
×
3451
               sizeof(unsigned short) * (size_t)SIXEL_PALETTE_MAX);
3452
        for (y = 0; y < height; ++y) {
×
3453
            int start;
3454
            int end;
3455
            int step;
3456
            int direction;
3457

3458
            scanline_params(serpentine, y, width,
×
3459
                            &start, &end, &step, &direction);
3460
            (void)direction;
3461
            for (x = start; x != end; x += step) {
×
3462
                int pos;
3463
                int d;
3464
                int color_index;
3465

3466
                pos = y * width + x;
×
3467
                for (d = 0; d < depth; ++d) {
×
3468
                    int val;
3469

3470
                    val = data[pos * depth + d]
×
3471
                        + f_mask(x, y, d) * 32;
×
3472
                    copy[d] = val < 0 ? 0
×
3473
                               : val > 255 ? 255 : val;
×
3474
                }
3475
                color_index = f_lookup(copy, depth, palette,
×
3476
                                       reqcolor, indextable,
3477
                                       complexion);
3478
                if (migration_map[color_index] == 0) {
×
3479
                    result[pos] = *ncolors;
×
3480
                    for (d = 0; d < depth; ++d) {
×
3481
                        new_palette[*ncolors * depth + d]
×
3482
                            = palette[color_index * depth + d];
×
3483
                    }
3484
                    ++*ncolors;
×
3485
                    migration_map[color_index] = *ncolors;
×
3486
                } else {
3487
                    result[pos] = migration_map[color_index] - 1;
×
3488
                }
3489
            }
3490
        }
3491
        memcpy(palette, new_palette, (size_t)(*ncolors * depth));
×
3492
    } else {
3493
        int x;
3494

3495
        for (y = 0; y < height; ++y) {
×
3496
            int start;
3497
            int end;
3498
            int step;
3499
            int direction;
3500

3501
            scanline_params(serpentine, y, width,
×
3502
                            &start, &end, &step, &direction);
3503
            (void)direction;
3504
            for (x = start; x != end; x += step) {
×
3505
                int pos;
3506
                int d;
3507

3508
                pos = y * width + x;
×
3509
                for (d = 0; d < depth; ++d) {
×
3510
                    int val;
3511

3512
                    val = data[pos * depth + d]
×
3513
                        + f_mask(x, y, d) * 32;
×
3514
                    copy[d] = val < 0 ? 0
×
3515
                               : val > 255 ? 255 : val;
×
3516
                }
3517
                result[pos] = f_lookup(copy, depth, palette,
×
3518
                                       reqcolor, indextable,
3519
                                       complexion);
3520
            }
3521
        }
3522
        *ncolors = reqcolor;
×
3523
    }
3524

3525
    return SIXEL_OK;
×
3526
}
3527

3528

3529
static SIXELSTATUS
3530
apply_palette_variable(
×
3531
    sixel_index_t *result,
3532
    unsigned char *data,
3533
    int width,
3534
    int height,
3535
    int depth,
3536
    unsigned char *palette,
3537
    int reqcolor,
3538
    int methodForScan,
3539
    int foptimize_palette,
3540
    int (*f_lookup)(const unsigned char *pixel,
3541
                    int depth,
3542
                    const unsigned char *palette,
3543
                    int reqcolor,
3544
                    unsigned short *cachetable,
3545
                    int complexion),
3546
    unsigned short *indextable,
3547
    int complexion,
3548
    unsigned char new_palette[],
3549
    unsigned short migration_map[],
3550
    int *ncolors,
3551
    int methodForDiffuse,
3552
    int methodForCarry)
3553
{
3554
    SIXELSTATUS status = SIXEL_FALSE;
×
3555
#if _MSC_VER
3556
    enum { max_channels = 4 };
3557
#else
3558
    const int max_channels = 4;
×
3559
#endif
3560
    int serpentine;
3561
    int y;
3562
    diffuse_varerr_mode varerr_diffuse;
3563
    diffuse_varerr_carry_mode varerr_diffuse_carry;
3564
    int use_carry;
3565
    size_t carry_len;
3566
    int32_t *carry_curr = NULL;
×
3567
    int32_t *carry_next = NULL;
×
3568
    int32_t *carry_far = NULL;
×
3569
    unsigned char corrected[max_channels];
3570
    int32_t sample_scaled[max_channels];
3571
    int32_t accum_scaled[max_channels];
3572
    int start;
3573
    int end;
3574
    int step;
3575
    int direction;
3576
    int x;
3577
    int pos;
3578
    size_t base;
3579
    size_t carry_base;
3580
    const unsigned char *source_pixel;
3581
    int color_index;
3582
    int output_index;
3583
    int n;
3584
    int palette_value;
3585
    int diff;
3586
    int table_index;
3587
    int64_t accum;
3588
    int64_t clamped;
3589
    int32_t target_scaled;
3590
    int32_t error_scaled;
3591
    int32_t *tmp;
3592

3593
    if (depth > max_channels) {
×
3594
        status = SIXEL_BAD_ARGUMENT;
×
3595
        goto end;
×
3596
    }
3597

3598
    use_carry = (methodForCarry == SIXEL_CARRY_ENABLE);
×
3599
    carry_len = 0;
×
3600

3601
    switch (methodForDiffuse) {
×
3602
    case SIXEL_DIFFUSE_LSO2:
3603
        varerr_diffuse = diffuse_lso2;
×
3604
        varerr_diffuse_carry = diffuse_lso2_carry;
×
3605
        break;
×
3606
    case SIXEL_DIFFUSE_LSO3:
3607
        varerr_diffuse = diffuse_lso3;
×
3608
        varerr_diffuse_carry = diffuse_lso3_carry;
×
3609
        srand((unsigned int)time(NULL));
×
3610
        break;
×
3611
    default:
3612
        varerr_diffuse = diffuse_lso2;
×
3613
        varerr_diffuse_carry = diffuse_lso2_carry;
×
3614
        break;
×
3615
    }
3616

3617
    if (use_carry) {
×
3618
        carry_len = (size_t)width * (size_t)depth;
×
3619
        carry_curr = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
3620
        if (carry_curr == NULL) {
×
3621
            status = SIXEL_BAD_ALLOCATION;
×
3622
            goto end;
×
3623
        }
3624
        carry_next = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
3625
        if (carry_next == NULL) {
×
3626
            status = SIXEL_BAD_ALLOCATION;
×
3627
            goto end;
×
3628
        }
3629
        carry_far = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
3630
        if (carry_far == NULL) {
×
3631
            status = SIXEL_BAD_ALLOCATION;
×
3632
            goto end;
×
3633
        }
3634
    }
3635

3636
    serpentine = (methodForScan == SIXEL_SCAN_SERPENTINE);
×
3637

3638
    if (foptimize_palette) {
×
3639
        *ncolors = 0;
×
3640
        memset(new_palette, 0x00,
×
3641
               (size_t)SIXEL_PALETTE_MAX * (size_t)depth);
3642
        memset(migration_map, 0x00,
×
3643
               sizeof(unsigned short) * (size_t)SIXEL_PALETTE_MAX);
3644
    }
3645

3646
    for (y = 0; y < height; ++y) {
×
3647
        scanline_params(serpentine, y, width,
×
3648
                        &start, &end, &step, &direction);
3649
        for (x = start; x != end; x += step) {
×
3650
            pos = y * width + x;
×
3651
            base = (size_t)pos * (size_t)depth;
×
3652
            carry_base = (size_t)x * (size_t)depth;
×
3653
            if (use_carry) {
×
3654
                for (n = 0; n < depth; ++n) {
×
3655
                    accum = ((int64_t)data[base + n]
×
3656
                             << VARERR_SCALE_SHIFT)
×
3657
                          + carry_curr[carry_base + (size_t)n];
×
3658
                    if (accum < INT32_MIN) {
×
3659
                        accum = INT32_MIN;
×
3660
                    } else if (accum > INT32_MAX) {
×
3661
                        accum = INT32_MAX;
×
3662
                    }
3663
                    carry_curr[carry_base + (size_t)n] = 0;
×
3664
                    clamped = accum;
×
3665
                    if (clamped < 0) {
×
3666
                        clamped = 0;
×
3667
                    } else if (clamped > VARERR_MAX_VALUE) {
×
3668
                        clamped = VARERR_MAX_VALUE;
×
3669
                    }
3670
                    accum_scaled[n] = (int32_t)clamped;
×
3671
                    corrected[n]
3672
                        = (unsigned char)((clamped + VARERR_ROUND)
×
3673
                                          >> VARERR_SCALE_SHIFT);
×
3674
                }
3675
                source_pixel = corrected;
×
3676
            } else {
3677
                for (n = 0; n < depth; ++n) {
×
3678
                    sample_scaled[n]
3679
                        = (int32_t)data[base + n]
×
3680
                        << VARERR_SCALE_SHIFT;
×
3681
                    corrected[n] = data[base + n];
×
3682
                }
3683
                source_pixel = data + base;
×
3684
            }
3685

3686
            color_index = f_lookup(source_pixel, depth, palette,
×
3687
                                   reqcolor, indextable,
3688
                                   complexion);
3689

3690
            if (foptimize_palette) {
×
3691
                if (migration_map[color_index] == 0) {
×
3692
                    output_index = *ncolors;
×
3693
                    for (n = 0; n < depth; ++n) {
×
3694
                        new_palette[output_index * depth + n]
×
3695
                            = palette[color_index * depth + n];
×
3696
                    }
3697
                    ++*ncolors;
×
3698
                    migration_map[color_index] = *ncolors;
×
3699
                } else {
3700
                    output_index = migration_map[color_index] - 1;
×
3701
                }
3702
                result[pos] = output_index;
×
3703
            } else {
3704
                output_index = color_index;
×
3705
                result[pos] = output_index;
×
3706
            }
3707

3708
            for (n = 0; n < depth; ++n) {
×
3709
                if (foptimize_palette) {
×
3710
                    palette_value = new_palette[output_index * depth + n];
×
3711
                } else {
3712
                    palette_value = palette[color_index * depth + n];
×
3713
                }
3714
                diff = (int)source_pixel[n] - palette_value;
×
3715
                if (diff < 0) {
×
3716
                    diff = -diff;
×
3717
                }
3718
                if (diff > 255) {
×
3719
                    diff = 255;
×
3720
                }
3721
                table_index = diff;
×
3722
                if (methodForDiffuse == SIXEL_DIFFUSE_LSO3) {
×
3723
                    table_index = zhoufang_index_from_byte(
×
3724
                        (unsigned char)source_pixel[n]);
×
3725
                }
3726
                if (use_carry) {
×
3727
                    target_scaled = (int32_t)palette_value
×
3728
                                  << VARERR_SCALE_SHIFT;
3729
                    error_scaled = accum_scaled[n] - target_scaled;
×
3730
                    varerr_diffuse_carry(carry_curr, carry_next, carry_far,
×
3731
                                         width, height, depth,
3732
                                         x, y, error_scaled,
3733
                                         table_index,
3734
                                         direction, n);
3735
                } else {
3736
                    target_scaled = (int32_t)palette_value
×
3737
                                  << VARERR_SCALE_SHIFT;
3738
                    error_scaled = sample_scaled[n] - target_scaled;
×
3739
                    varerr_diffuse(data + n, width, height,
×
3740
                                   x, y, depth, error_scaled,
3741
                                   table_index,
3742
                                   direction);
3743
                }
3744
            }
3745
        }
3746
        if (use_carry) {
×
3747
            tmp = carry_curr;
×
3748
            carry_curr = carry_next;
×
3749
            carry_next = carry_far;
×
3750
            carry_far = tmp;
×
3751
            if (carry_len > 0) {
×
3752
                memset(carry_far, 0x00, carry_len * sizeof(int32_t));
×
3753
            }
3754
        }
3755
    }
3756

3757
    if (foptimize_palette) {
×
3758
        memcpy(palette, new_palette, (size_t)(*ncolors * depth));
×
3759
    } else {
3760
        *ncolors = reqcolor;
×
3761
    }
3762

3763
    status = SIXEL_OK;
×
3764

3765
end:
3766
    free(carry_next);
×
3767
    free(carry_curr);
×
3768
    free(carry_far);
×
3769
    return status;
×
3770
}
3771

3772

3773
static SIXELSTATUS
3774
apply_palette_fixed(
257✔
3775
    sixel_index_t *result,
3776
    unsigned char *data,
3777
    int width,
3778
    int height,
3779
    int depth,
3780
    unsigned char *palette,
3781
    int reqcolor,
3782
    int methodForScan,
3783
    int foptimize_palette,
3784
    int (*f_lookup)(const unsigned char *pixel,
3785
                    int depth,
3786
                    const unsigned char *palette,
3787
                    int reqcolor,
3788
                    unsigned short *cachetable,
3789
                    int complexion),
3790
    unsigned short *indextable,
3791
    int complexion,
3792
    unsigned char new_palette[],
3793
    unsigned short migration_map[],
3794
    int *ncolors,
3795
    int methodForDiffuse,
3796
    int methodForCarry)
3797
{
152✔
3798
#if _MSC_VER
3799
    enum { max_channels = 4 };
3800
#else
3801
    const int max_channels = 4;
257✔
3802
#endif
3803
    SIXELSTATUS status = SIXEL_FALSE;
257✔
3804
    int serpentine;
3805
    int y;
3806
    void (*f_diffuse)(unsigned char *data,
3807
                      int width,
3808
                      int height,
3809
                      int x,
3810
                      int y,
3811
                      int depth,
3812
                      int offset,
3813
                      int direction);
3814
    diffuse_fixed_carry_mode f_diffuse_carry;
3815
    int use_carry;
3816
    size_t carry_len;
3817
    int32_t *carry_curr = NULL;
257✔
3818
    int32_t *carry_next = NULL;
257✔
3819
    int32_t *carry_far = NULL;
257✔
3820
    unsigned char corrected[max_channels];
152✔
3821
    int32_t accum_scaled[max_channels];
152✔
3822
    int start;
3823
    int end;
3824
    int step;
3825
    int direction;
3826
    int x;
3827
    int pos;
3828
    size_t base;
3829
    size_t carry_base;
3830
    const unsigned char *source_pixel;
3831
    int color_index;
3832
    int output_index;
3833
    int n;
3834
    int palette_value;
3835
    int64_t accum;
3836
    int64_t clamped;
3837
    int32_t target_scaled;
3838
    int32_t error_scaled;
3839
    int offset;
3840
    int32_t *tmp;
3841

3842
    if (depth > max_channels) {
257!
3843
        status = SIXEL_BAD_ARGUMENT;
×
3844
        goto end;
×
3845
    }
3846

3847
    use_carry = (methodForCarry == SIXEL_CARRY_ENABLE);
257✔
3848
    carry_len = 0;
257✔
3849

3850
    if (depth != 3) {
257!
3851
        f_diffuse = diffuse_none;
×
3852
        f_diffuse_carry = diffuse_none_carry;
×
3853
        use_carry = 0;
×
3854
    } else {
3855
        switch (methodForDiffuse) {
257!
3856
        case SIXEL_DIFFUSE_NONE:
86✔
3857
            f_diffuse = diffuse_none;
157✔
3858
            f_diffuse_carry = diffuse_none_carry;
157✔
3859
            break;
157✔
3860
        case SIXEL_DIFFUSE_ATKINSON:
44✔
3861
            f_diffuse = diffuse_atkinson;
67✔
3862
            f_diffuse_carry = diffuse_atkinson_carry;
67✔
3863
            break;
67✔
3864
        case SIXEL_DIFFUSE_FS:
16✔
3865
            f_diffuse = diffuse_fs;
24✔
3866
            f_diffuse_carry = diffuse_fs_carry;
24✔
3867
            break;
24✔
3868
        case SIXEL_DIFFUSE_JAJUNI:
2✔
3869
            f_diffuse = diffuse_jajuni;
3✔
3870
            f_diffuse_carry = diffuse_jajuni_carry;
3✔
3871
            break;
3✔
3872
        case SIXEL_DIFFUSE_STUCKI:
2✔
3873
            f_diffuse = diffuse_stucki;
3✔
3874
            f_diffuse_carry = diffuse_stucki_carry;
3✔
3875
            break;
3✔
3876
        case SIXEL_DIFFUSE_BURKES:
2✔
3877
            f_diffuse = diffuse_burkes;
3✔
3878
            f_diffuse_carry = diffuse_burkes_carry;
3✔
3879
            break;
3✔
3880
        case SIXEL_DIFFUSE_SIERRA1:
NEW
3881
            f_diffuse = diffuse_sierra1;
×
NEW
3882
            f_diffuse_carry = diffuse_sierra1_carry;
×
NEW
3883
            break;
×
3884
        case SIXEL_DIFFUSE_SIERRA2:
NEW
3885
            f_diffuse = diffuse_sierra2;
×
NEW
3886
            f_diffuse_carry = diffuse_sierra2_carry;
×
NEW
3887
            break;
×
3888
        case SIXEL_DIFFUSE_SIERRA3:
NEW
3889
            f_diffuse = diffuse_sierra3;
×
NEW
3890
            f_diffuse_carry = diffuse_sierra3_carry;
×
NEW
3891
            break;
×
3892
        case SIXEL_DIFFUSE_LSO1:
3893
            f_diffuse = diffuse_lso1;
×
3894
            f_diffuse_carry = diffuse_lso1_carry;
×
3895
            break;
×
3896
        default:
3897
            quant_trace(stderr,
×
3898
                        "Internal error: invalid methodForDiffuse: %d\n",
3899
                        methodForDiffuse);
3900
            f_diffuse = diffuse_none;
×
3901
            f_diffuse_carry = diffuse_none_carry;
×
3902
            break;
×
3903
        }
3904
    }
3905

3906
    if (use_carry) {
257!
3907
        carry_len = (size_t)width * (size_t)depth;
×
3908
        if (carry_len > 0) {
×
3909
            carry_curr = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
3910
            if (carry_curr == NULL) {
×
3911
                status = SIXEL_BAD_ALLOCATION;
×
3912
                goto end;
×
3913
            }
3914
            carry_next = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
3915
            if (carry_next == NULL) {
×
3916
                status = SIXEL_BAD_ALLOCATION;
×
3917
                goto end;
×
3918
            }
3919
            carry_far = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
3920
            if (carry_far == NULL) {
×
3921
                status = SIXEL_BAD_ALLOCATION;
×
3922
                goto end;
×
3923
            }
3924
        } else {
3925
            use_carry = 0;
×
3926
        }
3927
    }
3928

3929
    serpentine = (methodForScan == SIXEL_SCAN_SERPENTINE);
257✔
3930

3931
    if (foptimize_palette) {
257✔
3932
        *ncolors = 0;
178✔
3933
        memset(new_palette, 0x00,
178✔
3934
               (size_t)SIXEL_PALETTE_MAX * (size_t)depth);
112✔
3935
        memset(migration_map, 0x00,
178✔
3936
               sizeof(unsigned short) * (size_t)SIXEL_PALETTE_MAX);
3937
    } else {
66✔
3938
        *ncolors = reqcolor;
79✔
3939
    }
3940

3941
    for (y = 0; y < height; ++y) {
43,251✔
3942
        scanline_params(serpentine, y, width,
42,994✔
3943
                        &start, &end, &step, &direction);
3944
        for (x = start; x != end; x += step) {
19,978,700✔
3945
            pos = y * width + x;
19,935,706✔
3946
            base = (size_t)pos * (size_t)depth;
19,935,706✔
3947
            carry_base = (size_t)x * (size_t)depth;
19,935,706✔
3948
            if (use_carry) {
19,935,706!
3949
                for (n = 0; n < depth; ++n) {
×
3950
                    accum = ((int64_t)data[base + n]
×
3951
                             << VARERR_SCALE_SHIFT)
×
3952
                           + carry_curr[carry_base + (size_t)n];
×
3953
                    if (accum < INT32_MIN) {
×
3954
                        accum = INT32_MIN;
×
3955
                    } else if (accum > INT32_MAX) {
×
3956
                        accum = INT32_MAX;
×
3957
                    }
3958
                    clamped = accum;
×
3959
                    if (clamped < 0) {
×
3960
                        clamped = 0;
×
3961
                    } else if (clamped > VARERR_MAX_VALUE) {
×
3962
                        clamped = VARERR_MAX_VALUE;
×
3963
                    }
3964
                    accum_scaled[n] = (int32_t)clamped;
×
3965
                    corrected[n]
3966
                        = (unsigned char)((clamped + VARERR_ROUND)
×
3967
                                          >> VARERR_SCALE_SHIFT);
×
3968
                    data[base + n] = corrected[n];
×
3969
                    carry_curr[carry_base + (size_t)n] = 0;
×
3970
                }
3971
                source_pixel = corrected;
×
3972
            } else {
3973
                source_pixel = data + base;
19,935,706✔
3974
            }
3975

3976
            color_index = f_lookup(source_pixel, depth, palette,
29,385,328✔
3977
                                   reqcolor, indextable,
9,449,622✔
3978
                                   complexion);
9,449,622✔
3979

3980
            if (foptimize_palette) {
19,935,706✔
3981
                if (migration_map[color_index] == 0) {
7,834,206✔
3982
                    output_index = *ncolors;
9,502✔
3983
                    for (n = 0; n < depth; ++n) {
38,008✔
3984
                        new_palette[output_index * depth + n]
28,506✔
3985
                            = palette[color_index * depth + n];
38,094✔
3986
                    }
9,588✔
3987
                    ++*ncolors;
9,502✔
3988
                    migration_map[color_index] = *ncolors;
9,502✔
3989
                } else {
3,196✔
3990
                    output_index = migration_map[color_index] - 1;
7,824,704✔
3991
                }
3992
                result[pos] = output_index;
7,834,206✔
3993
            } else {
3,609,802✔
3994
                output_index = color_index;
12,101,500✔
3995
                result[pos] = output_index;
12,101,500✔
3996
            }
3997

3998
            for (n = 0; n < depth; ++n) {
79,742,824✔
3999
                if (foptimize_palette) {
59,807,118✔
4000
                    palette_value = new_palette[output_index * depth + n];
23,502,618✔
4001
                } else {
10,829,406✔
4002
                    palette_value = palette[color_index * depth + n];
36,304,500✔
4003
                }
4004
                if (use_carry) {
59,807,118!
4005
                    target_scaled = (int32_t)palette_value
×
4006
                                  << VARERR_SCALE_SHIFT;
4007
                    error_scaled = accum_scaled[n] - target_scaled;
×
4008
                    f_diffuse_carry(carry_curr, carry_next, carry_far,
×
4009
                                    width, height, depth,
4010
                                    x, y, error_scaled, direction, n);
4011
                } else {
4012
                    offset = (int)source_pixel[n] - palette_value;
59,807,118✔
4013
                    f_diffuse(data + n, width, height, x, y,
88,155,984✔
4014
                              depth, offset, direction);
28,348,866✔
4015
                }
4016
            }
28,348,866✔
4017
        }
9,449,622✔
4018
        if (use_carry) {
42,994!
4019
            tmp = carry_curr;
×
4020
            carry_curr = carry_next;
×
4021
            carry_next = carry_far;
×
4022
            carry_far = tmp;
×
4023
            if (carry_len > 0) {
×
4024
                memset(carry_far, 0x00, carry_len * sizeof(int32_t));
×
4025
            }
4026
        }
4027
    }
20,222✔
4028

4029
    if (foptimize_palette) {
257✔
4030
        memcpy(palette, new_palette, (size_t)(*ncolors * depth));
178✔
4031
    }
66✔
4032

4033
    status = SIXEL_OK;
257✔
4034

4035
end:
152✔
4036
    free(carry_far);
257✔
4037
    free(carry_next);
257✔
4038
    free(carry_curr);
257✔
4039
    return status;
257✔
4040
}
4041

4042

4043
static void
4044
diffuse_none(unsigned char *data, int width, int height,
18,248,814✔
4045
             int x, int y, int depth, int error, int direction)
4046
{
4047
    /* unused */ (void) data;
14,469,498✔
4048
    /* unused */ (void) width;
14,469,498✔
4049
    /* unused */ (void) height;
14,469,498✔
4050
    /* unused */ (void) x;
14,469,498✔
4051
    /* unused */ (void) y;
14,469,498✔
4052
    /* unused */ (void) depth;
14,469,498✔
4053
    /* unused */ (void) error;
14,469,498✔
4054
    /* unused */ (void) direction;
14,469,498✔
4055
}
18,248,814✔
4056

4057

4058
static void
4059
diffuse_none_carry(int32_t *carry_curr, int32_t *carry_next,
×
4060
                   int32_t *carry_far, int width, int height,
4061
                   int depth, int x, int y, int32_t error,
4062
                   int direction, int channel)
4063
{
4064
    /* unused */ (void) carry_curr;
4065
    /* unused */ (void) carry_next;
4066
    /* unused */ (void) carry_far;
4067
    /* unused */ (void) width;
4068
    /* unused */ (void) height;
4069
    /* unused */ (void) depth;
4070
    /* unused */ (void) x;
4071
    /* unused */ (void) y;
4072
    /* unused */ (void) error;
4073
    /* unused */ (void) direction;
4074
    /* unused */ (void) channel;
4075
}
×
4076

4077

4078
static void
4079
diffuse_fs(unsigned char *data, int width, int height,
12,621,744✔
4080
           int x, int y, int depth, int error, int direction)
4081
{
4082
    /* Floyd Steinberg Method
4083
     *          curr    7/16
4084
     *  3/16    5/48    1/16
4085
     */
4086
    int pos;
4087
    int forward;
4088

4089
    pos = y * width + x;
12,621,744✔
4090
    forward = direction >= 0;
12,621,744✔
4091

4092
    if (forward) {
12,621,744!
4093
        if (x < width - 1) {
12,621,744✔
4094
            error_diffuse_normal(data, pos + 1, depth, error, 7, 16);
12,594,798✔
4095
        }
4,198,266✔
4096
        if (y < height - 1) {
12,621,744✔
4097
            if (x > 0) {
12,591,828✔
4098
                error_diffuse_normal(data,
16,753,272✔
4099
                                     pos + width - 1,
12,564,954✔
4100
                                     depth, error, 3, 16);
4,188,318✔
4101
            }
4,188,318✔
4102
            error_diffuse_normal(data,
16,789,104✔
4103
                                 pos + width,
4,197,276✔
4104
                                 depth, error, 5, 16);
4,197,276✔
4105
            if (x < width - 1) {
12,591,828✔
4106
                error_diffuse_normal(data,
16,753,272✔
4107
                                     pos + width + 1,
12,564,954✔
4108
                                     depth, error, 1, 16);
4,188,318✔
4109
            }
4,188,318✔
4110
        }
4,197,276✔
4111
    } else {
4,207,248✔
4112
        if (x > 0) {
×
4113
            error_diffuse_normal(data, pos - 1, depth, error, 7, 16);
×
4114
        }
4115
        if (y < height - 1) {
×
4116
            if (x < width - 1) {
×
4117
                error_diffuse_normal(data,
×
4118
                                     pos + width + 1,
×
4119
                                     depth, error, 3, 16);
4120
            }
4121
            error_diffuse_normal(data,
×
4122
                                 pos + width,
4123
                                 depth, error, 5, 16);
4124
            if (x > 0) {
×
4125
                error_diffuse_normal(data,
×
4126
                                     pos + width - 1,
×
4127
                                     depth, error, 1, 16);
4128
            }
4129
        }
4130
    }
4131
}
12,621,744✔
4132

4133

4134
static void
4135
diffuse_fs_carry(int32_t *carry_curr, int32_t *carry_next,
×
4136
                 int32_t *carry_far, int width, int height,
4137
                 int depth, int x, int y, int32_t error,
4138
                 int direction, int channel)
4139
{
4140
    /* Floyd Steinberg Method
4141
     *          curr    7/16
4142
     *  3/16    5/48    1/16
4143
     */
4144
    int forward;
4145

4146
    /* unused */ (void) carry_far;
4147
    if (error == 0) {
×
4148
        return;
×
4149
    }
4150

4151
    forward = direction >= 0;
×
4152
    if (forward) {
×
4153
        if (x + 1 < width) {
×
4154
            size_t base;
4155
            int32_t term;
4156

4157
            base = ((size_t)(x + 1) * (size_t)depth)
×
4158
                 + (size_t)channel;
×
4159
            term = diffuse_fixed_term(error, 7, 16);
×
4160
            carry_curr[base] += term;
×
4161
        }
4162
        if (y + 1 < height) {
×
4163
            if (x > 0) {
×
4164
                size_t base;
4165
                int32_t term;
4166

4167
                base = ((size_t)(x - 1) * (size_t)depth)
×
4168
                     + (size_t)channel;
×
4169
                term = diffuse_fixed_term(error, 3, 16);
×
4170
                carry_next[base] += term;
×
4171
            }
4172
            {
4173
                size_t base;
4174
                int32_t term;
4175

4176
                base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
4177
                term = diffuse_fixed_term(error, 5, 16);
×
4178
                carry_next[base] += term;
×
4179
            }
4180
            if (x + 1 < width) {
×
4181
                size_t base;
4182
                int32_t term;
4183

4184
                base = ((size_t)(x + 1) * (size_t)depth)
×
4185
                     + (size_t)channel;
×
4186
                term = diffuse_fixed_term(error, 1, 16);
×
4187
                carry_next[base] += term;
×
4188
            }
4189
        }
4190
    } else {
4191
        if (x - 1 >= 0) {
×
4192
            size_t base;
4193
            int32_t term;
4194

4195
            base = ((size_t)(x - 1) * (size_t)depth)
×
4196
                 + (size_t)channel;
×
4197
            term = diffuse_fixed_term(error, 7, 16);
×
4198
            carry_curr[base] += term;
×
4199
        }
4200
        if (y + 1 < height) {
×
4201
            if (x + 1 < width) {
×
4202
                size_t base;
4203
                int32_t term;
4204

4205
                base = ((size_t)(x + 1) * (size_t)depth)
×
4206
                     + (size_t)channel;
×
4207
                term = diffuse_fixed_term(error, 3, 16);
×
4208
                carry_next[base] += term;
×
4209
            }
4210
            {
4211
                size_t base;
4212
                int32_t term;
4213

4214
                base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
4215
                term = diffuse_fixed_term(error, 5, 16);
×
4216
                carry_next[base] += term;
×
4217
            }
4218
            if (x - 1 >= 0) {
×
4219
                size_t base;
4220
                int32_t term;
4221

4222
                base = ((size_t)(x - 1) * (size_t)depth)
×
4223
                     + (size_t)channel;
×
4224
                term = diffuse_fixed_term(error, 1, 16);
×
4225
                carry_next[base] += term;
×
4226
            }
4227
        }
4228
    }
4229
}
4230

4231

4232
static void
4233
diffuse_atkinson(unsigned char *data, int width, int height,
28,352,460✔
4234
                 int x, int y, int depth, int error, int direction)
4235
{
4236
    /* Atkinson's Method
4237
     *          curr    1/8    1/8
4238
     *   1/8     1/8    1/8
4239
     *           1/8
4240
     */
4241
    int pos;
4242
    int sign;
4243

4244
    pos = y * width + x;
28,352,460✔
4245
    sign = direction >= 0 ? 1 : -1;
28,352,460!
4246

4247
    if (x + sign >= 0 && x + sign < width) {
28,352,460!
4248
        error_diffuse_fast(data, pos + sign, depth, error, 1, 8);
28,296,945✔
4249
    }
9,458,715✔
4250
    if (x + sign * 2 >= 0 && x + sign * 2 < width) {
28,352,460!
4251
        error_diffuse_fast(data, pos + sign * 2, depth, error, 1, 8);
28,241,430✔
4252
    }
9,440,010✔
4253
    if (y < height - 1) {
28,352,460✔
4254
        int row;
4255

4256
        row = pos + width;
28,278,756✔
4257
        if (x - sign >= 0 && x - sign < width) {
28,278,756!
4258
            error_diffuse_fast(data,
37,657,392✔
4259
                               row + (-sign),
9,433,950✔
4260
                               depth, error, 1, 8);
9,433,950✔
4261
        }
9,433,950✔
4262
        error_diffuse_fast(data, row, depth, error, 1, 8);
28,278,756✔
4263
        if (x + sign >= 0 && x + sign < width) {
28,278,756!
4264
            error_diffuse_fast(data,
37,657,392✔
4265
                               row + sign,
9,433,950✔
4266
                               depth, error, 1, 8);
9,433,950✔
4267
        }
9,433,950✔
4268
    }
9,452,586✔
4269
    if (y < height - 2) {
28,352,460✔
4270
        error_diffuse_fast(data, pos + width * 2, depth, error, 1, 8);
28,205,052✔
4271
    }
9,427,752✔
4272
}
28,352,460✔
4273

4274

4275
static void
4276
diffuse_atkinson_carry(int32_t *carry_curr, int32_t *carry_next,
×
4277
                       int32_t *carry_far, int width, int height,
4278
                       int depth, int x, int y, int32_t error,
4279
                       int direction, int channel)
4280
{
4281
    /* Atkinson's Method
4282
     *          curr    1/8    1/8
4283
     *   1/8     1/8    1/8
4284
     *           1/8
4285
     */
4286
    int sign;
4287
    int32_t term;
4288

4289
    if (error == 0) {
×
4290
        return;
×
4291
    }
4292

4293
    term = diffuse_fixed_term(error, 1, 8);
×
4294
    sign = direction >= 0 ? 1 : -1;
×
4295
    if (x + sign >= 0 && x + sign < width) {
×
4296
        size_t base;
4297

4298
        base = ((size_t)(x + sign) * (size_t)depth)
×
4299
             + (size_t)channel;
×
4300
        carry_curr[base] += term;
×
4301
    }
4302
    if (x + sign * 2 >= 0 && x + sign * 2 < width) {
×
4303
        size_t base;
4304

4305
        base = ((size_t)(x + sign * 2) * (size_t)depth)
×
4306
             + (size_t)channel;
×
4307
        carry_curr[base] += term;
×
4308
    }
4309
    if (y + 1 < height) {
×
4310
        if (x - sign >= 0 && x - sign < width) {
×
4311
            size_t base;
4312

4313
            base = ((size_t)(x - sign) * (size_t)depth)
×
4314
                 + (size_t)channel;
×
4315
            carry_next[base] += term;
×
4316
        }
4317
        {
4318
            size_t base;
4319

4320
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
4321
            carry_next[base] += term;
×
4322
        }
4323
        if (x + sign >= 0 && x + sign < width) {
×
4324
            size_t base;
4325

4326
            base = ((size_t)(x + sign) * (size_t)depth)
×
4327
                 + (size_t)channel;
×
4328
            carry_next[base] += term;
×
4329
        }
4330
    }
4331
    if (y + 2 < height) {
×
4332
        size_t base;
4333

4334
        base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
4335
        carry_far[base] += term;
×
4336
    }
4337
}
4338

4339

4340
static void
4341
diffuse_jajuni(unsigned char *data, int width, int height,
396,900✔
4342
               int x, int y, int depth, int error, int direction)
4343
{
4344
    /* Jarvis, Judice & Ninke Method
4345
     *                  curr    7/48    5/48
4346
     *  3/48    5/48    7/48    5/48    3/48
4347
     *  1/48    3/48    5/48    3/48    1/48
4348
     */
4349
    int pos;
4350
    int sign;
4351
    static const int row0_offsets[] = { 1, 2 };
4352
    static const int row0_weights[] = { 7, 5 };
4353
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4354
    static const int row1_weights[] = { 3, 5, 7, 5, 3 };
4355
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
4356
    static const int row2_weights[] = { 1, 3, 5, 3, 1 };
4357
    int i;
4358

4359
    pos = y * width + x;
396,900✔
4360
    sign = direction >= 0 ? 1 : -1;
396,900!
4361

4362
    for (i = 0; i < 2; ++i) {
1,190,700✔
4363
        int neighbor;
4364

4365
        neighbor = x + sign * row0_offsets[i];
793,800✔
4366
        if (neighbor < 0 || neighbor >= width) {
793,800!
4367
            continue;
5,670✔
4368
        }
4369
        error_diffuse_precise(data,
1,050,840✔
4370
                              pos + (neighbor - x),
788,130✔
4371
                              depth, error,
262,710✔
4372
                              row0_weights[i], 48);
788,130✔
4373
    }
262,710✔
4374
    if (y < height - 1) {
396,900✔
4375
        int row;
4376

4377
        row = pos + width;
395,010✔
4378
        for (i = 0; i < 5; ++i) {
2,370,060✔
4379
            int neighbor;
4380

4381
            neighbor = x + sign * row1_offsets[i];
1,975,050✔
4382
            if (neighbor < 0 || neighbor >= width) {
1,975,050✔
4383
                continue;
11,286✔
4384
            }
4385
            error_diffuse_precise(data,
2,618,352✔
4386
                                  row + (neighbor - x),
1,963,764✔
4387
                                  depth, error,
654,588✔
4388
                                  row1_weights[i], 48);
1,963,764✔
4389
        }
654,588✔
4390
    }
131,670✔
4391
    if (y < height - 2) {
396,900✔
4392
        int row;
4393

4394
        row = pos + width * 2;
393,120✔
4395
        for (i = 0; i < 5; ++i) {
2,358,720✔
4396
            int neighbor;
4397

4398
            neighbor = x + sign * row2_offsets[i];
1,965,600✔
4399
            if (neighbor < 0 || neighbor >= width) {
1,965,600✔
4400
                continue;
11,232✔
4401
            }
4402
            error_diffuse_precise(data,
2,605,824✔
4403
                                  row + (neighbor - x),
1,954,368✔
4404
                                  depth, error,
651,456✔
4405
                                  row2_weights[i], 48);
1,954,368✔
4406
        }
651,456✔
4407
    }
131,040✔
4408
}
396,900✔
4409

4410

4411
static void
4412
diffuse_jajuni_carry(int32_t *carry_curr, int32_t *carry_next,
×
4413
                     int32_t *carry_far, int width, int height,
4414
                     int depth, int x, int y, int32_t error,
4415
                     int direction, int channel)
4416
{
4417
    /* Jarvis, Judice & Ninke Method
4418
     *                  curr    7/48    5/48
4419
     *  3/48    5/48    7/48    5/48    3/48
4420
     *  1/48    3/48    5/48    3/48    1/48
4421
     */
4422
    static const int row0_offsets[] = { 1, 2 };
4423
    static const int row0_weights[] = { 7, 5 };
4424
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4425
    static const int row1_weights[] = { 3, 5, 7, 5, 3 };
4426
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
4427
    static const int row2_weights[] = { 1, 3, 5, 3, 1 };
4428
    int sign;
4429
    int i;
4430

4431
    if (error == 0) {
×
4432
        return;
×
4433
    }
4434

4435
    sign = direction >= 0 ? 1 : -1;
×
4436
    for (i = 0; i < 2; ++i) {
×
4437
        int neighbor;
4438
        int32_t term;
4439

4440
        neighbor = x + sign * row0_offsets[i];
×
4441
        if (neighbor < 0 || neighbor >= width) {
×
4442
            continue;
×
4443
        }
4444
        term = diffuse_fixed_term(error, row0_weights[i], 48);
×
4445
        carry_curr[((size_t)neighbor * (size_t)depth)
×
4446
                   + (size_t)channel] += term;
×
4447
    }
4448
    if (y + 1 < height) {
×
4449
        for (i = 0; i < 5; ++i) {
×
4450
            int neighbor;
4451
            int32_t term;
4452

4453
            neighbor = x + sign * row1_offsets[i];
×
4454
            if (neighbor < 0 || neighbor >= width) {
×
4455
                continue;
×
4456
            }
4457
            term = diffuse_fixed_term(error, row1_weights[i], 48);
×
4458
            carry_next[((size_t)neighbor * (size_t)depth)
×
4459
                       + (size_t)channel] += term;
×
4460
        }
4461
    }
4462
    if (y + 2 < height) {
×
4463
        for (i = 0; i < 5; ++i) {
×
4464
            int neighbor;
4465
            int32_t term;
4466

4467
            neighbor = x + sign * row2_offsets[i];
×
4468
            if (neighbor < 0 || neighbor >= width) {
×
4469
                continue;
×
4470
            }
4471
            term = diffuse_fixed_term(error, row2_weights[i], 48);
×
4472
            carry_far[((size_t)neighbor * (size_t)depth)
×
4473
                      + (size_t)channel] += term;
×
4474
        }
4475
    }
4476
}
4477

4478

4479
static void
4480
diffuse_stucki(unsigned char *data, int width, int height,
119,700✔
4481
               int x, int y, int depth, int error, int direction)
4482
{
4483
    /* Stucki's Method
4484
     *                  curr    8/48    4/48
4485
     *  2/48    4/48    8/48    4/48    2/48
4486
     *  1/48    2/48    4/48    2/48    1/48
4487
     */
4488
    int pos;
4489
    int sign;
4490
    static const int row0_offsets[] = { 1, 2 };
4491
    static const int row0_num[] = { 1, 1 };
4492
    static const int row0_den[] = { 6, 12 };
4493
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4494
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
4495
    static const int row1_den[] = { 24, 12, 6, 12, 24 };
4496
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
4497
    static const int row2_num[] = { 1, 1, 1, 1, 1 };
4498
    static const int row2_den[] = { 48, 24, 12, 24, 48 };
4499
    int i;
4500

4501
    pos = y * width + x;
119,700✔
4502
    sign = direction >= 0 ? 1 : -1;
119,700!
4503

4504
    for (i = 0; i < 2; ++i) {
359,100✔
4505
        int neighbor;
4506

4507
        neighbor = x + sign * row0_offsets[i];
239,400✔
4508
        if (neighbor < 0 || neighbor >= width) {
239,400!
4509
            continue;
2,700✔
4510
        }
4511
        error_diffuse_precise(data,
315,600✔
4512
                              pos + (neighbor - x),
236,700✔
4513
                              depth, error,
78,900✔
4514
                              row0_num[i], row0_den[i]);
236,700✔
4515
    }
78,900✔
4516
    if (y < height - 1) {
119,700✔
4517
        int row;
4518

4519
        row = pos + width;
118,503✔
4520
        for (i = 0; i < 5; ++i) {
711,018✔
4521
            int neighbor;
4522

4523
            neighbor = x + sign * row1_offsets[i];
592,515✔
4524
            if (neighbor < 0 || neighbor >= width) {
592,515✔
4525
                continue;
5,346✔
4526
            }
4527
            error_diffuse_precise(data,
782,892✔
4528
                                  row + (neighbor - x),
587,169✔
4529
                                  depth, error,
195,723✔
4530
                                  row1_num[i], row1_den[i]);
587,169✔
4531
        }
195,723✔
4532
    }
39,501✔
4533
    if (y < height - 2) {
119,700✔
4534
        int row;
4535

4536
        row = pos + width * 2;
117,306✔
4537
        for (i = 0; i < 5; ++i) {
703,836✔
4538
            int neighbor;
4539

4540
            neighbor = x + sign * row2_offsets[i];
586,530✔
4541
            if (neighbor < 0 || neighbor >= width) {
586,530✔
4542
                continue;
5,292✔
4543
            }
4544
            error_diffuse_precise(data,
774,984✔
4545
                                  row + (neighbor - x),
581,238✔
4546
                                  depth, error,
193,746✔
4547
                                  row2_num[i], row2_den[i]);
581,238✔
4548
        }
193,746✔
4549
    }
39,102✔
4550
}
119,700✔
4551

4552

4553
static void
4554
diffuse_stucki_carry(int32_t *carry_curr, int32_t *carry_next,
×
4555
                     int32_t *carry_far, int width, int height,
4556
                     int depth, int x, int y, int32_t error,
4557
                     int direction, int channel)
4558
{
4559
    /* Stucki's Method
4560
     *                  curr    8/48    4/48
4561
     *  2/48    4/48    8/48    4/48    2/48
4562
     *  1/48    2/48    4/48    2/48    1/48
4563
     */
4564
    static const int row0_offsets[] = { 1, 2 };
4565
    static const int row0_num[] = { 1, 1 };
4566
    static const int row0_den[] = { 6, 12 };
4567
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4568
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
4569
    static const int row1_den[] = { 24, 12, 6, 12, 24 };
4570
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
4571
    static const int row2_num[] = { 1, 1, 1, 1, 1 };
4572
    static const int row2_den[] = { 48, 24, 12, 24, 48 };
4573
    int sign;
4574
    int i;
4575

4576
    if (error == 0) {
×
4577
        return;
×
4578
    }
4579

4580
    sign = direction >= 0 ? 1 : -1;
×
4581
    for (i = 0; i < 2; ++i) {
×
4582
        int neighbor;
4583
        int32_t term;
4584

4585
        neighbor = x + sign * row0_offsets[i];
×
4586
        if (neighbor < 0 || neighbor >= width) {
×
4587
            continue;
×
4588
        }
4589
        term = diffuse_fixed_term(error, row0_num[i], row0_den[i]);
×
4590
        carry_curr[((size_t)neighbor * (size_t)depth)
×
4591
                   + (size_t)channel] += term;
×
4592
    }
4593
    if (y + 1 < height) {
×
4594
        for (i = 0; i < 5; ++i) {
×
4595
            int neighbor;
4596
            int32_t term;
4597

4598
            neighbor = x + sign * row1_offsets[i];
×
4599
            if (neighbor < 0 || neighbor >= width) {
×
4600
                continue;
×
4601
            }
4602
            term = diffuse_fixed_term(error, row1_num[i], row1_den[i]);
×
4603
            carry_next[((size_t)neighbor * (size_t)depth)
×
4604
                       + (size_t)channel] += term;
×
4605
        }
4606
    }
4607
    if (y + 2 < height) {
×
4608
        for (i = 0; i < 5; ++i) {
×
4609
            int neighbor;
4610
            int32_t term;
4611

4612
            neighbor = x + sign * row2_offsets[i];
×
4613
            if (neighbor < 0 || neighbor >= width) {
×
4614
                continue;
×
4615
            }
4616
            term = diffuse_fixed_term(error, row2_num[i], row2_den[i]);
×
4617
            carry_far[((size_t)neighbor * (size_t)depth)
×
4618
                      + (size_t)channel] += term;
×
4619
        }
4620
    }
4621
}
4622

4623

4624
static void
4625
diffuse_burkes(unsigned char *data, int width, int height,
67,500✔
4626
               int x, int y, int depth, int error, int direction)
4627
{
4628
    /* Burkes' Method
4629
     *                  curr    4/16    2/16
4630
     *  1/16    2/16    4/16    2/16    1/16
4631
     */
4632
    int pos;
4633
    int sign;
4634
    static const int row0_offsets[] = { 1, 2 };
4635
    static const int row0_num[] = { 1, 1 };
4636
    static const int row0_den[] = { 4, 8 };
4637
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4638
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
4639
    static const int row1_den[] = { 16, 8, 4, 8, 16 };
4640
    int i;
4641

4642
    pos = y * width + x;
67,500✔
4643
    sign = direction >= 0 ? 1 : -1;
67,500!
4644

4645
    for (i = 0; i < 2; ++i) {
202,500✔
4646
        int neighbor;
4647

4648
        neighbor = x + sign * row0_offsets[i];
135,000✔
4649
        if (neighbor < 0 || neighbor >= width) {
135,000!
4650
            continue;
2,025✔
4651
        }
4652
        error_diffuse_normal(data,
177,300✔
4653
                             pos + (neighbor - x),
132,975✔
4654
                             depth, error,
44,325✔
4655
                             row0_num[i], row0_den[i]);
132,975✔
4656
    }
44,325✔
4657
    if (y < height - 1) {
67,500✔
4658
        int row;
4659

4660
        row = pos + width;
66,600✔
4661
        for (i = 0; i < 5; ++i) {
399,600✔
4662
            int neighbor;
4663

4664
            neighbor = x + sign * row1_offsets[i];
333,000✔
4665
            if (neighbor < 0 || neighbor >= width) {
333,000✔
4666
                continue;
3,996✔
4667
            }
4668
            error_diffuse_normal(data,
438,672✔
4669
                                 row + (neighbor - x),
329,004✔
4670
                                 depth, error,
109,668✔
4671
                                 row1_num[i], row1_den[i]);
329,004✔
4672
        }
109,668✔
4673
    }
22,200✔
4674
}
67,500✔
4675

4676
static void
4677
diffuse_burkes_carry(int32_t *carry_curr, int32_t *carry_next,
×
4678
                     int32_t *carry_far, int width, int height,
4679
                     int depth, int x, int y, int32_t error,
4680
                     int direction, int channel)
4681
{
4682
    /* Burkes' Method
4683
     *                  curr    4/16    2/16
4684
     *  1/16    2/16    4/16    2/16    1/16
4685
     */
4686
    static const int row0_offsets[] = { 1, 2 };
4687
    static const int row0_num[] = { 1, 1 };
4688
    static const int row0_den[] = { 4, 8 };
4689
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4690
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
4691
    static const int row1_den[] = { 16, 8, 4, 8, 16 };
4692
    int sign;
4693
    int i;
4694

4695
    /* unused */ (void) carry_far;
4696

4697
    if (error == 0) {
×
4698
        return;
×
4699
    }
4700

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

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

4719
            neighbor = x + sign * row1_offsets[i];
×
4720
            if (neighbor < 0 || neighbor >= width) {
×
4721
                continue;
×
4722
            }
4723
            term = diffuse_fixed_term(error, row1_num[i], row1_den[i]);
×
4724
            carry_next[((size_t)neighbor * (size_t)depth)
×
4725
                       + (size_t)channel] += term;
×
4726
        }
4727
    }
4728
}
4729

4730
static void
NEW
4731
diffuse_sierra1(unsigned char *data, int width, int height,
×
4732
                int x, int y, int depth, int error, int direction)
4733
{
4734
    /* Sierra Lite Method
4735
     *          curr    2/4
4736
     *  1/4     1/4
4737
     */
4738
    static const int row0_offsets[] = { 1 };
4739
    static const int row0_num[] = { 1 };
4740
    static const int row0_den[] = { 2 };
4741
    static const int row1_offsets[] = { -1, 0 };
4742
    static const int row1_num[] = { 1, 1 };
4743
    static const int row1_den[] = { 4, 4 };
4744
    int pos;
4745
    int sign;
4746
    int i;
4747
    int neighbor;
4748
    int row;
4749

NEW
4750
    pos = y * width + x;
×
NEW
4751
    sign = direction >= 0 ? 1 : -1;
×
4752

NEW
4753
    for (i = 0; i < 1; ++i) {
×
NEW
4754
        neighbor = x + sign * row0_offsets[i];
×
NEW
4755
        if (neighbor < 0 || neighbor >= width) {
×
NEW
4756
            continue;
×
4757
        }
NEW
4758
        error_diffuse_normal(data,
×
NEW
4759
                             pos + (neighbor - x),
×
4760
                             depth, error,
NEW
4761
                             row0_num[i], row0_den[i]);
×
4762
    }
NEW
4763
    if (y < height - 1) {
×
NEW
4764
        row = pos + width;
×
NEW
4765
        for (i = 0; i < 2; ++i) {
×
NEW
4766
            neighbor = x + sign * row1_offsets[i];
×
NEW
4767
            if (neighbor < 0 || neighbor >= width) {
×
NEW
4768
                continue;
×
4769
            }
NEW
4770
            error_diffuse_normal(data,
×
NEW
4771
                                 row + (neighbor - x),
×
4772
                                 depth, error,
NEW
4773
                                 row1_num[i], row1_den[i]);
×
4774
        }
4775
    }
NEW
4776
}
×
4777

4778

4779
static void
NEW
4780
diffuse_sierra1_carry(int32_t *carry_curr, int32_t *carry_next,
×
4781
                      int32_t *carry_far, int width, int height,
4782
                      int depth, int x, int y, int32_t error,
4783
                      int direction, int channel)
4784
{
4785
    /* Sierra Lite Method
4786
     *          curr    2/4
4787
     *  1/4     1/4
4788
     */
4789
    static const int row0_offsets[] = { 1 };
4790
    static const int row0_num[] = { 1 };
4791
    static const int row0_den[] = { 2 };
4792
    static const int row1_offsets[] = { -1, 0 };
4793
    static const int row1_num[] = { 1, 1 };
4794
    static const int row1_den[] = { 4, 4 };
4795
    int sign;
4796
    int i;
4797
    int neighbor;
4798
    int32_t term;
4799

4800
    /* unused */ (void) carry_far;
4801

NEW
4802
    if (error == 0) {
×
NEW
4803
        return;
×
4804
    }
4805

NEW
4806
    sign = direction >= 0 ? 1 : -1;
×
NEW
4807
    for (i = 0; i < 1; ++i) {
×
NEW
4808
        neighbor = x + sign * row0_offsets[i];
×
NEW
4809
        if (neighbor < 0 || neighbor >= width) {
×
NEW
4810
            continue;
×
4811
        }
NEW
4812
        term = diffuse_fixed_term(error, row0_num[i], row0_den[i]);
×
NEW
4813
        carry_curr[((size_t)neighbor * (size_t)depth)
×
NEW
4814
                   + (size_t)channel] += term;
×
4815
    }
NEW
4816
    if (y + 1 < height) {
×
NEW
4817
        for (i = 0; i < 2; ++i) {
×
NEW
4818
            neighbor = x + sign * row1_offsets[i];
×
NEW
4819
            if (neighbor < 0 || neighbor >= width) {
×
NEW
4820
                continue;
×
4821
            }
NEW
4822
            term = diffuse_fixed_term(error, row1_num[i], row1_den[i]);
×
NEW
4823
            carry_next[((size_t)neighbor * (size_t)depth)
×
NEW
4824
                       + (size_t)channel] += term;
×
4825
        }
4826
    }
4827
}
4828

4829

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

NEW
4854
    pos = y * width + x;
×
NEW
4855
    sign = direction >= 0 ? 1 : -1;
×
4856

NEW
4857
    for (i = 0; i < 2; ++i) {
×
NEW
4858
        neighbor = x + sign * row0_offsets[i];
×
NEW
4859
        if (neighbor < 0 || neighbor >= width) {
×
NEW
4860
            continue;
×
4861
        }
NEW
4862
        error_diffuse_precise(data,
×
NEW
4863
                              pos + (neighbor - x),
×
4864
                              depth, error,
NEW
4865
                              row0_num[i], row0_den[i]);
×
4866
    }
NEW
4867
    if (y < height - 1) {
×
NEW
4868
        row = pos + width;
×
NEW
4869
        for (i = 0; i < 5; ++i) {
×
NEW
4870
            neighbor = x + sign * row1_offsets[i];
×
NEW
4871
            if (neighbor < 0 || neighbor >= width) {
×
NEW
4872
                continue;
×
4873
            }
NEW
4874
            error_diffuse_precise(data,
×
NEW
4875
                                  row + (neighbor - x),
×
4876
                                  depth, error,
NEW
4877
                                  row1_num[i], row1_den[i]);
×
4878
        }
4879
    }
NEW
4880
    if (y < height - 2) {
×
NEW
4881
        row = pos + width * 2;
×
NEW
4882
        for (i = 0; i < 3; ++i) {
×
NEW
4883
            neighbor = x + sign * row2_offsets[i];
×
NEW
4884
            if (neighbor < 0 || neighbor >= width) {
×
NEW
4885
                continue;
×
4886
            }
NEW
4887
            error_diffuse_precise(data,
×
NEW
4888
                                  row + (neighbor - x),
×
4889
                                  depth, error,
NEW
4890
                                  row2_num[i], row2_den[i]);
×
4891
        }
4892
    }
NEW
4893
}
×
4894

4895

4896
static void
NEW
4897
diffuse_sierra2_carry(int32_t *carry_curr, int32_t *carry_next,
×
4898
                      int32_t *carry_far, int width, int height,
4899
                      int depth, int x, int y, int32_t error,
4900
                      int direction, int channel)
4901
{
4902
    /* Sierra Two-row Method
4903
     *                  curr    4/32    3/32
4904
     *  1/32    2/32    3/32    2/32    1/32
4905
     *                  2/32    3/32    2/32
4906
     */
4907
    static const int row0_offsets[] = { 1, 2 };
4908
    static const int row0_num[] = { 4, 3 };
4909
    static const int row0_den[] = { 32, 32 };
4910
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4911
    static const int row1_num[] = { 1, 2, 3, 2, 1 };
4912
    static const int row1_den[] = { 32, 32, 32, 32, 32 };
4913
    static const int row2_offsets[] = { -1, 0, 1 };
4914
    static const int row2_num[] = { 2, 3, 2 };
4915
    static const int row2_den[] = { 32, 32, 32 };
4916
    int sign;
4917
    int i;
4918
    int neighbor;
4919
    int32_t term;
4920

NEW
4921
    if (error == 0) {
×
NEW
4922
        return;
×
4923
    }
4924

NEW
4925
    sign = direction >= 0 ? 1 : -1;
×
NEW
4926
    for (i = 0; i < 2; ++i) {
×
NEW
4927
        neighbor = x + sign * row0_offsets[i];
×
NEW
4928
        if (neighbor < 0 || neighbor >= width) {
×
NEW
4929
            continue;
×
4930
        }
NEW
4931
        term = diffuse_fixed_term(error, row0_num[i], row0_den[i]);
×
NEW
4932
        carry_curr[((size_t)neighbor * (size_t)depth)
×
NEW
4933
                   + (size_t)channel] += term;
×
4934
    }
NEW
4935
    if (y + 1 < height) {
×
NEW
4936
        for (i = 0; i < 5; ++i) {
×
NEW
4937
            neighbor = x + sign * row1_offsets[i];
×
NEW
4938
            if (neighbor < 0 || neighbor >= width) {
×
NEW
4939
                continue;
×
4940
            }
NEW
4941
            term = diffuse_fixed_term(error, row1_num[i], row1_den[i]);
×
NEW
4942
            carry_next[((size_t)neighbor * (size_t)depth)
×
NEW
4943
                       + (size_t)channel] += term;
×
4944
        }
4945
    }
NEW
4946
    if (y + 2 < height) {
×
NEW
4947
        for (i = 0; i < 3; ++i) {
×
NEW
4948
            neighbor = x + sign * row2_offsets[i];
×
NEW
4949
            if (neighbor < 0 || neighbor >= width) {
×
NEW
4950
                continue;
×
4951
            }
NEW
4952
            term = diffuse_fixed_term(error, row2_num[i], row2_den[i]);
×
NEW
4953
            carry_far[((size_t)neighbor * (size_t)depth)
×
NEW
4954
                      + (size_t)channel] += term;
×
4955
        }
4956
    }
4957
}
4958

4959

4960
static void
NEW
4961
diffuse_sierra3(unsigned char *data, int width, int height,
×
4962
                int x, int y, int depth, int error, int direction)
4963
{
4964
    /* Sierra-3 Method
4965
     *                  curr    5/32    3/32
4966
     *  2/32    4/32    5/32    4/32    2/32
4967
     *                  2/32    3/32    2/32
4968
     */
4969
    static const int row0_offsets[] = { 1, 2 };
4970
    static const int row0_num[] = { 5, 3 };
4971
    static const int row0_den[] = { 32, 32 };
4972
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
4973
    static const int row1_num[] = { 2, 4, 5, 4, 2 };
4974
    static const int row1_den[] = { 32, 32, 32, 32, 32 };
4975
    static const int row2_offsets[] = { -1, 0, 1 };
4976
    static const int row2_num[] = { 2, 3, 2 };
4977
    static const int row2_den[] = { 32, 32, 32 };
4978
    int pos;
4979
    int sign;
4980
    int i;
4981
    int neighbor;
4982
    int row;
4983

NEW
4984
    pos = y * width + x;
×
NEW
4985
    sign = direction >= 0 ? 1 : -1;
×
4986

NEW
4987
    for (i = 0; i < 2; ++i) {
×
NEW
4988
        neighbor = x + sign * row0_offsets[i];
×
NEW
4989
        if (neighbor < 0 || neighbor >= width) {
×
NEW
4990
            continue;
×
4991
        }
NEW
4992
        error_diffuse_precise(data,
×
NEW
4993
                              pos + (neighbor - x),
×
4994
                              depth, error,
NEW
4995
                              row0_num[i], row0_den[i]);
×
4996
    }
NEW
4997
    if (y < height - 1) {
×
NEW
4998
        row = pos + width;
×
NEW
4999
        for (i = 0; i < 5; ++i) {
×
NEW
5000
            neighbor = x + sign * row1_offsets[i];
×
NEW
5001
            if (neighbor < 0 || neighbor >= width) {
×
NEW
5002
                continue;
×
5003
            }
NEW
5004
            error_diffuse_precise(data,
×
NEW
5005
                                  row + (neighbor - x),
×
5006
                                  depth, error,
NEW
5007
                                  row1_num[i], row1_den[i]);
×
5008
        }
5009
    }
NEW
5010
    if (y < height - 2) {
×
NEW
5011
        row = pos + width * 2;
×
NEW
5012
        for (i = 0; i < 3; ++i) {
×
NEW
5013
            neighbor = x + sign * row2_offsets[i];
×
NEW
5014
            if (neighbor < 0 || neighbor >= width) {
×
NEW
5015
                continue;
×
5016
            }
NEW
5017
            error_diffuse_precise(data,
×
NEW
5018
                                  row + (neighbor - x),
×
5019
                                  depth, error,
NEW
5020
                                  row2_num[i], row2_den[i]);
×
5021
        }
5022
    }
NEW
5023
}
×
5024

5025

5026
static void
NEW
5027
diffuse_sierra3_carry(int32_t *carry_curr, int32_t *carry_next,
×
5028
                      int32_t *carry_far, int width, int height,
5029
                      int depth, int x, int y, int32_t error,
5030
                      int direction, int channel)
5031
{
5032
    /* Sierra-3 Method
5033
     *                  curr    5/32    3/32
5034
     *  2/32    4/32    5/32    4/32    2/32
5035
     *                  2/32    3/32    2/32
5036
     */
5037
    static const int row0_offsets[] = { 1, 2 };
5038
    static const int row0_num[] = { 5, 3 };
5039
    static const int row0_den[] = { 32, 32 };
5040
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
5041
    static const int row1_num[] = { 2, 4, 5, 4, 2 };
5042
    static const int row1_den[] = { 32, 32, 32, 32, 32 };
5043
    static const int row2_offsets[] = { -1, 0, 1 };
5044
    static const int row2_num[] = { 2, 3, 2 };
5045
    static const int row2_den[] = { 32, 32, 32 };
5046
    int sign;
5047
    int i;
5048
    int neighbor;
5049
    int32_t term;
5050

NEW
5051
    if (error == 0) {
×
NEW
5052
        return;
×
5053
    }
5054

NEW
5055
    sign = direction >= 0 ? 1 : -1;
×
NEW
5056
    for (i = 0; i < 2; ++i) {
×
NEW
5057
        neighbor = x + sign * row0_offsets[i];
×
NEW
5058
        if (neighbor < 0 || neighbor >= width) {
×
NEW
5059
            continue;
×
5060
        }
NEW
5061
        term = diffuse_fixed_term(error, row0_num[i], row0_den[i]);
×
NEW
5062
        carry_curr[((size_t)neighbor * (size_t)depth)
×
NEW
5063
                   + (size_t)channel] += term;
×
5064
    }
NEW
5065
    if (y + 1 < height) {
×
NEW
5066
        for (i = 0; i < 5; ++i) {
×
NEW
5067
            neighbor = x + sign * row1_offsets[i];
×
NEW
5068
            if (neighbor < 0 || neighbor >= width) {
×
NEW
5069
                continue;
×
5070
            }
NEW
5071
            term = diffuse_fixed_term(error, row1_num[i], row1_den[i]);
×
NEW
5072
            carry_next[((size_t)neighbor * (size_t)depth)
×
NEW
5073
                       + (size_t)channel] += term;
×
5074
        }
5075
    }
NEW
5076
    if (y + 2 < height) {
×
NEW
5077
        for (i = 0; i < 3; ++i) {
×
NEW
5078
            neighbor = x + sign * row2_offsets[i];
×
NEW
5079
            if (neighbor < 0 || neighbor >= width) {
×
NEW
5080
                continue;
×
5081
            }
NEW
5082
            term = diffuse_fixed_term(error, row2_num[i], row2_den[i]);
×
NEW
5083
            carry_far[((size_t)neighbor * (size_t)depth)
×
NEW
5084
                      + (size_t)channel] += term;
×
5085
        }
5086
    }
5087
}
5088

5089

5090
static void
5091
diffuse_lso1(unsigned char *data, int width, int height,
×
5092
             int x, int y, int depth, int error, int direction)
5093
{
5094
    int pos;
5095
    int sign;
5096

5097
    pos = y * width + x;
×
5098
    sign = direction >= 0 ? 1 : -1;
×
5099

5100
    /* lso1 (libsixel original) method:
5101
     *
5102
     * libsixel-specific error diffusion (dithering) to improve sixel
5103
     * compression; by steering error propagation so out-of-palette
5104
     * intermediate colors render as horizontal bands rather than grainy
5105
     * noise, we increase RLE more effective.
5106
     *
5107
     *          curr
5108
     *   1/8    4/8    1/8
5109
     *          2/8
5110
     */
5111
    if (y < height - 1) {
×
5112
        int row;
5113

5114
        row = pos + width;
×
5115
        if (x - sign >= 0 && x - sign < width) {
×
5116
            error_diffuse_fast(data,
×
5117
                               row + (-sign),
5118
                               depth, error, 1, 8);
5119
        }
5120
        error_diffuse_fast(data, row, depth, error, 4, 8);
×
5121
        if (x + sign >= 0 && x + sign < width) {
×
5122
            error_diffuse_fast(data,
×
5123
                               row + sign,
5124
                               depth, error, 1, 8);
5125
        }
5126
    }
5127
    if (y < height - 2) {
×
5128
        error_diffuse_fast(data, pos + width * 2, depth, error, 2, 8);
×
5129
    }
5130
}
×
5131

5132

5133
static void
5134
diffuse_lso1_carry(int32_t *carry_curr, int32_t *carry_next,
×
5135
                   int32_t *carry_far, int width, int height,
5136
                   int depth, int x, int y, int32_t error,
5137
                   int direction, int channel)
5138
{
5139
    int sign;
5140
    int32_t edge_term;
5141
    int32_t center_term;
5142
    int32_t far_term;
5143

5144
    /* unused */ (void) carry_curr;
5145
    if (error == 0) {
×
5146
        return;
×
5147
    }
5148

5149
    sign = direction >= 0 ? 1 : -1;
×
5150
    edge_term = diffuse_fixed_term(error, 1, 8);
×
5151
    center_term = diffuse_fixed_term(error, 4, 8);
×
5152
    far_term = diffuse_fixed_term(error, 2, 8);
×
5153

5154
    if (y + 1 < height) {
×
5155
        if (x - sign >= 0 && x - sign < width) {
×
5156
            size_t base;
5157

5158
            base = ((size_t)(x - sign) * (size_t)depth)
×
5159
                 + (size_t)channel;
×
5160
            carry_next[base] += edge_term;
×
5161
        }
5162
        {
5163
            size_t base;
5164

5165
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
5166
            carry_next[base] += center_term;
×
5167
        }
5168
        if (x + sign >= 0 && x + sign < width) {
×
5169
            size_t base;
5170

5171
            base = ((size_t)(x + sign) * (size_t)depth)
×
5172
                 + (size_t)channel;
×
5173
            carry_next[base] += edge_term;
×
5174
        }
5175
    }
5176
    if (y + 2 < height) {
×
5177
        size_t base;
5178

5179
        base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
5180
        carry_far[base] += far_term;
×
5181
    }
5182
}
5183

5184
static float
5185
mask_a (int x, int y, int c)
×
5186
{
5187
    return ((((x + c * 67) + y * 236) * 119) & 255 ) / 128.0 - 1.0;
×
5188
}
5189

5190
static float
5191
mask_x (int x, int y, int c)
×
5192
{
5193
    return ((((x + c * 29) ^ y* 149) * 1234) & 511 ) / 256.0 - 1.0;
×
5194
}
5195

5196
/* lookup closest color from palette with "normal" strategy */
5197
static int
5198
lookup_normal(unsigned char const * const pixel,
849,900✔
5199
              int const depth,
5200
              unsigned char const * const palette,
5201
              int const reqcolor,
5202
              unsigned short * const cachetable,
5203
              int const complexion)
5204
{
5205
    int result;
5206
    int diff;
5207
    int r;
5208
    int i;
5209
    int n;
5210
    int distant;
5211

5212
    result = (-1);
849,900✔
5213
    diff = INT_MAX;
849,900✔
5214

5215
    /* don't use cachetable in 'normal' strategy */
5216
    (void) cachetable;
283,300✔
5217

5218
    for (i = 0; i < reqcolor; i++) {
15,309,900✔
5219
        distant = 0;
14,460,000✔
5220
        r = pixel[0] - palette[i * depth + 0];
14,460,000✔
5221
        distant += r * r * complexion;
14,460,000✔
5222
        for (n = 1; n < depth; ++n) {
43,380,000✔
5223
            r = pixel[n] - palette[i * depth + n];
28,920,000✔
5224
            distant += r * r;
28,920,000✔
5225
        }
9,640,000✔
5226
        if (distant < diff) {
14,460,000✔
5227
            diff = distant;
2,211,216✔
5228
            result = i;
2,211,216✔
5229
        }
736,152✔
5230
    }
4,820,000✔
5231

5232
    return result;
849,900✔
5233
}
5234

5235

5236
/*
5237
 * Shared fast lookup flow:
5238
 *
5239
 *   pixel --> quantize --> cuckoo cache --> palette index
5240
 */
5241
static int
5242
lookup_fast_common(unsigned char const *pixel,
16,545,286✔
5243
                   unsigned char const *palette,
5244
                   int reqcolor,
5245
                   unsigned short *cachetable,
5246
                   int complexion,
5247
                   struct histogram_control control)
5248
{
5249
    int result;
5250
    unsigned int hash;
5251
    int diff;
5252
    int i;
5253
    int distant;
5254
    struct cuckoo_table32 *table;
5255
    uint32_t *slot;
5256
    SIXELSTATUS status;
5257

5258
    result = (-1);
16,545,286✔
5259
    diff = INT_MAX;
16,545,286✔
5260
    hash = computeHash(pixel, 3U, &control);
16,545,286✔
5261

5262
    table = (struct cuckoo_table32 *)cachetable;
16,545,286✔
5263
    if (table != NULL) {
16,545,286!
5264
        slot = cuckoo_table32_lookup(table, hash);
16,545,286✔
5265
        if (slot != NULL && *slot != 0U) {
16,545,286!
5266
            return (int)(*slot - 1U);
15,547,401✔
5267
        }
5268
    }
325,595✔
5269

5270
    for (i = 0; i < reqcolor; i++) {
124,910,445✔
5271
        distant = (pixel[0] - palette[i * 3 + 0])
164,819,522✔
5272
                * (pixel[0] - palette[i * 3 + 0]) * complexion
123,912,560✔
5273
                + (pixel[1] - palette[i * 3 + 1])
164,819,522✔
5274
                * (pixel[1] - palette[i * 3 + 1])
123,912,560✔
5275
                + (pixel[2] - palette[i * 3 + 2])
164,819,522✔
5276
                * (pixel[2] - palette[i * 3 + 2]);
123,912,560✔
5277
        if (distant < diff) {
123,912,560✔
5278
            diff = distant;
6,236,045✔
5279
            result = i;
6,236,045✔
5280
        }
2,012,527✔
5281
    }
40,906,962✔
5282

5283
    if (table != NULL && result >= 0) {
997,885!
5284
        status = cuckoo_table32_insert(table,
1,323,480✔
5285
                                       hash,
325,595✔
5286
                                       (uint32_t)(result + 1));
997,885✔
5287
        if (SIXEL_FAILED(status)) {
997,885!
5288
            /* ignore cache update failure */
5289
        }
5290
    }
325,595✔
5291

5292
    return result;
997,885✔
5293
}
8,319,482✔
5294

5295
/* lookup closest color from palette with "fast" strategy */
5296
static int
5297
lookup_fast(unsigned char const * const pixel,
16,545,286✔
5298
            int const depth,
5299
            unsigned char const * const palette,
5300
            int const reqcolor,
5301
            unsigned short * const cachetable,
5302
            int const complexion)
5303
{
5304
    struct histogram_control control;
5305

5306
    (void)depth;
8,319,482✔
5307

5308
    control = histogram_control_make(3U);
16,545,286✔
5309

5310
    return lookup_fast_common(pixel,
24,864,768✔
5311
                              palette,
8,319,482✔
5312
                              reqcolor,
8,319,482✔
5313
                              cachetable,
8,319,482✔
5314
                              complexion,
8,319,482✔
5315
                              control);
5316
}
5317

5318
static int
5319
lookup_fast_robinhood(unsigned char const * const pixel,
×
5320
                      int const depth,
5321
                      unsigned char const * const palette,
5322
                      int const reqcolor,
5323
                      unsigned short * const cachetable,
5324
                      int const complexion)
5325
{
5326
    struct histogram_control control;
5327

5328
    (void)depth;
5329

5330
    control = histogram_control_make_for_policy(3U,
×
5331
                                                SIXEL_LUT_POLICY_ROBINHOOD);
5332

5333
    return lookup_fast_common(pixel,
×
5334
                              palette,
5335
                              reqcolor,
5336
                              cachetable,
5337
                              complexion,
5338
                              control);
5339
}
5340

5341
static int
5342
lookup_fast_hopscotch(unsigned char const * const pixel,
×
5343
                      int const depth,
5344
                      unsigned char const * const palette,
5345
                      int const reqcolor,
5346
                      unsigned short * const cachetable,
5347
                      int const complexion)
5348
{
5349
    struct histogram_control control;
5350

5351
    (void)depth;
5352

5353
    control = histogram_control_make_for_policy(3U,
×
5354
                                                SIXEL_LUT_POLICY_HOPSCOTCH);
5355

5356
    return lookup_fast_common(pixel,
×
5357
                              palette,
5358
                              reqcolor,
5359
                              cachetable,
5360
                              complexion,
5361
                              control);
5362
}
5363

5364

5365
static int
5366
lookup_mono_darkbg(unsigned char const * const pixel,
1,730,520✔
5367
                   int const depth,
5368
                   unsigned char const * const palette,
5369
                   int const reqcolor,
5370
                   unsigned short * const cachetable,
5371
                   int const complexion)
5372
{
5373
    int n;
5374
    int distant;
5375

5376
    /* unused */ (void) palette;
576,840✔
5377
    /* unused */ (void) cachetable;
576,840✔
5378
    /* unused */ (void) complexion;
576,840✔
5379

5380
    distant = 0;
1,730,520✔
5381
    for (n = 0; n < depth; ++n) {
6,922,080✔
5382
        distant += pixel[n];
5,191,560✔
5383
    }
1,730,520✔
5384
    return distant >= 128 * reqcolor ? 1: 0;
1,730,520✔
5385
}
5386

5387

5388
static int
5389
lookup_mono_lightbg(unsigned char const * const pixel,
810,000✔
5390
                    int const depth,
5391
                    unsigned char const * const palette,
5392
                    int const reqcolor,
5393
                    unsigned short * const cachetable,
5394
                    int const complexion)
5395
{
5396
    int n;
5397
    int distant;
5398

5399
    /* unused */ (void) palette;
270,000✔
5400
    /* unused */ (void) cachetable;
270,000✔
5401
    /* unused */ (void) complexion;
270,000✔
5402

5403
    distant = 0;
810,000✔
5404
    for (n = 0; n < depth; ++n) {
3,240,000✔
5405
        distant += pixel[n];
2,430,000✔
5406
    }
810,000✔
5407
    return distant < 128 * reqcolor ? 1: 0;
810,000✔
5408
}
5409

5410

5411
/* choose colors using median-cut method */
5412
SIXELSTATUS
5413
sixel_quant_make_palette(
235✔
5414
    unsigned char          /* out */ **result,
5415
    unsigned char const    /* in */  *data,
5416
    unsigned int           /* in */  length,
5417
    int                    /* in */  pixelformat,
5418
    unsigned int           /* in */  reqcolors,
5419
    unsigned int           /* in */  *ncolors,
5420
    unsigned int           /* in */  *origcolors,
5421
    int                    /* in */  methodForLargest,
5422
    int                    /* in */  methodForRep,
5423
    int                    /* in */  qualityMode,
5424
    int                    /* in */  force_palette,
5425
    sixel_allocator_t      /* in */  *allocator)
5426
{
5427
    SIXELSTATUS status = SIXEL_FALSE;
235✔
5428
    unsigned int i;
5429
    unsigned int n;
5430
    int ret;
5431
    tupletable2 colormap;
5432
    unsigned int depth;
5433
    int result_depth;
5434

5435
    result_depth = sixel_helper_compute_depth(pixelformat);
235✔
5436
    if (result_depth <= 0) {
235!
5437
        *result = NULL;
×
5438
        goto end;
×
5439
    }
5440

5441
    depth = (unsigned int)result_depth;
235✔
5442

5443
    ret = computeColorMapFromInput(data, length, depth,
344✔
5444
                                   reqcolors, methodForLargest,
109✔
5445
                                   methodForRep, qualityMode,
109✔
5446
                                   force_palette, &colormap,
109✔
5447
                                   origcolors, allocator);
109✔
5448
    if (ret != 0) {
235!
5449
        *result = NULL;
×
5450
        goto end;
×
5451
    }
5452
    *ncolors = colormap.size;
235✔
5453
    quant_trace(stderr, "tupletable size: %d\n", *ncolors);
235✔
5454
    *result = (unsigned char *)sixel_allocator_malloc(allocator, *ncolors * depth);
235✔
5455
    for (i = 0; i < *ncolors; i++) {
10,621✔
5456
        for (n = 0; n < depth; ++n) {
41,544✔
5457
            (*result)[i * depth + n] = colormap.table[i]->tuple[n];
31,158✔
5458
        }
11,100✔
5459
    }
3,700✔
5460

5461
    sixel_allocator_free(allocator, colormap.table);
235✔
5462

5463
    status = SIXEL_OK;
235✔
5464

5465
end:
126✔
5466
    return status;
235✔
5467
}
5468

5469

5470
/* apply color palette into specified pixel buffers */
5471
SIXELSTATUS
5472
sixel_quant_apply_palette(
257✔
5473
    sixel_index_t     /* out */ *result,
5474
    unsigned char     /* in */  *data,
5475
    int               /* in */  width,
5476
    int               /* in */  height,
5477
    int               /* in */  depth,
5478
    unsigned char     /* in */  *palette,
5479
    int               /* in */  reqcolor,
5480
    int               /* in */  methodForDiffuse,
5481
    int               /* in */  methodForScan,
5482
    int               /* in */  methodForCarry,
5483
    int               /* in */  foptimize,
5484
    int               /* in */  foptimize_palette,
5485
    int               /* in */  complexion,
5486
    unsigned short    /* in */  *cachetable,
5487
    int               /* in */  *ncolors,
5488
    sixel_allocator_t /* in */  *allocator)
5489
{
152✔
5490
#if _MSC_VER
5491
    enum { max_depth = 4 };
5492
#else
5493
    const size_t max_depth = 4;
257✔
5494
#endif
5495
    unsigned char copy[max_depth];
152✔
5496
    SIXELSTATUS status = SIXEL_FALSE;
257✔
5497
    int sum1, sum2;
5498
    int n;
5499
    unsigned short *indextable;
5500
    size_t cache_size;
5501
    int allocated_cache;
5502
    int use_cache;
5503
    int cache_policy;
5504
    unsigned char new_palette[SIXEL_PALETTE_MAX * 4];
5505
    unsigned short migration_map[SIXEL_PALETTE_MAX];
5506
    int (*f_lookup)(unsigned char const * const pixel,
5507
                    int const depth,
5508
                    unsigned char const * const palette,
5509
                    int const reqcolor,
5510
                    unsigned short * const cachetable,
5511
                    int const complexion);
5512
    int use_varerr;
5513
    int use_positional;
5514
    int carry_mode;
5515

5516
    /* check bad reqcolor */
5517
    if (reqcolor < 1) {
257!
5518
        status = SIXEL_BAD_ARGUMENT;
×
5519
        sixel_helper_set_additional_message(
×
5520
            "sixel_quant_apply_palette: "
5521
            "a bad argument is detected, reqcolor < 0.");
5522
        goto end;
×
5523
    }
5524

5525
    /* NOTE: diffuse_jajuni, diffuse_stucki, and diffuse_burkes reference at
5526
     * minimum the position pos + width * 1 - 2, so width must be at least 2
5527
     * to avoid underflow.
5528
     * On the other hand, diffuse_fs and diffuse_atkinson
5529
     * reference pos + width * 1 - 1, but since these functions are only called
5530
     * when width >= 1, they do not cause underflow.
5531
     */
5532
    use_varerr = (depth == 3
362✔
5533
                  && (methodForDiffuse == SIXEL_DIFFUSE_LSO2
514!
5534
                      || methodForDiffuse == SIXEL_DIFFUSE_LSO3));
257!
5535
    use_positional = (methodForDiffuse == SIXEL_DIFFUSE_A_DITHER
362✔
5536
                      || methodForDiffuse == SIXEL_DIFFUSE_X_DITHER);
257!
5537
    carry_mode = (methodForCarry == SIXEL_CARRY_ENABLE)
257✔
5538
               ? SIXEL_CARRY_ENABLE
5539
               : SIXEL_CARRY_DISABLE;
152!
5540

5541
    f_lookup = NULL;
257✔
5542
    if (reqcolor == 2) {
257✔
5543
        sum1 = 0;
19✔
5544
        sum2 = 0;
19✔
5545
        for (n = 0; n < depth; ++n) {
76✔
5546
            sum1 += palette[n];
57✔
5547
        }
21✔
5548
        for (n = depth; n < depth + depth; ++n) {
76✔
5549
            sum2 += palette[n];
57✔
5550
        }
21✔
5551
        if (sum1 == 0 && sum2 == 255 * 3) {
19!
5552
            f_lookup = lookup_mono_darkbg;
12✔
5553
        } else if (sum1 == 255 * 3 && sum2 == 0) {
11!
5554
            f_lookup = lookup_mono_lightbg;
3✔
5555
        }
1✔
5556
    }
7✔
5557
    if (f_lookup == NULL) {
257✔
5558
        if (foptimize && depth == 3) {
242!
5559
            f_lookup = lookup_fast;
236✔
5560
        } else {
98✔
5561
            f_lookup = lookup_normal;
6✔
5562
        }
5563
    }
100✔
5564

5565
    if (f_lookup == lookup_fast) {
257✔
5566
        if (histogram_lut_policy == SIXEL_LUT_POLICY_ROBINHOOD) {
236!
5567
            f_lookup = lookup_fast_robinhood;
×
5568
        } else if (histogram_lut_policy == SIXEL_LUT_POLICY_HOPSCOTCH) {
236!
5569
            f_lookup = lookup_fast_hopscotch;
×
5570
        }
5571
    }
98✔
5572

5573
    indextable = cachetable;
257✔
5574
    allocated_cache = 0;
257✔
5575
    cache_policy = SIXEL_LUT_POLICY_AUTO;
257✔
5576
    use_cache = 0;
257✔
5577
    if (f_lookup == lookup_fast) {
257✔
5578
        cache_policy = histogram_lut_policy;
236✔
5579
        use_cache = 1;
236✔
5580
    } else if (f_lookup == lookup_fast_robinhood) {
119!
5581
        cache_policy = SIXEL_LUT_POLICY_ROBINHOOD;
×
5582
        use_cache = 1;
×
5583
    } else if (f_lookup == lookup_fast_hopscotch) {
21!
5584
        cache_policy = SIXEL_LUT_POLICY_HOPSCOTCH;
×
5585
        use_cache = 1;
×
5586
    }
5587
    if (cache_policy == SIXEL_LUT_POLICY_AUTO) {
257!
5588
        cache_policy = SIXEL_LUT_POLICY_6BIT;
257✔
5589
    }
105✔
5590
    if (use_cache) {
257✔
5591
        if (cachetable == NULL) {
236!
5592
            status = sixel_quant_cache_prepare(&indextable,
×
5593
                                               &cache_size,
5594
                                               cache_policy,
5595
                                               reqcolor,
5596
                                               allocator);
5597
            if (SIXEL_FAILED(status)) {
×
5598
                quant_trace(stderr,
×
5599
                            "Unable to allocate lookup cache.\n");
5600
                goto end;
×
5601
            }
5602
            allocated_cache = 1;
×
5603
        } else {
5604
            sixel_quant_cache_clear(indextable, cache_policy);
236✔
5605
        }
5606
    }
98✔
5607

5608
    if (use_positional) {
257!
5609
        status = apply_palette_positional(result, data, width, height,
×
5610
                                          depth, palette, reqcolor,
5611
                                          methodForDiffuse, methodForScan,
5612
                                          foptimize_palette, f_lookup,
5613
                                          indextable, complexion, copy,
5614
                                          new_palette, migration_map,
5615
                                          ncolors);
5616
    } else if (use_varerr) {
257!
5617
        status = apply_palette_variable(result, data, width, height,
×
5618
                                        depth, palette, reqcolor,
5619
                                        methodForScan, foptimize_palette,
5620
                                        f_lookup, indextable, complexion,
5621
                                        new_palette, migration_map,
5622
                                        ncolors,
5623
                                        methodForDiffuse,
5624
                                        carry_mode);
5625
    } else {
5626
        status = apply_palette_fixed(result, data, width, height,
362✔
5627
                                     depth, palette, reqcolor,
105✔
5628
                                     methodForScan, foptimize_palette,
105✔
5629
                                     f_lookup, indextable, complexion,
105✔
5630
                                     new_palette, migration_map,
105✔
5631
                                     ncolors, methodForDiffuse,
105✔
5632
                                     carry_mode);
105✔
5633
    }
5634
    if (SIXEL_FAILED(status)) {
257!
5635
        goto end;
×
5636
    }
5637

5638
    if (allocated_cache) {
257!
5639
        sixel_quant_cache_release(indextable,
×
5640
                                  cache_policy,
5641
                                  allocator);
5642
    }
5643

5644
    status = SIXEL_OK;
257✔
5645

5646
end:
152✔
5647
    return status;
257✔
5648
}
5649

5650

5651
void
5652
sixel_quant_free_palette(
235✔
5653
    unsigned char       /* in */ *data,
5654
    sixel_allocator_t   /* in */ *allocator)
5655
{
5656
    sixel_allocator_free(allocator, data);
235✔
5657
}
235✔
5658

5659

5660
#if HAVE_TESTS
5661
static int
5662
test1(void)
×
5663
{
5664
    int nret = EXIT_FAILURE;
×
5665
    sample minval[1] = { 1 };
×
5666
    sample maxval[1] = { 2 };
×
5667
    unsigned int retval;
5668

5669
    retval = largestByLuminosity(minval, maxval, 1);
×
5670
    if (retval != 0) {
×
5671
        goto error;
×
5672
    }
5673
    nret = EXIT_SUCCESS;
×
5674

5675
error:
5676
    return nret;
×
5677
}
5678

5679

5680
int
5681
sixel_quant_tests_main(void)
×
5682
{
5683
    int nret = EXIT_FAILURE;
×
5684
    size_t i;
5685
    typedef int (* testcase)(void);
5686

5687
    static testcase const testcases[] = {
5688
        test1,
5689
    };
5690

5691
    for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) {
×
5692
        nret = testcases[i]();
×
5693
        if (nret != EXIT_SUCCESS) {
×
5694
            goto error;
×
5695
        }
5696
    }
5697

5698
    nret = EXIT_SUCCESS;
×
5699

5700
error:
5701
    return nret;
×
5702
}
5703
#endif  /* HAVE_TESTS */
5704

5705
/* emacs Local Variables:      */
5706
/* emacs mode: c               */
5707
/* emacs tab-width: 4          */
5708
/* emacs indent-tabs-mode: nil */
5709
/* emacs c-basic-offset: 4     */
5710
/* emacs End:                  */
5711
/* vim: set expandtab ts=4 sts=4 sw=4 : */
5712
/* EOF */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc