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

saitoha / libsixel / 18629971945

19 Oct 2025 09:03AM UTC coverage: 54.584% (-0.3%) from 54.844%
18629971945

push

github

saitoha
quant: update lso2/lso3 parameter table

5214 of 14213 branches covered (36.68%)

0 of 3 new or added lines in 1 file covered. (0.0%)

43 existing lines in 4 files now uncovered.

7163 of 13123 relevant lines covered (54.58%)

1136714.05 hits per line

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

45.67
/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
#include "quant.h"
72

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

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

148
static const int (*
149
lso2_table(void))[7]
×
150
{
151
#include "lso2.h"
152
    return var_coefs;
153
}
154

155
static const int (*
156
lso3_table(void))[7]
×
157
{
158
/* Auto-generated by gen_varcoefs.awk */
159
#include "lso3.h"
160
    return var_coefs;
161
}
162

163

164
#define VARERR_SCALE_SHIFT 12
165
#define VARERR_SCALE       (1 << VARERR_SCALE_SHIFT)
166
#define VARERR_ROUND       (1 << (VARERR_SCALE_SHIFT - 1))
167
#define VARERR_MAX_VALUE   (255 * VARERR_SCALE)
168

169
#if HAVE_DEBUG
170
#define quant_trace fprintf
171
#else
172
static inline void quant_trace(FILE *f, ...) { (void) f; }
173
#endif
174

175
/*****************************************************************************
176
 *
177
 * quantization
178
 *
179
 *****************************************************************************/
180

181
typedef struct box* boxVector;
182
struct box {
183
    unsigned int ind;
184
    unsigned int colors;
185
    unsigned int sum;
186
};
187

188
typedef unsigned long sample;
189
typedef sample * tuple;
190

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

205
typedef struct {
206
    unsigned int size;
207
    tupletable table;
208
} tupletable2;
209

210
static unsigned int compareplanePlane;
211
    /* This is a parameter to compareplane().  We use this global variable
212
       so that compareplane() can be called by qsort(), to compare two
213
       tuples.  qsort() doesn't pass any arguments except the two tuples.
214
    */
215
static int
216
compareplane(const void * const arg1,
1,585,775✔
217
             const void * const arg2)
218
{
219
    int lhs, rhs;
220

221
    typedef const struct tupleint * const * const sortarg;
222
    sortarg comparandPP  = (sortarg) arg1;
1,585,775✔
223
    sortarg comparatorPP = (sortarg) arg2;
1,585,775✔
224
    lhs = (int)(*comparandPP)->tuple[compareplanePlane];
1,585,775✔
225
    rhs = (int)(*comparatorPP)->tuple[compareplanePlane];
1,585,775✔
226

227
    return lhs - rhs;
1,585,775✔
228
}
229

230

231
static int
232
sumcompare(const void * const b1, const void * const b2)
4,028,293✔
233
{
234
    return (int)((boxVector)b2)->sum - (int)((boxVector)b1)->sum;
4,028,293✔
235
}
236

237

238
static SIXELSTATUS
239
alloctupletable(
464✔
240
    tupletable          /* out */ *result,
241
    unsigned int const  /* in */  depth,
242
    unsigned int const  /* in */  size,
243
    sixel_allocator_t   /* in */  *allocator)
244
{
245
    SIXELSTATUS status = SIXEL_FALSE;
464✔
246
    enum { message_buffer_size = 256 };
247
    char message[message_buffer_size];
248
    int nwrite;
249
    unsigned int mainTableSize;
250
    unsigned int tupleIntSize;
251
    unsigned int allocSize;
252
    void * pool;
253
    tupletable tbl;
254
    unsigned int i;
255

256
    if (UINT_MAX / sizeof(struct tupleint) < size) {
464!
257
        nwrite = sprintf(message,
×
258
                         "size %u is too big for arithmetic",
259
                         size);
260
        if (nwrite > 0) {
×
261
            sixel_helper_set_additional_message(message);
×
262
        }
263
        status = SIXEL_RUNTIME_ERROR;
×
264
        goto end;
×
265
    }
266

267
    mainTableSize = size * sizeof(struct tupleint *);
464✔
268
    tupleIntSize = sizeof(struct tupleint) - sizeof(sample)
464✔
269
        + depth * sizeof(sample);
216✔
270

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

286
    allocSize = mainTableSize + size * tupleIntSize;
464✔
287

288
    pool = sixel_allocator_malloc(allocator, allocSize);
464✔
289
    if (pool == NULL) {
464!
290
        sprintf(message,
×
291
                "unable to allocate %u bytes for a %u-entry "
292
                "tuple table",
293
                 allocSize, size);
294
        sixel_helper_set_additional_message(message);
×
295
        status = SIXEL_BAD_ALLOCATION;
×
296
        goto end;
×
297
    }
298
    tbl = (tupletable) pool;
464✔
299

300
    for (i = 0; i < size; ++i)
68,412✔
301
        tbl[i] = (struct tupleint *)
67,948✔
302
            ((char*)pool + mainTableSize + i * tupleIntSize);
67,948✔
303

304
    *result = tbl;
464✔
305

306
    status = SIXEL_OK;
464✔
307

308
end:
248✔
309
    return status;
464✔
310
}
311

312

313
/*
314
** Here is the fun part, the median-cut colormap generator.  This is based
315
** on Paul Heckbert's paper "Color Image Quantization for Frame Buffer
316
** Display", SIGGRAPH '82 Proceedings, page 297.
317
*/
318

319
static tupletable2
320
newColorMap(unsigned int const newcolors, unsigned int const depth, sixel_allocator_t *allocator)
42✔
321
{
322
    SIXELSTATUS status = SIXEL_FALSE;
42✔
323
    tupletable2 colormap;
324
    unsigned int i;
325

326
    colormap.size = 0;
42✔
327
    status = alloctupletable(&colormap.table, depth, newcolors, allocator);
42✔
328
    if (SIXEL_FAILED(status)) {
42!
329
        goto end;
×
330
    }
331
    if (colormap.table) {
56!
332
        for (i = 0; i < newcolors; ++i) {
6,894✔
333
            unsigned int plane;
334
            for (plane = 0; plane < depth; ++plane)
27,408✔
335
                colormap.table[i]->tuple[plane] = 0;
20,556✔
336
        }
2,284✔
337
        colormap.size = newcolors;
42✔
338
    }
14✔
339

340
end:
341
    return colormap;
42✔
342
}
343

344

345
static boxVector
346
newBoxVector(
42✔
347
    unsigned int const  /* in */ colors,
348
    unsigned int const  /* in */ sum,
349
    unsigned int const  /* in */ newcolors,
350
    sixel_allocator_t   /* in */ *allocator)
351
{
352
    boxVector bv;
353

354
    bv = (boxVector)sixel_allocator_malloc(allocator,
56✔
355
                                           sizeof(struct box) * (size_t)newcolors);
42✔
356
    if (bv == NULL) {
42!
357
        quant_trace(stderr, "out of memory allocating box vector table\n");
×
358
        return NULL;
×
359
    }
360

361
    /* Set up the initial box. */
362
    bv[0].ind = 0;
42✔
363
    bv[0].colors = colors;
42✔
364
    bv[0].sum = sum;
42✔
365

366
    return bv;
42✔
367
}
14✔
368

369

370
static void
371
findBoxBoundaries(tupletable2  const colorfreqtable,
6,810✔
372
                  unsigned int const depth,
373
                  unsigned int const boxStart,
374
                  unsigned int const boxSize,
375
                  sample             minval[],
376
                  sample             maxval[])
377
{
378
/*----------------------------------------------------------------------------
379
  Go through the box finding the minimum and maximum of each
380
  component - the boundaries of the box.
381
-----------------------------------------------------------------------------*/
382
    unsigned int plane;
383
    unsigned int i;
384

385
    for (plane = 0; plane < depth; ++plane) {
27,240✔
386
        minval[plane] = colorfreqtable.table[boxStart]->tuple[plane];
20,430✔
387
        maxval[plane] = minval[plane];
20,430✔
388
    }
6,810✔
389

390
    for (i = 1; i < boxSize; ++i) {
341,460✔
391
        for (plane = 0; plane < depth; ++plane) {
1,338,600✔
392
            sample const v = colorfreqtable.table[boxStart + i]->tuple[plane];
1,003,950✔
393
            if (v < minval[plane]) minval[plane] = v;
1,003,950✔
394
            if (v > maxval[plane]) maxval[plane] = v;
1,003,950✔
395
        }
331,914✔
396
    }
110,638✔
397
}
6,810✔
398

399

400

401
static unsigned int
402
largestByNorm(sample minval[], sample maxval[], unsigned int const depth)
6,810✔
403
{
404

405
    unsigned int largestDimension;
406
    unsigned int plane;
407
    sample largestSpreadSoFar;
408

409
    largestSpreadSoFar = 0;
6,810✔
410
    largestDimension = 0;
6,810✔
411
    for (plane = 0; plane < depth; ++plane) {
27,240✔
412
        sample const spread = maxval[plane]-minval[plane];
20,430✔
413
        if (spread > largestSpreadSoFar) {
20,430✔
414
            largestDimension = plane;
11,321✔
415
            largestSpreadSoFar = spread;
11,321✔
416
        }
3,735✔
417
    }
6,810✔
418
    return largestDimension;
6,810✔
419
}
420

421

422

423
static unsigned int
424
largestByLuminosity(sample minval[], sample maxval[], unsigned int const depth)
×
425
{
426
/*----------------------------------------------------------------------------
427
   This subroutine presumes that the tuple type is either
428
   BLACKANDWHITE, GRAYSCALE, or RGB (which implies pamP->depth is 1 or 3).
429
   To save time, we don't actually check it.
430
-----------------------------------------------------------------------------*/
431
    unsigned int retval;
432

433
    double lumin_factor[3] = {0.2989, 0.5866, 0.1145};
×
434

435
    if (depth == 1) {
×
436
        retval = 0;
×
437
    } else {
438
        /* An RGB tuple */
439
        unsigned int largestDimension;
440
        unsigned int plane;
441
        double largestSpreadSoFar;
442

443
        largestSpreadSoFar = 0.0;
×
444
        largestDimension = 0;
×
445

446
        for (plane = 0; plane < 3; ++plane) {
×
447
            double const spread =
×
448
                lumin_factor[plane] * (maxval[plane]-minval[plane]);
×
449
            if (spread > largestSpreadSoFar) {
×
450
                largestDimension = plane;
×
451
                largestSpreadSoFar = spread;
×
452
            }
453
        }
454
        retval = largestDimension;
×
455
    }
456
    return retval;
×
457
}
458

459

460

461
static void
462
centerBox(unsigned int const boxStart,
6,084✔
463
          unsigned int const boxSize,
464
          tupletable2  const colorfreqtable,
465
          unsigned int const depth,
466
          tuple        const newTuple)
467
{
468

469
    unsigned int plane;
470
    sample minval, maxval;
471
    unsigned int i;
472

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

476
        for (i = 1; i < boxSize; ++i) {
156,249✔
477
            sample v = colorfreqtable.table[boxStart + i]->tuple[plane];
137,997✔
478
            minval = minval < v ? minval: v;
137,997✔
479
            maxval = maxval > v ? maxval: v;
137,997✔
480
        }
45,633✔
481
        newTuple[plane] = (minval + maxval) / 2;
18,252✔
482
    }
6,084✔
483
}
6,084✔
484

485

486

487
static void
488
averageColors(unsigned int const boxStart,
×
489
              unsigned int const boxSize,
490
              tupletable2  const colorfreqtable,
491
              unsigned int const depth,
492
              tuple        const newTuple)
493
{
494
    unsigned int plane;
495
    sample sum;
496
    unsigned int i;
497

498
    for (plane = 0; plane < depth; ++plane) {
×
499
        sum = 0;
×
500

501
        for (i = 0; i < boxSize; ++i) {
×
502
            sum += colorfreqtable.table[boxStart + i]->tuple[plane];
×
503
        }
504

505
        newTuple[plane] = sum / boxSize;
×
506
    }
507
}
×
508

509

510

511
static void
512
averagePixels(unsigned int const boxStart,
768✔
513
              unsigned int const boxSize,
514
              tupletable2 const colorfreqtable,
515
              unsigned int const depth,
516
              tuple const newTuple)
517
{
518

519
    unsigned int n;
520
        /* Number of tuples represented by the box */
521
    unsigned int plane;
522
    unsigned int i;
523

524
    /* Count the tuples in question */
525
    n = 0;  /* initial value */
768✔
526
    for (i = 0; i < boxSize; ++i) {
4,967✔
527
        n += (unsigned int)colorfreqtable.table[boxStart + i]->value;
4,199✔
528
    }
1,393✔
529

530
    for (plane = 0; plane < depth; ++plane) {
3,072✔
531
        sample sum;
532

533
        sum = 0;
2,304✔
534

535
        for (i = 0; i < boxSize; ++i) {
14,901✔
536
            sum += colorfreqtable.table[boxStart + i]->tuple[plane]
16,776✔
537
                * (unsigned int)colorfreqtable.table[boxStart + i]->value;
12,597✔
538
        }
4,179✔
539

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

544

545

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

566
    colormap = newColorMap(newcolors, depth, allocator);
42✔
567
    if (!colormap.size) {
42!
568
        return colormap;
×
569
    }
570

571
    for (bi = 0; bi < boxes; ++bi) {
6,894✔
572
        switch (methodForRep) {
6,852!
573
        case SIXEL_REP_CENTER_BOX:
4,056✔
574
            centerBox(bv[bi].ind, bv[bi].colors,
8,112✔
575
                      colorfreqtable, depth,
2,028✔
576
                      colormap.table[bi]->tuple);
6,084✔
577
            break;
6,084✔
578
        case SIXEL_REP_AVERAGE_COLORS:
579
            averageColors(bv[bi].ind, bv[bi].colors,
×
580
                          colorfreqtable, depth,
581
                          colormap.table[bi]->tuple);
×
582
            break;
×
583
        case SIXEL_REP_AVERAGE_PIXELS:
512✔
584
            averagePixels(bv[bi].ind, bv[bi].colors,
1,024✔
585
                          colorfreqtable, depth,
256✔
586
                          colormap.table[bi]->tuple);
768✔
587
            break;
768✔
588
        default:
589
            quant_trace(stderr, "Internal error: "
×
590
                                "invalid value of methodForRep: %d\n",
591
                        methodForRep);
592
        }
593
    }
2,284✔
594
    return colormap;
42✔
595
}
14✔
596

597

598
static SIXELSTATUS
599
splitBox(boxVector const bv,
6,810✔
600
         unsigned int *const boxesP,
601
         unsigned int const bi,
602
         tupletable2 const colorfreqtable,
603
         unsigned int const depth,
604
         int const methodForLargest)
605
{
606
/*----------------------------------------------------------------------------
607
   Split Box 'bi' in the box vector bv (so that bv contains one more box
608
   than it did as input).  Split it so that each new box represents about
609
   half of the pixels in the distribution given by 'colorfreqtable' for
610
   the colors in the original box, but with distinct colors in each of the
611
   two new boxes.
612

613
   Assume the box contains at least two colors.
614
-----------------------------------------------------------------------------*/
615
    SIXELSTATUS status = SIXEL_FALSE;
6,810✔
616
    unsigned int const boxStart = bv[bi].ind;
6,810✔
617
    unsigned int const boxSize  = bv[bi].colors;
6,810✔
618
    unsigned int const sm       = bv[bi].sum;
6,810✔
619

620
    enum { max_depth= 16 };
621
    sample minval[max_depth];
622
    sample maxval[max_depth];
623

624
    /* assert(max_depth >= depth); */
625

626
    unsigned int largestDimension;
627
        /* number of the plane with the largest spread */
628
    unsigned int medianIndex;
629
    unsigned int lowersum;
630
        /* Number of pixels whose value is "less than" the median */
631

632
    findBoxBoundaries(colorfreqtable, depth, boxStart, boxSize,
9,080✔
633
                      minval, maxval);
2,270✔
634

635
    /* Find the largest dimension, and sort by that component.  I have
636
       included two methods for determining the "largest" dimension;
637
       first by simply comparing the range in RGB space, and second by
638
       transforming into luminosities before the comparison.
639
    */
640
    switch (methodForLargest) {
6,810!
641
    case SIXEL_LARGE_NORM:
4,540✔
642
        largestDimension = largestByNorm(minval, maxval, depth);
6,810✔
643
        break;
6,810✔
644
    case SIXEL_LARGE_LUM:
645
        largestDimension = largestByLuminosity(minval, maxval, depth);
×
646
        break;
×
647
    default:
648
        sixel_helper_set_additional_message(
×
649
            "Internal error: invalid value of methodForLargest.");
650
        status = SIXEL_LOGIC_ERROR;
×
651
        goto end;
×
652
    }
653

654
    /* TODO: I think this sort should go after creating a box,
655
       not before splitting.  Because you need the sort to use
656
       the SIXEL_REP_CENTER_BOX method of choosing a color to
657
       represent the final boxes
658
    */
659

660
    /* Set the gross global variable 'compareplanePlane' as a
661
       parameter to compareplane(), which is called by qsort().
662
    */
663
    compareplanePlane = largestDimension;
6,810✔
664
    qsort((char*) &colorfreqtable.table[boxStart], boxSize,
6,810✔
665
          sizeof(colorfreqtable.table[boxStart]),
666
          compareplane);
667

668
    {
669
        /* Now find the median based on the counts, so that about half
670
           the pixels (not colors, pixels) are in each subdivision.  */
671

672
        unsigned int i;
673

674
        lowersum = colorfreqtable.table[boxStart]->value; /* initial value */
6,810✔
675
        for (i = 1; i < boxSize - 1 && lowersum < sm / 2; ++i) {
143,202✔
676
            lowersum += colorfreqtable.table[boxStart + i]->value;
136,392✔
677
        }
44,566✔
678
        medianIndex = i;
6,810✔
679
    }
680
    /* Split the box, and sort to bring the biggest boxes to the top.  */
681

682
    bv[bi].colors = medianIndex;
6,810✔
683
    bv[bi].sum = lowersum;
6,810✔
684
    bv[*boxesP].ind = boxStart + medianIndex;
6,810✔
685
    bv[*boxesP].colors = boxSize - medianIndex;
6,810✔
686
    bv[*boxesP].sum = sm - lowersum;
6,810✔
687
    ++(*boxesP);
6,810✔
688
    qsort((char*) bv, *boxesP, sizeof(struct box), sumcompare);
6,810✔
689

690
    status = SIXEL_OK;
6,810✔
691

692
end:
4,540✔
693
    return status;
6,810✔
694
}
695

696

697

698
static SIXELSTATUS
699
mediancut(tupletable2 const colorfreqtable,
42✔
700
          unsigned int const depth,
701
          unsigned int const newcolors,
702
          int const methodForLargest,
703
          int const methodForRep,
704
          tupletable2 *const colormapP,
705
          sixel_allocator_t *allocator)
706
{
707
/*----------------------------------------------------------------------------
708
   Compute a set of only 'newcolors' colors that best represent an
709
   image whose pixels are summarized by the histogram
710
   'colorfreqtable'.  Each tuple in that table has depth 'depth'.
711
   colorfreqtable.table[i] tells the number of pixels in the subject image
712
   have a particular color.
713

714
   As a side effect, sort 'colorfreqtable'.
715
-----------------------------------------------------------------------------*/
716
    boxVector bv;
717
    unsigned int bi;
718
    unsigned int boxes;
719
    int multicolorBoxesExist;
720
    unsigned int i;
721
    unsigned int sum;
722
    SIXELSTATUS status = SIXEL_FALSE;
42✔
723

724
    sum = 0;
42✔
725

726
    for (i = 0; i < colorfreqtable.size; ++i) {
56,324✔
727
        sum += colorfreqtable.table[i]->value;
56,282✔
728
    }
18,632✔
729

730
    /* There is at least one box that contains at least 2 colors; ergo,
731
       there is more splitting we can do.  */
732
    bv = newBoxVector(colorfreqtable.size, sum, newcolors, allocator);
42✔
733
    if (bv == NULL) {
42!
734
        goto end;
×
735
    }
736
    boxes = 1;
42✔
737
    multicolorBoxesExist = (colorfreqtable.size > 1);
42✔
738

739
    /* Main loop: split boxes until we have enough. */
740
    while (boxes < newcolors && multicolorBoxesExist) {
6,852!
741
        /* Find the first splittable box. */
742
        for (bi = 0; bi < boxes && bv[bi].colors < 2; ++bi)
133,351!
743
            ;
744
        if (bi >= boxes) {
6,810!
745
            multicolorBoxesExist = 0;
×
746
        } else {
747
            status = splitBox(bv, &boxes, bi,
9,080✔
748
                              colorfreqtable, depth,
2,270✔
749
                              methodForLargest);
2,270✔
750
            if (SIXEL_FAILED(status)) {
6,810!
751
                goto end;
×
752
            }
753
        }
754
    }
755
    *colormapP = colormapFromBv(newcolors, bv, boxes,
56✔
756
                                colorfreqtable, depth,
14✔
757
                                methodForRep, allocator);
14✔
758

759
    sixel_allocator_free(allocator, bv);
42✔
760

761
    status = SIXEL_OK;
42✔
762

763
end:
28✔
764
    return status;
42✔
765
}
766

767

768
static unsigned int
769
computeHash(unsigned char const *data, unsigned int const depth)
18,771,064✔
770
{
771
    unsigned int hash = 0;
18,771,064✔
772
    unsigned int n;
773

774
    for (n = 0; n < depth; n++) {
75,084,256✔
775
        hash |= (unsigned int)(data[depth - 1 - n] >> 3) << n * 5;
56,313,192✔
776
    }
28,906,464✔
777

778
    return hash;
18,771,064✔
779
}
780

781

782
static SIXELSTATUS
783
computeHistogram(unsigned char const    /* in */  *data,
232✔
784
                 unsigned int           /* in */  length,
785
                 unsigned long const    /* in */  depth,
786
                 tupletable2 * const    /* out */ colorfreqtableP,
787
                 int const              /* in */  qualityMode,
788
                 sixel_allocator_t      /* in */  *allocator)
789
{
790
    SIXELSTATUS status = SIXEL_FALSE;
232✔
791
    typedef unsigned short unit_t;
792
    unsigned int i, n;
793
    unit_t *histogram = NULL;
232✔
794
    unit_t *refmap = NULL;
232✔
795
    unit_t *ref;
796
    unit_t *it;
797
    unsigned int bucket_index;
798
    unsigned int step;
799
    unsigned int max_sample;
800

801
    switch (qualityMode) {
232!
802
    case SIXEL_QUALITY_LOW:
102✔
803
        max_sample = 18383;
199✔
804
        break;
199✔
805
    case SIXEL_QUALITY_HIGH:
20✔
806
        max_sample = 1118383;
30✔
807
        break;
30✔
808
    case SIXEL_QUALITY_FULL:
3✔
809
    default:
810
        max_sample = 4003079;
3✔
811
        break;
3✔
812
    }
813

814
    step = length / depth / max_sample * depth;
232✔
815
    if (step <= 0) {
232✔
816
        step = depth;
156✔
817
    }
52✔
818

819
    quant_trace(stderr, "making histogram...\n");
232✔
820

821
    histogram = (unit_t *)sixel_allocator_calloc(allocator,
340✔
822
                                                 (size_t)(1 << depth * 5),
232✔
823
                                                 sizeof(unit_t));
824
    if (histogram == NULL) {
232!
825
        sixel_helper_set_additional_message(
×
826
            "unable to allocate memory for histogram.");
827
        status = SIXEL_BAD_ALLOCATION;
×
828
        goto end;
×
829
    }
830
    it = ref = refmap
232✔
831
        = (unsigned short *)sixel_allocator_malloc(allocator,
340✔
832
                                                   (size_t)(1 << depth * 5) * sizeof(unit_t));
232✔
833
    if (!it) {
232!
834
        sixel_helper_set_additional_message(
×
835
            "unable to allocate memory for lookup table.");
836
        status = SIXEL_BAD_ALLOCATION;
×
837
        goto end;
×
838
    }
839

840
    for (i = 0; i < length; i += step) {
3,036,010✔
841
        bucket_index = computeHash(data + i, 3);
3,035,778✔
842
        if (histogram[bucket_index] == 0) {
3,035,778✔
843
            *ref++ = bucket_index;
58,689✔
844
        }
19,661✔
845
        if (histogram[bucket_index] < (unsigned int)(1 << sizeof(unsigned short) * 8) - 1) {
3,035,778!
846
            histogram[bucket_index]++;
3,035,778✔
847
        }
1,586,006✔
848
    }
1,586,006✔
849

850
    colorfreqtableP->size = (unsigned int)(ref - refmap);
232✔
851

852
    status = alloctupletable(&colorfreqtableP->table, depth, (unsigned int)(ref - refmap), allocator);
232✔
853
    if (SIXEL_FAILED(status)) {
232!
854
        goto end;
×
855
    }
856
    for (i = 0; i < colorfreqtableP->size; ++i) {
58,921✔
857
        if (histogram[refmap[i]] > 0) {
58,689!
858
            colorfreqtableP->table[i]->value = histogram[refmap[i]];
58,689✔
859
            for (n = 0; n < depth; n++) {
234,756✔
860
                colorfreqtableP->table[i]->tuple[depth - 1 - n]
176,067✔
861
                    = (sample)((*it >> n * 5 & 0x1f) << 3);
235,050✔
862
            }
58,983✔
863
        }
19,661✔
864
        it++;
58,689✔
865
    }
19,661✔
866

867
    quant_trace(stderr, "%u colors found\n", colorfreqtableP->size);
232✔
868

869
    status = SIXEL_OK;
232✔
870

871
end:
124✔
872
    sixel_allocator_free(allocator, refmap);
232✔
873
    sixel_allocator_free(allocator, histogram);
232✔
874

875
    return status;
232✔
876
}
877

878

879
static int
880
computeColorMapFromInput(unsigned char const *data,
232✔
881
                         unsigned int const length,
882
                         unsigned int const depth,
883
                         unsigned int const reqColors,
884
                         int const methodForLargest,
885
                         int const methodForRep,
886
                         int const qualityMode,
887
                         tupletable2 * const colormapP,
888
                         unsigned int *origcolors,
889
                         sixel_allocator_t *allocator)
890
{
891
/*----------------------------------------------------------------------------
892
   Produce a colormap containing the best colors to represent the
893
   image stream in file 'ifP'.  Figure it out using the median cut
894
   technique.
895

896
   The colormap will have 'reqcolors' or fewer colors in it, unless
897
   'allcolors' is true, in which case it will have all the colors that
898
   are in the input.
899

900
   The colormap has the same maxval as the input.
901

902
   Put the colormap in newly allocated storage as a tupletable2
903
   and return its address as *colormapP.  Return the number of colors in
904
   it as *colorsP and its maxval as *colormapMaxvalP.
905

906
   Return the characteristics of the input file as
907
   *formatP and *freqPamP.  (This information is not really
908
   relevant to our colormap mission; just a fringe benefit).
909
-----------------------------------------------------------------------------*/
910
    SIXELSTATUS status = SIXEL_FALSE;
232✔
911
    tupletable2 colorfreqtable = {0, NULL};
232✔
912
    unsigned int i;
913
    unsigned int n;
914

915
    status = computeHistogram(data, length, depth,
340✔
916
                              &colorfreqtable, qualityMode, allocator);
108✔
917
    if (SIXEL_FAILED(status)) {
232!
918
        goto end;
×
919
    }
920
    if (origcolors) {
232!
921
        *origcolors = colorfreqtable.size;
232✔
922
    }
108✔
923

924
    if (colorfreqtable.size <= reqColors) {
232✔
925
        quant_trace(stderr,
284✔
926
                    "Image already has few enough colors (<=%d).  "
927
                    "Keeping same colors.\n", reqColors);
94✔
928
        /* *colormapP = colorfreqtable; */
929
        colormapP->size = colorfreqtable.size;
190✔
930
        status = alloctupletable(&colormapP->table, depth, colorfreqtable.size, allocator);
190✔
931
        if (SIXEL_FAILED(status)) {
190!
932
            goto end;
×
933
        }
934
        for (i = 0; i < colorfreqtable.size; ++i) {
2,597✔
935
            colormapP->table[i]->value = colorfreqtable.table[i]->value;
2,407✔
936
            for (n = 0; n < depth; ++n) {
9,628✔
937
                colormapP->table[i]->tuple[n] = colorfreqtable.table[i]->tuple[n];
7,221✔
938
            }
3,087✔
939
        }
1,029✔
940
    } else {
94✔
941
        quant_trace(stderr, "choosing %d colors...\n", reqColors);
42✔
942
        status = mediancut(colorfreqtable, depth, reqColors,
56✔
943
                           methodForLargest, methodForRep, colormapP, allocator);
14✔
944
        if (SIXEL_FAILED(status)) {
42!
945
            goto end;
×
946
        }
947
        quant_trace(stderr, "%d colors are choosed.\n", colorfreqtable.size);
42✔
948
    }
949

950
    status = SIXEL_OK;
232✔
951

952
end:
124✔
953
    sixel_allocator_free(allocator, colorfreqtable.table);
232✔
954
    return status;
232✔
955
}
956

957

958
/* diffuse error energy to surround pixels (normal strategy) */
959
static void
960
error_diffuse_normal(
50,778,513✔
961
    unsigned char /* in */    *data,      /* base address of pixel buffer */
962
    int           /* in */    pos,        /* address of the destination pixel */
963
    int           /* in */    depth,      /* color depth in bytes */
964
    int           /* in */    error,      /* error energy */
965
    int           /* in */    numerator,  /* numerator of diffusion coefficient */
966
    int           /* in */    denominator /* denominator of diffusion coefficient */)
967
{
968
    int c;
969

970
    data += pos * depth;
50,778,513✔
971

972
    c = *data + (error * numerator * 2 / denominator + 1) / 2;
50,778,513✔
973
    if (c < 0) {
50,778,513✔
974
        c = 0;
1,926,205✔
975
    }
650,141✔
976
    if (c >= 1 << 8) {
50,778,513✔
977
        c = (1 << 8) - 1;
656,770✔
978
    }
218,160✔
979
    *data = (unsigned char)c;
50,778,513✔
980
}
50,778,513✔
981

982
/* error diffusion with fast strategy */
983
static void
984
error_diffuse_fast(
154,936,299✔
985
    unsigned char /* in */    *data,      /* base address of pixel buffer */
986
    int           /* in */    pos,        /* address of the destination pixel */
987
    int           /* in */    depth,      /* color depth in bytes */
988
    int           /* in */    error,      /* error energy */
989
    int           /* in */    numerator,  /* numerator of diffusion coefficient */
990
    int           /* in */    denominator /* denominator of diffusion coefficient */)
991
{
992
    int c;
993

994
    data += pos * depth;
154,936,299✔
995

996
    c = *data + error * numerator / denominator;
154,936,299✔
997
    if (c < 0) {
154,936,299✔
998
        c = 0;
7,399,651✔
999
    }
2,753,975✔
1000
    if (c >= 1 << 8) {
154,936,299✔
1001
        c = (1 << 8) - 1;
465,287✔
1002
    }
152,291✔
1003
    *data = (unsigned char)c;
154,936,299✔
1004
}
154,936,299✔
1005

1006

1007
/* error diffusion with precise strategy */
1008
static void
1009
error_diffuse_precise(
6,111,369✔
1010
    unsigned char /* in */    *data,      /* base address of pixel buffer */
1011
    int           /* in */    pos,        /* address of the destination pixel */
1012
    int           /* in */    depth,      /* color depth in bytes */
1013
    int           /* in */    error,      /* error energy */
1014
    int           /* in */    numerator,  /* numerator of diffusion coefficient */
1015
    int           /* in */    denominator /* denominator of diffusion coefficient */)
1016
{
1017
    int c;
1018

1019
    data += pos * depth;
6,111,369✔
1020

1021
    c = (int)(*data + error * numerator / (double)denominator + 0.5);
6,111,369✔
1022
    if (c < 0) {
6,111,369✔
1023
        c = 0;
74,306✔
1024
    }
25,570✔
1025
    if (c >= 1 << 8) {
6,111,369!
1026
        c = (1 << 8) - 1;
×
1027
    }
1028
    *data = (unsigned char)c;
6,111,369✔
1029
}
6,111,369✔
1030

1031

1032
typedef void (*diffuse_fixed_carry_mode)(int32_t *carry_curr,
1033
                                         int32_t *carry_next,
1034
                                         int32_t *carry_far,
1035
                                         int width,
1036
                                         int height,
1037
                                         int depth,
1038
                                         int x,
1039
                                         int y,
1040
                                         int32_t error,
1041
                                         int direction,
1042
                                         int channel);
1043

1044

1045
typedef void (*diffuse_varerr_mode)(unsigned char *data,
1046
                                    int width,
1047
                                    int height,
1048
                                    int x,
1049
                                    int y,
1050
                                    int depth,
1051
                                    int32_t error,
1052
                                    int index,
1053
                                    int direction);
1054

1055
typedef void (*diffuse_varerr_carry_mode)(int32_t *carry_curr,
1056
                                          int32_t *carry_next,
1057
                                          int32_t *carry_far,
1058
                                          int width,
1059
                                          int height,
1060
                                          int depth,
1061
                                          int x,
1062
                                          int y,
1063
                                          int32_t error,
1064
                                          int index,
1065
                                          int direction,
1066
                                          int channel);
1067

1068
static int
1069
zhoufang_index_from_byte(unsigned char value)
×
1070
{
1071
    double px;
1072
    double remapped;
1073
    int scale_index;
1074
    double scale;
1075
    double jitter;
1076
    int index;
1077

1078
    px = (double)value / 255.0;
×
1079
    remapped = px;
×
1080
    if (remapped >= 0.5) {
×
1081
        remapped = 1.0 - remapped;
×
1082
    }
1083
    if (remapped < 0.0) {
×
1084
        remapped = 0.0;
×
1085
    }
1086
    if (remapped > 0.5) {
×
1087
        remapped = 0.5;
×
1088
    }
1089

1090
    scale_index = (int)(remapped * 128.0);
×
1091
    if (scale_index < 0) {
×
1092
        scale_index = 0;
×
1093
    }
1094
    if (scale_index > 127) {
×
1095
        scale_index = 127;
×
1096
    }
1097

1098
    scale = zhoufang_threshold_gain[scale_index] / 100.0;
×
1099
    jitter = ((double)(rand() & 127) / 128.0) * scale;
×
1100
    remapped += (0.5 - remapped) * jitter;
×
1101
    if (remapped < 0.0) {
×
1102
        remapped = 0.0;
×
1103
    }
1104
    if (remapped > 0.5) {
×
1105
        remapped = 0.5;
×
1106
    }
1107

1108
    index = (int)(remapped * 255.0 + 0.5);
×
1109
    if (index > 127) {
×
1110
        index = 127;
×
1111
    }
1112
    if (index < 0) {
×
1113
        index = 0;
×
1114
    }
1115

1116
    return index;
×
1117
}
1118

1119

1120
static int32_t
UNCOV
1121
diffuse_varerr_term(int32_t error, int weight, int denom)
×
1122
{
1123
    int64_t delta;
1124

1125
    delta = (int64_t)error * (int64_t)weight;
×
1126
    if (delta >= 0) {
×
1127
        delta = (delta + denom / 2) / denom;
×
1128
    } else {
1129
        delta = (delta - denom / 2) / denom;
×
1130
    }
1131

1132
    return (int32_t)delta;
×
1133
}
1134

1135

1136
static int32_t
1137
diffuse_fixed_term(int32_t error, int numerator, int denominator)
×
1138
{
1139
    int64_t delta;
1140

1141
    delta = (int64_t)error * (int64_t)numerator;
×
1142
    if (delta >= 0) {
×
1143
        delta = (delta + denominator / 2) / denominator;
×
1144
    } else {
1145
        delta = (delta - denominator / 2) / denominator;
×
1146
    }
1147

1148
    return (int32_t)delta;
×
1149
}
1150

1151

1152
static void
1153
diffuse_varerr_apply_direct(unsigned char *target, int depth, size_t offset,
×
1154
                            int32_t delta)
1155
{
1156
    int64_t value;
1157
    int result;
1158

1159
    value = (int64_t)target[offset * depth] << VARERR_SCALE_SHIFT;
×
1160
    value += delta;
×
1161
    if (value < 0) {
×
1162
        value = 0;
×
1163
    } else {
1164
        int64_t max_value;
1165

1166
        max_value = VARERR_MAX_VALUE;
×
1167
        if (value > max_value) {
×
1168
            value = max_value;
×
1169
        }
1170
    }
1171

1172
    result = (int)((value + VARERR_ROUND) >> VARERR_SCALE_SHIFT);
×
1173
    if (result < 0) {
×
1174
        result = 0;
×
1175
    }
1176
    if (result > 255) {
×
1177
        result = 255;
×
1178
    }
1179
    target[offset * depth] = (unsigned char)result;
×
1180
}
×
1181

1182

1183
static void
1184
diffuse_lso2(unsigned char *data, int width, int height,
×
1185
             int x, int y, int depth, int32_t error,
1186
             int index, int direction)
1187
{
1188
    const int (*table)[7];
1189
    const int *entry;
1190
    int denom;
1191
    int32_t term_r = 0;
×
1192
    int32_t term_r2 = 0;
×
1193
    int32_t term_dl = 0;
×
1194
    int32_t term_d = 0;
×
1195
    int32_t term_dr = 0;
×
1196
    int32_t term_d2 = 0;
×
1197
    size_t offset;
1198

1199
    if (error == 0) {
×
1200
        return;
×
1201
    }
1202
    if (index < 0) {
×
1203
        index = 0;
×
1204
    }
1205
    if (index > 255) {
×
1206
        index = 255;
×
1207
    }
1208

1209
    table = lso2_table();
×
1210
    entry = table[index];
×
1211
    denom = entry[6];
×
1212
    if (denom == 0) {
×
1213
        return;
×
1214
    }
1215

1216
    term_r = diffuse_varerr_term(error, entry[0], denom);
×
1217
    term_r2 = diffuse_varerr_term(error, entry[1], denom);
×
1218
    term_dl = diffuse_varerr_term(error, entry[2], denom);
×
1219
    term_d = diffuse_varerr_term(error, entry[3], denom);
×
1220
    term_dr = diffuse_varerr_term(error, entry[4], denom);
×
NEW
1221
    term_d2 = diffuse_varerr_term(error, entry[5], denom);
×
1222

1223

1224
    if (direction >= 0) {
×
1225
        if (x + 1 < width) {
×
1226
            offset = (size_t)y * (size_t)width + (size_t)(x + 1);
×
1227
            diffuse_varerr_apply_direct(data, depth, offset, term_r);
×
1228
        }
1229
        if (x + 2 < width) {
×
1230
            offset = (size_t)y * (size_t)width + (size_t)(x + 2);
×
1231
            diffuse_varerr_apply_direct(data, depth, offset, term_r2);
×
1232
        }
1233
        if (y + 1 < height && x - 1 >= 0) {
×
1234
            offset = (size_t)(y + 1) * (size_t)width;
×
1235
            offset += (size_t)(x - 1);
×
1236
            diffuse_varerr_apply_direct(data, depth, offset, term_dl);
×
1237
        }
1238
        if (y + 1 < height) {
×
1239
            offset = (size_t)(y + 1) * (size_t)width + (size_t)x;
×
1240
            diffuse_varerr_apply_direct(data, depth, offset, term_d);
×
1241
        }
1242
        if (y + 1 < height && x + 1 < width) {
×
1243
            offset = (size_t)(y + 1) * (size_t)width;
×
1244
            offset += (size_t)(x + 1);
×
1245
            diffuse_varerr_apply_direct(data, depth, offset, term_dr);
×
1246
        }
1247
        if (y + 2 < height) {
×
1248
            offset = (size_t)(y + 2) * (size_t)width + (size_t)x;
×
1249
            diffuse_varerr_apply_direct(data, depth, offset, term_d2);
×
1250
        }
1251
    } else {
1252
        if (x - 1 >= 0) {
×
1253
            offset = (size_t)y * (size_t)width + (size_t)(x - 1);
×
1254
            diffuse_varerr_apply_direct(data, depth, offset, term_r);
×
1255
        }
1256
        if (x - 2 >= 0) {
×
1257
            offset = (size_t)y * (size_t)width + (size_t)(x - 2);
×
1258
            diffuse_varerr_apply_direct(data, depth, offset, term_r2);
×
1259
        }
1260
        if (y + 1 < height && x + 1 < width) {
×
1261
            offset = (size_t)(y + 1) * (size_t)width;
×
1262
            offset += (size_t)(x + 1);
×
1263
            diffuse_varerr_apply_direct(data, depth, offset, term_dl);
×
1264
        }
1265
        if (y + 1 < height) {
×
1266
            offset = (size_t)(y + 1) * (size_t)width + (size_t)x;
×
1267
            diffuse_varerr_apply_direct(data, depth, offset, term_d);
×
1268
        }
1269
        if (y + 1 < height && x - 1 >= 0) {
×
1270
            offset = (size_t)(y + 1) * (size_t)width;
×
1271
            offset += (size_t)(x - 1);
×
1272
            diffuse_varerr_apply_direct(data, depth, offset, term_dr);
×
1273
        }
1274
        if (y + 2 < height) {
×
1275
            offset = (size_t)(y + 2) * (size_t)width + (size_t)x;
×
1276
            diffuse_varerr_apply_direct(data, depth, offset, term_d2);
×
1277
        }
1278
    }
1279
}
1280

1281

1282
static void
1283
diffuse_lso3(unsigned char *data, int width, int height,
×
1284
             int x, int y, int depth, int32_t error,
1285
             int index, int direction)
1286
{
1287
    const int (*table)[7];
1288
    const int *entry;
1289
    int denom;
1290
    int32_t term_r = 0;
×
1291
    int32_t term_r2 = 0;
×
1292
    int32_t term_dl = 0;
×
1293
    int32_t term_d = 0;
×
1294
    int32_t term_dr = 0;
×
1295
    int32_t term_d2 = 0;
×
1296
    size_t offset;
1297

1298
    if (error == 0) {
×
1299
        return;
×
1300
    }
1301
    if (index < 0) {
×
1302
        index = 0;
×
1303
    }
1304
    if (index > 255) {
×
1305
        index = 255;
×
1306
    }
1307

1308
    table = lso3_table();
×
1309
    entry = table[index];
×
1310
    denom = entry[6];
×
1311
    if (denom == 0) {
×
1312
        return;
×
1313
    }
1314

1315
    term_r = diffuse_varerr_term(error, entry[0], denom);
×
1316
    term_r2 = diffuse_varerr_term(error, entry[1], denom);
×
1317
    term_dl = diffuse_varerr_term(error, entry[2], denom);
×
1318
    term_d = diffuse_varerr_term(error, entry[3], denom);
×
1319
    term_dr = diffuse_varerr_term(error, entry[4], denom);
×
UNCOV
1320
    term_d2 = error - term_r - term_r2 - term_dl - term_d - term_dr;
×
1321

1322
    if (direction >= 0) {
×
1323
        if (x + 1 < width) {
×
1324
            offset = (size_t)y * (size_t)width + (size_t)(x + 1);
×
1325
            diffuse_varerr_apply_direct(data, depth, offset, term_r);
×
1326
        }
1327
        if (x + 2 < width) {
×
1328
            offset = (size_t)y * (size_t)width + (size_t)(x + 2);
×
1329
            diffuse_varerr_apply_direct(data, depth, offset, term_r2);
×
1330
        }
1331
        if (y + 1 < height && x - 1 >= 0) {
×
1332
            offset = (size_t)(y + 1) * (size_t)width;
×
1333
            offset += (size_t)(x - 1);
×
1334
            diffuse_varerr_apply_direct(data, depth, offset, term_dl);
×
1335
        }
1336
        if (y + 1 < height) {
×
1337
            offset = (size_t)(y + 1) * (size_t)width + (size_t)x;
×
1338
            diffuse_varerr_apply_direct(data, depth, offset, term_d);
×
1339
        }
1340
        if (y + 1 < height && x + 1 < width) {
×
1341
            offset = (size_t)(y + 1) * (size_t)width;
×
1342
            offset += (size_t)(x + 1);
×
1343
            diffuse_varerr_apply_direct(data, depth, offset, term_dr);
×
1344
        }
1345
        if (y + 2 < height) {
×
1346
            offset = (size_t)(y + 2) * (size_t)width + (size_t)x;
×
1347
            diffuse_varerr_apply_direct(data, depth, offset, term_d2);
×
1348
        }
1349
    } else {
1350
        if (x - 1 >= 0) {
×
1351
            offset = (size_t)y * (size_t)width + (size_t)(x - 1);
×
1352
            diffuse_varerr_apply_direct(data, depth, offset, term_r);
×
1353
        }
1354
        if (x - 1 >= 0) {
×
1355
            offset = (size_t)y * (size_t)width + (size_t)(x - 2);
×
1356
            diffuse_varerr_apply_direct(data, depth, offset, term_r2);
×
1357
        }
1358
        if (y + 1 < height && x + 1 < width) {
×
1359
            offset = (size_t)(y + 1) * (size_t)width;
×
1360
            offset += (size_t)(x + 1);
×
1361
            diffuse_varerr_apply_direct(data, depth, offset, term_dl);
×
1362
        }
1363
        if (y + 1 < height) {
×
1364
            offset = (size_t)(y + 1) * (size_t)width + (size_t)x;
×
1365
            diffuse_varerr_apply_direct(data, depth, offset, term_d);
×
1366
        }
1367
        if (y + 1 < height && x - 1 >= 0) {
×
1368
            offset = (size_t)(y + 1) * (size_t)width;
×
1369
            offset += (size_t)(x - 1);
×
1370
            diffuse_varerr_apply_direct(data, depth, offset, term_dr);
×
1371
        }
1372
        if (y + 2 < height) {
×
1373
            offset = (size_t)(y + 2) * (size_t)width + (size_t)x;
×
1374
            diffuse_varerr_apply_direct(data, depth, offset, term_d2);
×
1375
        }
1376
    }
1377
}
1378

1379

1380
static void
1381
diffuse_lso2_carry(int32_t *carry_curr, int32_t *carry_next, int32_t *carry_far,
×
1382
                   int width, int height, int depth,
1383
                   int x, int y, int32_t error,
1384
                   int index, int direction, int channel)
1385
{
1386
    const int (*table)[7];
1387
    const int *entry;
1388
    int denom;
1389
    int32_t term_r = 0;
×
1390
    int32_t term_r2 = 0;
×
1391
    int32_t term_dl = 0;
×
1392
    int32_t term_d = 0;
×
1393
    int32_t term_dr = 0;
×
1394
    int32_t term_d2 = 0;
×
1395
    size_t base;
1396

1397
    if (error == 0) {
×
1398
        return;
×
1399
    }
1400
    if (index < 0) {
×
1401
        index = 0;
×
1402
    }
1403
    if (index > 255) {
×
1404
        index = 255;
×
1405
    }
1406

1407
    table = lso2_table();
×
1408
    entry = table[index];
×
1409
    denom = entry[6];
×
1410
    if (denom == 0) {
×
1411
        return;
×
1412
    }
1413

1414
    term_r = diffuse_varerr_term(error, entry[0], denom);
×
1415
    term_r2 = diffuse_varerr_term(error, entry[1], denom);
×
1416
    term_dl = diffuse_varerr_term(error, entry[2], denom);
×
1417
    term_d = diffuse_varerr_term(error, entry[3], denom);
×
1418
    term_dr = diffuse_varerr_term(error, entry[4], denom);
×
UNCOV
1419
    term_d2 = error - term_r - term_r2 - term_dl - term_d - term_dr;
×
1420

1421
    if (direction >= 0) {
×
1422
        if (x + 1 < width) {
×
1423
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
1424
            carry_curr[base] += term_r;
×
1425
        }
1426
        if (x + 2 < width) {
×
1427
            base = ((size_t)(x + 2) * (size_t)depth) + (size_t)channel;
×
1428
            carry_curr[base] += term_r2;
×
1429
        }
1430
        if (y + 1 < height && x - 1 >= 0) {
×
1431
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
1432
            carry_next[base] += term_dl;
×
1433
        }
1434
        if (y + 1 < height) {
×
1435
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
1436
            carry_next[base] += term_d;
×
1437
        }
1438
        if (y + 1 < height && x + 1 < width) {
×
1439
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
1440
            carry_next[base] += term_dr;
×
1441
        }
1442
        if (y + 2 < height) {
×
1443
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
1444
            carry_far[base] += term_d2;
×
1445
        }
1446
    } else {
1447
        if (x - 1 >= 0) {
×
1448
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
1449
            carry_curr[base] += term_r;
×
1450
        }
1451
        if (x - 2 >= 0) {
×
1452
            base = ((size_t)(x - 2) * (size_t)depth) + (size_t)channel;
×
1453
            carry_curr[base] += term_r;
×
1454
        }
1455
        if (y + 1 < height && x + 1 < width) {
×
1456
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
1457
            carry_next[base] += term_dl;
×
1458
        }
1459
        if (y + 1 < height) {
×
1460
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
1461
            carry_next[base] += term_d;
×
1462
        }
1463
        if (y + 1 < height && x - 1 >= 0) {
×
1464
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
1465
            carry_next[base] += term_dr;
×
1466
        }
1467
        if (y + 2 < height) {
×
1468
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
1469
            carry_far[base] += term_d2;
×
1470
        }
1471
    }
1472
}
1473

1474

1475
static void
1476
diffuse_lso3_carry(int32_t *carry_curr, int32_t *carry_next, int32_t *carry_far,
×
1477
                   int width, int height, int depth,
1478
                   int x, int y, int32_t error,
1479
                   int index, int direction, int channel)
1480
{
1481
    const int (*table)[7];
1482
    const int *entry;
1483
    int denom;
1484
    int32_t term_r = 0;
×
1485
    int32_t term_r2 = 0;
×
1486
    int32_t term_dl = 0;
×
1487
    int32_t term_d = 0;
×
1488
    int32_t term_dr = 0;
×
1489
    int32_t term_d2 = 0;
×
1490
    size_t base;
1491

1492
    if (error == 0) {
×
1493
        return;
×
1494
    }
1495
    if (index < 0) {
×
1496
        index = 0;
×
1497
    }
1498
    if (index > 255) {
×
1499
        index = 255;
×
1500
    }
1501

1502
    table = lso3_table();
×
1503
    entry = table[index];
×
1504
    denom = entry[6];
×
1505
    if (denom == 0) {
×
1506
        return;
×
1507
    }
1508

1509
    term_r = diffuse_varerr_term(error, entry[0], denom);
×
1510
    term_r2 = diffuse_varerr_term(error, entry[1], denom);
×
1511
    term_dl = diffuse_varerr_term(error, entry[2], denom);
×
1512
    term_d = diffuse_varerr_term(error, entry[3], denom);
×
1513
    term_dr = diffuse_varerr_term(error, entry[4], denom);
×
NEW
1514
    term_d2 = diffuse_varerr_term(error, entry[5], denom);
×
1515

1516
    if (direction >= 0) {
×
1517
        if (x + 1 < width) {
×
1518
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
1519
            carry_curr[base] += term_r;
×
1520
        }
1521
        if (x + 2 < width) {
×
1522
            base = ((size_t)(x + 2) * (size_t)depth) + (size_t)channel;
×
1523
            carry_curr[base] += term_r2;
×
1524
        }
1525
        if (y + 1 < height && x - 1 >= 0) {
×
1526
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
1527
            carry_next[base] += term_dl;
×
1528
        }
1529
        if (y + 1 < height) {
×
1530
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
1531
            carry_next[base] += term_d;
×
1532
        }
1533
        if (y + 1 < height && x + 1 < width) {
×
1534
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
1535
            carry_next[base] += term_dr;
×
1536
        }
1537
        if (y + 2 < height) {
×
1538
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
1539
            carry_far[base] += term_d2;
×
1540
        }
1541
    } else {
1542
        if (x - 1 >= 0) {
×
1543
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
1544
            carry_curr[base] += term_r;
×
1545
        }
1546
        if (x - 2 >= 0) {
×
1547
            base = ((size_t)(x - 2) * (size_t)depth) + (size_t)channel;
×
1548
            carry_curr[base] += term_r2;
×
1549
        }
1550
        if (y + 1 < height && x + 1 < width) {
×
1551
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
1552
            carry_next[base] += term_dl;
×
1553
        }
1554
        if (y + 1 < height) {
×
1555
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
1556
            carry_next[base] += term_d;
×
1557
        }
1558
        if (y + 1 < height && x - 1 >= 0) {
×
1559
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
1560
            carry_next[base] += term_dr;
×
1561
        }
1562
        if (y + 2 < height) {
×
1563
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
1564
            carry_far[base] += term_d2;
×
1565
        }
1566
    }
1567
}
1568

1569

1570
static void
1571
scanline_params(int serpentine, int index, int limit,
41,644✔
1572
                int *start, int *end, int *step, int *direction)
1573
{
1574
    if (serpentine && (index & 1)) {
41,644!
1575
        *start = limit - 1;
×
1576
        *end = -1;
×
1577
        *step = -1;
×
1578
        *direction = -1;
×
1579
    } else {
1580
        *start = 0;
41,644✔
1581
        *end = limit;
41,644✔
1582
        *step = 1;
41,644✔
1583
        *direction = 1;
41,644✔
1584
    }
1585
}
41,644✔
1586

1587

1588
static SIXELSTATUS
1589
apply_palette_positional(
×
1590
    sixel_index_t *result,
1591
    unsigned char *data,
1592
    int width,
1593
    int height,
1594
    int depth,
1595
    unsigned char *palette,
1596
    int reqcolor,
1597
    int methodForDiffuse,
1598
    int methodForScan,
1599
    int foptimize_palette,
1600
    int (*f_lookup)(const unsigned char *pixel,
1601
                    int depth,
1602
                    const unsigned char *palette,
1603
                    int reqcolor,
1604
                    unsigned short *cachetable,
1605
                    int complexion),
1606
    unsigned short *indextable,
1607
    int complexion,
1608
    unsigned char copy[],
1609
    unsigned char new_palette[],
1610
    unsigned short migration_map[],
1611
    int *ncolors)
1612
{
1613
    int serpentine;
1614
    int y;
1615
    float (*f_mask)(int x, int y, int c);
1616

1617
    switch (methodForDiffuse) {
×
1618
    case SIXEL_DIFFUSE_A_DITHER:
1619
        f_mask = mask_a;
×
1620
        break;
×
1621
    case SIXEL_DIFFUSE_X_DITHER:
×
1622
    default:
1623
        f_mask = mask_x;
×
1624
        break;
×
1625
    }
1626

1627
    serpentine = (methodForScan == SIXEL_SCAN_SERPENTINE);
×
1628

1629
    if (foptimize_palette) {
×
1630
        int x;
1631

1632
        *ncolors = 0;
×
1633
        memset(new_palette, 0x00,
×
1634
               (size_t)SIXEL_PALETTE_MAX * (size_t)depth);
1635
        memset(migration_map, 0x00,
×
1636
               sizeof(unsigned short) * (size_t)SIXEL_PALETTE_MAX);
1637
        for (y = 0; y < height; ++y) {
×
1638
            int start;
1639
            int end;
1640
            int step;
1641
            int direction;
1642

1643
            scanline_params(serpentine, y, width,
×
1644
                            &start, &end, &step, &direction);
1645
            (void)direction;
1646
            for (x = start; x != end; x += step) {
×
1647
                int pos;
1648
                int d;
1649
                int color_index;
1650

1651
                pos = y * width + x;
×
1652
                for (d = 0; d < depth; ++d) {
×
1653
                    int val;
1654

1655
                    val = data[pos * depth + d]
×
1656
                        + f_mask(x, y, d) * 32;
×
1657
                    copy[d] = val < 0 ? 0
×
1658
                               : val > 255 ? 255 : val;
×
1659
                }
1660
                color_index = f_lookup(copy, depth, palette,
×
1661
                                       reqcolor, indextable,
1662
                                       complexion);
1663
                if (migration_map[color_index] == 0) {
×
1664
                    result[pos] = *ncolors;
×
1665
                    for (d = 0; d < depth; ++d) {
×
1666
                        new_palette[*ncolors * depth + d]
×
1667
                            = palette[color_index * depth + d];
×
1668
                    }
1669
                    ++*ncolors;
×
1670
                    migration_map[color_index] = *ncolors;
×
1671
                } else {
1672
                    result[pos] = migration_map[color_index] - 1;
×
1673
                }
1674
            }
1675
        }
1676
        memcpy(palette, new_palette, (size_t)(*ncolors * depth));
×
1677
    } else {
1678
        int x;
1679

1680
        for (y = 0; y < height; ++y) {
×
1681
            int start;
1682
            int end;
1683
            int step;
1684
            int direction;
1685

1686
            scanline_params(serpentine, y, width,
×
1687
                            &start, &end, &step, &direction);
1688
            (void)direction;
1689
            for (x = start; x != end; x += step) {
×
1690
                int pos;
1691
                int d;
1692

1693
                pos = y * width + x;
×
1694
                for (d = 0; d < depth; ++d) {
×
1695
                    int val;
1696

1697
                    val = data[pos * depth + d]
×
1698
                        + f_mask(x, y, d) * 32;
×
1699
                    copy[d] = val < 0 ? 0
×
1700
                               : val > 255 ? 255 : val;
×
1701
                }
1702
                result[pos] = f_lookup(copy, depth, palette,
×
1703
                                       reqcolor, indextable,
1704
                                       complexion);
1705
            }
1706
        }
1707
        *ncolors = reqcolor;
×
1708
    }
1709

1710
    return SIXEL_OK;
×
1711
}
1712

1713

1714
static SIXELSTATUS
1715
apply_palette_variable(
×
1716
    sixel_index_t *result,
1717
    unsigned char *data,
1718
    int width,
1719
    int height,
1720
    int depth,
1721
    unsigned char *palette,
1722
    int reqcolor,
1723
    int methodForScan,
1724
    int foptimize_palette,
1725
    int (*f_lookup)(const unsigned char *pixel,
1726
                    int depth,
1727
                    const unsigned char *palette,
1728
                    int reqcolor,
1729
                    unsigned short *cachetable,
1730
                    int complexion),
1731
    unsigned short *indextable,
1732
    int complexion,
1733
    unsigned char new_palette[],
1734
    unsigned short migration_map[],
1735
    int *ncolors,
1736
    int methodForDiffuse,
1737
    int methodForCarry)
1738
{
1739
    SIXELSTATUS status = SIXEL_FALSE;
×
1740
#if _MSC_VER
1741
    enum { max_channels = 4 };
1742
#else
1743
    const int max_channels = 4;
×
1744
#endif
1745
    int serpentine;
1746
    int y;
1747
    diffuse_varerr_mode varerr_diffuse;
1748
    diffuse_varerr_carry_mode varerr_diffuse_carry;
1749
    int use_carry;
1750
    size_t carry_len;
1751
    int32_t *carry_curr = NULL;
×
1752
    int32_t *carry_next = NULL;
×
1753
    int32_t *carry_far = NULL;
×
1754
    unsigned char corrected[max_channels];
1755
    int32_t sample_scaled[max_channels];
1756
    int32_t accum_scaled[max_channels];
1757
    int start;
1758
    int end;
1759
    int step;
1760
    int direction;
1761
    int x;
1762
    int pos;
1763
    size_t base;
1764
    size_t carry_base;
1765
    const unsigned char *source_pixel;
1766
    int color_index;
1767
    int output_index;
1768
    int n;
1769
    int palette_value;
1770
    int diff;
1771
    int table_index;
1772
    int64_t accum;
1773
    int64_t clamped;
1774
    int32_t target_scaled;
1775
    int32_t error_scaled;
1776
    int32_t *tmp;
1777

1778
    if (depth > max_channels) {
×
1779
        status = SIXEL_BAD_ARGUMENT;
×
1780
        goto end;
×
1781
    }
1782

1783
    use_carry = (methodForCarry == SIXEL_CARRY_ENABLE);
×
1784
    carry_len = 0;
×
1785

1786
    switch (methodForDiffuse) {
×
1787
    case SIXEL_DIFFUSE_LSO2:
1788
        varerr_diffuse = diffuse_lso2;
×
1789
        varerr_diffuse_carry = diffuse_lso2_carry;
×
1790
        break;
×
1791
    case SIXEL_DIFFUSE_LSO3:
1792
        varerr_diffuse = diffuse_lso3;
×
1793
        varerr_diffuse_carry = diffuse_lso3_carry;
×
1794
        srand((unsigned int)time(NULL));
×
1795
        break;
×
1796
    default:
1797
        varerr_diffuse = diffuse_lso2;
×
1798
        varerr_diffuse_carry = diffuse_lso2_carry;
×
1799
        break;
×
1800
    }
1801

1802
    if (use_carry) {
×
1803
        carry_len = (size_t)width * (size_t)depth;
×
1804
        carry_curr = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
1805
        if (carry_curr == NULL) {
×
1806
            status = SIXEL_BAD_ALLOCATION;
×
1807
            goto end;
×
1808
        }
1809
        carry_next = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
1810
        if (carry_next == NULL) {
×
1811
            status = SIXEL_BAD_ALLOCATION;
×
1812
            goto end;
×
1813
        }
1814
        carry_far = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
1815
        if (carry_far == NULL) {
×
1816
            status = SIXEL_BAD_ALLOCATION;
×
1817
            goto end;
×
1818
        }
1819
    }
1820

1821
    serpentine = (methodForScan == SIXEL_SCAN_SERPENTINE);
×
1822

1823
    if (foptimize_palette) {
×
1824
        *ncolors = 0;
×
1825
        memset(new_palette, 0x00,
×
1826
               (size_t)SIXEL_PALETTE_MAX * (size_t)depth);
1827
        memset(migration_map, 0x00,
×
1828
               sizeof(unsigned short) * (size_t)SIXEL_PALETTE_MAX);
1829
    }
1830

1831
    for (y = 0; y < height; ++y) {
×
1832
        scanline_params(serpentine, y, width,
×
1833
                        &start, &end, &step, &direction);
1834
        for (x = start; x != end; x += step) {
×
1835
            pos = y * width + x;
×
1836
            base = (size_t)pos * (size_t)depth;
×
1837
            carry_base = (size_t)x * (size_t)depth;
×
1838
            if (use_carry) {
×
1839
                for (n = 0; n < depth; ++n) {
×
1840
                    accum = ((int64_t)data[base + n]
×
1841
                             << VARERR_SCALE_SHIFT)
×
1842
                          + carry_curr[carry_base + (size_t)n];
×
1843
                    if (accum < INT32_MIN) {
×
1844
                        accum = INT32_MIN;
×
1845
                    } else if (accum > INT32_MAX) {
×
1846
                        accum = INT32_MAX;
×
1847
                    }
1848
                    carry_curr[carry_base + (size_t)n] = 0;
×
1849
                    clamped = accum;
×
1850
                    if (clamped < 0) {
×
1851
                        clamped = 0;
×
1852
                    } else if (clamped > VARERR_MAX_VALUE) {
×
1853
                        clamped = VARERR_MAX_VALUE;
×
1854
                    }
1855
                    accum_scaled[n] = (int32_t)clamped;
×
1856
                    corrected[n]
1857
                        = (unsigned char)((clamped + VARERR_ROUND)
×
1858
                                          >> VARERR_SCALE_SHIFT);
×
1859
                }
1860
                source_pixel = corrected;
×
1861
            } else {
1862
                for (n = 0; n < depth; ++n) {
×
1863
                    sample_scaled[n]
1864
                        = (int32_t)data[base + n]
×
1865
                        << VARERR_SCALE_SHIFT;
×
1866
                    corrected[n] = data[base + n];
×
1867
                }
1868
                source_pixel = data + base;
×
1869
            }
1870

1871
            color_index = f_lookup(source_pixel, depth, palette,
×
1872
                                   reqcolor, indextable,
1873
                                   complexion);
1874

1875
            if (foptimize_palette) {
×
1876
                if (migration_map[color_index] == 0) {
×
1877
                    output_index = *ncolors;
×
1878
                    for (n = 0; n < depth; ++n) {
×
1879
                        new_palette[output_index * depth + n]
×
1880
                            = palette[color_index * depth + n];
×
1881
                    }
1882
                    ++*ncolors;
×
1883
                    migration_map[color_index] = *ncolors;
×
1884
                } else {
1885
                    output_index = migration_map[color_index] - 1;
×
1886
                }
1887
                result[pos] = output_index;
×
1888
            } else {
1889
                output_index = color_index;
×
1890
                result[pos] = output_index;
×
1891
            }
1892

1893
            for (n = 0; n < depth; ++n) {
×
1894
                if (foptimize_palette) {
×
1895
                    palette_value = new_palette[output_index * depth + n];
×
1896
                } else {
1897
                    palette_value = palette[color_index * depth + n];
×
1898
                }
1899
                diff = (int)source_pixel[n] - palette_value;
×
1900
                if (diff < 0) {
×
1901
                    diff = -diff;
×
1902
                }
1903
                if (diff > 255) {
×
1904
                    diff = 255;
×
1905
                }
1906
                table_index = diff;
×
1907
                if (methodForDiffuse == SIXEL_DIFFUSE_LSO3) {
×
1908
                    table_index = zhoufang_index_from_byte(
×
NEW
1909
                        (unsigned char)source_pixel[n]);
×
1910
                }
1911
                if (use_carry) {
×
1912
                    target_scaled = (int32_t)palette_value
×
1913
                                  << VARERR_SCALE_SHIFT;
1914
                    error_scaled = accum_scaled[n] - target_scaled;
×
1915
                    varerr_diffuse_carry(carry_curr, carry_next, carry_far,
×
1916
                                         width, height, depth,
1917
                                         x, y, error_scaled,
1918
                                         table_index,
1919
                                         direction, n);
1920
                } else {
1921
                    target_scaled = (int32_t)palette_value
×
1922
                                  << VARERR_SCALE_SHIFT;
1923
                    error_scaled = sample_scaled[n] - target_scaled;
×
1924
                    varerr_diffuse(data + n, width, height,
×
1925
                                   x, y, depth, error_scaled,
1926
                                   table_index,
1927
                                   direction);
1928
                }
1929
            }
1930
        }
1931
        if (use_carry) {
×
1932
            tmp = carry_curr;
×
1933
            carry_curr = carry_next;
×
1934
            carry_next = carry_far;
×
1935
            carry_far = tmp;
×
1936
            if (carry_len > 0) {
×
1937
                memset(carry_far, 0x00, carry_len * sizeof(int32_t));
×
1938
            }
1939
        }
1940
    }
1941

1942
    if (foptimize_palette) {
×
1943
        memcpy(palette, new_palette, (size_t)(*ncolors * depth));
×
1944
    } else {
1945
        *ncolors = reqcolor;
×
1946
    }
1947

1948
    status = SIXEL_OK;
×
1949

1950
end:
1951
    free(carry_next);
×
1952
    free(carry_curr);
×
1953
    free(carry_far);
×
1954
    return status;
×
1955
}
1956

1957

1958
static SIXELSTATUS
1959
apply_palette_fixed(
254✔
1960
    sixel_index_t *result,
1961
    unsigned char *data,
1962
    int width,
1963
    int height,
1964
    int depth,
1965
    unsigned char *palette,
1966
    int reqcolor,
1967
    int methodForScan,
1968
    int foptimize_palette,
1969
    int (*f_lookup)(const unsigned char *pixel,
1970
                    int depth,
1971
                    const unsigned char *palette,
1972
                    int reqcolor,
1973
                    unsigned short *cachetable,
1974
                    int complexion),
1975
    unsigned short *indextable,
1976
    int complexion,
1977
    unsigned char new_palette[],
1978
    unsigned short migration_map[],
1979
    int *ncolors,
1980
    int methodForDiffuse,
1981
    int methodForCarry)
1982
{
150✔
1983
#if _MSC_VER
1984
    enum { max_channels = 4 };
1985
#else
1986
    const int max_channels = 4;
254✔
1987
#endif
1988
    SIXELSTATUS status = SIXEL_FALSE;
254✔
1989
    int serpentine;
1990
    int y;
1991
    void (*f_diffuse)(unsigned char *data,
1992
                      int width,
1993
                      int height,
1994
                      int x,
1995
                      int y,
1996
                      int depth,
1997
                      int offset,
1998
                      int direction);
1999
    diffuse_fixed_carry_mode f_diffuse_carry;
2000
    int use_carry;
2001
    size_t carry_len;
2002
    int32_t *carry_curr = NULL;
254✔
2003
    int32_t *carry_next = NULL;
254✔
2004
    int32_t *carry_far = NULL;
254✔
2005
    unsigned char corrected[max_channels];
150✔
2006
    int32_t accum_scaled[max_channels];
150✔
2007
    int start;
2008
    int end;
2009
    int step;
2010
    int direction;
2011
    int x;
2012
    int pos;
2013
    size_t base;
2014
    size_t carry_base;
2015
    const unsigned char *source_pixel;
2016
    int color_index;
2017
    int output_index;
2018
    int n;
2019
    int palette_value;
2020
    int64_t accum;
2021
    int64_t clamped;
2022
    int32_t target_scaled;
2023
    int32_t error_scaled;
2024
    int offset;
2025
    int32_t *tmp;
2026

2027
    if (depth > max_channels) {
254!
2028
        status = SIXEL_BAD_ARGUMENT;
×
2029
        goto end;
×
2030
    }
2031

2032
    use_carry = (methodForCarry == SIXEL_CARRY_ENABLE);
254✔
2033
    carry_len = 0;
254✔
2034

2035
    if (depth != 3) {
254!
2036
        f_diffuse = diffuse_none;
×
2037
        f_diffuse_carry = diffuse_none_carry;
×
2038
        use_carry = 0;
×
2039
    } else {
2040
        switch (methodForDiffuse) {
254!
2041
        case SIXEL_DIFFUSE_NONE:
86✔
2042
            f_diffuse = diffuse_none;
157✔
2043
            f_diffuse_carry = diffuse_none_carry;
157✔
2044
            break;
157✔
2045
        case SIXEL_DIFFUSE_ATKINSON:
42✔
2046
            f_diffuse = diffuse_atkinson;
64✔
2047
            f_diffuse_carry = diffuse_atkinson_carry;
64✔
2048
            break;
64✔
2049
        case SIXEL_DIFFUSE_FS:
16✔
2050
            f_diffuse = diffuse_fs;
24✔
2051
            f_diffuse_carry = diffuse_fs_carry;
24✔
2052
            break;
24✔
2053
        case SIXEL_DIFFUSE_JAJUNI:
2✔
2054
            f_diffuse = diffuse_jajuni;
3✔
2055
            f_diffuse_carry = diffuse_jajuni_carry;
3✔
2056
            break;
3✔
2057
        case SIXEL_DIFFUSE_STUCKI:
2✔
2058
            f_diffuse = diffuse_stucki;
3✔
2059
            f_diffuse_carry = diffuse_stucki_carry;
3✔
2060
            break;
3✔
2061
        case SIXEL_DIFFUSE_BURKES:
2✔
2062
            f_diffuse = diffuse_burkes;
3✔
2063
            f_diffuse_carry = diffuse_burkes_carry;
3✔
2064
            break;
3✔
2065
        case SIXEL_DIFFUSE_LSO1:
2066
            f_diffuse = diffuse_lso1;
×
2067
            f_diffuse_carry = diffuse_lso1_carry;
×
2068
            break;
×
2069
        default:
2070
            quant_trace(stderr,
×
2071
                        "Internal error: invalid methodForDiffuse: %d\n",
2072
                        methodForDiffuse);
2073
            f_diffuse = diffuse_none;
×
2074
            f_diffuse_carry = diffuse_none_carry;
×
2075
            break;
×
2076
        }
2077
    }
2078

2079
    if (use_carry) {
254!
2080
        carry_len = (size_t)width * (size_t)depth;
×
2081
        if (carry_len > 0) {
×
2082
            carry_curr = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
2083
            if (carry_curr == NULL) {
×
2084
                status = SIXEL_BAD_ALLOCATION;
×
2085
                goto end;
×
2086
            }
2087
            carry_next = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
2088
            if (carry_next == NULL) {
×
2089
                status = SIXEL_BAD_ALLOCATION;
×
2090
                goto end;
×
2091
            }
2092
            carry_far = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
2093
            if (carry_far == NULL) {
×
2094
                status = SIXEL_BAD_ALLOCATION;
×
2095
                goto end;
×
2096
            }
2097
        } else {
2098
            use_carry = 0;
×
2099
        }
2100
    }
2101

2102
    serpentine = (methodForScan == SIXEL_SCAN_SERPENTINE);
254✔
2103

2104
    if (foptimize_palette) {
254✔
2105
        *ncolors = 0;
175✔
2106
        memset(new_palette, 0x00,
175✔
2107
               (size_t)SIXEL_PALETTE_MAX * (size_t)depth);
110✔
2108
        memset(migration_map, 0x00,
175✔
2109
               sizeof(unsigned short) * (size_t)SIXEL_PALETTE_MAX);
2110
    } else {
65✔
2111
        *ncolors = reqcolor;
79✔
2112
    }
2113

2114
    for (y = 0; y < height; ++y) {
41,898✔
2115
        scanline_params(serpentine, y, width,
41,644✔
2116
                        &start, &end, &step, &direction);
2117
        for (x = start; x != end; x += step) {
19,167,350✔
2118
            pos = y * width + x;
19,125,706✔
2119
            base = (size_t)pos * (size_t)depth;
19,125,706✔
2120
            carry_base = (size_t)x * (size_t)depth;
19,125,706✔
2121
            if (use_carry) {
19,125,706!
2122
                for (n = 0; n < depth; ++n) {
×
2123
                    accum = ((int64_t)data[base + n]
×
2124
                             << VARERR_SCALE_SHIFT)
×
2125
                           + carry_curr[carry_base + (size_t)n];
×
2126
                    if (accum < INT32_MIN) {
×
2127
                        accum = INT32_MIN;
×
2128
                    } else if (accum > INT32_MAX) {
×
2129
                        accum = INT32_MAX;
×
2130
                    }
2131
                    clamped = accum;
×
2132
                    if (clamped < 0) {
×
2133
                        clamped = 0;
×
2134
                    } else if (clamped > VARERR_MAX_VALUE) {
×
2135
                        clamped = VARERR_MAX_VALUE;
×
2136
                    }
2137
                    accum_scaled[n] = (int32_t)clamped;
×
2138
                    corrected[n]
2139
                        = (unsigned char)((clamped + VARERR_ROUND)
×
2140
                                          >> VARERR_SCALE_SHIFT);
×
2141
                    data[base + n] = corrected[n];
×
2142
                    carry_curr[carry_base + (size_t)n] = 0;
×
2143
                }
2144
                source_pixel = corrected;
×
2145
            } else {
2146
                source_pixel = data + base;
19,125,706✔
2147
            }
2148

2149
            color_index = f_lookup(source_pixel, depth, palette,
28,305,328✔
2150
                                   reqcolor, indextable,
9,179,622✔
2151
                                   complexion);
9,179,622✔
2152

2153
            if (foptimize_palette) {
19,125,706✔
2154
                if (migration_map[color_index] == 0) {
7,024,206✔
2155
                    output_index = *ncolors;
8,103✔
2156
                    for (n = 0; n < depth; ++n) {
32,412✔
2157
                        new_palette[output_index * depth + n]
24,309✔
2158
                            = palette[color_index * depth + n];
32,478✔
2159
                    }
8,169✔
2160
                    ++*ncolors;
8,103✔
2161
                    migration_map[color_index] = *ncolors;
8,103✔
2162
                } else {
2,723✔
2163
                    output_index = migration_map[color_index] - 1;
7,016,103✔
2164
                }
2165
                result[pos] = output_index;
7,024,206✔
2166
            } else {
3,339,802✔
2167
                output_index = color_index;
12,101,500✔
2168
                result[pos] = output_index;
12,101,500✔
2169
            }
2170

2171
            for (n = 0; n < depth; ++n) {
76,502,824✔
2172
                if (foptimize_palette) {
57,377,118✔
2173
                    palette_value = new_palette[output_index * depth + n];
21,072,618✔
2174
                } else {
10,019,406✔
2175
                    palette_value = palette[color_index * depth + n];
36,304,500✔
2176
                }
2177
                if (use_carry) {
57,377,118!
2178
                    target_scaled = (int32_t)palette_value
×
2179
                                  << VARERR_SCALE_SHIFT;
2180
                    error_scaled = accum_scaled[n] - target_scaled;
×
2181
                    f_diffuse_carry(carry_curr, carry_next, carry_far,
×
2182
                                    width, height, depth,
2183
                                    x, y, error_scaled, direction, n);
2184
                } else {
2185
                    offset = (int)source_pixel[n] - palette_value;
57,377,118✔
2186
                    f_diffuse(data + n, width, height, x, y,
84,915,984✔
2187
                              depth, offset, direction);
27,538,866✔
2188
                }
2189
            }
27,538,866✔
2190
        }
9,179,622✔
2191
        if (use_carry) {
41,644!
2192
            tmp = carry_curr;
×
2193
            carry_curr = carry_next;
×
2194
            carry_next = carry_far;
×
2195
            carry_far = tmp;
×
2196
            if (carry_len > 0) {
×
2197
                memset(carry_far, 0x00, carry_len * sizeof(int32_t));
×
2198
            }
2199
        }
2200
    }
19,772✔
2201

2202
    if (foptimize_palette) {
254✔
2203
        memcpy(palette, new_palette, (size_t)(*ncolors * depth));
175✔
2204
    }
65✔
2205

2206
    status = SIXEL_OK;
254✔
2207

2208
end:
150✔
2209
    free(carry_far);
254✔
2210
    free(carry_next);
254✔
2211
    free(carry_curr);
254✔
2212
    return status;
254✔
2213
}
2214

2215

2216
static void
2217
diffuse_none(unsigned char *data, int width, int height,
18,248,814✔
2218
             int x, int y, int depth, int error, int direction)
2219
{
2220
    /* unused */ (void) data;
14,469,498✔
2221
    /* unused */ (void) width;
14,469,498✔
2222
    /* unused */ (void) height;
14,469,498✔
2223
    /* unused */ (void) x;
14,469,498✔
2224
    /* unused */ (void) y;
14,469,498✔
2225
    /* unused */ (void) depth;
14,469,498✔
2226
    /* unused */ (void) error;
14,469,498✔
2227
    /* unused */ (void) direction;
14,469,498✔
2228
}
18,248,814✔
2229

2230

2231
static void
2232
diffuse_none_carry(int32_t *carry_curr, int32_t *carry_next,
×
2233
                   int32_t *carry_far, int width, int height,
2234
                   int depth, int x, int y, int32_t error,
2235
                   int direction, int channel)
2236
{
2237
    /* unused */ (void) carry_curr;
2238
    /* unused */ (void) carry_next;
2239
    /* unused */ (void) carry_far;
2240
    /* unused */ (void) width;
2241
    /* unused */ (void) height;
2242
    /* unused */ (void) depth;
2243
    /* unused */ (void) x;
2244
    /* unused */ (void) y;
2245
    /* unused */ (void) error;
2246
    /* unused */ (void) direction;
2247
    /* unused */ (void) channel;
2248
}
×
2249

2250

2251
static void
2252
diffuse_fs(unsigned char *data, int width, int height,
12,621,744✔
2253
           int x, int y, int depth, int error, int direction)
2254
{
2255
    /* Floyd Steinberg Method
2256
     *          curr    7/16
2257
     *  3/16    5/48    1/16
2258
     */
2259
    int pos;
2260
    int forward;
2261

2262
    pos = y * width + x;
12,621,744✔
2263
    forward = direction >= 0;
12,621,744✔
2264

2265
    if (forward) {
12,621,744!
2266
        if (x < width - 1) {
12,621,744✔
2267
            error_diffuse_normal(data, pos + 1, depth, error, 7, 16);
12,594,798✔
2268
        }
4,198,266✔
2269
        if (y < height - 1) {
12,621,744✔
2270
            if (x > 0) {
12,591,828✔
2271
                error_diffuse_normal(data,
16,753,272✔
2272
                                     pos + width - 1,
12,564,954✔
2273
                                     depth, error, 3, 16);
4,188,318✔
2274
            }
4,188,318✔
2275
            error_diffuse_normal(data,
16,789,104✔
2276
                                 pos + width,
4,197,276✔
2277
                                 depth, error, 5, 16);
4,197,276✔
2278
            if (x < width - 1) {
12,591,828✔
2279
                error_diffuse_normal(data,
16,753,272✔
2280
                                     pos + width + 1,
12,564,954✔
2281
                                     depth, error, 1, 16);
4,188,318✔
2282
            }
4,188,318✔
2283
        }
4,197,276✔
2284
    } else {
4,207,248✔
2285
        if (x > 0) {
×
2286
            error_diffuse_normal(data, pos - 1, depth, error, 7, 16);
×
2287
        }
2288
        if (y < height - 1) {
×
2289
            if (x < width - 1) {
×
2290
                error_diffuse_normal(data,
×
2291
                                     pos + width + 1,
×
2292
                                     depth, error, 3, 16);
2293
            }
2294
            error_diffuse_normal(data,
×
2295
                                 pos + width,
2296
                                 depth, error, 5, 16);
2297
            if (x > 0) {
×
2298
                error_diffuse_normal(data,
×
2299
                                     pos + width - 1,
×
2300
                                     depth, error, 1, 16);
2301
            }
2302
        }
2303
    }
2304
}
12,621,744✔
2305

2306

2307
static void
2308
diffuse_fs_carry(int32_t *carry_curr, int32_t *carry_next,
×
2309
                 int32_t *carry_far, int width, int height,
2310
                 int depth, int x, int y, int32_t error,
2311
                 int direction, int channel)
2312
{
2313
    /* Floyd Steinberg Method
2314
     *          curr    7/16
2315
     *  3/16    5/48    1/16
2316
     */
2317
    int forward;
2318

2319
    /* unused */ (void) carry_far;
2320
    if (error == 0) {
×
2321
        return;
×
2322
    }
2323

2324
    forward = direction >= 0;
×
2325
    if (forward) {
×
2326
        if (x + 1 < width) {
×
2327
            size_t base;
2328
            int32_t term;
2329

2330
            base = ((size_t)(x + 1) * (size_t)depth)
×
2331
                 + (size_t)channel;
×
2332
            term = diffuse_fixed_term(error, 7, 16);
×
2333
            carry_curr[base] += term;
×
2334
        }
2335
        if (y + 1 < height) {
×
2336
            if (x > 0) {
×
2337
                size_t base;
2338
                int32_t term;
2339

2340
                base = ((size_t)(x - 1) * (size_t)depth)
×
2341
                     + (size_t)channel;
×
2342
                term = diffuse_fixed_term(error, 3, 16);
×
2343
                carry_next[base] += term;
×
2344
            }
2345
            {
2346
                size_t base;
2347
                int32_t term;
2348

2349
                base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
2350
                term = diffuse_fixed_term(error, 5, 16);
×
2351
                carry_next[base] += term;
×
2352
            }
2353
            if (x + 1 < width) {
×
2354
                size_t base;
2355
                int32_t term;
2356

2357
                base = ((size_t)(x + 1) * (size_t)depth)
×
2358
                     + (size_t)channel;
×
2359
                term = diffuse_fixed_term(error, 1, 16);
×
2360
                carry_next[base] += term;
×
2361
            }
2362
        }
2363
    } else {
2364
        if (x - 1 >= 0) {
×
2365
            size_t base;
2366
            int32_t term;
2367

2368
            base = ((size_t)(x - 1) * (size_t)depth)
×
2369
                 + (size_t)channel;
×
2370
            term = diffuse_fixed_term(error, 7, 16);
×
2371
            carry_curr[base] += term;
×
2372
        }
2373
        if (y + 1 < height) {
×
2374
            if (x + 1 < width) {
×
2375
                size_t base;
2376
                int32_t term;
2377

2378
                base = ((size_t)(x + 1) * (size_t)depth)
×
2379
                     + (size_t)channel;
×
2380
                term = diffuse_fixed_term(error, 3, 16);
×
2381
                carry_next[base] += term;
×
2382
            }
2383
            {
2384
                size_t base;
2385
                int32_t term;
2386

2387
                base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
2388
                term = diffuse_fixed_term(error, 5, 16);
×
2389
                carry_next[base] += term;
×
2390
            }
2391
            if (x - 1 >= 0) {
×
2392
                size_t base;
2393
                int32_t term;
2394

2395
                base = ((size_t)(x - 1) * (size_t)depth)
×
2396
                     + (size_t)channel;
×
2397
                term = diffuse_fixed_term(error, 1, 16);
×
2398
                carry_next[base] += term;
×
2399
            }
2400
        }
2401
    }
2402
}
2403

2404

2405
static void
2406
diffuse_atkinson(unsigned char *data, int width, int height,
25,922,460✔
2407
                 int x, int y, int depth, int error, int direction)
2408
{
2409
    /* Atkinson's Method
2410
     *          curr    1/8    1/8
2411
     *   1/8     1/8    1/8
2412
     *           1/8
2413
     */
2414
    int pos;
2415
    int sign;
2416

2417
    pos = y * width + x;
25,922,460✔
2418
    sign = direction >= 0 ? 1 : -1;
25,922,460!
2419

2420
    if (x + sign >= 0 && x + sign < width) {
25,922,460!
2421
        error_diffuse_fast(data, pos + sign, depth, error, 1, 8);
25,870,995✔
2422
    }
8,650,065✔
2423
    if (x + sign * 2 >= 0 && x + sign * 2 < width) {
25,922,460!
2424
        error_diffuse_fast(data, pos + sign * 2, depth, error, 1, 8);
25,819,530✔
2425
    }
8,632,710✔
2426
    if (y < height - 1) {
25,922,460✔
2427
        int row;
2428

2429
        row = pos + width;
25,854,156✔
2430
        if (x - sign >= 0 && x - sign < width) {
25,854,156!
2431
            error_diffuse_fast(data,
34,429,980✔
2432
                               row + (-sign),
8,627,097✔
2433
                               depth, error, 1, 8);
8,627,097✔
2434
        }
8,627,097✔
2435
        error_diffuse_fast(data, row, depth, error, 1, 8);
25,854,156✔
2436
        if (x + sign >= 0 && x + sign < width) {
25,854,156!
2437
            error_diffuse_fast(data,
34,429,980✔
2438
                               row + sign,
8,627,097✔
2439
                               depth, error, 1, 8);
8,627,097✔
2440
        }
8,627,097✔
2441
    }
8,644,386✔
2442
    if (y < height - 2) {
25,922,460✔
2443
        error_diffuse_fast(data, pos + width * 2, depth, error, 1, 8);
25,785,852✔
2444
    }
8,621,352✔
2445
}
25,922,460✔
2446

2447

2448
static void
2449
diffuse_atkinson_carry(int32_t *carry_curr, int32_t *carry_next,
×
2450
                       int32_t *carry_far, int width, int height,
2451
                       int depth, int x, int y, int32_t error,
2452
                       int direction, int channel)
2453
{
2454
    /* Atkinson's Method
2455
     *          curr    1/8    1/8
2456
     *   1/8     1/8    1/8
2457
     *           1/8
2458
     */
2459
    int sign;
2460
    int32_t term;
2461

2462
    if (error == 0) {
×
2463
        return;
×
2464
    }
2465

2466
    term = diffuse_fixed_term(error, 1, 8);
×
2467
    sign = direction >= 0 ? 1 : -1;
×
2468
    if (x + sign >= 0 && x + sign < width) {
×
2469
        size_t base;
2470

2471
        base = ((size_t)(x + sign) * (size_t)depth)
×
2472
             + (size_t)channel;
×
2473
        carry_curr[base] += term;
×
2474
    }
2475
    if (x + sign * 2 >= 0 && x + sign * 2 < width) {
×
2476
        size_t base;
2477

2478
        base = ((size_t)(x + sign * 2) * (size_t)depth)
×
2479
             + (size_t)channel;
×
2480
        carry_curr[base] += term;
×
2481
    }
2482
    if (y + 1 < height) {
×
2483
        if (x - sign >= 0 && x - sign < width) {
×
2484
            size_t base;
2485

2486
            base = ((size_t)(x - sign) * (size_t)depth)
×
2487
                 + (size_t)channel;
×
2488
            carry_next[base] += term;
×
2489
        }
2490
        {
2491
            size_t base;
2492

2493
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
2494
            carry_next[base] += term;
×
2495
        }
2496
        if (x + sign >= 0 && x + sign < width) {
×
2497
            size_t base;
2498

2499
            base = ((size_t)(x + sign) * (size_t)depth)
×
2500
                 + (size_t)channel;
×
2501
            carry_next[base] += term;
×
2502
        }
2503
    }
2504
    if (y + 2 < height) {
×
2505
        size_t base;
2506

2507
        base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
2508
        carry_far[base] += term;
×
2509
    }
2510
}
2511

2512

2513
static void
2514
diffuse_jajuni(unsigned char *data, int width, int height,
396,900✔
2515
               int x, int y, int depth, int error, int direction)
2516
{
2517
    /* Jarvis, Judice & Ninke Method
2518
     *                  curr    7/48    5/48
2519
     *  3/48    5/48    7/48    5/48    3/48
2520
     *  1/48    3/48    5/48    3/48    1/48
2521
     */
2522
    int pos;
2523
    int sign;
2524
    static const int row0_offsets[] = { 1, 2 };
2525
    static const int row0_weights[] = { 7, 5 };
2526
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
2527
    static const int row1_weights[] = { 3, 5, 7, 5, 3 };
2528
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
2529
    static const int row2_weights[] = { 1, 3, 5, 3, 1 };
2530
    int i;
2531

2532
    pos = y * width + x;
396,900✔
2533
    sign = direction >= 0 ? 1 : -1;
396,900!
2534

2535
    for (i = 0; i < 2; ++i) {
1,190,700✔
2536
        int neighbor;
2537

2538
        neighbor = x + sign * row0_offsets[i];
793,800✔
2539
        if (neighbor < 0 || neighbor >= width) {
793,800!
2540
            continue;
5,670✔
2541
        }
2542
        error_diffuse_precise(data,
1,050,840✔
2543
                              pos + (neighbor - x),
788,130✔
2544
                              depth, error,
262,710✔
2545
                              row0_weights[i], 48);
788,130✔
2546
    }
262,710✔
2547
    if (y < height - 1) {
396,900✔
2548
        int row;
2549

2550
        row = pos + width;
395,010✔
2551
        for (i = 0; i < 5; ++i) {
2,370,060✔
2552
            int neighbor;
2553

2554
            neighbor = x + sign * row1_offsets[i];
1,975,050✔
2555
            if (neighbor < 0 || neighbor >= width) {
1,975,050✔
2556
                continue;
11,286✔
2557
            }
2558
            error_diffuse_precise(data,
2,618,352✔
2559
                                  row + (neighbor - x),
1,963,764✔
2560
                                  depth, error,
654,588✔
2561
                                  row1_weights[i], 48);
1,963,764✔
2562
        }
654,588✔
2563
    }
131,670✔
2564
    if (y < height - 2) {
396,900✔
2565
        int row;
2566

2567
        row = pos + width * 2;
393,120✔
2568
        for (i = 0; i < 5; ++i) {
2,358,720✔
2569
            int neighbor;
2570

2571
            neighbor = x + sign * row2_offsets[i];
1,965,600✔
2572
            if (neighbor < 0 || neighbor >= width) {
1,965,600✔
2573
                continue;
11,232✔
2574
            }
2575
            error_diffuse_precise(data,
2,605,824✔
2576
                                  row + (neighbor - x),
1,954,368✔
2577
                                  depth, error,
651,456✔
2578
                                  row2_weights[i], 48);
1,954,368✔
2579
        }
651,456✔
2580
    }
131,040✔
2581
}
396,900✔
2582

2583

2584
static void
2585
diffuse_jajuni_carry(int32_t *carry_curr, int32_t *carry_next,
×
2586
                     int32_t *carry_far, int width, int height,
2587
                     int depth, int x, int y, int32_t error,
2588
                     int direction, int channel)
2589
{
2590
    /* Jarvis, Judice & Ninke Method
2591
     *                  curr    7/48    5/48
2592
     *  3/48    5/48    7/48    5/48    3/48
2593
     *  1/48    3/48    5/48    3/48    1/48
2594
     */
2595
    static const int row0_offsets[] = { 1, 2 };
2596
    static const int row0_weights[] = { 7, 5 };
2597
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
2598
    static const int row1_weights[] = { 3, 5, 7, 5, 3 };
2599
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
2600
    static const int row2_weights[] = { 1, 3, 5, 3, 1 };
2601
    int sign;
2602
    int i;
2603

2604
    if (error == 0) {
×
2605
        return;
×
2606
    }
2607

2608
    sign = direction >= 0 ? 1 : -1;
×
2609
    for (i = 0; i < 2; ++i) {
×
2610
        int neighbor;
2611
        int32_t term;
2612

2613
        neighbor = x + sign * row0_offsets[i];
×
2614
        if (neighbor < 0 || neighbor >= width) {
×
2615
            continue;
×
2616
        }
2617
        term = diffuse_fixed_term(error, row0_weights[i], 48);
×
2618
        carry_curr[((size_t)neighbor * (size_t)depth)
×
2619
                   + (size_t)channel] += term;
×
2620
    }
2621
    if (y + 1 < height) {
×
2622
        for (i = 0; i < 5; ++i) {
×
2623
            int neighbor;
2624
            int32_t term;
2625

2626
            neighbor = x + sign * row1_offsets[i];
×
2627
            if (neighbor < 0 || neighbor >= width) {
×
2628
                continue;
×
2629
            }
2630
            term = diffuse_fixed_term(error, row1_weights[i], 48);
×
2631
            carry_next[((size_t)neighbor * (size_t)depth)
×
2632
                       + (size_t)channel] += term;
×
2633
        }
2634
    }
2635
    if (y + 2 < height) {
×
2636
        for (i = 0; i < 5; ++i) {
×
2637
            int neighbor;
2638
            int32_t term;
2639

2640
            neighbor = x + sign * row2_offsets[i];
×
2641
            if (neighbor < 0 || neighbor >= width) {
×
2642
                continue;
×
2643
            }
2644
            term = diffuse_fixed_term(error, row2_weights[i], 48);
×
2645
            carry_far[((size_t)neighbor * (size_t)depth)
×
2646
                      + (size_t)channel] += term;
×
2647
        }
2648
    }
2649
}
2650

2651

2652
static void
2653
diffuse_stucki(unsigned char *data, int width, int height,
119,700✔
2654
               int x, int y, int depth, int error, int direction)
2655
{
2656
    /* Stucki's Method
2657
     *                  curr    8/48    4/48
2658
     *  2/48    4/48    8/48    4/48    2/48
2659
     *  1/48    2/48    4/48    2/48    1/48
2660
     */
2661
    int pos;
2662
    int sign;
2663
    static const int row0_offsets[] = { 1, 2 };
2664
    static const int row0_num[] = { 1, 1 };
2665
    static const int row0_den[] = { 6, 12 };
2666
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
2667
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
2668
    static const int row1_den[] = { 24, 12, 6, 12, 24 };
2669
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
2670
    static const int row2_num[] = { 1, 1, 1, 1, 1 };
2671
    static const int row2_den[] = { 48, 24, 12, 24, 48 };
2672
    int i;
2673

2674
    pos = y * width + x;
119,700✔
2675
    sign = direction >= 0 ? 1 : -1;
119,700!
2676

2677
    for (i = 0; i < 2; ++i) {
359,100✔
2678
        int neighbor;
2679

2680
        neighbor = x + sign * row0_offsets[i];
239,400✔
2681
        if (neighbor < 0 || neighbor >= width) {
239,400!
2682
            continue;
2,700✔
2683
        }
2684
        error_diffuse_precise(data,
315,600✔
2685
                              pos + (neighbor - x),
236,700✔
2686
                              depth, error,
78,900✔
2687
                              row0_num[i], row0_den[i]);
236,700✔
2688
    }
78,900✔
2689
    if (y < height - 1) {
119,700✔
2690
        int row;
2691

2692
        row = pos + width;
118,503✔
2693
        for (i = 0; i < 5; ++i) {
711,018✔
2694
            int neighbor;
2695

2696
            neighbor = x + sign * row1_offsets[i];
592,515✔
2697
            if (neighbor < 0 || neighbor >= width) {
592,515✔
2698
                continue;
5,346✔
2699
            }
2700
            error_diffuse_precise(data,
782,892✔
2701
                                  row + (neighbor - x),
587,169✔
2702
                                  depth, error,
195,723✔
2703
                                  row1_num[i], row1_den[i]);
587,169✔
2704
        }
195,723✔
2705
    }
39,501✔
2706
    if (y < height - 2) {
119,700✔
2707
        int row;
2708

2709
        row = pos + width * 2;
117,306✔
2710
        for (i = 0; i < 5; ++i) {
703,836✔
2711
            int neighbor;
2712

2713
            neighbor = x + sign * row2_offsets[i];
586,530✔
2714
            if (neighbor < 0 || neighbor >= width) {
586,530✔
2715
                continue;
5,292✔
2716
            }
2717
            error_diffuse_precise(data,
774,984✔
2718
                                  row + (neighbor - x),
581,238✔
2719
                                  depth, error,
193,746✔
2720
                                  row2_num[i], row2_den[i]);
581,238✔
2721
        }
193,746✔
2722
    }
39,102✔
2723
}
119,700✔
2724

2725

2726
static void
2727
diffuse_stucki_carry(int32_t *carry_curr, int32_t *carry_next,
×
2728
                     int32_t *carry_far, int width, int height,
2729
                     int depth, int x, int y, int32_t error,
2730
                     int direction, int channel)
2731
{
2732
    /* Stucki's Method
2733
     *                  curr    8/48    4/48
2734
     *  2/48    4/48    8/48    4/48    2/48
2735
     *  1/48    2/48    4/48    2/48    1/48
2736
     */
2737
    static const int row0_offsets[] = { 1, 2 };
2738
    static const int row0_num[] = { 1, 1 };
2739
    static const int row0_den[] = { 6, 12 };
2740
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
2741
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
2742
    static const int row1_den[] = { 24, 12, 6, 12, 24 };
2743
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
2744
    static const int row2_num[] = { 1, 1, 1, 1, 1 };
2745
    static const int row2_den[] = { 48, 24, 12, 24, 48 };
2746
    int sign;
2747
    int i;
2748

2749
    if (error == 0) {
×
2750
        return;
×
2751
    }
2752

2753
    sign = direction >= 0 ? 1 : -1;
×
2754
    for (i = 0; i < 2; ++i) {
×
2755
        int neighbor;
2756
        int32_t term;
2757

2758
        neighbor = x + sign * row0_offsets[i];
×
2759
        if (neighbor < 0 || neighbor >= width) {
×
2760
            continue;
×
2761
        }
2762
        term = diffuse_fixed_term(error, row0_num[i], row0_den[i]);
×
2763
        carry_curr[((size_t)neighbor * (size_t)depth)
×
2764
                   + (size_t)channel] += term;
×
2765
    }
2766
    if (y + 1 < height) {
×
2767
        for (i = 0; i < 5; ++i) {
×
2768
            int neighbor;
2769
            int32_t term;
2770

2771
            neighbor = x + sign * row1_offsets[i];
×
2772
            if (neighbor < 0 || neighbor >= width) {
×
2773
                continue;
×
2774
            }
2775
            term = diffuse_fixed_term(error, row1_num[i], row1_den[i]);
×
2776
            carry_next[((size_t)neighbor * (size_t)depth)
×
2777
                       + (size_t)channel] += term;
×
2778
        }
2779
    }
2780
    if (y + 2 < height) {
×
2781
        for (i = 0; i < 5; ++i) {
×
2782
            int neighbor;
2783
            int32_t term;
2784

2785
            neighbor = x + sign * row2_offsets[i];
×
2786
            if (neighbor < 0 || neighbor >= width) {
×
2787
                continue;
×
2788
            }
2789
            term = diffuse_fixed_term(error, row2_num[i], row2_den[i]);
×
2790
            carry_far[((size_t)neighbor * (size_t)depth)
×
2791
                      + (size_t)channel] += term;
×
2792
        }
2793
    }
2794
}
2795

2796

2797
static void
2798
diffuse_burkes(unsigned char *data, int width, int height,
67,500✔
2799
               int x, int y, int depth, int error, int direction)
2800
{
2801
    /* Burkes' Method
2802
     *                  curr    4/16    2/16
2803
     *  1/16    2/16    4/16    2/16    1/16
2804
     */
2805
    int pos;
2806
    int sign;
2807
    static const int row0_offsets[] = { 1, 2 };
2808
    static const int row0_num[] = { 1, 1 };
2809
    static const int row0_den[] = { 4, 8 };
2810
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
2811
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
2812
    static const int row1_den[] = { 16, 8, 4, 8, 16 };
2813
    int i;
2814

2815
    pos = y * width + x;
67,500✔
2816
    sign = direction >= 0 ? 1 : -1;
67,500!
2817

2818
    for (i = 0; i < 2; ++i) {
202,500✔
2819
        int neighbor;
2820

2821
        neighbor = x + sign * row0_offsets[i];
135,000✔
2822
        if (neighbor < 0 || neighbor >= width) {
135,000!
2823
            continue;
2,025✔
2824
        }
2825
        error_diffuse_normal(data,
177,300✔
2826
                             pos + (neighbor - x),
132,975✔
2827
                             depth, error,
44,325✔
2828
                             row0_num[i], row0_den[i]);
132,975✔
2829
    }
44,325✔
2830
    if (y < height - 1) {
67,500✔
2831
        int row;
2832

2833
        row = pos + width;
66,600✔
2834
        for (i = 0; i < 5; ++i) {
399,600✔
2835
            int neighbor;
2836

2837
            neighbor = x + sign * row1_offsets[i];
333,000✔
2838
            if (neighbor < 0 || neighbor >= width) {
333,000✔
2839
                continue;
3,996✔
2840
            }
2841
            error_diffuse_normal(data,
438,672✔
2842
                                 row + (neighbor - x),
329,004✔
2843
                                 depth, error,
109,668✔
2844
                                 row1_num[i], row1_den[i]);
329,004✔
2845
        }
109,668✔
2846
    }
22,200✔
2847
}
67,500✔
2848

2849
static void
2850
diffuse_burkes_carry(int32_t *carry_curr, int32_t *carry_next,
×
2851
                     int32_t *carry_far, int width, int height,
2852
                     int depth, int x, int y, int32_t error,
2853
                     int direction, int channel)
2854
{
2855
    /* Burkes' Method
2856
     *                  curr    4/16    2/16
2857
     *  1/16    2/16    4/16    2/16    1/16
2858
     */
2859
    static const int row0_offsets[] = { 1, 2 };
2860
    static const int row0_num[] = { 1, 1 };
2861
    static const int row0_den[] = { 4, 8 };
2862
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
2863
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
2864
    static const int row1_den[] = { 16, 8, 4, 8, 16 };
2865
    int sign;
2866
    int i;
2867

2868
    /* unused */ (void) carry_far;
2869

2870
    if (error == 0) {
×
2871
        return;
×
2872
    }
2873

2874
    sign = direction >= 0 ? 1 : -1;
×
2875
    for (i = 0; i < 2; ++i) {
×
2876
        int neighbor;
2877
        int32_t term;
2878

2879
        neighbor = x + sign * row0_offsets[i];
×
2880
        if (neighbor < 0 || neighbor >= width) {
×
2881
            continue;
×
2882
        }
2883
        term = diffuse_fixed_term(error, row0_num[i], row0_den[i]);
×
2884
        carry_curr[((size_t)neighbor * (size_t)depth)
×
2885
                   + (size_t)channel] += term;
×
2886
    }
2887
    if (y + 1 < height) {
×
2888
        for (i = 0; i < 5; ++i) {
×
2889
            int neighbor;
2890
            int32_t term;
2891

2892
            neighbor = x + sign * row1_offsets[i];
×
2893
            if (neighbor < 0 || neighbor >= width) {
×
2894
                continue;
×
2895
            }
2896
            term = diffuse_fixed_term(error, row1_num[i], row1_den[i]);
×
2897
            carry_next[((size_t)neighbor * (size_t)depth)
×
2898
                       + (size_t)channel] += term;
×
2899
        }
2900
    }
2901
}
2902

2903
static void
2904
diffuse_lso1(unsigned char *data, int width, int height,
×
2905
             int x, int y, int depth, int error, int direction)
2906
{
2907
    int pos;
2908
    int sign;
2909

2910
    pos = y * width + x;
×
2911
    sign = direction >= 0 ? 1 : -1;
×
2912

2913
    /* lso1 (libsixel original) method:
2914
     *
2915
     * libsixel-specific error diffusion (dithering) to improve sixel
2916
     * compression; by steering error propagation so out-of-palette
2917
     * intermediate colors render as horizontal bands rather than grainy
2918
     * noise, we increase RLE more effective.
2919
     *
2920
     *          curr
2921
     *   1/8    4/8    1/8
2922
     *          2/8
2923
     */
2924
    if (y < height - 1) {
×
2925
        int row;
2926

2927
        row = pos + width;
×
2928
        if (x - sign >= 0 && x - sign < width) {
×
2929
            error_diffuse_fast(data,
×
2930
                               row + (-sign),
2931
                               depth, error, 1, 8);
2932
        }
2933
        error_diffuse_fast(data, row, depth, error, 4, 8);
×
2934
        if (x + sign >= 0 && x + sign < width) {
×
2935
            error_diffuse_fast(data,
×
2936
                               row + sign,
2937
                               depth, error, 1, 8);
2938
        }
2939
    }
2940
    if (y < height - 2) {
×
2941
        error_diffuse_fast(data, pos + width * 2, depth, error, 2, 8);
×
2942
    }
2943
}
×
2944

2945

2946
static void
2947
diffuse_lso1_carry(int32_t *carry_curr, int32_t *carry_next,
×
2948
                   int32_t *carry_far, int width, int height,
2949
                   int depth, int x, int y, int32_t error,
2950
                   int direction, int channel)
2951
{
2952
    int sign;
2953
    int32_t edge_term;
2954
    int32_t center_term;
2955
    int32_t far_term;
2956

2957
    /* unused */ (void) carry_curr;
2958
    if (error == 0) {
×
2959
        return;
×
2960
    }
2961

2962
    sign = direction >= 0 ? 1 : -1;
×
2963
    edge_term = diffuse_fixed_term(error, 1, 8);
×
2964
    center_term = diffuse_fixed_term(error, 4, 8);
×
2965
    far_term = diffuse_fixed_term(error, 2, 8);
×
2966

2967
    if (y + 1 < height) {
×
2968
        if (x - sign >= 0 && x - sign < width) {
×
2969
            size_t base;
2970

2971
            base = ((size_t)(x - sign) * (size_t)depth)
×
2972
                 + (size_t)channel;
×
2973
            carry_next[base] += edge_term;
×
2974
        }
2975
        {
2976
            size_t base;
2977

2978
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
2979
            carry_next[base] += center_term;
×
2980
        }
2981
        if (x + sign >= 0 && x + sign < width) {
×
2982
            size_t base;
2983

2984
            base = ((size_t)(x + sign) * (size_t)depth)
×
2985
                 + (size_t)channel;
×
2986
            carry_next[base] += edge_term;
×
2987
        }
2988
    }
2989
    if (y + 2 < height) {
×
2990
        size_t base;
2991

2992
        base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
2993
        carry_far[base] += far_term;
×
2994
    }
2995
}
2996

2997
static float
2998
mask_a (int x, int y, int c)
×
2999
{
3000
    return ((((x + c * 67) + y * 236) * 119) & 255 ) / 128.0 - 1.0;
×
3001
}
3002

3003
static float
3004
mask_x (int x, int y, int c)
×
3005
{
3006
    return ((((x + c * 29) ^ y* 149) * 1234) & 511 ) / 256.0 - 1.0;
×
3007
}
3008

3009
/* lookup closest color from palette with "normal" strategy */
3010
static int
3011
lookup_normal(unsigned char const * const pixel,
849,900✔
3012
              int const depth,
3013
              unsigned char const * const palette,
3014
              int const reqcolor,
3015
              unsigned short * const cachetable,
3016
              int const complexion)
3017
{
3018
    int result;
3019
    int diff;
3020
    int r;
3021
    int i;
3022
    int n;
3023
    int distant;
3024

3025
    result = (-1);
849,900✔
3026
    diff = INT_MAX;
849,900✔
3027

3028
    /* don't use cachetable in 'normal' strategy */
3029
    (void) cachetable;
283,300✔
3030

3031
    for (i = 0; i < reqcolor; i++) {
15,309,900✔
3032
        distant = 0;
14,460,000✔
3033
        r = pixel[0] - palette[i * depth + 0];
14,460,000✔
3034
        distant += r * r * complexion;
14,460,000✔
3035
        for (n = 1; n < depth; ++n) {
43,380,000✔
3036
            r = pixel[n] - palette[i * depth + n];
28,920,000✔
3037
            distant += r * r;
28,920,000✔
3038
        }
9,640,000✔
3039
        if (distant < diff) {
14,460,000✔
3040
            diff = distant;
2,193,609✔
3041
            result = i;
2,193,609✔
3042
        }
731,017✔
3043
    }
4,820,000✔
3044

3045
    return result;
849,900✔
3046
}
3047

3048

3049
/* lookup closest color from palette with "fast" strategy */
3050
static int
3051
lookup_fast(unsigned char const * const pixel,
15,735,286✔
3052
            int const depth,
3053
            unsigned char const * const palette,
3054
            int const reqcolor,
3055
            unsigned short * const cachetable,
3056
            int const complexion)
3057
{
3058
    int result;
3059
    unsigned int hash;
3060
    int diff;
3061
    int cache;
3062
    int i;
3063
    int distant;
3064

3065
    /* don't use depth in 'fast' strategy because it's always 3 */
3066
    (void) depth;
8,049,482✔
3067

3068
    result = (-1);
15,735,286✔
3069
    diff = INT_MAX;
15,735,286✔
3070
    hash = computeHash(pixel, 3);
15,735,286✔
3071

3072
    cache = cachetable[hash];
15,735,286✔
3073
    if (cache) {  /* fast lookup */
15,735,286✔
3074
        return cache - 1;
15,455,783✔
3075
    }
3076
    /* collision */
3077
    for (i = 0; i < reqcolor; i++) {
32,823,098✔
3078
        distant = 0;
32,543,595✔
3079
#if 0
3080
        for (n = 0; n < 3; ++n) {
3081
            r = pixel[n] - palette[i * 3 + n];
3082
            distant += r * r;
3083
        }
3084
#elif 1  /* complexion correction */
3085
        distant = (pixel[0] - palette[i * 3 + 0]) * (pixel[0] - palette[i * 3 + 0]) * complexion
43,538,218✔
3086
                + (pixel[1] - palette[i * 3 + 1]) * (pixel[1] - palette[i * 3 + 1])
32,543,595✔
3087
                + (pixel[2] - palette[i * 3 + 2]) * (pixel[2] - palette[i * 3 + 2])
32,543,595✔
3088
                ;
3089
#endif
3090
        if (distant < diff) {
32,543,595✔
3091
            diff = distant;
1,839,632✔
3092
            result = i;
1,839,632✔
3093
        }
609,068✔
3094
    }
10,994,623✔
3095
    cachetable[hash] = result + 1;
279,503✔
3096

3097
    return result;
279,503✔
3098
}
8,049,482✔
3099

3100

3101
static int
3102
lookup_mono_darkbg(unsigned char const * const pixel,
1,730,520✔
3103
                   int const depth,
3104
                   unsigned char const * const palette,
3105
                   int const reqcolor,
3106
                   unsigned short * const cachetable,
3107
                   int const complexion)
3108
{
3109
    int n;
3110
    int distant;
3111

3112
    /* unused */ (void) palette;
576,840✔
3113
    /* unused */ (void) cachetable;
576,840✔
3114
    /* unused */ (void) complexion;
576,840✔
3115

3116
    distant = 0;
1,730,520✔
3117
    for (n = 0; n < depth; ++n) {
6,922,080✔
3118
        distant += pixel[n];
5,191,560✔
3119
    }
1,730,520✔
3120
    return distant >= 128 * reqcolor ? 1: 0;
1,730,520✔
3121
}
3122

3123

3124
static int
3125
lookup_mono_lightbg(unsigned char const * const pixel,
810,000✔
3126
                    int const depth,
3127
                    unsigned char const * const palette,
3128
                    int const reqcolor,
3129
                    unsigned short * const cachetable,
3130
                    int const complexion)
3131
{
3132
    int n;
3133
    int distant;
3134

3135
    /* unused */ (void) palette;
270,000✔
3136
    /* unused */ (void) cachetable;
270,000✔
3137
    /* unused */ (void) complexion;
270,000✔
3138

3139
    distant = 0;
810,000✔
3140
    for (n = 0; n < depth; ++n) {
3,240,000✔
3141
        distant += pixel[n];
2,430,000✔
3142
    }
810,000✔
3143
    return distant < 128 * reqcolor ? 1: 0;
810,000✔
3144
}
3145

3146

3147
/* choose colors using median-cut method */
3148
SIXELSTATUS
3149
sixel_quant_make_palette(
232✔
3150
    unsigned char          /* out */ **result,
3151
    unsigned char const    /* in */  *data,
3152
    unsigned int           /* in */  length,
3153
    int                    /* in */  pixelformat,
3154
    unsigned int           /* in */  reqcolors,
3155
    unsigned int           /* in */  *ncolors,
3156
    unsigned int           /* in */  *origcolors,
3157
    int                    /* in */  methodForLargest,
3158
    int                    /* in */  methodForRep,
3159
    int                    /* in */  qualityMode,
3160
    sixel_allocator_t      /* in */  *allocator)
3161
{
3162
    SIXELSTATUS status = SIXEL_FALSE;
232✔
3163
    unsigned int i;
3164
    unsigned int n;
3165
    int ret;
3166
    tupletable2 colormap;
3167
    unsigned int depth;
3168
    int result_depth;
3169

3170
    result_depth = sixel_helper_compute_depth(pixelformat);
232✔
3171
    if (result_depth <= 0) {
232!
3172
        *result = NULL;
×
3173
        goto end;
×
3174
    }
3175

3176
    depth = (unsigned int)result_depth;
232✔
3177

3178
    ret = computeColorMapFromInput(data, length, depth,
340✔
3179
                                   reqcolors, methodForLargest,
108✔
3180
                                   methodForRep, qualityMode,
108✔
3181
                                   &colormap, origcolors, allocator);
108✔
3182
    if (ret != 0) {
232!
3183
        *result = NULL;
×
3184
        goto end;
×
3185
    }
3186
    *ncolors = colormap.size;
232✔
3187
    quant_trace(stderr, "tupletable size: %d\n", *ncolors);
232✔
3188
    *result = (unsigned char *)sixel_allocator_malloc(allocator, *ncolors * depth);
232✔
3189
    for (i = 0; i < *ncolors; i++) {
9,491✔
3190
        for (n = 0; n < depth; ++n) {
37,036✔
3191
            (*result)[i * depth + n] = colormap.table[i]->tuple[n];
27,777✔
3192
        }
9,939✔
3193
    }
3,313✔
3194

3195
    sixel_allocator_free(allocator, colormap.table);
232✔
3196

3197
    status = SIXEL_OK;
232✔
3198

3199
end:
124✔
3200
    return status;
232✔
3201
}
3202

3203

3204
/* apply color palette into specified pixel buffers */
3205
SIXELSTATUS
3206
sixel_quant_apply_palette(
254✔
3207
    sixel_index_t     /* out */ *result,
3208
    unsigned char     /* in */  *data,
3209
    int               /* in */  width,
3210
    int               /* in */  height,
3211
    int               /* in */  depth,
3212
    unsigned char     /* in */  *palette,
3213
    int               /* in */  reqcolor,
3214
    int               /* in */  methodForDiffuse,
3215
    int               /* in */  methodForScan,
3216
    int               /* in */  methodForCarry,
3217
    int               /* in */  foptimize,
3218
    int               /* in */  foptimize_palette,
3219
    int               /* in */  complexion,
3220
    unsigned short    /* in */  *cachetable,
3221
    int               /* in */  *ncolors,
3222
    sixel_allocator_t /* in */  *allocator)
3223
{
150✔
3224
#if _MSC_VER
3225
    enum { max_depth = 4 };
3226
#else
3227
    const size_t max_depth = 4;
254✔
3228
#endif
3229
    unsigned char copy[max_depth];
150✔
3230
    SIXELSTATUS status = SIXEL_FALSE;
254✔
3231
    int sum1, sum2;
3232
    int n;
3233
    unsigned short *indextable;
3234
    unsigned char new_palette[SIXEL_PALETTE_MAX * 4];
3235
    unsigned short migration_map[SIXEL_PALETTE_MAX];
3236
    int (*f_lookup)(unsigned char const * const pixel,
3237
                    int const depth,
3238
                    unsigned char const * const palette,
3239
                    int const reqcolor,
3240
                    unsigned short * const cachetable,
3241
                    int const complexion);
3242
    int use_varerr;
3243
    int use_positional;
3244
    int carry_mode;
3245

3246
    /* check bad reqcolor */
3247
    if (reqcolor < 1) {
254!
3248
        status = SIXEL_BAD_ARGUMENT;
×
3249
        sixel_helper_set_additional_message(
×
3250
            "sixel_quant_apply_palette: "
3251
            "a bad argument is detected, reqcolor < 0.");
3252
        goto end;
×
3253
    }
3254

3255
    /* NOTE: diffuse_jajuni, diffuse_stucki, and diffuse_burkes reference at
3256
     * minimum the position pos + width * 1 - 2, so width must be at least 2
3257
     * to avoid underflow.
3258
     * On the other hand, diffuse_fs and diffuse_atkinson
3259
     * reference pos + width * 1 - 1, but since these functions are only called
3260
     * when width >= 1, they do not cause underflow.
3261
     */
3262
    use_varerr = (depth == 3
358✔
3263
                  && (methodForDiffuse == SIXEL_DIFFUSE_LSO2
508!
3264
                      || methodForDiffuse == SIXEL_DIFFUSE_LSO3));
254!
3265
    use_positional = (methodForDiffuse == SIXEL_DIFFUSE_A_DITHER
358✔
3266
                      || methodForDiffuse == SIXEL_DIFFUSE_X_DITHER);
254!
3267
    carry_mode = (methodForCarry == SIXEL_CARRY_ENABLE)
254✔
3268
               ? SIXEL_CARRY_ENABLE
3269
               : SIXEL_CARRY_DISABLE;
150!
3270

3271
    f_lookup = NULL;
254✔
3272
    if (reqcolor == 2) {
254✔
3273
        sum1 = 0;
19✔
3274
        sum2 = 0;
19✔
3275
        for (n = 0; n < depth; ++n) {
76✔
3276
            sum1 += palette[n];
57✔
3277
        }
21✔
3278
        for (n = depth; n < depth + depth; ++n) {
76✔
3279
            sum2 += palette[n];
57✔
3280
        }
21✔
3281
        if (sum1 == 0 && sum2 == 255 * 3) {
19!
3282
            f_lookup = lookup_mono_darkbg;
12✔
3283
        } else if (sum1 == 255 * 3 && sum2 == 0) {
11!
3284
            f_lookup = lookup_mono_lightbg;
3✔
3285
        }
1✔
3286
    }
7✔
3287
    if (f_lookup == NULL) {
254✔
3288
        if (foptimize && depth == 3) {
239!
3289
            f_lookup = lookup_fast;
233✔
3290
        } else {
97✔
3291
            f_lookup = lookup_normal;
6✔
3292
        }
3293
    }
99✔
3294

3295
    indextable = cachetable;
254✔
3296
    if (cachetable == NULL && f_lookup == lookup_fast) {
254!
3297
        indextable = (unsigned short *)sixel_allocator_calloc(allocator,
×
3298
                                                              (size_t)(1 << depth * 5),
×
3299
                                                              sizeof(unsigned short));
3300
        if (!indextable) {
×
3301
            quant_trace(stderr, "Unable to allocate memory for indextable.\n");
×
3302
            goto end;
×
3303
        }
3304
    }
3305

3306
    if (use_positional) {
254!
3307
        status = apply_palette_positional(result, data, width, height,
×
3308
                                          depth, palette, reqcolor,
3309
                                          methodForDiffuse, methodForScan,
3310
                                          foptimize_palette, f_lookup,
3311
                                          indextable, complexion, copy,
3312
                                          new_palette, migration_map,
3313
                                          ncolors);
3314
    } else if (use_varerr) {
254!
3315
        status = apply_palette_variable(result, data, width, height,
×
3316
                                        depth, palette, reqcolor,
3317
                                        methodForScan, foptimize_palette,
3318
                                        f_lookup, indextable, complexion,
3319
                                        new_palette, migration_map,
3320
                                        ncolors,
3321
                                        methodForDiffuse,
3322
                                        carry_mode);
3323
    } else {
3324
        status = apply_palette_fixed(result, data, width, height,
358✔
3325
                                     depth, palette, reqcolor,
104✔
3326
                                     methodForScan, foptimize_palette,
104✔
3327
                                     f_lookup, indextable, complexion,
104✔
3328
                                     new_palette, migration_map,
104✔
3329
                                     ncolors, methodForDiffuse,
104✔
3330
                                     carry_mode);
104✔
3331
    }
3332
    if (SIXEL_FAILED(status)) {
254!
3333
        goto end;
×
3334
    }
3335

3336
    if (cachetable == NULL) {
254✔
3337
        sixel_allocator_free(allocator, indextable);
18✔
3338
    }
6✔
3339

3340
    status = SIXEL_OK;
254✔
3341

3342
end:
150✔
3343
    return status;
254✔
3344
}
3345

3346

3347
void
3348
sixel_quant_free_palette(
232✔
3349
    unsigned char       /* in */ *data,
3350
    sixel_allocator_t   /* in */ *allocator)
3351
{
3352
    sixel_allocator_free(allocator, data);
232✔
3353
}
232✔
3354

3355

3356
#if HAVE_TESTS
3357
static int
3358
test1(void)
×
3359
{
3360
    int nret = EXIT_FAILURE;
×
3361
    sample minval[1] = { 1 };
×
3362
    sample maxval[1] = { 2 };
×
3363
    unsigned int retval;
3364

3365
    retval = largestByLuminosity(minval, maxval, 1);
×
3366
    if (retval != 0) {
×
3367
        goto error;
×
3368
    }
3369
    nret = EXIT_SUCCESS;
×
3370

3371
error:
3372
    return nret;
×
3373
}
3374

3375

3376
int
3377
sixel_quant_tests_main(void)
×
3378
{
3379
    int nret = EXIT_FAILURE;
×
3380
    size_t i;
3381
    typedef int (* testcase)(void);
3382

3383
    static testcase const testcases[] = {
3384
        test1,
3385
    };
3386

3387
    for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) {
×
3388
        nret = testcases[i]();
×
3389
        if (nret != EXIT_SUCCESS) {
×
3390
            goto error;
×
3391
        }
3392
    }
3393

3394
    nret = EXIT_SUCCESS;
×
3395

3396
error:
3397
    return nret;
×
3398
}
3399
#endif  /* HAVE_TESTS */
3400

3401
/* emacs Local Variables:      */
3402
/* emacs mode: c               */
3403
/* emacs tab-width: 4          */
3404
/* emacs indent-tabs-mode: nil */
3405
/* emacs c-basic-offset: 4     */
3406
/* emacs End:                  */
3407
/* vim: set expandtab ts=4 sts=4 sw=4 : */
3408
/* EOF */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc