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

saitoha / libsixel / 18607471127

17 Oct 2025 11:46PM UTC coverage: 54.284%. First build
18607471127

push

github

saitoha
feat: introduce new diffusion method lso2, lso3

5408 of 14133 branches covered (38.27%)

41 of 232 new or added lines in 3 files covered. (17.67%)

7071 of 13026 relevant lines covered (54.28%)

1145864.07 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 (*
NEW
149
lso2_table(void))[7]
×
150
{
151
    /*
152
     * Coefficients follow Victor Ostromoukhov, "A Simple and Efficient
153
     * Error-Diffusion Algorithm", SIGGRAPH 2001
154
     * (https://perso.liris.cnrs.fr/victor.ostromoukhov/publications/pdf/
155
     *  SIGGRAPH01_varcoeffED.pdf).  Each tuple lists the weights for the
156
     * neighbours documented near diffuse_varerr_term().  The fourth element
157
     * matches the sum of the first three, preserving the normalization from
158
     * the paper.
159
     */
160
#include "lso2.h"
161
    return var_coefs;
162
}
163

164
static const int (*
NEW
165
lso3_table(void))[7]
×
166
{
167
/* Auto-generated by gen_varcoefs.awk */
168
#include "lso3.h"
169
    return var_coefs;
170
}
171

172

173
#define VARERR_SCALE_SHIFT 12
174
#define VARERR_SCALE       (1 << VARERR_SCALE_SHIFT)
175
#define VARERR_ROUND       (1 << (VARERR_SCALE_SHIFT - 1))
176
#define VARERR_MAX_VALUE   (255 * VARERR_SCALE)
177

178
#if HAVE_DEBUG
179
#define quant_trace fprintf
180
#else
181
static inline void quant_trace(FILE *f, ...) { (void) f; }
182
#endif
183

184
/*****************************************************************************
185
 *
186
 * quantization
187
 *
188
 *****************************************************************************/
189

190
typedef struct box* boxVector;
191
struct box {
192
    unsigned int ind;
193
    unsigned int colors;
194
    unsigned int sum;
195
};
196

197
typedef unsigned long sample;
198
typedef sample * tuple;
199

200
struct tupleint {
201
    /* An ordered pair of a tuple value and an integer, such as you
202
       would find in a tuple table or tuple hash.
203
       Note that this is a variable length structure.
204
    */
205
    unsigned int value;
206
    sample tuple[1];
207
    /* This is actually a variable size array -- its size is the
208
       depth of the tuple in question.  Some compilers do not let us
209
       declare a variable length array.
210
    */
211
};
212
typedef struct tupleint ** tupletable;
213

214
typedef struct {
215
    unsigned int size;
216
    tupletable table;
217
} tupletable2;
218

219
static unsigned int compareplanePlane;
220
    /* This is a parameter to compareplane().  We use this global variable
221
       so that compareplane() can be called by qsort(), to compare two
222
       tuples.  qsort() doesn't pass any arguments except the two tuples.
223
    */
224
static int
225
compareplane(const void * const arg1,
1,585,775✔
226
             const void * const arg2)
227
{
228
    int lhs, rhs;
229

230
    typedef const struct tupleint * const * const sortarg;
231
    sortarg comparandPP  = (sortarg) arg1;
1,585,775✔
232
    sortarg comparatorPP = (sortarg) arg2;
1,585,775✔
233
    lhs = (int)(*comparandPP)->tuple[compareplanePlane];
1,585,775✔
234
    rhs = (int)(*comparatorPP)->tuple[compareplanePlane];
1,585,775✔
235

236
    return lhs - rhs;
1,585,775✔
237
}
238

239

240
static int
241
sumcompare(const void * const b1, const void * const b2)
4,028,293✔
242
{
243
    return (int)((boxVector)b2)->sum - (int)((boxVector)b1)->sum;
4,028,293✔
244
}
245

246

247
static SIXELSTATUS
248
alloctupletable(
464✔
249
    tupletable          /* out */ *result,
250
    unsigned int const  /* in */  depth,
251
    unsigned int const  /* in */  size,
252
    sixel_allocator_t   /* in */  *allocator)
253
{
254
    SIXELSTATUS status = SIXEL_FALSE;
464✔
255
    enum { message_buffer_size = 256 };
256
    char message[message_buffer_size];
257
    int nwrite;
258
    unsigned int mainTableSize;
259
    unsigned int tupleIntSize;
260
    unsigned int allocSize;
261
    void * pool;
262
    tupletable tbl;
263
    unsigned int i;
264

265
    if (UINT_MAX / sizeof(struct tupleint) < size) {
464!
266
        nwrite = sprintf(message,
×
267
                         "size %u is too big for arithmetic",
268
                         size);
269
        if (nwrite > 0) {
×
270
            sixel_helper_set_additional_message(message);
×
271
        }
272
        status = SIXEL_RUNTIME_ERROR;
×
273
        goto end;
×
274
    }
275

276
    mainTableSize = size * sizeof(struct tupleint *);
464✔
277
    tupleIntSize = sizeof(struct tupleint) - sizeof(sample)
464✔
278
        + depth * sizeof(sample);
216✔
279

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

295
    allocSize = mainTableSize + size * tupleIntSize;
464✔
296

297
    pool = sixel_allocator_malloc(allocator, allocSize);
464✔
298
    if (pool == NULL) {
464!
299
        sprintf(message,
×
300
                "unable to allocate %u bytes for a %u-entry "
301
                "tuple table",
302
                 allocSize, size);
303
        sixel_helper_set_additional_message(message);
×
304
        status = SIXEL_BAD_ALLOCATION;
×
305
        goto end;
×
306
    }
307
    tbl = (tupletable) pool;
464✔
308

309
    for (i = 0; i < size; ++i)
68,508✔
310
        tbl[i] = (struct tupleint *)
68,044✔
311
            ((char*)pool + mainTableSize + i * tupleIntSize);
68,044✔
312

313
    *result = tbl;
464✔
314

315
    status = SIXEL_OK;
464✔
316

317
end:
248✔
318
    return status;
464✔
319
}
320

321

322
/*
323
** Here is the fun part, the median-cut colormap generator.  This is based
324
** on Paul Heckbert's paper "Color Image Quantization for Frame Buffer
325
** Display", SIGGRAPH '82 Proceedings, page 297.
326
*/
327

328
static tupletable2
329
newColorMap(unsigned int const newcolors, unsigned int const depth, sixel_allocator_t *allocator)
42✔
330
{
331
    SIXELSTATUS status = SIXEL_FALSE;
42✔
332
    tupletable2 colormap;
333
    unsigned int i;
334

335
    colormap.size = 0;
42✔
336
    status = alloctupletable(&colormap.table, depth, newcolors, allocator);
42✔
337
    if (SIXEL_FAILED(status)) {
42!
338
        goto end;
×
339
    }
340
    if (colormap.table) {
56!
341
        for (i = 0; i < newcolors; ++i) {
6,894✔
342
            unsigned int plane;
343
            for (plane = 0; plane < depth; ++plane)
27,408✔
344
                colormap.table[i]->tuple[plane] = 0;
20,556✔
345
        }
2,284✔
346
        colormap.size = newcolors;
42✔
347
    }
14✔
348

349
end:
350
    return colormap;
42✔
351
}
352

353

354
static boxVector
355
newBoxVector(
42✔
356
    unsigned int const  /* in */ colors,
357
    unsigned int const  /* in */ sum,
358
    unsigned int const  /* in */ newcolors,
359
    sixel_allocator_t   /* in */ *allocator)
360
{
361
    boxVector bv;
362

363
    bv = (boxVector)sixel_allocator_malloc(allocator,
56✔
364
                                           sizeof(struct box) * (size_t)newcolors);
42✔
365
    if (bv == NULL) {
42!
366
        quant_trace(stderr, "out of memory allocating box vector table\n");
×
367
        return NULL;
×
368
    }
369

370
    /* Set up the initial box. */
371
    bv[0].ind = 0;
42✔
372
    bv[0].colors = colors;
42✔
373
    bv[0].sum = sum;
42✔
374

375
    return bv;
42✔
376
}
14✔
377

378

379
static void
380
findBoxBoundaries(tupletable2  const colorfreqtable,
6,810✔
381
                  unsigned int const depth,
382
                  unsigned int const boxStart,
383
                  unsigned int const boxSize,
384
                  sample             minval[],
385
                  sample             maxval[])
386
{
387
/*----------------------------------------------------------------------------
388
  Go through the box finding the minimum and maximum of each
389
  component - the boundaries of the box.
390
-----------------------------------------------------------------------------*/
391
    unsigned int plane;
392
    unsigned int i;
393

394
    for (plane = 0; plane < depth; ++plane) {
27,240✔
395
        minval[plane] = colorfreqtable.table[boxStart]->tuple[plane];
20,430✔
396
        maxval[plane] = minval[plane];
20,430✔
397
    }
6,810✔
398

399
    for (i = 1; i < boxSize; ++i) {
341,460✔
400
        for (plane = 0; plane < depth; ++plane) {
1,338,600✔
401
            sample const v = colorfreqtable.table[boxStart + i]->tuple[plane];
1,003,950✔
402
            if (v < minval[plane]) minval[plane] = v;
1,003,950✔
403
            if (v > maxval[plane]) maxval[plane] = v;
1,003,950✔
404
        }
331,914✔
405
    }
110,638✔
406
}
6,810✔
407

408

409

410
static unsigned int
411
largestByNorm(sample minval[], sample maxval[], unsigned int const depth)
6,810✔
412
{
413

414
    unsigned int largestDimension;
415
    unsigned int plane;
416
    sample largestSpreadSoFar;
417

418
    largestSpreadSoFar = 0;
6,810✔
419
    largestDimension = 0;
6,810✔
420
    for (plane = 0; plane < depth; ++plane) {
27,240✔
421
        sample const spread = maxval[plane]-minval[plane];
20,430✔
422
        if (spread > largestSpreadSoFar) {
20,430✔
423
            largestDimension = plane;
11,321✔
424
            largestSpreadSoFar = spread;
11,321✔
425
        }
3,735✔
426
    }
6,810✔
427
    return largestDimension;
6,810✔
428
}
429

430

431

432
static unsigned int
433
largestByLuminosity(sample minval[], sample maxval[], unsigned int const depth)
×
434
{
435
/*----------------------------------------------------------------------------
436
   This subroutine presumes that the tuple type is either
437
   BLACKANDWHITE, GRAYSCALE, or RGB (which implies pamP->depth is 1 or 3).
438
   To save time, we don't actually check it.
439
-----------------------------------------------------------------------------*/
440
    unsigned int retval;
441

442
    double lumin_factor[3] = {0.2989, 0.5866, 0.1145};
×
443

444
    if (depth == 1) {
×
445
        retval = 0;
×
446
    } else {
447
        /* An RGB tuple */
448
        unsigned int largestDimension;
449
        unsigned int plane;
450
        double largestSpreadSoFar;
451

452
        largestSpreadSoFar = 0.0;
×
453
        largestDimension = 0;
×
454

455
        for (plane = 0; plane < 3; ++plane) {
×
456
            double const spread =
×
457
                lumin_factor[plane] * (maxval[plane]-minval[plane]);
×
458
            if (spread > largestSpreadSoFar) {
×
459
                largestDimension = plane;
×
460
                largestSpreadSoFar = spread;
×
461
            }
462
        }
463
        retval = largestDimension;
×
464
    }
465
    return retval;
×
466
}
467

468

469

470
static void
471
centerBox(unsigned int const boxStart,
6,084✔
472
          unsigned int const boxSize,
473
          tupletable2  const colorfreqtable,
474
          unsigned int const depth,
475
          tuple        const newTuple)
476
{
477

478
    unsigned int plane;
479
    sample minval, maxval;
480
    unsigned int i;
481

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

485
        for (i = 1; i < boxSize; ++i) {
156,249✔
486
            sample v = colorfreqtable.table[boxStart + i]->tuple[plane];
137,997✔
487
            minval = minval < v ? minval: v;
137,997✔
488
            maxval = maxval > v ? maxval: v;
137,997✔
489
        }
45,633✔
490
        newTuple[plane] = (minval + maxval) / 2;
18,252✔
491
    }
6,084✔
492
}
6,084✔
493

494

495

496
static void
497
averageColors(unsigned int const boxStart,
×
498
              unsigned int const boxSize,
499
              tupletable2  const colorfreqtable,
500
              unsigned int const depth,
501
              tuple        const newTuple)
502
{
503
    unsigned int plane;
504
    sample sum;
505
    unsigned int i;
506

507
    for (plane = 0; plane < depth; ++plane) {
×
508
        sum = 0;
×
509

510
        for (i = 0; i < boxSize; ++i) {
×
511
            sum += colorfreqtable.table[boxStart + i]->tuple[plane];
×
512
        }
513

514
        newTuple[plane] = sum / boxSize;
×
515
    }
516
}
×
517

518

519

520
static void
521
averagePixels(unsigned int const boxStart,
768✔
522
              unsigned int const boxSize,
523
              tupletable2 const colorfreqtable,
524
              unsigned int const depth,
525
              tuple const newTuple)
526
{
527

528
    unsigned int n;
529
        /* Number of tuples represented by the box */
530
    unsigned int plane;
531
    unsigned int i;
532

533
    /* Count the tuples in question */
534
    n = 0;  /* initial value */
768✔
535
    for (i = 0; i < boxSize; ++i) {
4,967✔
536
        n += (unsigned int)colorfreqtable.table[boxStart + i]->value;
4,199✔
537
    }
1,393✔
538

539
    for (plane = 0; plane < depth; ++plane) {
3,072✔
540
        sample sum;
541

542
        sum = 0;
2,304✔
543

544
        for (i = 0; i < boxSize; ++i) {
14,901✔
545
            sum += colorfreqtable.table[boxStart + i]->tuple[plane]
16,776✔
546
                * (unsigned int)colorfreqtable.table[boxStart + i]->value;
12,597✔
547
        }
4,179✔
548

549
        newTuple[plane] = sum / n;
2,304✔
550
    }
768✔
551
}
768✔
552

553

554

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

575
    colormap = newColorMap(newcolors, depth, allocator);
42✔
576
    if (!colormap.size) {
42!
577
        return colormap;
×
578
    }
579

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

606

607
static SIXELSTATUS
608
splitBox(boxVector const bv,
6,810✔
609
         unsigned int *const boxesP,
610
         unsigned int const bi,
611
         tupletable2 const colorfreqtable,
612
         unsigned int const depth,
613
         int const methodForLargest)
614
{
615
/*----------------------------------------------------------------------------
616
   Split Box 'bi' in the box vector bv (so that bv contains one more box
617
   than it did as input).  Split it so that each new box represents about
618
   half of the pixels in the distribution given by 'colorfreqtable' for
619
   the colors in the original box, but with distinct colors in each of the
620
   two new boxes.
621

622
   Assume the box contains at least two colors.
623
-----------------------------------------------------------------------------*/
624
    SIXELSTATUS status = SIXEL_FALSE;
6,810✔
625
    unsigned int const boxStart = bv[bi].ind;
6,810✔
626
    unsigned int const boxSize  = bv[bi].colors;
6,810✔
627
    unsigned int const sm       = bv[bi].sum;
6,810✔
628

629
    enum { max_depth= 16 };
630
    sample minval[max_depth];
631
    sample maxval[max_depth];
632

633
    /* assert(max_depth >= depth); */
634

635
    unsigned int largestDimension;
636
        /* number of the plane with the largest spread */
637
    unsigned int medianIndex;
638
    unsigned int lowersum;
639
        /* Number of pixels whose value is "less than" the median */
640

641
    findBoxBoundaries(colorfreqtable, depth, boxStart, boxSize,
9,080✔
642
                      minval, maxval);
2,270✔
643

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

663
    /* TODO: I think this sort should go after creating a box,
664
       not before splitting.  Because you need the sort to use
665
       the SIXEL_REP_CENTER_BOX method of choosing a color to
666
       represent the final boxes
667
    */
668

669
    /* Set the gross global variable 'compareplanePlane' as a
670
       parameter to compareplane(), which is called by qsort().
671
    */
672
    compareplanePlane = largestDimension;
6,810✔
673
    qsort((char*) &colorfreqtable.table[boxStart], boxSize,
6,810✔
674
          sizeof(colorfreqtable.table[boxStart]),
675
          compareplane);
676

677
    {
678
        /* Now find the median based on the counts, so that about half
679
           the pixels (not colors, pixels) are in each subdivision.  */
680

681
        unsigned int i;
682

683
        lowersum = colorfreqtable.table[boxStart]->value; /* initial value */
6,810✔
684
        for (i = 1; i < boxSize - 1 && lowersum < sm / 2; ++i) {
143,202✔
685
            lowersum += colorfreqtable.table[boxStart + i]->value;
136,392✔
686
        }
44,566✔
687
        medianIndex = i;
6,810✔
688
    }
689
    /* Split the box, and sort to bring the biggest boxes to the top.  */
690

691
    bv[bi].colors = medianIndex;
6,810✔
692
    bv[bi].sum = lowersum;
6,810✔
693
    bv[*boxesP].ind = boxStart + medianIndex;
6,810✔
694
    bv[*boxesP].colors = boxSize - medianIndex;
6,810✔
695
    bv[*boxesP].sum = sm - lowersum;
6,810✔
696
    ++(*boxesP);
6,810✔
697
    qsort((char*) bv, *boxesP, sizeof(struct box), sumcompare);
6,810✔
698

699
    status = SIXEL_OK;
6,810✔
700

701
end:
4,540✔
702
    return status;
6,810✔
703
}
704

705

706

707
static SIXELSTATUS
708
mediancut(tupletable2 const colorfreqtable,
42✔
709
          unsigned int const depth,
710
          unsigned int const newcolors,
711
          int const methodForLargest,
712
          int const methodForRep,
713
          tupletable2 *const colormapP,
714
          sixel_allocator_t *allocator)
715
{
716
/*----------------------------------------------------------------------------
717
   Compute a set of only 'newcolors' colors that best represent an
718
   image whose pixels are summarized by the histogram
719
   'colorfreqtable'.  Each tuple in that table has depth 'depth'.
720
   colorfreqtable.table[i] tells the number of pixels in the subject image
721
   have a particular color.
722

723
   As a side effect, sort 'colorfreqtable'.
724
-----------------------------------------------------------------------------*/
725
    boxVector bv;
726
    unsigned int bi;
727
    unsigned int boxes;
728
    int multicolorBoxesExist;
729
    unsigned int i;
730
    unsigned int sum;
731
    SIXELSTATUS status = SIXEL_FALSE;
42✔
732

733
    sum = 0;
42✔
734

735
    for (i = 0; i < colorfreqtable.size; ++i) {
56,324✔
736
        sum += colorfreqtable.table[i]->value;
56,282✔
737
    }
18,632✔
738

739
    /* There is at least one box that contains at least 2 colors; ergo,
740
       there is more splitting we can do.  */
741
    bv = newBoxVector(colorfreqtable.size, sum, newcolors, allocator);
42✔
742
    if (bv == NULL) {
42!
743
        goto end;
×
744
    }
745
    boxes = 1;
42✔
746
    multicolorBoxesExist = (colorfreqtable.size > 1);
42✔
747

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

768
    sixel_allocator_free(allocator, bv);
42✔
769

770
    status = SIXEL_OK;
42✔
771

772
end:
28✔
773
    return status;
42✔
774
}
775

776

777
static unsigned int
778
computeHash(unsigned char const *data, unsigned int const depth)
18,771,064✔
779
{
780
    unsigned int hash = 0;
18,771,064✔
781
    unsigned int n;
782

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

787
    return hash;
18,771,064✔
788
}
789

790

791
static SIXELSTATUS
792
computeHistogram(unsigned char const    /* in */  *data,
232✔
793
                 unsigned int           /* in */  length,
794
                 unsigned long const    /* in */  depth,
795
                 tupletable2 * const    /* out */ colorfreqtableP,
796
                 int const              /* in */  qualityMode,
797
                 sixel_allocator_t      /* in */  *allocator)
798
{
799
    SIXELSTATUS status = SIXEL_FALSE;
232✔
800
    typedef unsigned short unit_t;
801
    unsigned int i, n;
802
    unit_t *histogram = NULL;
232✔
803
    unit_t *refmap = NULL;
232✔
804
    unit_t *ref;
805
    unit_t *it;
806
    unsigned int bucket_index;
807
    unsigned int step;
808
    unsigned int max_sample;
809

810
    switch (qualityMode) {
232!
811
    case SIXEL_QUALITY_LOW:
102✔
812
        max_sample = 18383;
199✔
813
        break;
199✔
814
    case SIXEL_QUALITY_HIGH:
20✔
815
        max_sample = 1118383;
30✔
816
        break;
30✔
817
    case SIXEL_QUALITY_FULL:
3✔
818
    default:
819
        max_sample = 4003079;
3✔
820
        break;
3✔
821
    }
822

823
    step = length / depth / max_sample * depth;
232✔
824
    if (step <= 0) {
232✔
825
        step = depth;
156✔
826
    }
52✔
827

828
    quant_trace(stderr, "making histogram...\n");
232✔
829

830
    histogram = (unit_t *)sixel_allocator_calloc(allocator,
340✔
831
                                                 (size_t)(1 << depth * 5),
232✔
832
                                                 sizeof(unit_t));
833
    if (histogram == NULL) {
232!
834
        sixel_helper_set_additional_message(
×
835
            "unable to allocate memory for histogram.");
836
        status = SIXEL_BAD_ALLOCATION;
×
837
        goto end;
×
838
    }
839
    it = ref = refmap
232✔
840
        = (unsigned short *)sixel_allocator_malloc(allocator,
340✔
841
                                                   (size_t)(1 << depth * 5) * sizeof(unit_t));
232✔
842
    if (!it) {
232!
843
        sixel_helper_set_additional_message(
×
844
            "unable to allocate memory for lookup table.");
845
        status = SIXEL_BAD_ALLOCATION;
×
846
        goto end;
×
847
    }
848

849
    for (i = 0; i < length; i += step) {
3,036,010✔
850
        bucket_index = computeHash(data + i, 3);
3,035,778✔
851
        if (histogram[bucket_index] == 0) {
3,035,778✔
852
            *ref++ = bucket_index;
58,737✔
853
        }
19,709✔
854
        if (histogram[bucket_index] < (unsigned int)(1 << sizeof(unsigned short) * 8) - 1) {
3,035,778!
855
            histogram[bucket_index]++;
3,035,778✔
856
        }
1,586,006✔
857
    }
1,586,006✔
858

859
    colorfreqtableP->size = (unsigned int)(ref - refmap);
232✔
860

861
    status = alloctupletable(&colorfreqtableP->table, depth, (unsigned int)(ref - refmap), allocator);
232✔
862
    if (SIXEL_FAILED(status)) {
232!
863
        goto end;
×
864
    }
865
    for (i = 0; i < colorfreqtableP->size; ++i) {
58,969✔
866
        if (histogram[refmap[i]] > 0) {
58,737!
867
            colorfreqtableP->table[i]->value = histogram[refmap[i]];
58,737✔
868
            for (n = 0; n < depth; n++) {
234,948✔
869
                colorfreqtableP->table[i]->tuple[depth - 1 - n]
176,211✔
870
                    = (sample)((*it >> n * 5 & 0x1f) << 3);
235,338✔
871
            }
59,127✔
872
        }
19,709✔
873
        it++;
58,737✔
874
    }
19,709✔
875

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

878
    status = SIXEL_OK;
232✔
879

880
end:
124✔
881
    sixel_allocator_free(allocator, refmap);
232✔
882
    sixel_allocator_free(allocator, histogram);
232✔
883

884
    return status;
232✔
885
}
886

887

888
static int
889
computeColorMapFromInput(unsigned char const *data,
232✔
890
                         unsigned int const length,
891
                         unsigned int const depth,
892
                         unsigned int const reqColors,
893
                         int const methodForLargest,
894
                         int const methodForRep,
895
                         int const qualityMode,
896
                         tupletable2 * const colormapP,
897
                         unsigned int *origcolors,
898
                         sixel_allocator_t *allocator)
899
{
900
/*----------------------------------------------------------------------------
901
   Produce a colormap containing the best colors to represent the
902
   image stream in file 'ifP'.  Figure it out using the median cut
903
   technique.
904

905
   The colormap will have 'reqcolors' or fewer colors in it, unless
906
   'allcolors' is true, in which case it will have all the colors that
907
   are in the input.
908

909
   The colormap has the same maxval as the input.
910

911
   Put the colormap in newly allocated storage as a tupletable2
912
   and return its address as *colormapP.  Return the number of colors in
913
   it as *colorsP and its maxval as *colormapMaxvalP.
914

915
   Return the characteristics of the input file as
916
   *formatP and *freqPamP.  (This information is not really
917
   relevant to our colormap mission; just a fringe benefit).
918
-----------------------------------------------------------------------------*/
919
    SIXELSTATUS status = SIXEL_FALSE;
232✔
920
    tupletable2 colorfreqtable = {0, NULL};
232✔
921
    unsigned int i;
922
    unsigned int n;
923

924
    status = computeHistogram(data, length, depth,
340✔
925
                              &colorfreqtable, qualityMode, allocator);
108✔
926
    if (SIXEL_FAILED(status)) {
232!
927
        goto end;
×
928
    }
929
    if (origcolors) {
232!
930
        *origcolors = colorfreqtable.size;
232✔
931
    }
108✔
932

933
    if (colorfreqtable.size <= reqColors) {
232✔
934
        quant_trace(stderr,
284✔
935
                    "Image already has few enough colors (<=%d).  "
936
                    "Keeping same colors.\n", reqColors);
94✔
937
        /* *colormapP = colorfreqtable; */
938
        colormapP->size = colorfreqtable.size;
190✔
939
        status = alloctupletable(&colormapP->table, depth, colorfreqtable.size, allocator);
190✔
940
        if (SIXEL_FAILED(status)) {
190!
941
            goto end;
×
942
        }
943
        for (i = 0; i < colorfreqtable.size; ++i) {
2,645✔
944
            colormapP->table[i]->value = colorfreqtable.table[i]->value;
2,455✔
945
            for (n = 0; n < depth; ++n) {
9,820✔
946
                colormapP->table[i]->tuple[n] = colorfreqtable.table[i]->tuple[n];
7,365✔
947
            }
3,231✔
948
        }
1,077✔
949
    } else {
94✔
950
        quant_trace(stderr, "choosing %d colors...\n", reqColors);
42✔
951
        status = mediancut(colorfreqtable, depth, reqColors,
56✔
952
                           methodForLargest, methodForRep, colormapP, allocator);
14✔
953
        if (SIXEL_FAILED(status)) {
42!
954
            goto end;
×
955
        }
956
        quant_trace(stderr, "%d colors are choosed.\n", colorfreqtable.size);
42✔
957
    }
958

959
    status = SIXEL_OK;
232✔
960

961
end:
124✔
962
    sixel_allocator_free(allocator, colorfreqtable.table);
232✔
963
    return status;
232✔
964
}
965

966

967
/* diffuse error energy to surround pixels (normal strategy) */
968
static void
969
error_diffuse_normal(
50,778,513✔
970
    unsigned char /* in */    *data,      /* base address of pixel buffer */
971
    int           /* in */    pos,        /* address of the destination pixel */
972
    int           /* in */    depth,      /* color depth in bytes */
973
    int           /* in */    error,      /* error energy */
974
    int           /* in */    numerator,  /* numerator of diffusion coefficient */
975
    int           /* in */    denominator /* denominator of diffusion coefficient */)
976
{
977
    int c;
978

979
    data += pos * depth;
50,778,513✔
980

981
    c = *data + (error * numerator * 2 / denominator + 1) / 2;
50,778,513✔
982
    if (c < 0) {
50,778,513✔
983
        c = 0;
1,926,205✔
984
    }
650,141✔
985
    if (c >= 1 << 8) {
50,778,513✔
986
        c = (1 << 8) - 1;
656,770✔
987
    }
218,160✔
988
    *data = (unsigned char)c;
50,778,513✔
989
}
50,778,513✔
990

991
/* error diffusion with fast strategy */
992
static void
993
error_diffuse_fast(
154,936,299✔
994
    unsigned char /* in */    *data,      /* base address of pixel buffer */
995
    int           /* in */    pos,        /* address of the destination pixel */
996
    int           /* in */    depth,      /* color depth in bytes */
997
    int           /* in */    error,      /* error energy */
998
    int           /* in */    numerator,  /* numerator of diffusion coefficient */
999
    int           /* in */    denominator /* denominator of diffusion coefficient */)
1000
{
1001
    int c;
1002

1003
    data += pos * depth;
154,936,299✔
1004

1005
    c = *data + error * numerator / denominator;
154,936,299✔
1006
    if (c < 0) {
154,936,299✔
1007
        c = 0;
7,399,651✔
1008
    }
2,753,975✔
1009
    if (c >= 1 << 8) {
154,936,299✔
1010
        c = (1 << 8) - 1;
465,287✔
1011
    }
152,291✔
1012
    *data = (unsigned char)c;
154,936,299✔
1013
}
154,936,299✔
1014

1015

1016
/* error diffusion with precise strategy */
1017
static void
1018
error_diffuse_precise(
6,111,369✔
1019
    unsigned char /* in */    *data,      /* base address of pixel buffer */
1020
    int           /* in */    pos,        /* address of the destination pixel */
1021
    int           /* in */    depth,      /* color depth in bytes */
1022
    int           /* in */    error,      /* error energy */
1023
    int           /* in */    numerator,  /* numerator of diffusion coefficient */
1024
    int           /* in */    denominator /* denominator of diffusion coefficient */)
1025
{
1026
    int c;
1027

1028
    data += pos * depth;
6,111,369✔
1029

1030
    c = (int)(*data + error * numerator / (double)denominator + 0.5);
6,111,369✔
1031
    if (c < 0) {
6,111,369✔
1032
        c = 0;
74,306✔
1033
    }
25,570✔
1034
    if (c >= 1 << 8) {
6,111,369!
1035
        c = (1 << 8) - 1;
×
1036
    }
1037
    *data = (unsigned char)c;
6,111,369✔
1038
}
6,111,369✔
1039

1040

1041
typedef void (*diffuse_fixed_carry_mode)(int32_t *carry_curr,
1042
                                         int32_t *carry_next,
1043
                                         int32_t *carry_far,
1044
                                         int width,
1045
                                         int height,
1046
                                         int depth,
1047
                                         int x,
1048
                                         int y,
1049
                                         int32_t error,
1050
                                         int direction,
1051
                                         int channel);
1052

1053

1054
typedef void (*diffuse_varerr_mode)(unsigned char *data,
1055
                                    int width,
1056
                                    int height,
1057
                                    int x,
1058
                                    int y,
1059
                                    int depth,
1060
                                    int32_t error,
1061
                                    int index,
1062
                                    int direction);
1063

1064
typedef void (*diffuse_varerr_carry_mode)(int32_t *carry_curr,
1065
                                          int32_t *carry_next,
1066
                                          int32_t *carry_far,
1067
                                          int width,
1068
                                          int height,
1069
                                          int depth,
1070
                                          int x,
1071
                                          int y,
1072
                                          int32_t error,
1073
                                          int index,
1074
                                          int direction,
1075
                                          int channel);
1076

1077
static int
1078
zhoufang_index_from_byte(unsigned char value)
×
1079
{
1080
    double px;
1081
    double remapped;
1082
    int scale_index;
1083
    double scale;
1084
    double jitter;
1085
    int index;
1086

1087
    px = (double)value / 255.0;
×
1088
    remapped = px;
×
1089
    if (remapped >= 0.5) {
×
1090
        remapped = 1.0 - remapped;
×
1091
    }
1092
    if (remapped < 0.0) {
×
1093
        remapped = 0.0;
×
1094
    }
1095
    if (remapped > 0.5) {
×
1096
        remapped = 0.5;
×
1097
    }
1098

1099
    scale_index = (int)(remapped * 128.0);
×
1100
    if (scale_index < 0) {
×
1101
        scale_index = 0;
×
1102
    }
1103
    if (scale_index > 127) {
×
1104
        scale_index = 127;
×
1105
    }
1106

1107
    scale = zhoufang_threshold_gain[scale_index] / 100.0;
×
1108
    jitter = ((double)(rand() & 127) / 128.0) * scale;
×
1109
    remapped += (0.5 - remapped) * jitter;
×
1110
    if (remapped < 0.0) {
×
1111
        remapped = 0.0;
×
1112
    }
1113
    if (remapped > 0.5) {
×
1114
        remapped = 0.5;
×
1115
    }
1116

1117
    index = (int)(remapped * 255.0 + 0.5);
×
1118
    if (index > 127) {
×
1119
        index = 127;
×
1120
    }
1121
    if (index < 0) {
×
1122
        index = 0;
×
1123
    }
1124

1125
    return index;
×
1126
}
1127

1128

1129
/*
1130
 * Table lookups expect a tone index derived from the pixel luminance.  Each
1131
 * table entry provides weights for the neighbours shown below.  The fourth
1132
 * value is the shared denominator, equal to the sum of the first three.  The
1133
 * downwards coefficient is not used directly; instead we compute the residual
1134
 * error so that rounding matches Ostromoukhov's reference implementation.
1135
 *
1136
 *     forward scan (direction >= 0)             reverse scan (direction < 0)
1137
 *
1138
 *     (x, y)  o----->o     [0] (x+1, y)    (x-1, y) [0]     o<----o  (x, y)
1139
 *                                                                
1140
 *            [1]    [2]                             [2]    [1]
1141
 *      (x-1, y+1)  (x, y+1)                     (x, y+1)  (x+1, y+1)
1142
 */
1143

1144
static int32_t
1145
diffuse_varerr_term(int32_t error, int weight, int denom)
×
1146
{
1147
    int64_t delta;
1148

1149
    delta = (int64_t)error * (int64_t)weight;
×
1150
    if (delta >= 0) {
×
1151
        delta = (delta + denom / 2) / denom;
×
1152
    } else {
1153
        delta = (delta - denom / 2) / denom;
×
1154
    }
1155

1156
    return (int32_t)delta;
×
1157
}
1158

1159

1160
static int32_t
1161
diffuse_fixed_term(int32_t error, int numerator, int denominator)
×
1162
{
1163
    int64_t delta;
1164

1165
    delta = (int64_t)error * (int64_t)numerator;
×
1166
    if (delta >= 0) {
×
1167
        delta = (delta + denominator / 2) / denominator;
×
1168
    } else {
1169
        delta = (delta - denominator / 2) / denominator;
×
1170
    }
1171

1172
    return (int32_t)delta;
×
1173
}
1174

1175

1176
static void
1177
diffuse_varerr_apply_direct(unsigned char *target, int depth, size_t offset,
×
1178
                            int32_t delta)
1179
{
1180
    int64_t value;
1181
    int result;
1182

1183
    value = (int64_t)target[offset * depth] << VARERR_SCALE_SHIFT;
×
1184
    value += delta;
×
1185
    if (value < 0) {
×
1186
        value = 0;
×
1187
    } else {
1188
        int64_t max_value;
1189

1190
        max_value = VARERR_MAX_VALUE;
×
1191
        if (value > max_value) {
×
1192
            value = max_value;
×
1193
        }
1194
    }
1195

1196
    result = (int)((value + VARERR_ROUND) >> VARERR_SCALE_SHIFT);
×
1197
    if (result < 0) {
×
1198
        result = 0;
×
1199
    }
1200
    if (result > 255) {
×
1201
        result = 255;
×
1202
    }
1203
    target[offset * depth] = (unsigned char)result;
×
1204
}
×
1205

1206

1207
static void
NEW
1208
diffuse_lso2(unsigned char *data, int width, int height,
×
1209
             int x, int y, int depth, int32_t error,
1210
             int index, int direction)
1211
{
1212
    const int (*table)[7];
1213
    const int *entry;
1214
    int denom;
NEW
1215
    int32_t term_r = 0;
×
NEW
1216
    int32_t term_r2 = 0;
×
NEW
1217
    int32_t term_dl = 0;
×
NEW
1218
    int32_t term_d = 0;
×
NEW
1219
    int32_t term_dr = 0;
×
NEW
1220
    int32_t term_d2 = 0;
×
1221
    size_t offset;
1222

1223
    if (error == 0) {
×
1224
        return;
×
1225
    }
1226
    if (index < 0) {
×
1227
        index = 0;
×
1228
    }
1229
    if (index > 255) {
×
1230
        index = 255;
×
1231
    }
1232

NEW
1233
    table = lso2_table();
×
1234
    entry = table[index];
×
NEW
1235
    denom = entry[6];
×
1236
    if (denom == 0) {
×
1237
        return;
×
1238
    }
1239

1240
    term_r = diffuse_varerr_term(error, entry[0], denom);
×
NEW
1241
    term_r2 = diffuse_varerr_term(error, entry[1], denom);
×
NEW
1242
    term_dl = diffuse_varerr_term(error, entry[2], denom);
×
NEW
1243
    term_d = diffuse_varerr_term(error, entry[3], denom);
×
NEW
1244
    term_dr = diffuse_varerr_term(error, entry[4], denom);
×
1245
#if 0
1246
    term_d2 = error - term_r - term_r2 - term_dl - term_d - term_dr;
1247
#else
NEW
1248
    term_dr = diffuse_varerr_term(error, entry[5], denom);
×
1249
#endif
1250

1251

NEW
1252
    if (direction >= 0) {
×
1253
        if (x + 1 < width) {
×
1254
            offset = (size_t)y * (size_t)width + (size_t)(x + 1);
×
1255
            diffuse_varerr_apply_direct(data, depth, offset, term_r);
×
1256
        }
NEW
1257
        if (x + 2 < width) {
×
NEW
1258
            offset = (size_t)y * (size_t)width + (size_t)(x + 2);
×
NEW
1259
            diffuse_varerr_apply_direct(data, depth, offset, term_r2);
×
1260
        }
1261
        if (y + 1 < height && x - 1 >= 0) {
×
1262
            offset = (size_t)(y + 1) * (size_t)width;
×
1263
            offset += (size_t)(x - 1);
×
1264
            diffuse_varerr_apply_direct(data, depth, offset, term_dl);
×
1265
        }
1266
        if (y + 1 < height) {
×
1267
            offset = (size_t)(y + 1) * (size_t)width + (size_t)x;
×
1268
            diffuse_varerr_apply_direct(data, depth, offset, term_d);
×
1269
        }
NEW
1270
        if (y + 1 < height && x + 1 < width) {
×
NEW
1271
            offset = (size_t)(y + 1) * (size_t)width;
×
NEW
1272
            offset += (size_t)(x + 1);
×
NEW
1273
            diffuse_varerr_apply_direct(data, depth, offset, term_dr);
×
1274
        }
NEW
1275
        if (y + 2 < height) {
×
NEW
1276
            offset = (size_t)(y + 2) * (size_t)width + (size_t)x;
×
NEW
1277
            diffuse_varerr_apply_direct(data, depth, offset, term_d2);
×
1278
        }
1279
    } else {
1280
        if (x - 1 >= 0) {
×
1281
            offset = (size_t)y * (size_t)width + (size_t)(x - 1);
×
1282
            diffuse_varerr_apply_direct(data, depth, offset, term_r);
×
1283
        }
NEW
1284
        if (x - 2 >= 0) {
×
NEW
1285
            offset = (size_t)y * (size_t)width + (size_t)(x - 2);
×
NEW
1286
            diffuse_varerr_apply_direct(data, depth, offset, term_r2);
×
1287
        }
1288
        if (y + 1 < height && x + 1 < width) {
×
1289
            offset = (size_t)(y + 1) * (size_t)width;
×
1290
            offset += (size_t)(x + 1);
×
1291
            diffuse_varerr_apply_direct(data, depth, offset, term_dl);
×
1292
        }
1293
        if (y + 1 < height) {
×
1294
            offset = (size_t)(y + 1) * (size_t)width + (size_t)x;
×
1295
            diffuse_varerr_apply_direct(data, depth, offset, term_d);
×
1296
        }
NEW
1297
        if (y + 1 < height && x - 1 >= 0) {
×
NEW
1298
            offset = (size_t)(y + 1) * (size_t)width;
×
NEW
1299
            offset += (size_t)(x - 1);
×
NEW
1300
            diffuse_varerr_apply_direct(data, depth, offset, term_dr);
×
1301
        }
NEW
1302
        if (y + 2 < height) {
×
NEW
1303
            offset = (size_t)(y + 2) * (size_t)width + (size_t)x;
×
NEW
1304
            diffuse_varerr_apply_direct(data, depth, offset, term_d2);
×
1305
        }
1306
    }
1307
}
1308

1309

1310
static void
NEW
1311
diffuse_lso3(unsigned char *data, int width, int height,
×
1312
             int x, int y, int depth, int32_t error,
1313
             int index, int direction)
1314
{
1315
    const int (*table)[7];
1316
    const int *entry;
1317
    int denom;
NEW
1318
    int32_t term_r = 0;
×
NEW
1319
    int32_t term_r2 = 0;
×
NEW
1320
    int32_t term_dl = 0;
×
NEW
1321
    int32_t term_d = 0;
×
NEW
1322
    int32_t term_dr = 0;
×
NEW
1323
    int32_t term_d2 = 0;
×
1324
    size_t offset;
1325

1326
    if (error == 0) {
×
1327
        return;
×
1328
    }
1329
    if (index < 0) {
×
1330
        index = 0;
×
1331
    }
1332
    if (index > 255) {
×
1333
        index = 255;
×
1334
    }
1335

NEW
1336
    table = lso3_table();
×
1337
    entry = table[index];
×
NEW
1338
    denom = entry[6];
×
1339
    if (denom == 0) {
×
1340
        return;
×
1341
    }
1342

1343
    term_r = diffuse_varerr_term(error, entry[0], denom);
×
NEW
1344
    term_r2 = diffuse_varerr_term(error, entry[1], denom);
×
NEW
1345
    term_dl = diffuse_varerr_term(error, entry[2], denom);
×
NEW
1346
    term_d = diffuse_varerr_term(error, entry[3], denom);
×
NEW
1347
    term_dr = diffuse_varerr_term(error, entry[4], denom);
×
1348
#if 0
1349
    term_d2 = error - term_r - term_r2 - term_dl - term_d - term_dr;
1350
#else
NEW
1351
    term_dr = diffuse_varerr_term(error, entry[5], denom);
×
1352
#endif
1353

1354
    if (direction >= 0) {
×
1355
        if (x + 1 < width) {
×
1356
            offset = (size_t)y * (size_t)width + (size_t)(x + 1);
×
1357
            diffuse_varerr_apply_direct(data, depth, offset, term_r);
×
1358
        }
NEW
1359
        if (x + 2 < width) {
×
NEW
1360
            offset = (size_t)y * (size_t)width + (size_t)(x + 2);
×
NEW
1361
            diffuse_varerr_apply_direct(data, depth, offset, term_r2);
×
1362
        }
1363
        if (y + 1 < height && x - 1 >= 0) {
×
1364
            offset = (size_t)(y + 1) * (size_t)width;
×
1365
            offset += (size_t)(x - 1);
×
1366
            diffuse_varerr_apply_direct(data, depth, offset, term_dl);
×
1367
        }
1368
        if (y + 1 < height) {
×
1369
            offset = (size_t)(y + 1) * (size_t)width + (size_t)x;
×
1370
            diffuse_varerr_apply_direct(data, depth, offset, term_d);
×
1371
        }
NEW
1372
        if (y + 1 < height && x + 1 < width) {
×
NEW
1373
            offset = (size_t)(y + 1) * (size_t)width;
×
NEW
1374
            offset += (size_t)(x + 1);
×
NEW
1375
            diffuse_varerr_apply_direct(data, depth, offset, term_dr);
×
1376
        }
NEW
1377
        if (y + 2 < height) {
×
NEW
1378
            offset = (size_t)(y + 2) * (size_t)width + (size_t)x;
×
NEW
1379
            diffuse_varerr_apply_direct(data, depth, offset, term_d2);
×
1380
        }
1381
    } else {
1382
        if (x - 1 >= 0) {
×
1383
            offset = (size_t)y * (size_t)width + (size_t)(x - 1);
×
1384
            diffuse_varerr_apply_direct(data, depth, offset, term_r);
×
1385
        }
NEW
1386
        if (x - 1 >= 0) {
×
NEW
1387
            offset = (size_t)y * (size_t)width + (size_t)(x - 2);
×
NEW
1388
            diffuse_varerr_apply_direct(data, depth, offset, term_r2);
×
1389
        }
1390
        if (y + 1 < height && x + 1 < width) {
×
1391
            offset = (size_t)(y + 1) * (size_t)width;
×
1392
            offset += (size_t)(x + 1);
×
1393
            diffuse_varerr_apply_direct(data, depth, offset, term_dl);
×
1394
        }
1395
        if (y + 1 < height) {
×
1396
            offset = (size_t)(y + 1) * (size_t)width + (size_t)x;
×
1397
            diffuse_varerr_apply_direct(data, depth, offset, term_d);
×
1398
        }
NEW
1399
        if (y + 1 < height && x - 1 >= 0) {
×
NEW
1400
            offset = (size_t)(y + 1) * (size_t)width;
×
NEW
1401
            offset += (size_t)(x - 1);
×
NEW
1402
            diffuse_varerr_apply_direct(data, depth, offset, term_dr);
×
1403
        }
NEW
1404
        if (y + 2 < height) {
×
NEW
1405
            offset = (size_t)(y + 2) * (size_t)width + (size_t)x;
×
NEW
1406
            diffuse_varerr_apply_direct(data, depth, offset, term_d2);
×
1407
        }
1408
    }
1409
}
1410

1411

1412
static void
NEW
1413
diffuse_lso2_carry(int32_t *carry_curr, int32_t *carry_next, int32_t *carry_far,
×
1414
                   int width, int height, int depth,
1415
                   int x, int y, int32_t error,
1416
                   int index, int direction, int channel)
1417
{
1418
    const int (*table)[7];
1419
    const int *entry;
1420
    int denom;
NEW
1421
    int32_t term_r = 0;
×
NEW
1422
    int32_t term_r2 = 0;
×
NEW
1423
    int32_t term_dl = 0;
×
NEW
1424
    int32_t term_d = 0;
×
NEW
1425
    int32_t term_dr = 0;
×
NEW
1426
    int32_t term_d2 = 0;
×
1427
    size_t base;
1428

1429
    if (error == 0) {
×
1430
        return;
×
1431
    }
1432
    if (index < 0) {
×
1433
        index = 0;
×
1434
    }
1435
    if (index > 255) {
×
1436
        index = 255;
×
1437
    }
1438

NEW
1439
    table = lso2_table();
×
1440
    entry = table[index];
×
NEW
1441
    denom = entry[6];
×
1442
    if (denom == 0) {
×
1443
        return;
×
1444
    }
1445

1446
    term_r = diffuse_varerr_term(error, entry[0], denom);
×
NEW
1447
    term_r2 = diffuse_varerr_term(error, entry[1], denom);
×
NEW
1448
    term_dl = diffuse_varerr_term(error, entry[2], denom);
×
NEW
1449
    term_d = diffuse_varerr_term(error, entry[3], denom);
×
NEW
1450
    term_dr = diffuse_varerr_term(error, entry[4], denom);
×
1451
#if 0
1452
    term_d2 = error - term_r - term_r2 - term_dl - term_d - term_dr;
1453
#else
NEW
1454
    term_dr = diffuse_varerr_term(error, entry[5], denom);
×
1455
#endif
1456

1457
    if (direction >= 0) {
×
1458
        if (x + 1 < width) {
×
1459
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
1460
            carry_curr[base] += term_r;
×
1461
        }
NEW
1462
        if (x + 2 < width) {
×
NEW
1463
            base = ((size_t)(x + 2) * (size_t)depth) + (size_t)channel;
×
NEW
1464
            carry_curr[base] += term_r2;
×
1465
        }
1466
        if (y + 1 < height && x - 1 >= 0) {
×
1467
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
1468
            carry_next[base] += term_dl;
×
1469
        }
1470
        if (y + 1 < height) {
×
1471
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
1472
            carry_next[base] += term_d;
×
1473
        }
NEW
1474
        if (y + 1 < height && x + 1 < width) {
×
NEW
1475
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
NEW
1476
            carry_next[base] += term_dr;
×
1477
        }
NEW
1478
        if (y + 2 < height) {
×
NEW
1479
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
NEW
1480
            carry_far[base] += term_d2;
×
1481
        }
1482
    } else {
1483
        if (x - 1 >= 0) {
×
1484
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
1485
            carry_curr[base] += term_r;
×
1486
        }
NEW
1487
        if (x - 2 >= 0) {
×
NEW
1488
            base = ((size_t)(x - 2) * (size_t)depth) + (size_t)channel;
×
NEW
1489
            carry_curr[base] += term_r;
×
1490
        }
1491
        if (y + 1 < height && x + 1 < width) {
×
1492
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
1493
            carry_next[base] += term_dl;
×
1494
        }
1495
        if (y + 1 < height) {
×
1496
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
1497
            carry_next[base] += term_d;
×
1498
        }
NEW
1499
        if (y + 1 < height && x - 1 >= 0) {
×
NEW
1500
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
NEW
1501
            carry_next[base] += term_dr;
×
1502
        }
NEW
1503
        if (y + 2 < height) {
×
NEW
1504
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
NEW
1505
            carry_far[base] += term_d2;
×
1506
        }
1507
    }
1508
}
1509

1510

1511
static void
NEW
1512
diffuse_lso3_carry(int32_t *carry_curr, int32_t *carry_next, int32_t *carry_far,
×
1513
                   int width, int height, int depth,
1514
                   int x, int y, int32_t error,
1515
                   int index, int direction, int channel)
1516
{
1517
    const int (*table)[7];
1518
    const int *entry;
1519
    int denom;
NEW
1520
    int32_t term_r = 0;
×
NEW
1521
    int32_t term_r2 = 0;
×
NEW
1522
    int32_t term_dl = 0;
×
NEW
1523
    int32_t term_d = 0;
×
NEW
1524
    int32_t term_dr = 0;
×
NEW
1525
    int32_t term_d2 = 0;
×
1526
    size_t base;
1527

1528
    if (error == 0) {
×
1529
        return;
×
1530
    }
1531
    if (index < 0) {
×
1532
        index = 0;
×
1533
    }
1534
    if (index > 255) {
×
1535
        index = 255;
×
1536
    }
1537

NEW
1538
    table = lso3_table();
×
1539
    entry = table[index];
×
NEW
1540
    denom = entry[6];
×
1541
    if (denom == 0) {
×
1542
        return;
×
1543
    }
1544

1545
    term_r = diffuse_varerr_term(error, entry[0], denom);
×
NEW
1546
    term_r2 = diffuse_varerr_term(error, entry[1], denom);
×
NEW
1547
    term_dl = diffuse_varerr_term(error, entry[2], denom);
×
NEW
1548
    term_d = diffuse_varerr_term(error, entry[3], denom);
×
NEW
1549
    term_dr = diffuse_varerr_term(error, entry[4], denom);
×
1550
#if 0
1551
    term_d2 = error - term_r - term_r2 - term_dl - term_d - term_dr;
1552
#else
NEW
1553
    term_dr = diffuse_varerr_term(error, entry[5], denom);
×
1554
#endif
1555

1556
    if (direction >= 0) {
×
1557
        if (x + 1 < width) {
×
1558
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
1559
            carry_curr[base] += term_r;
×
1560
        }
NEW
1561
        if (x + 2 < width) {
×
NEW
1562
            base = ((size_t)(x + 2) * (size_t)depth) + (size_t)channel;
×
NEW
1563
            carry_curr[base] += term_r2;
×
1564
        }
1565
        if (y + 1 < height && x - 1 >= 0) {
×
1566
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
1567
            carry_next[base] += term_dl;
×
1568
        }
1569
        if (y + 1 < height) {
×
1570
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
1571
            carry_next[base] += term_d;
×
1572
        }
NEW
1573
        if (y + 1 < height && x + 1 < width) {
×
NEW
1574
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
NEW
1575
            carry_next[base] += term_dr;
×
1576
        }
NEW
1577
        if (y + 2 < height) {
×
NEW
1578
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
NEW
1579
            carry_far[base] += term_d2;
×
1580
        }
1581
    } else {
1582
        if (x - 1 >= 0) {
×
1583
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
1584
            carry_curr[base] += term_r;
×
1585
        }
NEW
1586
        if (x - 2 >= 0) {
×
NEW
1587
            base = ((size_t)(x - 2) * (size_t)depth) + (size_t)channel;
×
NEW
1588
            carry_curr[base] += term_r2;
×
1589
        }
1590
        if (y + 1 < height && x + 1 < width) {
×
1591
            base = ((size_t)(x + 1) * (size_t)depth) + (size_t)channel;
×
1592
            carry_next[base] += term_dl;
×
1593
        }
1594
        if (y + 1 < height) {
×
1595
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
1596
            carry_next[base] += term_d;
×
1597
        }
NEW
1598
        if (y + 1 < height && x - 1 >= 0) {
×
NEW
1599
            base = ((size_t)(x - 1) * (size_t)depth) + (size_t)channel;
×
NEW
1600
            carry_next[base] += term_dr;
×
1601
        }
NEW
1602
        if (y + 2 < height) {
×
NEW
1603
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
NEW
1604
            carry_far[base] += term_d2;
×
1605
        }
1606
    }
1607
}
1608

1609

1610
static void
1611
scanline_params(int serpentine, int index, int limit,
41,644✔
1612
                int *start, int *end, int *step, int *direction)
1613
{
1614
    if (serpentine && (index & 1)) {
41,644!
1615
        *start = limit - 1;
×
1616
        *end = -1;
×
1617
        *step = -1;
×
1618
        *direction = -1;
×
1619
    } else {
1620
        *start = 0;
41,644✔
1621
        *end = limit;
41,644✔
1622
        *step = 1;
41,644✔
1623
        *direction = 1;
41,644✔
1624
    }
1625
}
41,644✔
1626

1627

1628
static SIXELSTATUS
1629
apply_palette_positional(
×
1630
    sixel_index_t *result,
1631
    unsigned char *data,
1632
    int width,
1633
    int height,
1634
    int depth,
1635
    unsigned char *palette,
1636
    int reqcolor,
1637
    int methodForDiffuse,
1638
    int methodForScan,
1639
    int foptimize_palette,
1640
    int (*f_lookup)(const unsigned char *pixel,
1641
                    int depth,
1642
                    const unsigned char *palette,
1643
                    int reqcolor,
1644
                    unsigned short *cachetable,
1645
                    int complexion),
1646
    unsigned short *indextable,
1647
    int complexion,
1648
    unsigned char copy[],
1649
    unsigned char new_palette[],
1650
    unsigned short migration_map[],
1651
    int *ncolors)
1652
{
1653
    int serpentine;
1654
    int y;
1655
    float (*f_mask)(int x, int y, int c);
1656

1657
    switch (methodForDiffuse) {
×
1658
    case SIXEL_DIFFUSE_A_DITHER:
1659
        f_mask = mask_a;
×
1660
        break;
×
1661
    case SIXEL_DIFFUSE_X_DITHER:
×
1662
    default:
1663
        f_mask = mask_x;
×
1664
        break;
×
1665
    }
1666

1667
    serpentine = (methodForScan == SIXEL_SCAN_SERPENTINE);
×
1668

1669
    if (foptimize_palette) {
×
1670
        int x;
1671

1672
        *ncolors = 0;
×
1673
        memset(new_palette, 0x00,
×
1674
               (size_t)SIXEL_PALETTE_MAX * (size_t)depth);
1675
        memset(migration_map, 0x00,
×
1676
               sizeof(unsigned short) * (size_t)SIXEL_PALETTE_MAX);
1677
        for (y = 0; y < height; ++y) {
×
1678
            int start;
1679
            int end;
1680
            int step;
1681
            int direction;
1682

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

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

1695
                    val = data[pos * depth + d]
×
1696
                        + f_mask(x, y, d) * 32;
×
1697
                    copy[d] = val < 0 ? 0
×
1698
                               : val > 255 ? 255 : val;
×
1699
                }
1700
                color_index = f_lookup(copy, depth, palette,
×
1701
                                       reqcolor, indextable,
1702
                                       complexion);
1703
                if (migration_map[color_index] == 0) {
×
1704
                    result[pos] = *ncolors;
×
1705
                    for (d = 0; d < depth; ++d) {
×
1706
                        new_palette[*ncolors * depth + d]
×
1707
                            = palette[color_index * depth + d];
×
1708
                    }
1709
                    ++*ncolors;
×
1710
                    migration_map[color_index] = *ncolors;
×
1711
                } else {
1712
                    result[pos] = migration_map[color_index] - 1;
×
1713
                }
1714
            }
1715
        }
1716
        memcpy(palette, new_palette, (size_t)(*ncolors * depth));
×
1717
    } else {
1718
        int x;
1719

1720
        for (y = 0; y < height; ++y) {
×
1721
            int start;
1722
            int end;
1723
            int step;
1724
            int direction;
1725

1726
            scanline_params(serpentine, y, width,
×
1727
                            &start, &end, &step, &direction);
1728
            (void)direction;
1729
            for (x = start; x != end; x += step) {
×
1730
                int pos;
1731
                int d;
1732

1733
                pos = y * width + x;
×
1734
                for (d = 0; d < depth; ++d) {
×
1735
                    int val;
1736

1737
                    val = data[pos * depth + d]
×
1738
                        + f_mask(x, y, d) * 32;
×
1739
                    copy[d] = val < 0 ? 0
×
1740
                               : val > 255 ? 255 : val;
×
1741
                }
1742
                result[pos] = f_lookup(copy, depth, palette,
×
1743
                                       reqcolor, indextable,
1744
                                       complexion);
1745
            }
1746
        }
1747
        *ncolors = reqcolor;
×
1748
    }
1749

1750
    return SIXEL_OK;
×
1751
}
1752

1753

1754
static SIXELSTATUS
1755
apply_palette_variable(
×
1756
    sixel_index_t *result,
1757
    unsigned char *data,
1758
    int width,
1759
    int height,
1760
    int depth,
1761
    unsigned char *palette,
1762
    int reqcolor,
1763
    int methodForScan,
1764
    int foptimize_palette,
1765
    int (*f_lookup)(const unsigned char *pixel,
1766
                    int depth,
1767
                    const unsigned char *palette,
1768
                    int reqcolor,
1769
                    unsigned short *cachetable,
1770
                    int complexion),
1771
    unsigned short *indextable,
1772
    int complexion,
1773
    unsigned char new_palette[],
1774
    unsigned short migration_map[],
1775
    int *ncolors,
1776
    int methodForDiffuse,
1777
    int methodForCarry)
1778
{
1779
    SIXELSTATUS status = SIXEL_FALSE;
×
1780
#if _MSC_VER
1781
    enum { max_channels = 4 };
1782
#else
1783
    const int max_channels = 4;
×
1784
#endif
1785
    int serpentine;
1786
    int y;
1787
    diffuse_varerr_mode varerr_diffuse;
1788
    diffuse_varerr_carry_mode varerr_diffuse_carry;
1789
    int use_carry;
1790
    size_t carry_len;
1791
    int32_t *carry_curr = NULL;
×
1792
    int32_t *carry_next = NULL;
×
NEW
1793
    int32_t *carry_far = NULL;
×
1794
    unsigned char corrected[max_channels];
1795
    int32_t sample_scaled[max_channels];
1796
    int32_t accum_scaled[max_channels];
1797
    int start;
1798
    int end;
1799
    int step;
1800
    int direction;
1801
    int x;
1802
    int pos;
1803
    size_t base;
1804
    size_t carry_base;
1805
    const unsigned char *source_pixel;
1806
    int color_index;
1807
    int output_index;
1808
    int n;
1809
    int palette_value;
1810
    int diff;
1811
    int table_index;
1812
    int64_t accum;
1813
    int64_t clamped;
1814
    int32_t target_scaled;
1815
    int32_t error_scaled;
1816
    int32_t *tmp;
1817

1818
    if (depth > max_channels) {
×
1819
        status = SIXEL_BAD_ARGUMENT;
×
1820
        goto end;
×
1821
    }
1822

1823
    use_carry = (methodForCarry == SIXEL_CARRY_ENABLE);
×
1824
    carry_len = 0;
×
1825

1826
    switch (methodForDiffuse) {
×
1827
    case SIXEL_DIFFUSE_LSO2:
NEW
1828
        varerr_diffuse = diffuse_lso2;
×
NEW
1829
        varerr_diffuse_carry = diffuse_lso2_carry;
×
1830
        break;
×
1831
    case SIXEL_DIFFUSE_LSO3:
NEW
1832
        varerr_diffuse = diffuse_lso3;
×
NEW
1833
        varerr_diffuse_carry = diffuse_lso3_carry;
×
1834
        srand((unsigned int)time(NULL));
×
1835
        break;
×
1836
    default:
NEW
1837
        varerr_diffuse = diffuse_lso2;
×
NEW
1838
        varerr_diffuse_carry = diffuse_lso2_carry;
×
1839
        break;
×
1840
    }
1841

1842
    if (use_carry) {
×
1843
        carry_len = (size_t)width * (size_t)depth;
×
1844
        carry_curr = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
1845
        if (carry_curr == NULL) {
×
1846
            status = SIXEL_BAD_ALLOCATION;
×
1847
            goto end;
×
1848
        }
1849
        carry_next = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
1850
        if (carry_next == NULL) {
×
1851
            status = SIXEL_BAD_ALLOCATION;
×
1852
            goto end;
×
1853
        }
NEW
1854
        carry_far = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
NEW
1855
        if (carry_far == NULL) {
×
NEW
1856
            status = SIXEL_BAD_ALLOCATION;
×
NEW
1857
            goto end;
×
1858
        }
1859
    }
1860

1861
    serpentine = (methodForScan == SIXEL_SCAN_SERPENTINE);
×
1862

1863
    if (foptimize_palette) {
×
1864
        *ncolors = 0;
×
1865
        memset(new_palette, 0x00,
×
1866
               (size_t)SIXEL_PALETTE_MAX * (size_t)depth);
1867
        memset(migration_map, 0x00,
×
1868
               sizeof(unsigned short) * (size_t)SIXEL_PALETTE_MAX);
1869
    }
1870

1871
    for (y = 0; y < height; ++y) {
×
1872
        scanline_params(serpentine, y, width,
×
1873
                        &start, &end, &step, &direction);
1874
        for (x = start; x != end; x += step) {
×
1875
            pos = y * width + x;
×
1876
            base = (size_t)pos * (size_t)depth;
×
1877
            carry_base = (size_t)x * (size_t)depth;
×
1878
            if (use_carry) {
×
1879
                for (n = 0; n < depth; ++n) {
×
1880
                    accum = ((int64_t)data[base + n]
×
1881
                             << VARERR_SCALE_SHIFT)
×
1882
                          + carry_curr[carry_base + (size_t)n];
×
1883
                    if (accum < INT32_MIN) {
×
1884
                        accum = INT32_MIN;
×
1885
                    } else if (accum > INT32_MAX) {
×
1886
                        accum = INT32_MAX;
×
1887
                    }
1888
                    carry_curr[carry_base + (size_t)n] = 0;
×
1889
                    clamped = accum;
×
1890
                    if (clamped < 0) {
×
1891
                        clamped = 0;
×
1892
                    } else if (clamped > VARERR_MAX_VALUE) {
×
1893
                        clamped = VARERR_MAX_VALUE;
×
1894
                    }
NEW
1895
                    accum_scaled[n] = (int32_t)clamped;
×
1896
                    corrected[n]
1897
                        = (unsigned char)((clamped + VARERR_ROUND)
×
1898
                                          >> VARERR_SCALE_SHIFT);
×
1899
                }
1900
                source_pixel = corrected;
×
1901
            } else {
1902
                for (n = 0; n < depth; ++n) {
×
1903
                    sample_scaled[n]
1904
                        = (int32_t)data[base + n]
×
1905
                        << VARERR_SCALE_SHIFT;
×
1906
                    corrected[n] = data[base + n];
×
1907
                }
1908
                source_pixel = data + base;
×
1909
            }
1910

1911
            color_index = f_lookup(source_pixel, depth, palette,
×
1912
                                   reqcolor, indextable,
1913
                                   complexion);
1914

1915
            if (foptimize_palette) {
×
1916
                if (migration_map[color_index] == 0) {
×
1917
                    output_index = *ncolors;
×
1918
                    for (n = 0; n < depth; ++n) {
×
1919
                        new_palette[output_index * depth + n]
×
1920
                            = palette[color_index * depth + n];
×
1921
                    }
1922
                    ++*ncolors;
×
1923
                    migration_map[color_index] = *ncolors;
×
1924
                } else {
1925
                    output_index = migration_map[color_index] - 1;
×
1926
                }
1927
                result[pos] = output_index;
×
1928
            } else {
1929
                output_index = color_index;
×
1930
                result[pos] = output_index;
×
1931
            }
1932

1933
            for (n = 0; n < depth; ++n) {
×
1934
                if (foptimize_palette) {
×
1935
                    palette_value = new_palette[output_index * depth + n];
×
1936
                } else {
1937
                    palette_value = palette[color_index * depth + n];
×
1938
                }
NEW
1939
                diff = (int)source_pixel[n] - palette_value;
×
NEW
1940
                if (diff < 0) {
×
NEW
1941
                    diff = -diff;
×
1942
                }
NEW
1943
                if (diff > 255) {
×
NEW
1944
                    diff = 255;
×
1945
                }
NEW
1946
                table_index = diff;
×
NEW
1947
                if (methodForDiffuse == SIXEL_DIFFUSE_LSO3) {
×
NEW
1948
                    table_index = zhoufang_index_from_byte(
×
NEW
1949
                        (unsigned char)table_index);
×
1950
                }
1951
                if (use_carry) {
×
1952
                    target_scaled = (int32_t)palette_value
×
1953
                                  << VARERR_SCALE_SHIFT;
1954
                    error_scaled = accum_scaled[n] - target_scaled;
×
NEW
1955
                    varerr_diffuse_carry(carry_curr, carry_next, carry_far,
×
1956
                                         width, height, depth,
1957
                                         x, y, error_scaled,
1958
                                         table_index,
1959
                                         direction, n);
1960
                } else {
1961
                    target_scaled = (int32_t)palette_value
×
1962
                                  << VARERR_SCALE_SHIFT;
1963
                    error_scaled = sample_scaled[n] - target_scaled;
×
1964
                    varerr_diffuse(data + n, width, height,
×
1965
                                   x, y, depth, error_scaled,
1966
                                   table_index,
1967
                                   direction);
1968
                }
1969
            }
1970
        }
1971
        if (use_carry) {
×
1972
            tmp = carry_curr;
×
1973
            carry_curr = carry_next;
×
NEW
1974
            carry_next = carry_far;
×
NEW
1975
            carry_far = tmp;
×
NEW
1976
            if (carry_len > 0) {
×
NEW
1977
                memset(carry_far, 0x00, carry_len * sizeof(int32_t));
×
1978
            }
1979
        }
1980
    }
1981

1982
    if (foptimize_palette) {
×
1983
        memcpy(palette, new_palette, (size_t)(*ncolors * depth));
×
1984
    } else {
1985
        *ncolors = reqcolor;
×
1986
    }
1987

1988
    status = SIXEL_OK;
×
1989

1990
end:
1991
    free(carry_next);
×
1992
    free(carry_curr);
×
NEW
1993
    free(carry_far);
×
1994
    return status;
×
1995
}
1996

1997

1998
static SIXELSTATUS
1999
apply_palette_fixed(
254✔
2000
    sixel_index_t *result,
2001
    unsigned char *data,
2002
    int width,
2003
    int height,
2004
    int depth,
2005
    unsigned char *palette,
2006
    int reqcolor,
2007
    int methodForScan,
2008
    int foptimize_palette,
2009
    int (*f_lookup)(const unsigned char *pixel,
2010
                    int depth,
2011
                    const unsigned char *palette,
2012
                    int reqcolor,
2013
                    unsigned short *cachetable,
2014
                    int complexion),
2015
    unsigned short *indextable,
2016
    int complexion,
2017
    unsigned char new_palette[],
2018
    unsigned short migration_map[],
2019
    int *ncolors,
2020
    int methodForDiffuse,
2021
    int methodForCarry)
2022
{
150✔
2023
#if _MSC_VER
2024
    enum { max_channels = 4 };
2025
#else
2026
    const int max_channels = 4;
254✔
2027
#endif
2028
    SIXELSTATUS status = SIXEL_FALSE;
254✔
2029
    int serpentine;
2030
    int y;
2031
    void (*f_diffuse)(unsigned char *data,
2032
                      int width,
2033
                      int height,
2034
                      int x,
2035
                      int y,
2036
                      int depth,
2037
                      int offset,
2038
                      int direction);
2039
    diffuse_fixed_carry_mode f_diffuse_carry;
2040
    int use_carry;
2041
    size_t carry_len;
2042
    int32_t *carry_curr = NULL;
254✔
2043
    int32_t *carry_next = NULL;
254✔
2044
    int32_t *carry_far = NULL;
254✔
2045
    unsigned char corrected[max_channels];
150✔
2046
    int32_t accum_scaled[max_channels];
150✔
2047
    int start;
2048
    int end;
2049
    int step;
2050
    int direction;
2051
    int x;
2052
    int pos;
2053
    size_t base;
2054
    size_t carry_base;
2055
    const unsigned char *source_pixel;
2056
    int color_index;
2057
    int output_index;
2058
    int n;
2059
    int palette_value;
2060
    int64_t accum;
2061
    int64_t clamped;
2062
    int32_t target_scaled;
2063
    int32_t error_scaled;
2064
    int offset;
2065
    int32_t *tmp;
2066

2067
    if (depth > max_channels) {
254!
2068
        status = SIXEL_BAD_ARGUMENT;
×
2069
        goto end;
×
2070
    }
2071

2072
    use_carry = (methodForCarry == SIXEL_CARRY_ENABLE);
254✔
2073
    carry_len = 0;
254✔
2074

2075
    if (depth != 3) {
254!
2076
        f_diffuse = diffuse_none;
×
2077
        f_diffuse_carry = diffuse_none_carry;
×
2078
        use_carry = 0;
×
2079
    } else {
2080
        switch (methodForDiffuse) {
254!
2081
        case SIXEL_DIFFUSE_NONE:
86✔
2082
            f_diffuse = diffuse_none;
157✔
2083
            f_diffuse_carry = diffuse_none_carry;
157✔
2084
            break;
157✔
2085
        case SIXEL_DIFFUSE_ATKINSON:
42✔
2086
            f_diffuse = diffuse_atkinson;
64✔
2087
            f_diffuse_carry = diffuse_atkinson_carry;
64✔
2088
            break;
64✔
2089
        case SIXEL_DIFFUSE_FS:
16✔
2090
            f_diffuse = diffuse_fs;
24✔
2091
            f_diffuse_carry = diffuse_fs_carry;
24✔
2092
            break;
24✔
2093
        case SIXEL_DIFFUSE_JAJUNI:
2✔
2094
            f_diffuse = diffuse_jajuni;
3✔
2095
            f_diffuse_carry = diffuse_jajuni_carry;
3✔
2096
            break;
3✔
2097
        case SIXEL_DIFFUSE_STUCKI:
2✔
2098
            f_diffuse = diffuse_stucki;
3✔
2099
            f_diffuse_carry = diffuse_stucki_carry;
3✔
2100
            break;
3✔
2101
        case SIXEL_DIFFUSE_BURKES:
2✔
2102
            f_diffuse = diffuse_burkes;
3✔
2103
            f_diffuse_carry = diffuse_burkes_carry;
3✔
2104
            break;
3✔
2105
        case SIXEL_DIFFUSE_LSO1:
2106
            f_diffuse = diffuse_lso1;
×
2107
            f_diffuse_carry = diffuse_lso1_carry;
×
2108
            break;
×
2109
        default:
2110
            quant_trace(stderr,
×
2111
                        "Internal error: invalid methodForDiffuse: %d\n",
2112
                        methodForDiffuse);
2113
            f_diffuse = diffuse_none;
×
2114
            f_diffuse_carry = diffuse_none_carry;
×
2115
            break;
×
2116
        }
2117
    }
2118

2119
    if (use_carry) {
254!
2120
        carry_len = (size_t)width * (size_t)depth;
×
2121
        if (carry_len > 0) {
×
2122
            carry_curr = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
2123
            if (carry_curr == NULL) {
×
2124
                status = SIXEL_BAD_ALLOCATION;
×
2125
                goto end;
×
2126
            }
2127
            carry_next = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
2128
            if (carry_next == NULL) {
×
2129
                status = SIXEL_BAD_ALLOCATION;
×
2130
                goto end;
×
2131
            }
2132
            carry_far = (int32_t *)calloc(carry_len, sizeof(int32_t));
×
2133
            if (carry_far == NULL) {
×
2134
                status = SIXEL_BAD_ALLOCATION;
×
2135
                goto end;
×
2136
            }
2137
        } else {
2138
            use_carry = 0;
×
2139
        }
2140
    }
2141

2142
    serpentine = (methodForScan == SIXEL_SCAN_SERPENTINE);
254✔
2143

2144
    if (foptimize_palette) {
254✔
2145
        *ncolors = 0;
175✔
2146
        memset(new_palette, 0x00,
175✔
2147
               (size_t)SIXEL_PALETTE_MAX * (size_t)depth);
110✔
2148
        memset(migration_map, 0x00,
175✔
2149
               sizeof(unsigned short) * (size_t)SIXEL_PALETTE_MAX);
2150
    } else {
65✔
2151
        *ncolors = reqcolor;
79✔
2152
    }
2153

2154
    for (y = 0; y < height; ++y) {
41,898✔
2155
        scanline_params(serpentine, y, width,
41,644✔
2156
                        &start, &end, &step, &direction);
2157
        for (x = start; x != end; x += step) {
19,167,350✔
2158
            pos = y * width + x;
19,125,706✔
2159
            base = (size_t)pos * (size_t)depth;
19,125,706✔
2160
            carry_base = (size_t)x * (size_t)depth;
19,125,706✔
2161
            if (use_carry) {
19,125,706!
2162
                for (n = 0; n < depth; ++n) {
×
2163
                    accum = ((int64_t)data[base + n]
×
2164
                             << VARERR_SCALE_SHIFT)
×
2165
                           + carry_curr[carry_base + (size_t)n];
×
2166
                    if (accum < INT32_MIN) {
×
2167
                        accum = INT32_MIN;
×
2168
                    } else if (accum > INT32_MAX) {
×
2169
                        accum = INT32_MAX;
×
2170
                    }
2171
                    clamped = accum;
×
2172
                    if (clamped < 0) {
×
2173
                        clamped = 0;
×
2174
                    } else if (clamped > VARERR_MAX_VALUE) {
×
2175
                        clamped = VARERR_MAX_VALUE;
×
2176
                    }
NEW
2177
                    accum_scaled[n] = (int32_t)clamped;
×
2178
                    corrected[n]
2179
                        = (unsigned char)((clamped + VARERR_ROUND)
×
2180
                                          >> VARERR_SCALE_SHIFT);
×
2181
                    data[base + n] = corrected[n];
×
2182
                    carry_curr[carry_base + (size_t)n] = 0;
×
2183
                }
2184
                source_pixel = corrected;
×
2185
            } else {
2186
                source_pixel = data + base;
19,125,706✔
2187
            }
2188

2189
            color_index = f_lookup(source_pixel, depth, palette,
28,305,328✔
2190
                                   reqcolor, indextable,
9,179,622✔
2191
                                   complexion);
9,179,622✔
2192

2193
            if (foptimize_palette) {
19,125,706✔
2194
                if (migration_map[color_index] == 0) {
7,024,206✔
2195
                    output_index = *ncolors;
8,148✔
2196
                    for (n = 0; n < depth; ++n) {
32,592✔
2197
                        new_palette[output_index * depth + n]
24,444✔
2198
                            = palette[color_index * depth + n];
32,748✔
2199
                    }
8,304✔
2200
                    ++*ncolors;
8,148✔
2201
                    migration_map[color_index] = *ncolors;
8,148✔
2202
                } else {
2,768✔
2203
                    output_index = migration_map[color_index] - 1;
7,016,058✔
2204
                }
2205
                result[pos] = output_index;
7,024,206✔
2206
            } else {
3,339,802✔
2207
                output_index = color_index;
12,101,500✔
2208
                result[pos] = output_index;
12,101,500✔
2209
            }
2210

2211
            for (n = 0; n < depth; ++n) {
76,502,824✔
2212
                if (foptimize_palette) {
57,377,118✔
2213
                    palette_value = new_palette[output_index * depth + n];
21,072,618✔
2214
                } else {
10,019,406✔
2215
                    palette_value = palette[color_index * depth + n];
36,304,500✔
2216
                }
2217
                if (use_carry) {
57,377,118!
2218
                    target_scaled = (int32_t)palette_value
×
2219
                                  << VARERR_SCALE_SHIFT;
2220
                    error_scaled = accum_scaled[n] - target_scaled;
×
2221
                    f_diffuse_carry(carry_curr, carry_next, carry_far,
×
2222
                                    width, height, depth,
2223
                                    x, y, error_scaled, direction, n);
2224
                } else {
2225
                    offset = (int)source_pixel[n] - palette_value;
57,377,118✔
2226
                    f_diffuse(data + n, width, height, x, y,
84,915,984✔
2227
                              depth, offset, direction);
27,538,866✔
2228
                }
2229
            }
27,538,866✔
2230
        }
9,179,622✔
2231
        if (use_carry) {
41,644!
2232
            tmp = carry_curr;
×
2233
            carry_curr = carry_next;
×
2234
            carry_next = carry_far;
×
2235
            carry_far = tmp;
×
2236
            if (carry_len > 0) {
×
2237
                memset(carry_far, 0x00, carry_len * sizeof(int32_t));
×
2238
            }
2239
        }
2240
    }
19,772✔
2241

2242
    if (foptimize_palette) {
254✔
2243
        memcpy(palette, new_palette, (size_t)(*ncolors * depth));
175✔
2244
    }
65✔
2245

2246
    status = SIXEL_OK;
254✔
2247

2248
end:
150✔
2249
    free(carry_far);
254✔
2250
    free(carry_next);
254✔
2251
    free(carry_curr);
254✔
2252
    return status;
254✔
2253
}
2254

2255

2256
static void
2257
diffuse_none(unsigned char *data, int width, int height,
18,248,814✔
2258
             int x, int y, int depth, int error, int direction)
2259
{
2260
    /* unused */ (void) data;
14,469,498✔
2261
    /* unused */ (void) width;
14,469,498✔
2262
    /* unused */ (void) height;
14,469,498✔
2263
    /* unused */ (void) x;
14,469,498✔
2264
    /* unused */ (void) y;
14,469,498✔
2265
    /* unused */ (void) depth;
14,469,498✔
2266
    /* unused */ (void) error;
14,469,498✔
2267
    /* unused */ (void) direction;
14,469,498✔
2268
}
18,248,814✔
2269

2270

2271
static void
2272
diffuse_none_carry(int32_t *carry_curr, int32_t *carry_next,
×
2273
                   int32_t *carry_far, int width, int height,
2274
                   int depth, int x, int y, int32_t error,
2275
                   int direction, int channel)
2276
{
2277
    /* unused */ (void) carry_curr;
2278
    /* unused */ (void) carry_next;
2279
    /* unused */ (void) carry_far;
2280
    /* unused */ (void) width;
2281
    /* unused */ (void) height;
2282
    /* unused */ (void) depth;
2283
    /* unused */ (void) x;
2284
    /* unused */ (void) y;
2285
    /* unused */ (void) error;
2286
    /* unused */ (void) direction;
2287
    /* unused */ (void) channel;
2288
}
×
2289

2290

2291
static void
2292
diffuse_fs(unsigned char *data, int width, int height,
12,621,744✔
2293
           int x, int y, int depth, int error, int direction)
2294
{
2295
    /* Floyd Steinberg Method
2296
     *          curr    7/16
2297
     *  3/16    5/48    1/16
2298
     */
2299
    int pos;
2300
    int forward;
2301

2302
    pos = y * width + x;
12,621,744✔
2303
    forward = direction >= 0;
12,621,744✔
2304

2305
    if (forward) {
12,621,744!
2306
        if (x < width - 1) {
12,621,744✔
2307
            error_diffuse_normal(data, pos + 1, depth, error, 7, 16);
12,594,798✔
2308
        }
4,198,266✔
2309
        if (y < height - 1) {
12,621,744✔
2310
            if (x > 0) {
12,591,828✔
2311
                error_diffuse_normal(data,
16,753,272✔
2312
                                     pos + width - 1,
12,564,954✔
2313
                                     depth, error, 3, 16);
4,188,318✔
2314
            }
4,188,318✔
2315
            error_diffuse_normal(data,
16,789,104✔
2316
                                 pos + width,
4,197,276✔
2317
                                 depth, error, 5, 16);
4,197,276✔
2318
            if (x < width - 1) {
12,591,828✔
2319
                error_diffuse_normal(data,
16,753,272✔
2320
                                     pos + width + 1,
12,564,954✔
2321
                                     depth, error, 1, 16);
4,188,318✔
2322
            }
4,188,318✔
2323
        }
4,197,276✔
2324
    } else {
4,207,248✔
2325
        if (x > 0) {
×
2326
            error_diffuse_normal(data, pos - 1, depth, error, 7, 16);
×
2327
        }
2328
        if (y < height - 1) {
×
2329
            if (x < width - 1) {
×
2330
                error_diffuse_normal(data,
×
2331
                                     pos + width + 1,
×
2332
                                     depth, error, 3, 16);
2333
            }
2334
            error_diffuse_normal(data,
×
2335
                                 pos + width,
2336
                                 depth, error, 5, 16);
2337
            if (x > 0) {
×
2338
                error_diffuse_normal(data,
×
2339
                                     pos + width - 1,
×
2340
                                     depth, error, 1, 16);
2341
            }
2342
        }
2343
    }
2344
}
12,621,744✔
2345

2346

2347
static void
2348
diffuse_fs_carry(int32_t *carry_curr, int32_t *carry_next,
×
2349
                 int32_t *carry_far, int width, int height,
2350
                 int depth, int x, int y, int32_t error,
2351
                 int direction, int channel)
2352
{
2353
    /* Floyd Steinberg Method
2354
     *          curr    7/16
2355
     *  3/16    5/48    1/16
2356
     */
2357
    int forward;
2358

2359
    /* unused */ (void) carry_far;
2360
    if (error == 0) {
×
2361
        return;
×
2362
    }
2363

2364
    forward = direction >= 0;
×
2365
    if (forward) {
×
2366
        if (x + 1 < width) {
×
2367
            size_t base;
2368
            int32_t term;
2369

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

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

2389
                base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
2390
                term = diffuse_fixed_term(error, 5, 16);
×
2391
                carry_next[base] += term;
×
2392
            }
2393
            if (x + 1 < width) {
×
2394
                size_t base;
2395
                int32_t term;
2396

2397
                base = ((size_t)(x + 1) * (size_t)depth)
×
2398
                     + (size_t)channel;
×
2399
                term = diffuse_fixed_term(error, 1, 16);
×
2400
                carry_next[base] += term;
×
2401
            }
2402
        }
2403
    } else {
2404
        if (x - 1 >= 0) {
×
2405
            size_t base;
2406
            int32_t term;
2407

2408
            base = ((size_t)(x - 1) * (size_t)depth)
×
2409
                 + (size_t)channel;
×
2410
            term = diffuse_fixed_term(error, 7, 16);
×
2411
            carry_curr[base] += term;
×
2412
        }
2413
        if (y + 1 < height) {
×
2414
            if (x + 1 < width) {
×
2415
                size_t base;
2416
                int32_t term;
2417

2418
                base = ((size_t)(x + 1) * (size_t)depth)
×
2419
                     + (size_t)channel;
×
2420
                term = diffuse_fixed_term(error, 3, 16);
×
2421
                carry_next[base] += term;
×
2422
            }
2423
            {
2424
                size_t base;
2425
                int32_t term;
2426

2427
                base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
2428
                term = diffuse_fixed_term(error, 5, 16);
×
2429
                carry_next[base] += term;
×
2430
            }
2431
            if (x - 1 >= 0) {
×
2432
                size_t base;
2433
                int32_t term;
2434

2435
                base = ((size_t)(x - 1) * (size_t)depth)
×
2436
                     + (size_t)channel;
×
2437
                term = diffuse_fixed_term(error, 1, 16);
×
2438
                carry_next[base] += term;
×
2439
            }
2440
        }
2441
    }
2442
}
2443

2444

2445
static void
2446
diffuse_atkinson(unsigned char *data, int width, int height,
25,922,460✔
2447
                 int x, int y, int depth, int error, int direction)
2448
{
2449
    /* Atkinson's Method
2450
     *          curr    1/8    1/8
2451
     *   1/8     1/8    1/8
2452
     *           1/8
2453
     */
2454
    int pos;
2455
    int sign;
2456

2457
    pos = y * width + x;
25,922,460✔
2458
    sign = direction >= 0 ? 1 : -1;
25,922,460!
2459

2460
    if (x + sign >= 0 && x + sign < width) {
25,922,460!
2461
        error_diffuse_fast(data, pos + sign, depth, error, 1, 8);
25,870,995✔
2462
    }
8,650,065✔
2463
    if (x + sign * 2 >= 0 && x + sign * 2 < width) {
25,922,460!
2464
        error_diffuse_fast(data, pos + sign * 2, depth, error, 1, 8);
25,819,530✔
2465
    }
8,632,710✔
2466
    if (y < height - 1) {
25,922,460✔
2467
        int row;
2468

2469
        row = pos + width;
25,854,156✔
2470
        if (x - sign >= 0 && x - sign < width) {
25,854,156!
2471
            error_diffuse_fast(data,
34,429,980✔
2472
                               row + (-sign),
8,627,097✔
2473
                               depth, error, 1, 8);
8,627,097✔
2474
        }
8,627,097✔
2475
        error_diffuse_fast(data, row, depth, error, 1, 8);
25,854,156✔
2476
        if (x + sign >= 0 && x + sign < width) {
25,854,156!
2477
            error_diffuse_fast(data,
34,429,980✔
2478
                               row + sign,
8,627,097✔
2479
                               depth, error, 1, 8);
8,627,097✔
2480
        }
8,627,097✔
2481
    }
8,644,386✔
2482
    if (y < height - 2) {
25,922,460✔
2483
        error_diffuse_fast(data, pos + width * 2, depth, error, 1, 8);
25,785,852✔
2484
    }
8,621,352✔
2485
}
25,922,460✔
2486

2487

2488
static void
2489
diffuse_atkinson_carry(int32_t *carry_curr, int32_t *carry_next,
×
2490
                       int32_t *carry_far, int width, int height,
2491
                       int depth, int x, int y, int32_t error,
2492
                       int direction, int channel)
2493
{
2494
    /* Atkinson's Method
2495
     *          curr    1/8    1/8
2496
     *   1/8     1/8    1/8
2497
     *           1/8
2498
     */
2499
    int sign;
2500
    int32_t term;
2501

2502
    if (error == 0) {
×
2503
        return;
×
2504
    }
2505

2506
    term = diffuse_fixed_term(error, 1, 8);
×
2507
    sign = direction >= 0 ? 1 : -1;
×
2508
    if (x + sign >= 0 && x + sign < width) {
×
2509
        size_t base;
2510

2511
        base = ((size_t)(x + sign) * (size_t)depth)
×
2512
             + (size_t)channel;
×
2513
        carry_curr[base] += term;
×
2514
    }
2515
    if (x + sign * 2 >= 0 && x + sign * 2 < width) {
×
2516
        size_t base;
2517

2518
        base = ((size_t)(x + sign * 2) * (size_t)depth)
×
2519
             + (size_t)channel;
×
2520
        carry_curr[base] += term;
×
2521
    }
2522
    if (y + 1 < height) {
×
2523
        if (x - sign >= 0 && x - sign < width) {
×
2524
            size_t base;
2525

2526
            base = ((size_t)(x - sign) * (size_t)depth)
×
2527
                 + (size_t)channel;
×
2528
            carry_next[base] += term;
×
2529
        }
2530
        {
2531
            size_t base;
2532

2533
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
2534
            carry_next[base] += term;
×
2535
        }
2536
        if (x + sign >= 0 && x + sign < width) {
×
2537
            size_t base;
2538

2539
            base = ((size_t)(x + sign) * (size_t)depth)
×
2540
                 + (size_t)channel;
×
2541
            carry_next[base] += term;
×
2542
        }
2543
    }
2544
    if (y + 2 < height) {
×
2545
        size_t base;
2546

2547
        base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
2548
        carry_far[base] += term;
×
2549
    }
2550
}
2551

2552

2553
static void
2554
diffuse_jajuni(unsigned char *data, int width, int height,
396,900✔
2555
               int x, int y, int depth, int error, int direction)
2556
{
2557
    /* Jarvis, Judice & Ninke Method
2558
     *                  curr    7/48    5/48
2559
     *  3/48    5/48    7/48    5/48    3/48
2560
     *  1/48    3/48    5/48    3/48    1/48
2561
     */
2562
    int pos;
2563
    int sign;
2564
    static const int row0_offsets[] = { 1, 2 };
2565
    static const int row0_weights[] = { 7, 5 };
2566
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
2567
    static const int row1_weights[] = { 3, 5, 7, 5, 3 };
2568
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
2569
    static const int row2_weights[] = { 1, 3, 5, 3, 1 };
2570
    int i;
2571

2572
    pos = y * width + x;
396,900✔
2573
    sign = direction >= 0 ? 1 : -1;
396,900!
2574

2575
    for (i = 0; i < 2; ++i) {
1,190,700✔
2576
        int neighbor;
2577

2578
        neighbor = x + sign * row0_offsets[i];
793,800✔
2579
        if (neighbor < 0 || neighbor >= width) {
793,800!
2580
            continue;
5,670✔
2581
        }
2582
        error_diffuse_precise(data,
1,050,840✔
2583
                              pos + (neighbor - x),
788,130✔
2584
                              depth, error,
262,710✔
2585
                              row0_weights[i], 48);
788,130✔
2586
    }
262,710✔
2587
    if (y < height - 1) {
396,900✔
2588
        int row;
2589

2590
        row = pos + width;
395,010✔
2591
        for (i = 0; i < 5; ++i) {
2,370,060✔
2592
            int neighbor;
2593

2594
            neighbor = x + sign * row1_offsets[i];
1,975,050✔
2595
            if (neighbor < 0 || neighbor >= width) {
1,975,050✔
2596
                continue;
11,286✔
2597
            }
2598
            error_diffuse_precise(data,
2,618,352✔
2599
                                  row + (neighbor - x),
1,963,764✔
2600
                                  depth, error,
654,588✔
2601
                                  row1_weights[i], 48);
1,963,764✔
2602
        }
654,588✔
2603
    }
131,670✔
2604
    if (y < height - 2) {
396,900✔
2605
        int row;
2606

2607
        row = pos + width * 2;
393,120✔
2608
        for (i = 0; i < 5; ++i) {
2,358,720✔
2609
            int neighbor;
2610

2611
            neighbor = x + sign * row2_offsets[i];
1,965,600✔
2612
            if (neighbor < 0 || neighbor >= width) {
1,965,600✔
2613
                continue;
11,232✔
2614
            }
2615
            error_diffuse_precise(data,
2,605,824✔
2616
                                  row + (neighbor - x),
1,954,368✔
2617
                                  depth, error,
651,456✔
2618
                                  row2_weights[i], 48);
1,954,368✔
2619
        }
651,456✔
2620
    }
131,040✔
2621
}
396,900✔
2622

2623

2624
static void
2625
diffuse_jajuni_carry(int32_t *carry_curr, int32_t *carry_next,
×
2626
                     int32_t *carry_far, int width, int height,
2627
                     int depth, int x, int y, int32_t error,
2628
                     int direction, int channel)
2629
{
2630
    /* Jarvis, Judice & Ninke Method
2631
     *                  curr    7/48    5/48
2632
     *  3/48    5/48    7/48    5/48    3/48
2633
     *  1/48    3/48    5/48    3/48    1/48
2634
     */
2635
    static const int row0_offsets[] = { 1, 2 };
2636
    static const int row0_weights[] = { 7, 5 };
2637
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
2638
    static const int row1_weights[] = { 3, 5, 7, 5, 3 };
2639
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
2640
    static const int row2_weights[] = { 1, 3, 5, 3, 1 };
2641
    int sign;
2642
    int i;
2643

2644
    if (error == 0) {
×
2645
        return;
×
2646
    }
2647

2648
    sign = direction >= 0 ? 1 : -1;
×
2649
    for (i = 0; i < 2; ++i) {
×
2650
        int neighbor;
2651
        int32_t term;
2652

2653
        neighbor = x + sign * row0_offsets[i];
×
2654
        if (neighbor < 0 || neighbor >= width) {
×
2655
            continue;
×
2656
        }
2657
        term = diffuse_fixed_term(error, row0_weights[i], 48);
×
2658
        carry_curr[((size_t)neighbor * (size_t)depth)
×
2659
                   + (size_t)channel] += term;
×
2660
    }
2661
    if (y + 1 < height) {
×
2662
        for (i = 0; i < 5; ++i) {
×
2663
            int neighbor;
2664
            int32_t term;
2665

2666
            neighbor = x + sign * row1_offsets[i];
×
2667
            if (neighbor < 0 || neighbor >= width) {
×
2668
                continue;
×
2669
            }
2670
            term = diffuse_fixed_term(error, row1_weights[i], 48);
×
2671
            carry_next[((size_t)neighbor * (size_t)depth)
×
2672
                       + (size_t)channel] += term;
×
2673
        }
2674
    }
2675
    if (y + 2 < height) {
×
2676
        for (i = 0; i < 5; ++i) {
×
2677
            int neighbor;
2678
            int32_t term;
2679

2680
            neighbor = x + sign * row2_offsets[i];
×
2681
            if (neighbor < 0 || neighbor >= width) {
×
2682
                continue;
×
2683
            }
2684
            term = diffuse_fixed_term(error, row2_weights[i], 48);
×
2685
            carry_far[((size_t)neighbor * (size_t)depth)
×
2686
                      + (size_t)channel] += term;
×
2687
        }
2688
    }
2689
}
2690

2691

2692
static void
2693
diffuse_stucki(unsigned char *data, int width, int height,
119,700✔
2694
               int x, int y, int depth, int error, int direction)
2695
{
2696
    /* Stucki's Method
2697
     *                  curr    8/48    4/48
2698
     *  2/48    4/48    8/48    4/48    2/48
2699
     *  1/48    2/48    4/48    2/48    1/48
2700
     */
2701
    int pos;
2702
    int sign;
2703
    static const int row0_offsets[] = { 1, 2 };
2704
    static const int row0_num[] = { 1, 1 };
2705
    static const int row0_den[] = { 6, 12 };
2706
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
2707
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
2708
    static const int row1_den[] = { 24, 12, 6, 12, 24 };
2709
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
2710
    static const int row2_num[] = { 1, 1, 1, 1, 1 };
2711
    static const int row2_den[] = { 48, 24, 12, 24, 48 };
2712
    int i;
2713

2714
    pos = y * width + x;
119,700✔
2715
    sign = direction >= 0 ? 1 : -1;
119,700!
2716

2717
    for (i = 0; i < 2; ++i) {
359,100✔
2718
        int neighbor;
2719

2720
        neighbor = x + sign * row0_offsets[i];
239,400✔
2721
        if (neighbor < 0 || neighbor >= width) {
239,400!
2722
            continue;
2,700✔
2723
        }
2724
        error_diffuse_precise(data,
315,600✔
2725
                              pos + (neighbor - x),
236,700✔
2726
                              depth, error,
78,900✔
2727
                              row0_num[i], row0_den[i]);
236,700✔
2728
    }
78,900✔
2729
    if (y < height - 1) {
119,700✔
2730
        int row;
2731

2732
        row = pos + width;
118,503✔
2733
        for (i = 0; i < 5; ++i) {
711,018✔
2734
            int neighbor;
2735

2736
            neighbor = x + sign * row1_offsets[i];
592,515✔
2737
            if (neighbor < 0 || neighbor >= width) {
592,515✔
2738
                continue;
5,346✔
2739
            }
2740
            error_diffuse_precise(data,
782,892✔
2741
                                  row + (neighbor - x),
587,169✔
2742
                                  depth, error,
195,723✔
2743
                                  row1_num[i], row1_den[i]);
587,169✔
2744
        }
195,723✔
2745
    }
39,501✔
2746
    if (y < height - 2) {
119,700✔
2747
        int row;
2748

2749
        row = pos + width * 2;
117,306✔
2750
        for (i = 0; i < 5; ++i) {
703,836✔
2751
            int neighbor;
2752

2753
            neighbor = x + sign * row2_offsets[i];
586,530✔
2754
            if (neighbor < 0 || neighbor >= width) {
586,530✔
2755
                continue;
5,292✔
2756
            }
2757
            error_diffuse_precise(data,
774,984✔
2758
                                  row + (neighbor - x),
581,238✔
2759
                                  depth, error,
193,746✔
2760
                                  row2_num[i], row2_den[i]);
581,238✔
2761
        }
193,746✔
2762
    }
39,102✔
2763
}
119,700✔
2764

2765

2766
static void
2767
diffuse_stucki_carry(int32_t *carry_curr, int32_t *carry_next,
×
2768
                     int32_t *carry_far, int width, int height,
2769
                     int depth, int x, int y, int32_t error,
2770
                     int direction, int channel)
2771
{
2772
    /* Stucki's Method
2773
     *                  curr    8/48    4/48
2774
     *  2/48    4/48    8/48    4/48    2/48
2775
     *  1/48    2/48    4/48    2/48    1/48
2776
     */
2777
    static const int row0_offsets[] = { 1, 2 };
2778
    static const int row0_num[] = { 1, 1 };
2779
    static const int row0_den[] = { 6, 12 };
2780
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
2781
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
2782
    static const int row1_den[] = { 24, 12, 6, 12, 24 };
2783
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
2784
    static const int row2_num[] = { 1, 1, 1, 1, 1 };
2785
    static const int row2_den[] = { 48, 24, 12, 24, 48 };
2786
    int sign;
2787
    int i;
2788

2789
    if (error == 0) {
×
2790
        return;
×
2791
    }
2792

2793
    sign = direction >= 0 ? 1 : -1;
×
2794
    for (i = 0; i < 2; ++i) {
×
2795
        int neighbor;
2796
        int32_t term;
2797

2798
        neighbor = x + sign * row0_offsets[i];
×
2799
        if (neighbor < 0 || neighbor >= width) {
×
2800
            continue;
×
2801
        }
2802
        term = diffuse_fixed_term(error, row0_num[i], row0_den[i]);
×
2803
        carry_curr[((size_t)neighbor * (size_t)depth)
×
2804
                   + (size_t)channel] += term;
×
2805
    }
2806
    if (y + 1 < height) {
×
2807
        for (i = 0; i < 5; ++i) {
×
2808
            int neighbor;
2809
            int32_t term;
2810

2811
            neighbor = x + sign * row1_offsets[i];
×
2812
            if (neighbor < 0 || neighbor >= width) {
×
2813
                continue;
×
2814
            }
2815
            term = diffuse_fixed_term(error, row1_num[i], row1_den[i]);
×
2816
            carry_next[((size_t)neighbor * (size_t)depth)
×
2817
                       + (size_t)channel] += term;
×
2818
        }
2819
    }
2820
    if (y + 2 < height) {
×
2821
        for (i = 0; i < 5; ++i) {
×
2822
            int neighbor;
2823
            int32_t term;
2824

2825
            neighbor = x + sign * row2_offsets[i];
×
2826
            if (neighbor < 0 || neighbor >= width) {
×
2827
                continue;
×
2828
            }
2829
            term = diffuse_fixed_term(error, row2_num[i], row2_den[i]);
×
2830
            carry_far[((size_t)neighbor * (size_t)depth)
×
2831
                      + (size_t)channel] += term;
×
2832
        }
2833
    }
2834
}
2835

2836

2837
static void
2838
diffuse_burkes(unsigned char *data, int width, int height,
67,500✔
2839
               int x, int y, int depth, int error, int direction)
2840
{
2841
    /* Burkes' Method
2842
     *                  curr    4/16    2/16
2843
     *  1/16    2/16    4/16    2/16    1/16
2844
     */
2845
    int pos;
2846
    int sign;
2847
    static const int row0_offsets[] = { 1, 2 };
2848
    static const int row0_num[] = { 1, 1 };
2849
    static const int row0_den[] = { 4, 8 };
2850
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
2851
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
2852
    static const int row1_den[] = { 16, 8, 4, 8, 16 };
2853
    int i;
2854

2855
    pos = y * width + x;
67,500✔
2856
    sign = direction >= 0 ? 1 : -1;
67,500!
2857

2858
    for (i = 0; i < 2; ++i) {
202,500✔
2859
        int neighbor;
2860

2861
        neighbor = x + sign * row0_offsets[i];
135,000✔
2862
        if (neighbor < 0 || neighbor >= width) {
135,000!
2863
            continue;
2,025✔
2864
        }
2865
        error_diffuse_normal(data,
177,300✔
2866
                             pos + (neighbor - x),
132,975✔
2867
                             depth, error,
44,325✔
2868
                             row0_num[i], row0_den[i]);
132,975✔
2869
    }
44,325✔
2870
    if (y < height - 1) {
67,500✔
2871
        int row;
2872

2873
        row = pos + width;
66,600✔
2874
        for (i = 0; i < 5; ++i) {
399,600✔
2875
            int neighbor;
2876

2877
            neighbor = x + sign * row1_offsets[i];
333,000✔
2878
            if (neighbor < 0 || neighbor >= width) {
333,000✔
2879
                continue;
3,996✔
2880
            }
2881
            error_diffuse_normal(data,
438,672✔
2882
                                 row + (neighbor - x),
329,004✔
2883
                                 depth, error,
109,668✔
2884
                                 row1_num[i], row1_den[i]);
329,004✔
2885
        }
109,668✔
2886
    }
22,200✔
2887
}
67,500✔
2888

2889
static void
2890
diffuse_burkes_carry(int32_t *carry_curr, int32_t *carry_next,
×
2891
                     int32_t *carry_far, int width, int height,
2892
                     int depth, int x, int y, int32_t error,
2893
                     int direction, int channel)
2894
{
2895
    /* Burkes' Method
2896
     *                  curr    4/16    2/16
2897
     *  1/16    2/16    4/16    2/16    1/16
2898
     */
2899
    static const int row0_offsets[] = { 1, 2 };
2900
    static const int row0_num[] = { 1, 1 };
2901
    static const int row0_den[] = { 4, 8 };
2902
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
2903
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
2904
    static const int row1_den[] = { 16, 8, 4, 8, 16 };
2905
    int sign;
2906
    int i;
2907

2908
    /* unused */ (void) carry_far;
2909

2910
    if (error == 0) {
×
2911
        return;
×
2912
    }
2913

2914
    sign = direction >= 0 ? 1 : -1;
×
2915
    for (i = 0; i < 2; ++i) {
×
2916
        int neighbor;
2917
        int32_t term;
2918

2919
        neighbor = x + sign * row0_offsets[i];
×
2920
        if (neighbor < 0 || neighbor >= width) {
×
2921
            continue;
×
2922
        }
2923
        term = diffuse_fixed_term(error, row0_num[i], row0_den[i]);
×
2924
        carry_curr[((size_t)neighbor * (size_t)depth)
×
2925
                   + (size_t)channel] += term;
×
2926
    }
2927
    if (y + 1 < height) {
×
2928
        for (i = 0; i < 5; ++i) {
×
2929
            int neighbor;
2930
            int32_t term;
2931

2932
            neighbor = x + sign * row1_offsets[i];
×
2933
            if (neighbor < 0 || neighbor >= width) {
×
2934
                continue;
×
2935
            }
2936
            term = diffuse_fixed_term(error, row1_num[i], row1_den[i]);
×
2937
            carry_next[((size_t)neighbor * (size_t)depth)
×
2938
                       + (size_t)channel] += term;
×
2939
        }
2940
    }
2941
}
2942

2943
static void
2944
diffuse_lso1(unsigned char *data, int width, int height,
×
2945
             int x, int y, int depth, int error, int direction)
2946
{
2947
    int pos;
2948
    int sign;
2949

2950
    pos = y * width + x;
×
2951
    sign = direction >= 0 ? 1 : -1;
×
2952

2953
    /* lso1 (libsixel original) method:
2954
     *
2955
     * libsixel-specific error diffusion (dithering) to improve sixel
2956
     * compression; by steering error propagation so out-of-palette
2957
     * intermediate colors render as horizontal bands rather than grainy
2958
     * noise, we increase RLE more effective.
2959
     *
2960
     *          curr
2961
     *   1/8    4/8    1/8
2962
     *          2/8
2963
     */
2964
    if (y < height - 1) {
×
2965
        int row;
2966

2967
        row = pos + width;
×
2968
        if (x - sign >= 0 && x - sign < width) {
×
2969
            error_diffuse_fast(data,
×
2970
                               row + (-sign),
2971
                               depth, error, 1, 8);
2972
        }
2973
        error_diffuse_fast(data, row, depth, error, 4, 8);
×
2974
        if (x + sign >= 0 && x + sign < width) {
×
2975
            error_diffuse_fast(data,
×
2976
                               row + sign,
2977
                               depth, error, 1, 8);
2978
        }
2979
    }
2980
    if (y < height - 2) {
×
2981
        error_diffuse_fast(data, pos + width * 2, depth, error, 2, 8);
×
2982
    }
2983
}
×
2984

2985

2986
static void
2987
diffuse_lso1_carry(int32_t *carry_curr, int32_t *carry_next,
×
2988
                   int32_t *carry_far, int width, int height,
2989
                   int depth, int x, int y, int32_t error,
2990
                   int direction, int channel)
2991
{
2992
    int sign;
2993
    int32_t edge_term;
2994
    int32_t center_term;
2995
    int32_t far_term;
2996

2997
    /* unused */ (void) carry_curr;
2998
    if (error == 0) {
×
2999
        return;
×
3000
    }
3001

3002
    sign = direction >= 0 ? 1 : -1;
×
3003
    edge_term = diffuse_fixed_term(error, 1, 8);
×
3004
    center_term = diffuse_fixed_term(error, 4, 8);
×
3005
    far_term = diffuse_fixed_term(error, 2, 8);
×
3006

3007
    if (y + 1 < height) {
×
3008
        if (x - sign >= 0 && x - sign < width) {
×
3009
            size_t base;
3010

3011
            base = ((size_t)(x - sign) * (size_t)depth)
×
3012
                 + (size_t)channel;
×
3013
            carry_next[base] += edge_term;
×
3014
        }
3015
        {
3016
            size_t base;
3017

3018
            base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
3019
            carry_next[base] += center_term;
×
3020
        }
3021
        if (x + sign >= 0 && x + sign < width) {
×
3022
            size_t base;
3023

3024
            base = ((size_t)(x + sign) * (size_t)depth)
×
3025
                 + (size_t)channel;
×
3026
            carry_next[base] += edge_term;
×
3027
        }
3028
    }
3029
    if (y + 2 < height) {
×
3030
        size_t base;
3031

3032
        base = ((size_t)x * (size_t)depth) + (size_t)channel;
×
3033
        carry_far[base] += far_term;
×
3034
    }
3035
}
3036

3037
static float
3038
mask_a (int x, int y, int c)
×
3039
{
3040
    return ((((x + c * 67) + y * 236) * 119) & 255 ) / 128.0 - 1.0;
×
3041
}
3042

3043
static float
3044
mask_x (int x, int y, int c)
×
3045
{
3046
    return ((((x + c * 29) ^ y* 149) * 1234) & 511 ) / 256.0 - 1.0;
×
3047
}
3048

3049
/* lookup closest color from palette with "normal" strategy */
3050
static int
3051
lookup_normal(unsigned char const * const pixel,
849,900✔
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
    int diff;
3060
    int r;
3061
    int i;
3062
    int n;
3063
    int distant;
3064

3065
    result = (-1);
849,900✔
3066
    diff = INT_MAX;
849,900✔
3067

3068
    /* don't use cachetable in 'normal' strategy */
3069
    (void) cachetable;
283,300✔
3070

3071
    for (i = 0; i < reqcolor; i++) {
15,309,900✔
3072
        distant = 0;
14,460,000✔
3073
        r = pixel[0] - palette[i * depth + 0];
14,460,000✔
3074
        distant += r * r * complexion;
14,460,000✔
3075
        for (n = 1; n < depth; ++n) {
43,380,000✔
3076
            r = pixel[n] - palette[i * depth + n];
28,920,000✔
3077
            distant += r * r;
28,920,000✔
3078
        }
9,640,000✔
3079
        if (distant < diff) {
14,460,000✔
3080
            diff = distant;
2,193,609✔
3081
            result = i;
2,193,609✔
3082
        }
731,017✔
3083
    }
4,820,000✔
3084

3085
    return result;
849,900✔
3086
}
3087

3088

3089
/* lookup closest color from palette with "fast" strategy */
3090
static int
3091
lookup_fast(unsigned char const * const pixel,
15,735,286✔
3092
            int const depth,
3093
            unsigned char const * const palette,
3094
            int const reqcolor,
3095
            unsigned short * const cachetable,
3096
            int const complexion)
3097
{
3098
    int result;
3099
    unsigned int hash;
3100
    int diff;
3101
    int cache;
3102
    int i;
3103
    int distant;
3104

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

3108
    result = (-1);
15,735,286✔
3109
    diff = INT_MAX;
15,735,286✔
3110
    hash = computeHash(pixel, 3);
15,735,286✔
3111

3112
    cache = cachetable[hash];
15,735,286✔
3113
    if (cache) {  /* fast lookup */
15,735,286✔
3114
        return cache - 1;
15,455,734✔
3115
    }
3116
    /* collision */
3117
    for (i = 0; i < reqcolor; i++) {
32,824,614✔
3118
        distant = 0;
32,545,062✔
3119
#if 0
3120
        for (n = 0; n < 3; ++n) {
3121
            r = pixel[n] - palette[i * 3 + n];
3122
            distant += r * r;
3123
        }
3124
#elif 1  /* complexion correction */
3125
        distant = (pixel[0] - palette[i * 3 + 0]) * (pixel[0] - palette[i * 3 + 0]) * complexion
43,541,152✔
3126
                + (pixel[1] - palette[i * 3 + 1]) * (pixel[1] - palette[i * 3 + 1])
32,545,062✔
3127
                + (pixel[2] - palette[i * 3 + 2]) * (pixel[2] - palette[i * 3 + 2])
32,545,062✔
3128
                ;
3129
#endif
3130
        if (distant < diff) {
32,545,062✔
3131
            diff = distant;
1,840,061✔
3132
            result = i;
1,840,061✔
3133
        }
609,497✔
3134
    }
10,996,090✔
3135
    cachetable[hash] = result + 1;
279,552✔
3136

3137
    return result;
279,552✔
3138
}
8,049,482✔
3139

3140

3141
static int
3142
lookup_mono_darkbg(unsigned char const * const pixel,
1,730,520✔
3143
                   int const depth,
3144
                   unsigned char const * const palette,
3145
                   int const reqcolor,
3146
                   unsigned short * const cachetable,
3147
                   int const complexion)
3148
{
3149
    int n;
3150
    int distant;
3151

3152
    /* unused */ (void) palette;
576,840✔
3153
    /* unused */ (void) cachetable;
576,840✔
3154
    /* unused */ (void) complexion;
576,840✔
3155

3156
    distant = 0;
1,730,520✔
3157
    for (n = 0; n < depth; ++n) {
6,922,080✔
3158
        distant += pixel[n];
5,191,560✔
3159
    }
1,730,520✔
3160
    return distant >= 128 * reqcolor ? 1: 0;
1,730,520✔
3161
}
3162

3163

3164
static int
3165
lookup_mono_lightbg(unsigned char const * const pixel,
810,000✔
3166
                    int const depth,
3167
                    unsigned char const * const palette,
3168
                    int const reqcolor,
3169
                    unsigned short * const cachetable,
3170
                    int const complexion)
3171
{
3172
    int n;
3173
    int distant;
3174

3175
    /* unused */ (void) palette;
270,000✔
3176
    /* unused */ (void) cachetable;
270,000✔
3177
    /* unused */ (void) complexion;
270,000✔
3178

3179
    distant = 0;
810,000✔
3180
    for (n = 0; n < depth; ++n) {
3,240,000✔
3181
        distant += pixel[n];
2,430,000✔
3182
    }
810,000✔
3183
    return distant < 128 * reqcolor ? 1: 0;
810,000✔
3184
}
3185

3186

3187
/* choose colors using median-cut method */
3188
SIXELSTATUS
3189
sixel_quant_make_palette(
232✔
3190
    unsigned char          /* out */ **result,
3191
    unsigned char const    /* in */  *data,
3192
    unsigned int           /* in */  length,
3193
    int                    /* in */  pixelformat,
3194
    unsigned int           /* in */  reqcolors,
3195
    unsigned int           /* in */  *ncolors,
3196
    unsigned int           /* in */  *origcolors,
3197
    int                    /* in */  methodForLargest,
3198
    int                    /* in */  methodForRep,
3199
    int                    /* in */  qualityMode,
3200
    sixel_allocator_t      /* in */  *allocator)
3201
{
3202
    SIXELSTATUS status = SIXEL_FALSE;
232✔
3203
    unsigned int i;
3204
    unsigned int n;
3205
    int ret;
3206
    tupletable2 colormap;
3207
    unsigned int depth;
3208
    int result_depth;
3209

3210
    result_depth = sixel_helper_compute_depth(pixelformat);
232✔
3211
    if (result_depth <= 0) {
232!
3212
        *result = NULL;
×
3213
        goto end;
×
3214
    }
3215

3216
    depth = (unsigned int)result_depth;
232✔
3217

3218
    ret = computeColorMapFromInput(data, length, depth,
340✔
3219
                                   reqcolors, methodForLargest,
108✔
3220
                                   methodForRep, qualityMode,
108✔
3221
                                   &colormap, origcolors, allocator);
108✔
3222
    if (ret != 0) {
232!
3223
        *result = NULL;
×
3224
        goto end;
×
3225
    }
3226
    *ncolors = colormap.size;
232✔
3227
    quant_trace(stderr, "tupletable size: %d\n", *ncolors);
232✔
3228
    *result = (unsigned char *)sixel_allocator_malloc(allocator, *ncolors * depth);
232✔
3229
    for (i = 0; i < *ncolors; i++) {
9,539✔
3230
        for (n = 0; n < depth; ++n) {
37,228✔
3231
            (*result)[i * depth + n] = colormap.table[i]->tuple[n];
27,921✔
3232
        }
10,083✔
3233
    }
3,361✔
3234

3235
    sixel_allocator_free(allocator, colormap.table);
232✔
3236

3237
    status = SIXEL_OK;
232✔
3238

3239
end:
124✔
3240
    return status;
232✔
3241
}
3242

3243

3244
/* apply color palette into specified pixel buffers */
3245
SIXELSTATUS
3246
sixel_quant_apply_palette(
254✔
3247
    sixel_index_t     /* out */ *result,
3248
    unsigned char     /* in */  *data,
3249
    int               /* in */  width,
3250
    int               /* in */  height,
3251
    int               /* in */  depth,
3252
    unsigned char     /* in */  *palette,
3253
    int               /* in */  reqcolor,
3254
    int               /* in */  methodForDiffuse,
3255
    int               /* in */  methodForScan,
3256
    int               /* in */  methodForCarry,
3257
    int               /* in */  foptimize,
3258
    int               /* in */  foptimize_palette,
3259
    int               /* in */  complexion,
3260
    unsigned short    /* in */  *cachetable,
3261
    int               /* in */  *ncolors,
3262
    sixel_allocator_t /* in */  *allocator)
3263
{
150✔
3264
#if _MSC_VER
3265
    enum { max_depth = 4 };
3266
#else
3267
    const size_t max_depth = 4;
254✔
3268
#endif
3269
    unsigned char copy[max_depth];
150✔
3270
    SIXELSTATUS status = SIXEL_FALSE;
254✔
3271
    int sum1, sum2;
3272
    int n;
3273
    unsigned short *indextable;
3274
    unsigned char new_palette[SIXEL_PALETTE_MAX * 4];
3275
    unsigned short migration_map[SIXEL_PALETTE_MAX];
3276
    int (*f_lookup)(unsigned char const * const pixel,
3277
                    int const depth,
3278
                    unsigned char const * const palette,
3279
                    int const reqcolor,
3280
                    unsigned short * const cachetable,
3281
                    int const complexion);
3282
    int use_varerr;
3283
    int use_positional;
3284
    int carry_mode;
3285

3286
    /* check bad reqcolor */
3287
    if (reqcolor < 1) {
254!
3288
        status = SIXEL_BAD_ARGUMENT;
×
3289
        sixel_helper_set_additional_message(
×
3290
            "sixel_quant_apply_palette: "
3291
            "a bad argument is detected, reqcolor < 0.");
3292
        goto end;
×
3293
    }
3294

3295
    /* NOTE: diffuse_jajuni, diffuse_stucki, and diffuse_burkes reference at
3296
     * minimum the position pos + width * 1 - 2, so width must be at least 2
3297
     * to avoid underflow.
3298
     * On the other hand, diffuse_fs and diffuse_atkinson
3299
     * reference pos + width * 1 - 1, but since these functions are only called
3300
     * when width >= 1, they do not cause underflow.
3301
     */
3302
    use_varerr = (depth == 3
358✔
3303
                  && (methodForDiffuse == SIXEL_DIFFUSE_LSO2
508!
3304
                      || methodForDiffuse == SIXEL_DIFFUSE_LSO3));
254!
3305
    use_positional = (methodForDiffuse == SIXEL_DIFFUSE_A_DITHER
358✔
3306
                      || methodForDiffuse == SIXEL_DIFFUSE_X_DITHER);
254!
3307
    carry_mode = (methodForCarry == SIXEL_CARRY_ENABLE)
254✔
3308
               ? SIXEL_CARRY_ENABLE
3309
               : SIXEL_CARRY_DISABLE;
150!
3310

3311
    f_lookup = NULL;
254✔
3312
    if (reqcolor == 2) {
254✔
3313
        sum1 = 0;
19✔
3314
        sum2 = 0;
19✔
3315
        for (n = 0; n < depth; ++n) {
76✔
3316
            sum1 += palette[n];
57✔
3317
        }
21✔
3318
        for (n = depth; n < depth + depth; ++n) {
76✔
3319
            sum2 += palette[n];
57✔
3320
        }
21✔
3321
        if (sum1 == 0 && sum2 == 255 * 3) {
19!
3322
            f_lookup = lookup_mono_darkbg;
12✔
3323
        } else if (sum1 == 255 * 3 && sum2 == 0) {
11!
3324
            f_lookup = lookup_mono_lightbg;
3✔
3325
        }
1✔
3326
    }
7✔
3327
    if (f_lookup == NULL) {
254✔
3328
        if (foptimize && depth == 3) {
239!
3329
            f_lookup = lookup_fast;
233✔
3330
        } else {
97✔
3331
            f_lookup = lookup_normal;
6✔
3332
        }
3333
    }
99✔
3334

3335
    indextable = cachetable;
254✔
3336
    if (cachetable == NULL && f_lookup == lookup_fast) {
254!
3337
        indextable = (unsigned short *)sixel_allocator_calloc(allocator,
×
3338
                                                              (size_t)(1 << depth * 5),
×
3339
                                                              sizeof(unsigned short));
3340
        if (!indextable) {
×
3341
            quant_trace(stderr, "Unable to allocate memory for indextable.\n");
×
3342
            goto end;
×
3343
        }
3344
    }
3345

3346
    if (use_positional) {
254!
3347
        status = apply_palette_positional(result, data, width, height,
×
3348
                                          depth, palette, reqcolor,
3349
                                          methodForDiffuse, methodForScan,
3350
                                          foptimize_palette, f_lookup,
3351
                                          indextable, complexion, copy,
3352
                                          new_palette, migration_map,
3353
                                          ncolors);
3354
    } else if (use_varerr) {
254!
3355
        status = apply_palette_variable(result, data, width, height,
×
3356
                                        depth, palette, reqcolor,
3357
                                        methodForScan, foptimize_palette,
3358
                                        f_lookup, indextable, complexion,
3359
                                        new_palette, migration_map,
3360
                                        ncolors,
3361
                                        methodForDiffuse,
3362
                                        carry_mode);
3363
    } else {
3364
        status = apply_palette_fixed(result, data, width, height,
358✔
3365
                                     depth, palette, reqcolor,
104✔
3366
                                     methodForScan, foptimize_palette,
104✔
3367
                                     f_lookup, indextable, complexion,
104✔
3368
                                     new_palette, migration_map,
104✔
3369
                                     ncolors, methodForDiffuse,
104✔
3370
                                     carry_mode);
104✔
3371
    }
3372
    if (SIXEL_FAILED(status)) {
254!
3373
        goto end;
×
3374
    }
3375

3376
    if (cachetable == NULL) {
254✔
3377
        sixel_allocator_free(allocator, indextable);
18✔
3378
    }
6✔
3379

3380
    status = SIXEL_OK;
254✔
3381

3382
end:
150✔
3383
    return status;
254✔
3384
}
3385

3386

3387
void
3388
sixel_quant_free_palette(
232✔
3389
    unsigned char       /* in */ *data,
3390
    sixel_allocator_t   /* in */ *allocator)
3391
{
3392
    sixel_allocator_free(allocator, data);
232✔
3393
}
232✔
3394

3395

3396
#if HAVE_TESTS
3397
static int
3398
test1(void)
×
3399
{
3400
    int nret = EXIT_FAILURE;
×
3401
    sample minval[1] = { 1 };
×
3402
    sample maxval[1] = { 2 };
×
3403
    unsigned int retval;
3404

3405
    retval = largestByLuminosity(minval, maxval, 1);
×
3406
    if (retval != 0) {
×
3407
        goto error;
×
3408
    }
3409
    nret = EXIT_SUCCESS;
×
3410

3411
error:
3412
    return nret;
×
3413
}
3414

3415

3416
int
3417
sixel_quant_tests_main(void)
×
3418
{
3419
    int nret = EXIT_FAILURE;
×
3420
    size_t i;
3421
    typedef int (* testcase)(void);
3422

3423
    static testcase const testcases[] = {
3424
        test1,
3425
    };
3426

3427
    for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) {
×
3428
        nret = testcases[i]();
×
3429
        if (nret != EXIT_SUCCESS) {
×
3430
            goto error;
×
3431
        }
3432
    }
3433

3434
    nret = EXIT_SUCCESS;
×
3435

3436
error:
3437
    return nret;
×
3438
}
3439
#endif  /* HAVE_TESTS */
3440

3441
/* emacs Local Variables:      */
3442
/* emacs mode: c               */
3443
/* emacs tab-width: 4          */
3444
/* emacs indent-tabs-mode: nil */
3445
/* emacs c-basic-offset: 4     */
3446
/* emacs End:                  */
3447
/* vim: set expandtab ts=4 sts=4 sw=4 : */
3448
/* EOF */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc