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

saitoha / libsixel / 22214775138

20 Feb 2026 06:58AM UTC coverage: 82.619% (-0.3%) from 82.889%
22214775138

push

github

saitoha
tests: add detailed bad-input reasons and fix invalid-case taps

25194 of 49563 branches covered (50.83%)

9 of 9 new or added lines in 1 file covered. (100.0%)

412 existing lines in 11 files now uncovered.

44616 of 54002 relevant lines covered (82.62%)

3765264.85 hits per line

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

92.54
/src/dither-positional-float32.c
1
/*
2
 * SPDX-License-Identifier: MIT
3
 *
4
 * Copyright (c) 2025 libsixel developers. See `AUTHORS`.
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to deal
8
 * in the Software without restriction, including without limitation the rights
9
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
 * copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in
14
 * all copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
 * SOFTWARE.
23
 */
24

25
#if defined(HAVE_CONFIG_H)
26
#include "config.h"
27
#endif
28

29
#if HAVE_MATH_H
30
# include <math.h>
31
#endif  /* HAVE_MATH_H */
32
#include <ctype.h>
33
#include <limits.h>
34
#include <stdlib.h>
35
#include <string.h>
36

37
#include "compat_stub.h"
38
#include "dither-positional-float32.h"
39
#include "dither-common-pipeline.h"
40
#include "pixelformat.h"
41
#include "lookup-common.h"
42
#include "bluenoise_64x64.h"
43

44
#if SIXEL_ENABLE_THREADS
45
# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__MSYS__) && \
46
        !defined(WITH_WINPTHREAD)
47
#  define SIXEL_POS_FLOAT32_USE_WIN32_ONCE 1
48
#  include <windows.h>
49
static INIT_ONCE g_sixel_pos_strength_once_float32 = INIT_ONCE_STATIC_INIT;
50
static INIT_ONCE g_sixel_bn_conf_once_float32 = INIT_ONCE_STATIC_INIT;
51
# else
52
#  include <pthread.h>
53
static pthread_once_t g_sixel_pos_strength_once_float32 = PTHREAD_ONCE_INIT;
54
static pthread_once_t g_sixel_bn_conf_once_float32 = PTHREAD_ONCE_INIT;
55
# endif
56
#endif
57

58
static void
59
sixel_dither_scanline_params_positional_float32(int serpentine,
33,102✔
60
                             int index,
61
                             int limit,
62
                             int *start,
63
                             int *end,
64
                             int *step,
65
                             int *direction)
66
{
67
    if (serpentine && (index & 1)) {
22,134!
68
        *start = limit - 1;
1,130✔
69
        *end = -1;
1,130✔
70
        *step = -1;
1,130✔
71
        *direction = -1;
1,130✔
72
    } else {
518✔
73
        *start = 0;
20,596✔
74
        *end = limit;
20,596✔
75
        *step = 1;
20,596✔
76
        *direction = 1;
32,380✔
77
    }
78
}
21,318✔
79

80
/*
81
 * Cache SIXEL_DITHER_*_DITHER_STRENGTH for positional arithmetic dithers to
82
 * avoid getenv() in inner loops. Invalid values fall back to defaults.
83
 */
84
static float g_sixel_pos_strength_a_float32 = 0.150f;
85
static float g_sixel_pos_strength_x_float32 = 0.100f;
86
static int g_sixel_pos_inited_float32 = 0;
87

88
static void sixel_positional_strength_init_float32(void);
89
static float positional_mask_blue_float32(int x, int y, int c);
90
static void sixel_bluenoise_conf_init_from_env_float32(void);
91

92
static float
93
positional_mask_a_float32(int x, int y, int c)
1,677,606✔
94
{
95
    return (((((x + c * 67) + y * 236) * 119) & 255) / 128.0f
2,464,682✔
96
            - 1.0f) * g_sixel_pos_strength_a_float32;
1,677,606✔
97
}
98

99
static float
100
positional_mask_x_float32(int x, int y, int c)
2,549,847✔
101
{
102
    return (((((x + c * 29) ^ (y * 149)) * 1234) & 511) / 256.0f
3,735,351✔
103
            - 1.0f) * g_sixel_pos_strength_x_float32;
2,549,847✔
104
}
105

106
/*
107
 * Keep per-file suffixes so unity builds do not merge identical static
108
 * helper symbols from other positional dither sources.
109
 */
110
typedef struct {
111
    float strength;
112
    int ox;
113
    int oy;
114
    int per_channel;
115
    int size;
116
} sixel_bluenoise_conf_float32_t;
117

118
static sixel_bluenoise_conf_float32_t g_sixel_bn_conf_float32;
119
static int g_sixel_bn_inited_float32 = 0;
120

121
static int
122
sixel_bn_parse_int_float32(char const *text, int *out_value)
34✔
123
{
124
    char *endptr;
20✔
125
    long value;
20✔
126

127
    if (text == NULL || text[0] == '\0') {
34!
128
        return 0;
129
    }
130

131
    value = strtol(text, &endptr, 10);
34✔
132
    if (endptr == text || *endptr != '\0') {
34!
133
        return 0;
134
    }
135
    if (value > INT_MAX || value < INT_MIN) {
28!
136
        return 0;
137
    }
138

139
    *out_value = (int)value;
34✔
140
    return 1;
34✔
141
}
16✔
142

143
static int
144
sixel_bn_parse_float_float32(char const *text, float *out_value)
34✔
145
{
146
    char *endptr;
20✔
147
    double value;
20✔
148

149
    if (text == NULL || text[0] == '\0') {
34!
150
        return 0;
151
    }
152

153
    value = strtod(text, &endptr);
34✔
154
    if (endptr == text || *endptr != '\0') {
34!
155
        return 0;
22✔
156
    }
157

158
    *out_value = (float)value;
×
159
    return 1;
×
160
}
16✔
161

162
static int
163
sixel_bn_parse_phase_float32(char const *text, int *out_ox, int *out_oy)
17✔
164
{
165
    char *endptr;
10✔
166
    char const *comma;
10✔
167
    long ox;
10✔
168
    long oy;
10✔
169

170
    if (text == NULL || text[0] == '\0') {
17!
171
        return 0;
172
    }
173

174
    comma = strchr(text, ',');
17✔
175
    if (comma == NULL) {
17!
176
        return 0;
11✔
177
    }
178

179
    ox = strtol(text, &endptr, 10);
×
180
    if (endptr == text || endptr != comma) {
×
181
        return 0;
182
    }
183

184
    oy = strtol(comma + 1, &endptr, 10);
×
185
    if (endptr == comma + 1 || *endptr != '\0') {
×
186
        return 0;
187
    }
188
    if (ox > INT_MAX || ox < INT_MIN || oy > INT_MAX || oy < INT_MIN) {
×
189
        return 0;
190
    }
191

192
    *out_ox = (int)ox;
×
193
    *out_oy = (int)oy;
×
194
    return 1;
×
195
}
8✔
196

197
static void
198
sixel_positional_strength_init_body_float32(void)
340✔
199
{
200
    char const *text;
200✔
201
    float strength_a;
200✔
202
    float strength_x;
200✔
203
    int parsed;
200✔
204

205
    /*
206
     * Default strengths are per-dither values. Environment overrides use
207
     * the same parser for consistency and fall back to defaults on error.
208
     */
209
    strength_a = 0.150f;
340✔
210
    text = sixel_compat_getenv("SIXEL_DITHER_A_DITHER_STRENGTH");
340✔
211
    if (text != NULL) {
340✔
212
        parsed = sixel_bn_parse_float_float32(text, &strength_a);
17✔
213
        if (parsed == 0) {
17!
214
            strength_a = 0.150f;
17✔
215
        }
8✔
216
    }
8✔
217

218
    strength_x = 0.100f;
340✔
219
    text = sixel_compat_getenv("SIXEL_DITHER_X_DITHER_STRENGTH");
340✔
220
    if (text != NULL) {
340✔
221
        parsed = sixel_bn_parse_float_float32(text, &strength_x);
17✔
222
        if (parsed == 0) {
17!
223
            strength_x = 0.100f;
17✔
224
        }
8✔
225
    }
8✔
226

227
    g_sixel_pos_strength_a_float32 = strength_a;
340✔
228
    g_sixel_pos_strength_x_float32 = strength_x;
340✔
229
    g_sixel_pos_inited_float32 = 1;
340✔
230
}
340✔
231

232
#if SIXEL_ENABLE_THREADS && defined(SIXEL_POS_FLOAT32_USE_WIN32_ONCE)
233
static BOOL CALLBACK
234
sixel_positional_strength_once_cb_float32(PINIT_ONCE init_once,
120✔
235
                                          PVOID parameter,
236
                                          PVOID *context)
237
{
238
    (void)init_once;
80✔
239
    (void)parameter;
80✔
240
    (void)context;
80✔
241
    sixel_positional_strength_init_body_float32();
120✔
242
    return TRUE;
120✔
243
}
244
#endif
245

246
static void
247
sixel_positional_strength_init_float32(void)
480✔
248
{
249
#if SIXEL_ENABLE_THREADS
250
# if defined(SIXEL_POS_FLOAT32_USE_WIN32_ONCE)
251
    BOOL executed;
120✔
252

253
    executed = InitOnceExecuteOnce(&g_sixel_pos_strength_once_float32,
240✔
254
                                   sixel_positional_strength_once_cb_float32,
255
                                   NULL,
256
                                   NULL);
257
    if (executed == FALSE) {
240✔
258
        sixel_positional_strength_init_body_float32();
259
    }
260
# else
261
    int status;
120✔
262

263
    status = pthread_once(&g_sixel_pos_strength_once_float32,
180✔
264
                          sixel_positional_strength_init_body_float32);
265
    if (status != 0) {
180!
266
        sixel_positional_strength_init_body_float32();
267
    }
268
# endif
269
#else
270
    if (g_sixel_pos_inited_float32 == 0) {
60✔
271
        sixel_positional_strength_init_body_float32();
60✔
272
    }
273
#endif
274
}
440✔
275

276
static unsigned int
277
sixel_bn_hash32_float32(unsigned int value)
17✔
278
{
279
    value += 0x9e3779b9U;
17✔
280
    value ^= value >> 16;
17✔
281
    value *= 0x85ebca6bU;
17✔
282
    value ^= value >> 13;
17✔
283
    value *= 0xc2b2ae35U;
17✔
284
    value ^= value >> 16;
17✔
285
    return value;
17✔
286
}
287

288
static int
289
sixel_bn_str_equal_nocase_float32(char const *left, char const *right)
17✔
290
{
291
    unsigned char lc;
10✔
292
    unsigned char rc;
10✔
293

294
    if (left == NULL || right == NULL) {
17!
295
        return 0;
296
    }
297

298
    while (*left != '\0' && *right != '\0') {
68!
299
        lc = (unsigned char)tolower((unsigned char)*left);
51✔
300
        rc = (unsigned char)tolower((unsigned char)*right);
51✔
301
        if (lc != rc) {
51!
302
            return 0;
303
        }
304
        ++left;
51✔
305
        ++right;
51✔
306
    }
307

308
    return (*left == '\0' && *right == '\0');
17!
309
}
8✔
310

311
/*
312
 * Cache bluenoise configuration at first use so we do not hit getenv()
313
 * inside pixel loops. Invalid values fall back to defaults.
314
 */
315
static void
316
sixel_bluenoise_conf_init_from_env_body_float32(void)
170✔
317
{
318
    char const *text;
100✔
319
    float strength;
100✔
320
    int size;
100✔
321
    int ox;
100✔
322
    int oy;
100✔
323
    int seed;
100✔
324
    int phase_set;
100✔
325
    int parsed;
100✔
326
    int per_channel;
100✔
327
    unsigned int hash;
100✔
328

329
    strength = 0.055f;
170✔
330
    text = sixel_compat_getenv("SIXEL_DITHER_BLUENOISE_STRENGTH");
170✔
331
    if (text != NULL) {
170!
332
        parsed = sixel_bn_parse_float_float32(text, &strength);
×
333
        if (parsed == 0) {
×
334
            strength = 0.055f;
×
335
        }
336
    }
337

338
    ox = 0;
170✔
339
    oy = 0;
170✔
340
    phase_set = 0;
170✔
341
    text = sixel_compat_getenv("SIXEL_DITHER_BLUENOISE_PHASE");
170✔
342
    if (text != NULL) {
170✔
343
        phase_set = 1;
17✔
344
        parsed = sixel_bn_parse_phase_float32(text, &ox, &oy);
17✔
345
        if (parsed == 0) {
17!
346
            ox = 0;
17✔
347
            oy = 0;
17✔
348
        }
8✔
349
    }
8✔
350
    if (phase_set == 0) {
116✔
351
        text = sixel_compat_getenv("SIXEL_DITHER_BLUENOISE_SEED");
153✔
352
        if (text != NULL) {
153✔
353
            parsed = sixel_bn_parse_int_float32(text, &seed);
17✔
354
            if (parsed != 0) {
17!
355
                hash = sixel_bn_hash32_float32((unsigned int)seed);
17✔
356
                ox = (int)(hash & 63U);
17✔
357
                oy = (int)((hash >> 8) & 63U);
17✔
358
            }
8✔
359
        }
8✔
360
    }
72✔
361

362
    per_channel = 0;
170✔
363
    text = sixel_compat_getenv("SIXEL_DITHER_BLUENOISE_CHANNEL");
170✔
364
    if (text != NULL) {
170✔
365
        if (sixel_bn_str_equal_nocase_float32(text, "rgb") != 0) {
17!
366
            per_channel = 1;
11✔
367
        } else if (sixel_bn_str_equal_nocase_float32(text, "mono") != 0) {
8!
368
            per_channel = 0;
×
369
        }
370
    }
8✔
371

372
    size = SIXEL_BN_W;
170✔
373
    text = sixel_compat_getenv("SIXEL_DITHER_BLUENOISE_SIZE");
170✔
374
    if (text != NULL) {
170✔
375
        parsed = sixel_bn_parse_int_float32(text, &size);
17✔
376
        if (parsed == 0 || size != SIXEL_BN_W) {
17!
377
            size = SIXEL_BN_W;
17✔
378
        }
8✔
379
    }
8✔
380

381
    g_sixel_bn_conf_float32.strength = strength;
170✔
382
    g_sixel_bn_conf_float32.ox = ox;
170✔
383
    g_sixel_bn_conf_float32.oy = oy;
170✔
384
    g_sixel_bn_conf_float32.per_channel = per_channel;
170✔
385
    g_sixel_bn_conf_float32.size = size;
170✔
386
    g_sixel_bn_inited_float32 = 1;
170✔
387
}
170✔
388

389
#if SIXEL_ENABLE_THREADS && defined(SIXEL_POS_FLOAT32_USE_WIN32_ONCE)
390
static BOOL CALLBACK
391
sixel_bluenoise_conf_once_cb_float32(PINIT_ONCE init_once,
60✔
392
                                     PVOID parameter,
393
                                     PVOID *context)
394
{
395
    (void)init_once;
40✔
396
    (void)parameter;
40✔
397
    (void)context;
40✔
398
    sixel_bluenoise_conf_init_from_env_body_float32();
60✔
399
    return TRUE;
60✔
400
}
401
#endif
402

403
static void
404
sixel_bluenoise_conf_init_from_env_float32(void)
240✔
405
{
406
#if SIXEL_ENABLE_THREADS
407
# if defined(SIXEL_POS_FLOAT32_USE_WIN32_ONCE)
408
    BOOL executed;
60✔
409

410
    executed = InitOnceExecuteOnce(&g_sixel_bn_conf_once_float32,
120✔
411
                                   sixel_bluenoise_conf_once_cb_float32,
412
                                   NULL,
413
                                   NULL);
414
    if (executed == FALSE) {
120✔
415
        sixel_bluenoise_conf_init_from_env_body_float32();
416
    }
417
# else
418
    int status;
60✔
419

420
    status = pthread_once(&g_sixel_bn_conf_once_float32,
90✔
421
                          sixel_bluenoise_conf_init_from_env_body_float32);
422
    if (status != 0) {
90!
423
        sixel_bluenoise_conf_init_from_env_body_float32();
424
    }
425
# endif
426
#else
427
    if (g_sixel_bn_inited_float32 == 0) {
30✔
428
        sixel_bluenoise_conf_init_from_env_body_float32();
30✔
429
    }
430
#endif
431
}
220✔
432

433
static float
434
sixel_bluenoise_tri_float32(int x, int y, int c)
2,117,049✔
435
{
436
    /* Triangular noise blends two samples from the same tile. */
437
    static int const channel_offset_x[3] = { 17, 34, 51 };
752,912✔
438
    static int const channel_offset_y[3] = { 31, 62, 93 };
752,912✔
439
    int ox;
1,244,432✔
440
    int oy;
1,244,432✔
441
    int per_channel;
1,244,432✔
442
    int channel_x;
1,244,432✔
443
    int channel_y;
1,244,432✔
444
    int ix0;
1,244,432✔
445
    int iy0;
1,244,432✔
446
    int ix1;
1,244,432✔
447
    int iy1;
1,244,432✔
448
    float u;
1,244,432✔
449
    float v;
1,244,432✔
450

451
    ox = g_sixel_bn_conf_float32.ox;
2,117,049✔
452
    oy = g_sixel_bn_conf_float32.oy;
2,117,049✔
453
    per_channel = g_sixel_bn_conf_float32.per_channel;
2,117,049✔
454
    channel_x = 0;
2,117,049✔
455
    channel_y = 0;
2,117,049✔
456
    if (per_channel != 0 && c >= 0 && c < 3) {
2,117,049!
457
        channel_x = channel_offset_x[c];
215,694✔
458
        channel_y = channel_offset_y[c];
215,694✔
459
    }
99,393✔
460

461
    ix0 = x + ox + channel_x;
2,117,049✔
462
    iy0 = y + oy + channel_y;
2,117,049✔
463
    ix1 = ix0 + 13;
2,117,049✔
464
    iy1 = iy0 + 29;
2,117,049✔
465
    u = (sixel_bn_mask(ix0, iy0) + 1.0f) * 0.5f;
2,117,049✔
466
    v = (sixel_bn_mask(ix1, iy1) + 1.0f) * 0.5f;
2,117,056✔
467

468
    return (u + v) - 1.0f;
2,608,705✔
469
}
491,520✔
470

471
static float
472
positional_mask_blue_float32(int x, int y, int c)
2,116,981✔
473
{
474
    return sixel_bluenoise_tri_float32(x, y, c)
3,103,802✔
475
        * g_sixel_bn_conf_float32.strength;
2,117,066✔
476
}
477

478
SIXELSTATUS
479
sixel_dither_apply_positional_float32(sixel_dither_t *dither,
720✔
480
                                      sixel_dither_context_t *context)
481
{
482
    int serpentine;
420✔
483
    int y;
420✔
484
    int absolute_y;
420✔
485
    float (*f_mask)(int x, int y, int c);
420✔
486
    float jitter_scale;
420✔
487
    float *palette_float;
420✔
488
    float *new_palette_float;
420✔
489
    int float_depth;
420✔
490
    int float_index;
420✔
491
    unsigned char *quantized;
420✔
492
    float lookup_pixel_float[SIXEL_MAX_CHANNELS];
420✔
493
    unsigned char const *lookup_pixel;
420✔
494
    sixel_lut_t *fast_lut;
420✔
495
    int use_fast_lut;
420✔
496
    int lookup_wants_float;
420✔
497
    int use_palette_float_lookup;
420✔
498
    int need_float_pixel;
420✔
499

500
    palette_float = NULL;
720✔
501
    new_palette_float = NULL;
720✔
502
    float_depth = 0;
720✔
503
    quantized = NULL;
720✔
504
    lookup_wants_float = 0;
720✔
505

506
    if (dither == NULL || context == NULL) {
720!
507
        return SIXEL_BAD_ARGUMENT;
508
    }
509
    if (context->pixels_float == NULL || context->scratch == NULL) {
720!
510
        return SIXEL_BAD_ARGUMENT;
511
    }
512
    if (context->palette == NULL || context->result == NULL) {
720!
513
        return SIXEL_BAD_ARGUMENT;
514
    }
515

516
    switch (context->method_for_diffuse) {
720!
517
    case SIXEL_DIFFUSE_A_DITHER:
104✔
518
        sixel_positional_strength_init_float32();
192!
519
        f_mask = positional_mask_a_float32;
176✔
520
        break;
176✔
521
    case SIXEL_DIFFUSE_X_DITHER:
156✔
522
        sixel_positional_strength_init_float32();
288!
523
        f_mask = positional_mask_x_float32;
264✔
524
        break;
264✔
525
    case SIXEL_DIFFUSE_BLUENOISE_DITHER:
130✔
526
        sixel_bluenoise_conf_init_from_env_float32();
240!
527
        f_mask = positional_mask_blue_float32;
220✔
528
        break;
220✔
529
    default:
UNCOV
530
        sixel_positional_strength_init_float32();
×
531
        f_mask = positional_mask_x_float32;
532
        break;
533
    }
534

535
    serpentine = (context->method_for_scan == SIXEL_SCAN_SERPENTINE);
720✔
536
    jitter_scale = 32.0f / 255.0f;
720✔
537
    palette_float = context->palette_float;
720✔
538
    new_palette_float = context->new_palette_float;
720✔
539
    float_depth = context->float_depth;
720✔
540
    quantized = context->scratch;
720✔
541
    fast_lut = context->lut;
720✔
542
    use_fast_lut = (fast_lut != NULL);
720✔
543
    lookup_wants_float = (context->lookup_source_is_float != 0);
720✔
544
    use_palette_float_lookup = 0;
720✔
545
    if (context->prefer_palette_float_lookup != 0
720!
546
            && palette_float != NULL
270!
547
            && float_depth >= context->depth) {
×
548
        use_palette_float_lookup = 1;
300✔
549
    }
550
    need_float_pixel = lookup_wants_float || use_palette_float_lookup;
720✔
551

552
    if (context->optimize_palette) {
720!
553
        int x;
58✔
554

555
        *context->ncolors = 0;
91✔
556
        memset(context->new_palette, 0x00,
95✔
557
               (size_t)SIXEL_PALETTE_MAX * (size_t)context->depth);
91!
558
        if (new_palette_float != NULL && float_depth > 0) {
91!
559
            memset(new_palette_float, 0x00,
×
560
                   (size_t)SIXEL_PALETTE_MAX
561
                       * (size_t)float_depth * sizeof(float));
562
        }
563
        memset(context->migration_map, 0x00,
91✔
564
               sizeof(unsigned short) * (size_t)SIXEL_PALETTE_MAX);
565
        for (y = 0; y < context->height; ++y) {
5,659!
566
            absolute_y = context->band_origin + y;
5,568✔
567
            int start;
3,712✔
568
            int end;
3,712✔
569
            int step;
3,712✔
570
            int direction;
3,712✔
571

572
            sixel_dither_scanline_params_positional_float32(serpentine, absolute_y,
5,568!
573
                                         context->width,
574
                                         &start, &end, &step, &direction);
575
            (void)direction;
3,712✔
576
            for (x = start; x != end; x += step) {
361,920!
577
                int pos;
237,568✔
578
                int d;
237,568✔
579
                int color_index;
237,568✔
580

581
                pos = y * context->width + x;
356,352✔
582
                for (d = 0; d < context->depth; ++d) {
1,425,408!
583
                    float val;
712,704✔
584

585
                    val = context->pixels_float[pos * context->depth + d]
2,138,112✔
586
                        + f_mask(x, y, d) * jitter_scale;
1,069,056✔
587
                    val = sixel_pixelformat_float_channel_clamp(
1,069,056✔
588
                        context->pixelformat,
589
                        d,
590
                        val);
591
                    if (need_float_pixel) {
1,069,056!
592
                        lookup_pixel_float[d] = val;
1,032,192✔
593
                    }
594
                    if (!lookup_wants_float && !use_palette_float_lookup) {
1,044,480!
595
                        quantized[d]
36,864✔
596
                            = sixel_pixelformat_float_channel_to_byte(
36,864✔
597
                                  context->pixelformat,
598
                                  d,
599
                                  val);
600
                    }
601
                }
602
                if (lookup_wants_float) {
356,352!
603
                    lookup_pixel = (unsigned char const *)(void const *)
344,064✔
604
                        lookup_pixel_float;
605
                    if (use_fast_lut) {
344,064!
606
                        color_index = sixel_lut_map_pixel(fast_lut,
344,064✔
607
                                                         lookup_pixel);
608
                    } else {
609
                        color_index = context->lookup(lookup_pixel,
×
610
                                                      context->depth,
611
                                                      context->palette,
×
612
                                                      context->reqcolor,
613
                                                      context->indextable,
614
                                                      context->complexion);
615
                    }
616
                } else if (use_palette_float_lookup) {
12,288!
617
                    color_index = sixel_dither_lookup_palette_float32(
×
618
                        lookup_pixel_float,
619
                        context->depth,
620
                        palette_float,
621
                        context->reqcolor,
622
                        context->complexion);
623
                } else {
624
                    lookup_pixel = quantized;
12,288✔
625
                    if (use_fast_lut) {
12,288!
626
                        color_index = sixel_lut_map_pixel(fast_lut,
×
627
                                                         lookup_pixel);
628
                    } else {
629
                        color_index = context->lookup(lookup_pixel,
12,288✔
630
                                                      context->depth,
631
                                                      context->palette,
12,288✔
632
                                                      context->reqcolor,
633
                                                      context->indextable,
634
                                                      context->complexion);
635
                    }
636
                }
637
                if (context->migration_map[color_index] == 0) {
356,352!
638
                    if (absolute_y >= context->output_start) {
14,236!
639
                        /*
640
                         * Palette indices never exceed SIXEL_PALETTE_MAX, so
641
                         * the cast to sixel_index_t (unsigned char) is safe.
642
                         */
643
                        context->result[pos]
14,236✔
644
                            = (sixel_index_t)(*context->ncolors);
14,236✔
645
                    }
646
                    for (d = 0; d < context->depth; ++d) {
56,944!
647
                        context->new_palette[*context->ncolors
42,708✔
648
                                             * context->depth + d]
42,708✔
649
                            = context->palette[color_index
42,708✔
650
                                               * context->depth + d];
42,708✔
651
                    }
652
                    if (palette_float != NULL
14,236!
653
                            && new_palette_float != NULL
9,491!
654
                            && float_depth > 0) {
×
655
                        for (float_index = 0;
×
656
                                float_index < float_depth;
×
657
                                ++float_index) {
×
658
                            new_palette_float[*context->ncolors
×
659
                                               * float_depth
×
660
                                               + float_index]
×
661
                                = palette_float[color_index * float_depth
×
662
                                                + float_index];
×
663
                        }
664
                    }
665
                    ++*context->ncolors;
14,236✔
666
                    /*
667
                     * Migration map entries are limited to the palette size
668
                     * (<= 256), so storing them as unsigned short is safe.
669
                     */
670
                    context->migration_map[color_index]
14,236✔
671
                        = (unsigned short)(*context->ncolors);
14,236✔
672
                } else {
673
                    if (absolute_y >= context->output_start) {
342,116!
674
                        context->result[pos]
342,116✔
675
                            = (sixel_index_t)(context->migration_map[
342,116✔
676
                                  color_index] - 1);
677
                    }
678
                }
679
            }
680
            if (absolute_y >= context->output_start) {
5,568!
681
                sixel_dither_pipeline_row_notify(dither, absolute_y);
5,568✔
682
            }
683
        }
684
        memcpy(context->palette, context->new_palette,
95✔
685
               (size_t)(*context->ncolors * context->depth));
91!
686
        if (palette_float != NULL
91!
687
                && new_palette_float != NULL
62!
688
                && float_depth > 0) {
×
689
            memcpy(palette_float,
×
690
                   new_palette_float,
691
                   (size_t)(*context->ncolors * float_depth)
692
                       * sizeof(float));
693
        }
694
    } else {
4✔
695
        int x;
120✔
696

697
        for (y = 0; y < context->height; ++y) {
28,163✔
698
            absolute_y = context->band_origin + y;
27,530✔
699
            int start;
15,752✔
700
            int end;
15,752✔
701
            int step;
15,752✔
702
            int direction;
15,752✔
703

704
            sixel_dither_scanline_params_positional_float32(serpentine, absolute_y,
42,952!
705
                                         context->width,
15,422✔
706
                                         &start, &end, &step, &direction);
707
            (void)direction;
23,494✔
708
            for (x = start; x != end; x += step) {
1,787,819✔
709
                int pos;
1,007,117✔
710
                int d;
1,007,117✔
711

712
                pos = y * context->width + x;
1,760,725✔
713
                for (d = 0; d < context->depth; ++d) {
7,035,831✔
714
                    float val;
3,017,932✔
715

716
                    val = context->pixels_float[pos * context->depth + d]
10,548,637✔
717
                        + f_mask(x, y, d) * jitter_scale;
5,275,531✔
718
                    val = sixel_pixelformat_float_channel_clamp(
5,273,106✔
719
                        context->pixelformat,
2,959,612✔
720
                        d,
2,959,612✔
721
                        val);
2,959,612✔
722
                    if (need_float_pixel) {
5,275,036!
723
                        lookup_pixel_float[d] = val;
5,095,345✔
724
                    }
2,860,184✔
725
                    if (!lookup_wants_float && !use_palette_float_lookup) {
5,221,380!
726
                        quantized[d]
179,439✔
727
                            = sixel_pixelformat_float_channel_to_byte(
278,628✔
728
                                  context->pixelformat,
99,189✔
729
                                  d,
99,189✔
730
                                  val);
99,189✔
731
                    }
99,189✔
732
                }
2,959,616✔
733
                if (absolute_y >= context->output_start) {
1,760,300!
734
                    /*
735
                     * Palette indices never exceed SIXEL_PALETTE_MAX, so
736
                     * narrowing to sixel_index_t (unsigned char) is safe.
737
                     */
738
                    if (lookup_wants_float) {
1,731,462!
739
                        lookup_pixel = (unsigned char const *)(void const *)
1,674,094✔
740
                            lookup_pixel_float;
950,083✔
741
                        context->result[pos] = (sixel_index_t)
1,674,120✔
742
                            context->lookup(
2,624,177✔
743
                                lookup_pixel,
950,083✔
744
                                context->depth,
950,083✔
745
                                context->palette,
1,674,094✔
746
                                context->reqcolor,
950,083✔
747
                                context->indextable,
950,083✔
748
                                context->complexion);
950,083✔
749
                    } else if (use_palette_float_lookup) {
1,007,451!
750
                        context->result[pos] = (sixel_index_t)
×
751
                            sixel_dither_lookup_palette_float32(
×
752
                                lookup_pixel_float,
753
                                context->depth,
754
                                palette_float,
755
                                context->reqcolor,
756
                                context->complexion);
757
                    } else {
758
                        lookup_pixel = quantized;
57,368✔
759
                        context->result[pos] = (sixel_index_t)
57,331✔
760
                            context->lookup(
90,129✔
761
                                lookup_pixel,
32,761✔
762
                                context->depth,
32,761✔
763
                                context->palette,
57,368✔
764
                                context->reqcolor,
32,761✔
765
                                context->indextable,
32,761✔
766
                                context->complexion);
32,761✔
767
                    }
768
                }
982,844✔
769
            }
987,097✔
770
            if (absolute_y >= context->output_start) {
27,094!
771
                sixel_dither_pipeline_row_notify(dither, absolute_y);
27,071✔
772
            }
15,360✔
773
        }
15,426✔
774
        *context->ncolors = context->reqcolor;
633✔
775
    }
120✔
776

777
    return SIXEL_OK;
424✔
778
}
274✔
779

780
/* emacs Local Variables:      */
781
/* emacs mode: c               */
782
/* emacs tab-width: 4          */
783
/* emacs indent-tabs-mode: nil */
784
/* emacs c-basic-offset: 4     */
785
/* emacs End:                  */
786
/* vim: set expandtab ts=4 sts=4 sw=4 : */
787
/* EOF */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc