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

saitoha / libsixel / 21346946305

26 Jan 2026 05:04AM UTC coverage: 76.116% (-0.1%) from 76.232%
21346946305

push

github

saitoha
feat: remove lpips metrics and onnx helpers

19890 of 44692 branches covered (44.5%)

36187 of 47542 relevant lines covered (76.12%)

13450223.36 hits per line

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

49.27
/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
static void
45
sixel_dither_scanline_params_positional_float32(int serpentine,
6,168✔
46
                             int index,
47
                             int limit,
48
                             int *start,
49
                             int *end,
50
                             int *step,
51
                             int *direction)
52
{
53
    if (serpentine && (index & 1)) {
5,784!
54
        *start = limit - 1;
2,700✔
55
        *end = -1;
2,700✔
56
        *step = -1;
2,700✔
57
        *direction = -1;
2,700✔
58
    } else {
675✔
59
        *start = 0;
1,734✔
60
        *end = limit;
1,734✔
61
        *step = 1;
1,734✔
62
        *direction = 1;
4,818✔
63
    }
64
}
3,084✔
65

66
/*
67
 * Cache SIXEL_DITHER_*_DITHER_STRENGTH for positional arithmetic dithers to
68
 * avoid getenv() in inner loops. Invalid values fall back to defaults.
69
 */
70
static float g_sixel_pos_strength_a_float32 = 0.150f;
71
static float g_sixel_pos_strength_x_float32 = 0.100f;
72
static int g_sixel_pos_inited_float32 = 0;
73

74
static void sixel_positional_strength_init_float32(void);
75
static float positional_mask_blue_float32(int x, int y, int c);
76

77
static float
78
positional_mask_a_float32(int x, int y, int c)
147,456✔
79
{
80
    sixel_positional_strength_init_float32();
147,456✔
81
    return (((((x + c * 67) + y * 236) * 119) & 255) / 128.0f
184,320✔
82
            - 1.0f) * g_sixel_pos_strength_a_float32;
147,456✔
83
}
84

85
static float
86
positional_mask_x_float32(int x, int y, int c)
9,720,000✔
87
{
88
    sixel_positional_strength_init_float32();
9,720,000✔
89
    return (((((x + c * 29) ^ (y * 149)) * 1234) & 511) / 256.0f
12,150,000✔
90
            - 1.0f) * g_sixel_pos_strength_x_float32;
9,720,000✔
91
}
92

93
/*
94
 * Keep per-file suffixes so unity builds do not merge identical static
95
 * helper symbols from other positional dither sources.
96
 */
97
typedef struct {
98
    float strength;
99
    int ox;
100
    int oy;
101
    int per_channel;
102
    int size;
103
} sixel_bluenoise_conf_float32_t;
104

105
static sixel_bluenoise_conf_float32_t g_sixel_bn_conf_float32;
106
static int g_sixel_bn_inited_float32 = 0;
107

108
static int
109
sixel_bn_parse_int_float32(char const *text, int *out_value)
×
110
{
111
    char *endptr;
112
    long value;
113

114
    if (text == NULL || text[0] == '\0') {
×
115
        return 0;
116
    }
117

118
    value = strtol(text, &endptr, 10);
×
119
    if (endptr == text || *endptr != '\0') {
×
120
        return 0;
121
    }
122
    if (value > INT_MAX || value < INT_MIN) {
×
123
        return 0;
124
    }
125

126
    *out_value = (int)value;
×
127
    return 1;
×
128
}
129

130
static int
131
sixel_bn_parse_float_float32(char const *text, float *out_value)
×
132
{
133
    char *endptr;
134
    double value;
135

136
    if (text == NULL || text[0] == '\0') {
×
137
        return 0;
138
    }
139

140
    value = strtod(text, &endptr);
×
141
    if (endptr == text || *endptr != '\0') {
×
142
        return 0;
143
    }
144

145
    *out_value = (float)value;
×
146
    return 1;
×
147
}
148

149
static int
150
sixel_bn_parse_phase_float32(char const *text, int *out_ox, int *out_oy)
×
151
{
152
    char *endptr;
153
    char const *comma;
154
    long ox;
155
    long oy;
156

157
    if (text == NULL || text[0] == '\0') {
×
158
        return 0;
159
    }
160

161
    comma = strchr(text, ',');
×
162
    if (comma == NULL) {
×
163
        return 0;
164
    }
165

166
    ox = strtol(text, &endptr, 10);
×
167
    if (endptr == text || endptr != comma) {
×
168
        return 0;
169
    }
170

171
    oy = strtol(comma + 1, &endptr, 10);
×
172
    if (endptr == comma + 1 || *endptr != '\0') {
×
173
        return 0;
174
    }
175
    if (ox > INT_MAX || ox < INT_MIN || oy > INT_MAX || oy < INT_MIN) {
×
176
        return 0;
177
    }
178

179
    *out_ox = (int)ox;
×
180
    *out_oy = (int)oy;
×
181
    return 1;
×
182
}
183

184
static void
185
sixel_positional_strength_init_float32(void)
9,867,456✔
186
{
187
    char const *text;
5,756,016✔
188
    float strength_default;
5,756,016✔
189
    float strength_a;
5,756,016✔
190
    float strength_x;
5,756,016✔
191
    int parsed;
5,756,016✔
192
    int common_set;
5,756,016✔
193

194
    if (g_sixel_pos_inited_float32 != 0) {
9,867,456✔
195
        return;
9,867,432✔
196
    }
197

198
    strength_default = 0.100f;
24✔
199
    common_set = 0;
24✔
200
    text = sixel_compat_getenv("SIXEL_DITHER_STRENGTH");
24✔
201
    if (text != NULL) {
24!
202
        parsed = sixel_bn_parse_float_float32(text, &strength_default);
×
203
        if (parsed != 0) {
×
204
            common_set = 1;
205
        } else {
206
            strength_default = 0.100f;
×
207
        }
208
    }
209

210
    strength_a = common_set ? strength_default : 0.150f;
12!
211
    text = sixel_compat_getenv("SIXEL_DITHER_A_DITHER_STRENGTH");
24✔
212
    if (text != NULL) {
24!
213
        parsed = sixel_bn_parse_float_float32(text, &strength_a);
×
214
        if (parsed == 0) {
×
215
            strength_a = common_set ? strength_default : 0.150f;
×
216
        }
217
    }
218

219
    strength_x = common_set ? strength_default : 0.100f;
24!
220
    text = sixel_compat_getenv("SIXEL_DITHER_X_DITHER_STRENGTH");
24✔
221
    if (text != NULL) {
24!
222
        parsed = sixel_bn_parse_float_float32(text, &strength_x);
×
223
        if (parsed == 0) {
×
224
            strength_x = common_set ? strength_default : 0.100f;
×
225
        }
226
    }
227

228
    g_sixel_pos_strength_a_float32 = strength_a;
24✔
229
    g_sixel_pos_strength_x_float32 = strength_x;
24✔
230
    g_sixel_pos_inited_float32 = 1;
24✔
231
}
2,466,864!
232

233
static unsigned int
234
sixel_bn_hash32_float32(unsigned int value)
×
235
{
236
    value += 0x9e3779b9U;
×
237
    value ^= value >> 16;
×
238
    value *= 0x85ebca6bU;
×
239
    value ^= value >> 13;
×
240
    value *= 0xc2b2ae35U;
×
241
    value ^= value >> 16;
×
242
    return value;
×
243
}
244

245
static int
246
sixel_bn_str_equal_nocase_float32(char const *left, char const *right)
×
247
{
248
    unsigned char lc;
249
    unsigned char rc;
250

251
    if (left == NULL || right == NULL) {
×
252
        return 0;
253
    }
254

255
    while (*left != '\0' && *right != '\0') {
×
256
        lc = (unsigned char)tolower((unsigned char)*left);
×
257
        rc = (unsigned char)tolower((unsigned char)*right);
×
258
        if (lc != rc) {
×
259
            return 0;
260
        }
261
        ++left;
×
262
        ++right;
×
263
    }
264

265
    return (*left == '\0' && *right == '\0');
×
266
}
267

268
/*
269
 * Cache bluenoise configuration at first use so we do not hit getenv()
270
 * inside pixel loops. Invalid values fall back to defaults.
271
 */
272
static void
273
sixel_bluenoise_conf_init_from_env_float32(void)
×
274
{
275
    char const *text;
276
    float strength;
277
    int size;
278
    int ox;
279
    int oy;
280
    int seed;
281
    int phase_set;
282
    int parsed;
283
    int per_channel;
284
    unsigned int hash;
285

286
    if (g_sixel_bn_inited_float32 != 0) {
×
287
        return;
×
288
    }
289

290
    strength = 0.100f;
×
291
    text = sixel_compat_getenv("SIXEL_DITHER_BLUENOISE_STRENGTH");
×
292
    if (text != NULL) {
×
293
        parsed = sixel_bn_parse_float_float32(text, &strength);
×
294
        if (parsed == 0) {
×
295
            strength = 0.100f;
×
296
        }
297
    } else {
298
        text = sixel_compat_getenv("SIXEL_DITHER_STRENGTH");
×
299
        if (text != NULL) {
×
300
            parsed = sixel_bn_parse_float_float32(text, &strength);
×
301
            if (parsed == 0) {
×
302
                strength = 0.100f;
×
303
            }
304
        }
305
    }
306

307
    ox = 0;
×
308
    oy = 0;
×
309
    phase_set = 0;
×
310
    text = sixel_compat_getenv("SIXEL_DITHER_BLUENOISE_PHASE");
×
311
    if (text != NULL) {
×
312
        phase_set = 1;
×
313
        parsed = sixel_bn_parse_phase_float32(text, &ox, &oy);
×
314
        if (parsed == 0) {
×
315
            ox = 0;
×
316
            oy = 0;
×
317
        }
318
    }
319
    if (phase_set == 0) {
×
320
        text = sixel_compat_getenv("SIXEL_DITHER_BLUENOISE_SEED");
×
321
        if (text != NULL) {
×
322
            parsed = sixel_bn_parse_int_float32(text, &seed);
×
323
            if (parsed != 0) {
×
324
                hash = sixel_bn_hash32_float32((unsigned int)seed);
×
325
                ox = (int)(hash & 63U);
×
326
                oy = (int)((hash >> 8) & 63U);
×
327
            }
328
        }
329
    }
330

331
    per_channel = 0;
×
332
    text = sixel_compat_getenv("SIXEL_DITHER_BLUENOISE_CHANNEL");
×
333
    if (text != NULL) {
×
334
        if (sixel_bn_str_equal_nocase_float32(text, "rgb") != 0) {
×
335
            per_channel = 1;
336
        } else if (sixel_bn_str_equal_nocase_float32(text, "mono") != 0) {
×
337
            per_channel = 0;
×
338
        }
339
    }
340

341
    size = SIXEL_BN_W;
×
342
    text = sixel_compat_getenv("SIXEL_DITHER_BLUENOISE_SIZE");
×
343
    if (text != NULL) {
×
344
        parsed = sixel_bn_parse_int_float32(text, &size);
×
345
        if (parsed == 0 || size != SIXEL_BN_W) {
×
346
            size = SIXEL_BN_W;
×
347
        }
348
    }
349

350
    g_sixel_bn_conf_float32.strength = strength;
×
351
    g_sixel_bn_conf_float32.ox = ox;
×
352
    g_sixel_bn_conf_float32.oy = oy;
×
353
    g_sixel_bn_conf_float32.per_channel = per_channel;
×
354
    g_sixel_bn_conf_float32.size = size;
×
355
    g_sixel_bn_inited_float32 = 1;
×
356
}
×
357

358
static float
359
sixel_bluenoise_tri_float32(int x, int y, int c)
×
360
{
361
    /* Triangular noise blends two samples from the same tile. */
362
    static int const channel_offset_x[3] = { 17, 34, 51 };
363
    static int const channel_offset_y[3] = { 31, 62, 93 };
364
    int ox;
365
    int oy;
366
    int per_channel;
367
    int channel_x;
368
    int channel_y;
369
    int ix0;
370
    int iy0;
371
    int ix1;
372
    int iy1;
373
    float u;
374
    float v;
375

376
    ox = g_sixel_bn_conf_float32.ox;
×
377
    oy = g_sixel_bn_conf_float32.oy;
×
378
    per_channel = g_sixel_bn_conf_float32.per_channel;
×
379
    channel_x = 0;
×
380
    channel_y = 0;
×
381
    if (per_channel != 0 && c >= 0 && c < 3) {
×
382
        channel_x = channel_offset_x[c];
×
383
        channel_y = channel_offset_y[c];
×
384
    }
385

386
    ix0 = x + ox + channel_x;
×
387
    iy0 = y + oy + channel_y;
×
388
    ix1 = ix0 + 13;
×
389
    iy1 = iy0 + 29;
×
390
    u = (sixel_bn_mask(ix0, iy0) + 1.0f) * 0.5f;
×
391
    v = (sixel_bn_mask(ix1, iy1) + 1.0f) * 0.5f;
×
392

393
    return (u + v) - 1.0f;
×
394
}
395

396
static float
397
positional_mask_blue_float32(int x, int y, int c)
×
398
{
399
    sixel_bluenoise_conf_init_from_env_float32();
×
400
    return sixel_bluenoise_tri_float32(x, y, c)
×
401
        * g_sixel_bn_conf_float32.strength;
×
402
}
403

404
SIXELSTATUS
405
sixel_dither_apply_positional_float32(sixel_dither_t *dither,
24✔
406
                                      sixel_dither_context_t *context)
407
{
18✔
408
#if _MSC_VER
409
    enum { max_channels = 4 };
410
#else
411
    const int max_channels = 4;
24✔
412
#endif
413
    int serpentine;
14✔
414
    int y;
14✔
415
    int absolute_y;
14✔
416
    float (*f_mask)(int x, int y, int c);
14✔
417
    float jitter_scale;
14✔
418
    float *palette_float;
14✔
419
    float *new_palette_float;
14✔
420
    int float_depth;
14✔
421
    int float_index;
14✔
422
    unsigned char *quantized;
14✔
423
    float lookup_pixel_float[max_channels];
24✔
424
    unsigned char const *lookup_pixel;
14✔
425
    sixel_lut_t *fast_lut;
14✔
426
    int use_fast_lut;
14✔
427
    int lookup_wants_float;
14✔
428
    int use_palette_float_lookup;
14✔
429
    int need_float_pixel;
14✔
430

431
    palette_float = NULL;
24✔
432
    new_palette_float = NULL;
24✔
433
    float_depth = 0;
24✔
434
    quantized = NULL;
24✔
435
    lookup_wants_float = 0;
24✔
436

437
    if (dither == NULL || context == NULL) {
24!
438
        return SIXEL_BAD_ARGUMENT;
439
    }
440
    if (context->pixels_float == NULL || context->scratch == NULL) {
24!
441
        return SIXEL_BAD_ARGUMENT;
442
    }
443
    if (context->palette == NULL || context->result == NULL) {
24!
444
        return SIXEL_BAD_ARGUMENT;
445
    }
446

447
    switch (context->method_for_diffuse) {
24!
448
    case SIXEL_DIFFUSE_A_DITHER:
3✔
449
        f_mask = positional_mask_a_float32;
6✔
450
        break;
6✔
451
    case SIXEL_DIFFUSE_X_DITHER:
3✔
452
        f_mask = positional_mask_x_float32;
12✔
453
        break;
6✔
454
    case SIXEL_DIFFUSE_BLUENOISE_DITHER:
455
        f_mask = positional_mask_blue_float32;
×
456
        break;
×
457
    default:
458
        f_mask = positional_mask_x_float32;
6✔
459
        break;
460
    }
461

462
    serpentine = (context->method_for_scan == SIXEL_SCAN_SERPENTINE);
24✔
463
    jitter_scale = 32.0f / 255.0f;
24✔
464
    palette_float = context->palette_float;
24✔
465
    new_palette_float = context->new_palette_float;
24✔
466
    float_depth = context->float_depth;
24✔
467
    quantized = context->scratch;
24✔
468
    fast_lut = context->lut;
24✔
469
    use_fast_lut = (fast_lut != NULL);
24✔
470
    lookup_wants_float = (context->lookup_source_is_float != 0);
24✔
471
    use_palette_float_lookup = 0;
24✔
472
    if (context->prefer_palette_float_lookup != 0
24!
473
            && palette_float != NULL
6!
474
            && float_depth >= context->depth) {
×
475
        use_palette_float_lookup = 1;
12✔
476
    }
477
    need_float_pixel = lookup_wants_float || use_palette_float_lookup;
24!
478

479
    if (context->optimize_palette) {
24!
480
        int x;
14✔
481

482
        *context->ncolors = 0;
24✔
483
        memset(context->new_palette, 0x00,
26✔
484
               (size_t)SIXEL_PALETTE_MAX * (size_t)context->depth);
20!
485
        if (new_palette_float != NULL && float_depth > 0) {
24!
486
            memset(new_palette_float, 0x00,
×
487
                   (size_t)SIXEL_PALETTE_MAX
488
                       * (size_t)float_depth * sizeof(float));
489
        }
490
        memset(context->migration_map, 0x00,
24✔
491
               sizeof(unsigned short) * (size_t)SIXEL_PALETTE_MAX);
492
        for (y = 0; y < context->height; ++y) {
6,192✔
493
            absolute_y = context->band_origin + y;
6,168✔
494
            int start;
3,598✔
495
            int end;
3,598✔
496
            int step;
3,598✔
497
            int direction;
3,598✔
498

499
            sixel_dither_scanline_params_positional_float32(serpentine, absolute_y,
7,710✔
500
                                         context->width,
1,542✔
501
                                         &start, &end, &step, &direction);
502
            (void)direction;
4,626✔
503
            for (x = start; x != end; x += step) {
3,295,320✔
504
                int pos;
1,918,672✔
505
                int d;
1,918,672✔
506
                int color_index;
1,918,672✔
507

508
                pos = y * context->width + x;
3,289,152✔
509
                for (d = 0; d < context->depth; ++d) {
13,156,608✔
510
                    float val;
5,756,016✔
511

512
                    val = context->pixels_float[pos * context->depth + d]
19,734,912✔
513
                        + f_mask(x, y, d) * jitter_scale;
9,867,456✔
514
                    val = sixel_pixelformat_float_channel_clamp(
9,867,456✔
515
                        context->pixelformat,
2,466,864✔
516
                        d,
2,466,864✔
517
                        val);
2,466,864✔
518
                    if (need_float_pixel) {
9,867,456!
519
                        lookup_pixel_float[d] = val;
9,867,456✔
520
                    }
2,466,864✔
521
                    if (!lookup_wants_float && !use_palette_float_lookup) {
9,867,456!
522
                        quantized[d]
×
523
                            = sixel_pixelformat_float_channel_to_byte(
×
524
                                  context->pixelformat,
525
                                  d,
526
                                  val);
527
                    }
528
                }
2,466,864✔
529
                if (lookup_wants_float) {
3,289,152!
530
                    lookup_pixel = (unsigned char const *)(void const *)
3,289,152✔
531
                        lookup_pixel_float;
532
                    if (use_fast_lut) {
3,289,152!
533
                        color_index = sixel_lut_map_pixel(fast_lut,
4,111,440✔
534
                                                         lookup_pixel);
822,288✔
535
                    } else {
822,288✔
536
                        color_index = context->lookup(lookup_pixel,
×
537
                                                      context->depth,
538
                                                      context->palette,
×
539
                                                      context->reqcolor,
540
                                                      context->indextable,
541
                                                      context->complexion);
542
                    }
543
                } else if (use_palette_float_lookup) {
822,288!
544
                    color_index = sixel_dither_lookup_palette_float32(
×
545
                        lookup_pixel_float,
546
                        context->depth,
547
                        palette_float,
548
                        context->reqcolor,
549
                        context->complexion,
550
                        0);
551
                } else {
552
                    lookup_pixel = quantized;
×
553
                    if (use_fast_lut) {
×
554
                        color_index = sixel_lut_map_pixel(fast_lut,
×
555
                                                         lookup_pixel);
556
                    } else {
557
                        color_index = context->lookup(lookup_pixel,
×
558
                                                      context->depth,
559
                                                      context->palette,
×
560
                                                      context->reqcolor,
561
                                                      context->indextable,
562
                                                      context->complexion);
563
                    }
564
                }
565
                if (context->migration_map[color_index] == 0) {
3,289,152✔
566
                    if (absolute_y >= context->output_start) {
3,118!
567
                        /*
568
                         * Palette indices never exceed SIXEL_PALETTE_MAX, so
569
                         * the cast to sixel_index_t (unsigned char) is safe.
570
                         */
571
                        context->result[pos]
3,118✔
572
                            = (sixel_index_t)(*context->ncolors);
3,902✔
573
                    }
784✔
574
                    for (d = 0; d < context->depth; ++d) {
12,472✔
575
                        context->new_palette[*context->ncolors
11,706✔
576
                                             * context->depth + d]
9,354✔
577
                            = context->palette[color_index
14,058✔
578
                                               * context->depth + d];
9,354✔
579
                    }
2,352✔
580
                    if (palette_float != NULL
3,118!
581
                            && new_palette_float != NULL
2,344!
582
                            && float_depth > 0) {
×
583
                        for (float_index = 0;
×
584
                                float_index < float_depth;
×
585
                                ++float_index) {
×
586
                            new_palette_float[*context->ncolors
×
587
                                               * float_depth
×
588
                                               + float_index]
×
589
                                = palette_float[color_index * float_depth
×
590
                                                + float_index];
×
591
                        }
592
                    }
593
                    ++*context->ncolors;
3,118✔
594
                    /*
595
                     * Migration map entries are limited to the palette size
596
                     * (<= 256), so storing them as unsigned short is safe.
597
                     */
598
                    context->migration_map[color_index]
3,118✔
599
                        = (unsigned short)(*context->ncolors);
3,902✔
600
                } else {
784✔
601
                    if (absolute_y >= context->output_start) {
3,286,034!
602
                        context->result[pos]
3,286,034✔
603
                            = (sixel_index_t)(context->migration_map[
5,750,546✔
604
                                  color_index] - 1);
1,643,008✔
605
                    }
821,504✔
606
                }
607
            }
822,288✔
608
            if (absolute_y >= context->output_start) {
6,168!
609
                sixel_dither_pipeline_row_notify(dither, absolute_y);
6,168✔
610
            }
1,542✔
611
        }
1,542✔
612
        memcpy(context->palette, context->new_palette,
26✔
613
               (size_t)(*context->ncolors * context->depth));
20!
614
        if (palette_float != NULL
24!
615
                && new_palette_float != NULL
18!
616
                && float_depth > 0) {
×
617
            memcpy(palette_float,
×
618
                   new_palette_float,
619
                   (size_t)(*context->ncolors * float_depth)
620
                       * sizeof(float));
621
        }
622
    } else {
6✔
623
        int x;
624

625
        for (y = 0; y < context->height; ++y) {
×
626
            absolute_y = context->band_origin + y;
×
627
            int start;
628
            int end;
629
            int step;
630
            int direction;
631

632
            sixel_dither_scanline_params_positional_float32(serpentine, absolute_y,
×
633
                                         context->width,
634
                                         &start, &end, &step, &direction);
635
            (void)direction;
636
            for (x = start; x != end; x += step) {
×
637
                int pos;
638
                int d;
639

640
                pos = y * context->width + x;
×
641
                for (d = 0; d < context->depth; ++d) {
×
642
                    float val;
643

644
                    val = context->pixels_float[pos * context->depth + d]
×
645
                        + f_mask(x, y, d) * jitter_scale;
×
646
                    val = sixel_pixelformat_float_channel_clamp(
×
647
                        context->pixelformat,
648
                        d,
649
                        val);
650
                    if (need_float_pixel) {
×
651
                        lookup_pixel_float[d] = val;
×
652
                    }
653
                    if (!lookup_wants_float && !use_palette_float_lookup) {
×
654
                        quantized[d]
×
655
                            = sixel_pixelformat_float_channel_to_byte(
×
656
                                  context->pixelformat,
657
                                  d,
658
                                  val);
659
                    }
660
                }
661
                if (absolute_y >= context->output_start) {
×
662
                    /*
663
                     * Palette indices never exceed SIXEL_PALETTE_MAX, so
664
                     * narrowing to sixel_index_t (unsigned char) is safe.
665
                     */
666
                    if (lookup_wants_float) {
×
667
                        lookup_pixel = (unsigned char const *)(void const *)
×
668
                            lookup_pixel_float;
669
                        context->result[pos] = (sixel_index_t)
×
670
                            context->lookup(
×
671
                                lookup_pixel,
672
                                context->depth,
673
                                context->palette,
×
674
                                context->reqcolor,
675
                                context->indextable,
676
                                context->complexion);
677
                    } else if (use_palette_float_lookup) {
×
678
                        context->result[pos] = (sixel_index_t)
×
679
                            sixel_dither_lookup_palette_float32(
×
680
                                lookup_pixel_float,
681
                                context->depth,
682
                                palette_float,
683
                                context->reqcolor,
684
                                context->complexion,
685
                                0);
686
                    } else {
687
                        lookup_pixel = quantized;
×
688
                        context->result[pos] = (sixel_index_t)
×
689
                            context->lookup(
×
690
                                lookup_pixel,
691
                                context->depth,
692
                                context->palette,
×
693
                                context->reqcolor,
694
                                context->indextable,
695
                                context->complexion);
696
                    }
697
                }
698
            }
699
            if (absolute_y >= context->output_start) {
×
700
                sixel_dither_pipeline_row_notify(dither, absolute_y);
×
701
            }
702
        }
703
        *context->ncolors = context->reqcolor;
×
704
    }
705

706
    return SIXEL_OK;
12✔
707
}
6✔
708

709
/* emacs Local Variables:      */
710
/* emacs mode: c               */
711
/* emacs tab-width: 4          */
712
/* emacs indent-tabs-mode: nil */
713
/* emacs c-basic-offset: 4     */
714
/* emacs End:                  */
715
/* vim: set expandtab ts=4 sts=4 sw=4 : */
716
/* 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