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

saitoha / libsixel / 19490171609

19 Nov 2025 04:48AM UTC coverage: 39.633% (-2.0%) from 41.622%
19490171609

push

github

saitoha
feat: add new pixelformat SIXEL_PIXELFORMAT_LINEARRGBFLOAT32/SIXEL_PIXELFORMAT_OKLABFLOAT32

8885 of 32084 branches covered (27.69%)

77 of 868 new or added lines in 14 files covered. (8.87%)

197 existing lines in 20 files now uncovered.

11758 of 29667 relevant lines covered (39.63%)

669587.84 hits per line

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

0.0
/src/dither-fixed-float32.c
1
#include "config.h"
2

3
#if HAVE_MATH_H
4
# include <math.h>
5
#endif  /* HAVE_MATH_H */
6
#include <string.h>
7

8
#include "dither-fixed-float32.h"
9
#include "dither-common-pipeline.h"
10
#include "pixelformat.h"
11

12
typedef void (*diffuse_fixed_float_fn)(float *data,
13
                                       int width,
14
                                       int height,
15
                                       int x,
16
                                       int y,
17
                                       int depth,
18
                                       float error,
19
                                       int direction,
20
                                       int pixelformat,
21
                                       int channel_index);
22

23
static void
24
error_diffuse_float(float *data,
×
25
                    int pos,
26
                    int depth,
27
                    float error,
28
                    int numerator,
29
                    int denominator,
30
                    int pixelformat,
31
                    int channel_index)
32
{
33
    float *channel;
34
    float delta;
35

36
    channel = data + ((size_t)pos * (size_t)depth);
×
37
    delta = error * ((float)numerator / (float)denominator);
×
38
    *channel += delta;
×
NEW
39
    *channel = sixel_pixelformat_float_channel_clamp(pixelformat,
×
40
                                                     channel_index,
41
                                                     *channel);
UNCOV
42
}
×
43

44
static void
45
sixel_dither_scanline_params(int serpentine,
×
46
                             int index,
47
                             int limit,
48
                             int *start,
49
                             int *end,
50
                             int *step,
51
                             int *direction)
52
{
53
    if (serpentine && (index & 1)) {
×
54
        *start = limit - 1;
×
55
        *end = -1;
×
56
        *step = -1;
×
57
        *direction = -1;
×
58
    } else {
59
        *start = 0;
×
60
        *end = limit;
×
61
        *step = 1;
×
62
        *direction = 1;
×
63
    }
64
}
×
65

66
static void
NEW
67
diffuse_none_float(float *data,
×
68
                   int width,
69
                   int height,
70
                   int x,
71
                   int y,
72
                   int depth,
73
                   float error,
74
                   int direction,
75
                   int pixelformat,
76
                   int channel_index)
77
{
78
    (void)data;
79
    (void)width;
80
    (void)height;
81
    (void)x;
82
    (void)y;
83
    (void)depth;
84
    (void)error;
85
    (void)direction;
86
    (void)pixelformat;
87
    (void)channel_index;
NEW
88
}
×
89

90
static void
91
diffuse_fs_float(float *data,
×
92
                 int width,
93
                 int height,
94
                 int x,
95
                 int y,
96
                 int depth,
97
                 float error,
98
                 int direction,
99
                 int pixelformat,
100
                 int channel_index)
101
{
102
    int pos;
103
    int forward;
104

105
    pos = y * width + x;
×
106
    forward = direction >= 0;
×
107

108
    if (forward) {
×
109
        if (x < width - 1) {
×
NEW
110
            error_diffuse_float(data,
×
111
                                pos + 1,
112
                                depth,
113
                                error,
114
                                7,
115
                                16,
116
                                pixelformat,
117
                                channel_index);
118
        }
119
        if (y < height - 1) {
×
120
            if (x > 0) {
×
121
                error_diffuse_float(data,
×
122
                                    pos + width - 1,
×
123
                                    depth,
124
                                    error,
125
                                    3,
126
                                    16,
127
                                    pixelformat,
128
                                    channel_index);
129
            }
130
            error_diffuse_float(data,
×
131
                                pos + width,
132
                                depth,
133
                                error,
134
                                5,
135
                                16,
136
                                pixelformat,
137
                                channel_index);
138
            if (x < width - 1) {
×
139
                error_diffuse_float(data,
×
140
                                    pos + width + 1,
×
141
                                    depth,
142
                                    error,
143
                                    1,
144
                                    16,
145
                                    pixelformat,
146
                                    channel_index);
147
            }
148
        }
149
    } else {
150
        if (x > 0) {
×
NEW
151
            error_diffuse_float(data,
×
152
                                pos - 1,
153
                                depth,
154
                                error,
155
                                7,
156
                                16,
157
                                pixelformat,
158
                                channel_index);
159
        }
160
        if (y < height - 1) {
×
161
            if (x < width - 1) {
×
162
                error_diffuse_float(data,
×
163
                                    pos + width + 1,
×
164
                                    depth,
165
                                    error,
166
                                    3,
167
                                    16,
168
                                    pixelformat,
169
                                    channel_index);
170
            }
171
            error_diffuse_float(data,
×
172
                                pos + width,
173
                                depth,
174
                                error,
175
                                5,
176
                                16,
177
                                pixelformat,
178
                                channel_index);
179
            if (x > 0) {
×
180
                error_diffuse_float(data,
×
181
                                    pos + width - 1,
×
182
                                    depth,
183
                                    error,
184
                                    1,
185
                                    16,
186
                                    pixelformat,
187
                                    channel_index);
188
            }
189
        }
190
    }
191
}
×
192

193
/*
194
 * Atkinson's kernel spreads the error within a 3x3 neighborhood using
195
 * symmetric 1/8 weights.  The float variant mirrors the integer version
196
 * but keeps the higher precision samples intact.
197
 */
198
static void
NEW
199
diffuse_atkinson_float(float *data,
×
200
                       int width,
201
                       int height,
202
                       int x,
203
                       int y,
204
                       int depth,
205
                       float error,
206
                       int direction,
207
                       int pixelformat,
208
                       int channel_index)
209
{
210
    int pos;
211
    int sign;
212
    int row;
213

NEW
214
    pos = y * width + x;
×
NEW
215
    sign = direction >= 0 ? 1 : -1;
×
216

NEW
217
    if (x + sign >= 0 && x + sign < width) {
×
NEW
218
        error_diffuse_float(data,
×
219
                            pos + sign,
220
                            depth,
221
                            error,
222
                            1,
223
                            8,
224
                            pixelformat,
225
                            channel_index);
226
    }
NEW
227
    if (x + sign * 2 >= 0 && x + sign * 2 < width) {
×
NEW
228
        error_diffuse_float(data,
×
NEW
229
                            pos + sign * 2,
×
230
                            depth,
231
                            error,
232
                            1,
233
                            8,
234
                            pixelformat,
235
                            channel_index);
236
    }
NEW
237
    if (y < height - 1) {
×
NEW
238
        row = pos + width;
×
NEW
239
        if (x - sign >= 0 && x - sign < width) {
×
NEW
240
            error_diffuse_float(data,
×
241
                                row - sign,
242
                                depth,
243
                                error,
244
                                1,
245
                                8,
246
                                pixelformat,
247
                                channel_index);
248
        }
NEW
249
        error_diffuse_float(data,
×
250
                            row,
251
                            depth,
252
                            error,
253
                            1,
254
                            8,
255
                            pixelformat,
256
                            channel_index);
NEW
257
        if (x + sign >= 0 && x + sign < width) {
×
NEW
258
            error_diffuse_float(data,
×
259
                                row + sign,
260
                                depth,
261
                                error,
262
                                1,
263
                                8,
264
                                pixelformat,
265
                                channel_index);
266
        }
267
    }
NEW
268
    if (y < height - 2) {
×
NEW
269
        error_diffuse_float(data,
×
NEW
270
                            pos + width * 2,
×
271
                            depth,
272
                            error,
273
                            1,
274
                            8,
275
                            pixelformat,
276
                            channel_index);
277
    }
NEW
278
}
×
279

280
/*
281
 * Shared helper that applies a row of diffusion weights to neighbors on the
282
 * current or subsequent scanlines.  Each caller provides the offset table and
283
 * numerator/denominator pairs so the classic kernels can be described using a
284
 * compact table instead of open-coded loops.
285
 */
286
static void
NEW
287
diffuse_weighted_row(float *data,
×
288
                     int pos,
289
                     int depth,
290
                     float error,
291
                     int direction,
292
                     int pixelformat,
293
                     int channel_index,
294
                     int x,
295
                     int width,
296
                     int row_offset,
297
                     int const *offsets,
298
                     int const *numerators,
299
                     int const *denominators,
300
                     int count)
301
{
302
    int i;
303
    int neighbor;
304
    int row_base;
305
    int sign;
306

NEW
307
    sign = direction >= 0 ? 1 : -1;
×
NEW
308
    row_base = pos + row_offset;
×
NEW
309
    for (i = 0; i < count; ++i) {
×
NEW
310
        neighbor = x + sign * offsets[i];
×
NEW
311
        if (neighbor < 0 || neighbor >= width) {
×
NEW
312
            continue;
×
313
        }
NEW
314
        error_diffuse_float(data,
×
NEW
315
                            row_base + (neighbor - x),
×
316
                            depth,
317
                            error,
NEW
318
                            numerators[i],
×
NEW
319
                            denominators[i],
×
320
                            pixelformat,
321
                            channel_index);
322
    }
NEW
323
}
×
324

325
/*
326
 * Jarvis, Judice, and Ninke kernel using the canonical 5x3 mask.  Three rows
327
 * of weights are applied with consistent 1/48 denominators to preserve the
328
 * reference diffusion matrix.
329
 */
330
static void
NEW
331
diffuse_jajuni_float(float *data,
×
332
                     int width,
333
                     int height,
334
                     int x,
335
                     int y,
336
                     int depth,
337
                     float error,
338
                     int direction,
339
                     int pixelformat,
340
                     int channel_index)
341
{
342
    static const int row0_offsets[] = { 1, 2 };
343
    static const int row0_num[] = { 7, 5 };
344
    static const int row0_den[] = { 48, 48 };
345
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
346
    static const int row1_num[] = { 3, 5, 7, 5, 3 };
347
    static const int row1_den[] = { 48, 48, 48, 48, 48 };
348
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
349
    static const int row2_num[] = { 1, 3, 5, 3, 1 };
350
    static const int row2_den[] = { 48, 48, 48, 48, 48 };
351
    int pos;
352

NEW
353
    pos = y * width + x;
×
NEW
354
    diffuse_weighted_row(data,
×
355
                         pos,
356
                         depth,
357
                         error,
358
                         direction,
359
                         pixelformat,
360
                         channel_index,
361
                         x,
362
                         width,
363
                         0,
364
                         row0_offsets,
365
                         row0_num,
366
                         row0_den,
367
                         2);
NEW
368
    if (y < height - 1) {
×
NEW
369
        diffuse_weighted_row(data,
×
370
                             pos,
371
                             depth,
372
                             error,
373
                             direction,
374
                             pixelformat,
375
                             channel_index,
376
                             x,
377
                             width,
378
                             width,
379
                             row1_offsets,
380
                             row1_num,
381
                             row1_den,
382
                             5);
383
    }
NEW
384
    if (y < height - 2) {
×
NEW
385
        diffuse_weighted_row(data,
×
386
                             pos,
387
                             depth,
388
                             error,
389
                             direction,
390
                             pixelformat,
391
                             channel_index,
392
                             x,
393
                             width,
394
                             width * 2,
395
                             row2_offsets,
396
                             row2_num,
397
                             row2_den,
398
                             5);
399
    }
NEW
400
}
×
401

402
/*
403
 * Stucki's method spreads the error across a 5x3 neighborhood with larger
404
 * emphasis on closer pixels.  The numerators/denominators match the classic
405
 * 8/48, 4/48, and related fractions from the integer backend.
406
 */
407
static void
NEW
408
diffuse_stucki_float(float *data,
×
409
                     int width,
410
                     int height,
411
                     int x,
412
                     int y,
413
                     int depth,
414
                     float error,
415
                     int direction,
416
                     int pixelformat,
417
                     int channel_index)
418
{
419
    static const int row0_offsets[] = { 1, 2 };
420
    static const int row0_num[] = { 1, 1 };
421
    static const int row0_den[] = { 6, 12 };
422
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
423
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
424
    static const int row1_den[] = { 24, 12, 6, 12, 24 };
425
    static const int row2_offsets[] = { -2, -1, 0, 1, 2 };
426
    static const int row2_num[] = { 1, 1, 1, 1, 1 };
427
    static const int row2_den[] = { 48, 24, 12, 24, 48 };
428
    int pos;
429

NEW
430
    pos = y * width + x;
×
NEW
431
    diffuse_weighted_row(data,
×
432
                         pos,
433
                         depth,
434
                         error,
435
                         direction,
436
                         pixelformat,
437
                         channel_index,
438
                         x,
439
                         width,
440
                         0,
441
                         row0_offsets,
442
                         row0_num,
443
                         row0_den,
444
                         2);
NEW
445
    if (y < height - 1) {
×
NEW
446
        diffuse_weighted_row(data,
×
447
                             pos,
448
                             depth,
449
                             error,
450
                             direction,
451
                             pixelformat,
452
                             channel_index,
453
                             x,
454
                             width,
455
                             width,
456
                             row1_offsets,
457
                             row1_num,
458
                             row1_den,
459
                             5);
460
    }
NEW
461
    if (y < height - 2) {
×
NEW
462
        diffuse_weighted_row(data,
×
463
                             pos,
464
                             depth,
465
                             error,
466
                             direction,
467
                             pixelformat,
468
                             channel_index,
469
                             x,
470
                             width,
471
                             width * 2,
472
                             row2_offsets,
473
                             row2_num,
474
                             row2_den,
475
                             5);
476
    }
NEW
477
}
×
478

479
/*
480
 * Burkes' kernel limits the spread to two rows to reduce directional artifacts
481
 * while keeping the symmetric 1/16-4/16 pattern.
482
 */
483
static void
NEW
484
diffuse_burkes_float(float *data,
×
485
                     int width,
486
                     int height,
487
                     int x,
488
                     int y,
489
                     int depth,
490
                     float error,
491
                     int direction,
492
                     int pixelformat,
493
                     int channel_index)
494
{
495
    static const int row0_offsets[] = { 1, 2 };
496
    static const int row0_num[] = { 1, 1 };
497
    static const int row0_den[] = { 4, 8 };
498
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
499
    static const int row1_num[] = { 1, 1, 1, 1, 1 };
500
    static const int row1_den[] = { 16, 8, 4, 8, 16 };
501
    int pos;
502

NEW
503
    pos = y * width + x;
×
NEW
504
    diffuse_weighted_row(data,
×
505
                         pos,
506
                         depth,
507
                         error,
508
                         direction,
509
                         pixelformat,
510
                         channel_index,
511
                         x,
512
                         width,
513
                         0,
514
                         row0_offsets,
515
                         row0_num,
516
                         row0_den,
517
                         2);
NEW
518
    if (y < height - 1) {
×
NEW
519
        diffuse_weighted_row(data,
×
520
                             pos,
521
                             depth,
522
                             error,
523
                             direction,
524
                             pixelformat,
525
                             channel_index,
526
                             x,
527
                             width,
528
                             width,
529
                             row1_offsets,
530
                             row1_num,
531
                             row1_den,
532
                             5);
533
    }
NEW
534
}
×
535

536
/*
537
 * Sierra Lite (Sierra1) uses a compact 2x2 mask to reduce ringing while
538
 * keeping serpentine traversal stable.
539
 */
540
static void
NEW
541
diffuse_sierra1_float(float *data,
×
542
                      int width,
543
                      int height,
544
                      int x,
545
                      int y,
546
                      int depth,
547
                      float error,
548
                      int direction,
549
                      int pixelformat,
550
                      int channel_index)
551
{
552
    static const int row0_offsets[] = { 1 };
553
    static const int row0_num[] = { 1 };
554
    static const int row0_den[] = { 2 };
555
    static const int row1_offsets[] = { -1, 0 };
556
    static const int row1_num[] = { 1, 1 };
557
    static const int row1_den[] = { 4, 4 };
558
    int pos;
559

NEW
560
    pos = y * width + x;
×
NEW
561
    diffuse_weighted_row(data,
×
562
                         pos,
563
                         depth,
564
                         error,
565
                         direction,
566
                         pixelformat,
567
                         channel_index,
568
                         x,
569
                         width,
570
                         0,
571
                         row0_offsets,
572
                         row0_num,
573
                         row0_den,
574
                         1);
NEW
575
    if (y < height - 1) {
×
NEW
576
        diffuse_weighted_row(data,
×
577
                             pos,
578
                             depth,
579
                             error,
580
                             direction,
581
                             pixelformat,
582
                             channel_index,
583
                             x,
584
                             width,
585
                             width,
586
                             row1_offsets,
587
                             row1_num,
588
                             row1_den,
589
                             2);
590
    }
NEW
591
}
×
592

593
/*
594
 * Sierra Two-row keeps the full 5x3 footprint but halves the lower row weights
595
 * relative to Sierra-3, matching the 32-denominator formulation.
596
 */
597
static void
NEW
598
diffuse_sierra2_float(float *data,
×
599
                      int width,
600
                      int height,
601
                      int x,
602
                      int y,
603
                      int depth,
604
                      float error,
605
                      int direction,
606
                      int pixelformat,
607
                      int channel_index)
608
{
609
    static const int row0_offsets[] = { 1, 2 };
610
    static const int row0_num[] = { 4, 3 };
611
    static const int row0_den[] = { 32, 32 };
612
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
613
    static const int row1_num[] = { 1, 2, 3, 2, 1 };
614
    static const int row1_den[] = { 32, 32, 32, 32, 32 };
615
    static const int row2_offsets[] = { -1, 0, 1 };
616
    static const int row2_num[] = { 2, 3, 2 };
617
    static const int row2_den[] = { 32, 32, 32 };
618
    int pos;
619

NEW
620
    pos = y * width + x;
×
NEW
621
    diffuse_weighted_row(data,
×
622
                         pos,
623
                         depth,
624
                         error,
625
                         direction,
626
                         pixelformat,
627
                         channel_index,
628
                         x,
629
                         width,
630
                         0,
631
                         row0_offsets,
632
                         row0_num,
633
                         row0_den,
634
                         2);
NEW
635
    if (y < height - 1) {
×
NEW
636
        diffuse_weighted_row(data,
×
637
                             pos,
638
                             depth,
639
                             error,
640
                             direction,
641
                             pixelformat,
642
                             channel_index,
643
                             x,
644
                             width,
645
                             width,
646
                             row1_offsets,
647
                             row1_num,
648
                             row1_den,
649
                             5);
650
    }
NEW
651
    if (y < height - 2) {
×
NEW
652
        diffuse_weighted_row(data,
×
653
                             pos,
654
                             depth,
655
                             error,
656
                             direction,
657
                             pixelformat,
658
                             channel_index,
659
                             x,
660
                             width,
661
                             width * 2,
662
                             row2_offsets,
663
                             row2_num,
664
                             row2_den,
665
                             3);
666
    }
NEW
667
}
×
668

669
/*
670
 * Sierra-3 restores the heavier middle-row contributions (5/32) that
671
 * characterize the original kernel.
672
 */
673
static void
NEW
674
diffuse_sierra3_float(float *data,
×
675
                      int width,
676
                      int height,
677
                      int x,
678
                      int y,
679
                      int depth,
680
                      float error,
681
                      int direction,
682
                      int pixelformat,
683
                      int channel_index)
684
{
685
    static const int row0_offsets[] = { 1, 2 };
686
    static const int row0_num[] = { 5, 3 };
687
    static const int row0_den[] = { 32, 32 };
688
    static const int row1_offsets[] = { -2, -1, 0, 1, 2 };
689
    static const int row1_num[] = { 2, 4, 5, 4, 2 };
690
    static const int row1_den[] = { 32, 32, 32, 32, 32 };
691
    static const int row2_offsets[] = { -1, 0, 1 };
692
    static const int row2_num[] = { 2, 3, 2 };
693
    static const int row2_den[] = { 32, 32, 32 };
694
    int pos;
695

NEW
696
    pos = y * width + x;
×
NEW
697
    diffuse_weighted_row(data,
×
698
                         pos,
699
                         depth,
700
                         error,
701
                         direction,
702
                         pixelformat,
703
                         channel_index,
704
                         x,
705
                         width,
706
                         0,
707
                         row0_offsets,
708
                         row0_num,
709
                         row0_den,
710
                         2);
NEW
711
    if (y < height - 1) {
×
NEW
712
        diffuse_weighted_row(data,
×
713
                             pos,
714
                             depth,
715
                             error,
716
                             direction,
717
                             pixelformat,
718
                             channel_index,
719
                             x,
720
                             width,
721
                             width,
722
                             row1_offsets,
723
                             row1_num,
724
                             row1_den,
725
                             5);
726
    }
NEW
727
    if (y < height - 2) {
×
NEW
728
        diffuse_weighted_row(data,
×
729
                             pos,
730
                             depth,
731
                             error,
732
                             direction,
733
                             pixelformat,
734
                             channel_index,
735
                             x,
736
                             width,
737
                             width * 2,
738
                             row2_offsets,
739
                             row2_num,
740
                             row2_den,
741
                             3);
742
    }
NEW
743
}
×
744

745
#if HAVE_TESTS
746
static int g_sixel_dither_float32_diffusion_hits = 0;
747

748
void
749
sixel_dither_diffusion_tests_reset_float32_hits(void)
×
750
{
751
    g_sixel_dither_float32_diffusion_hits = 0;
×
752
}
×
753

754
int
755
sixel_dither_diffusion_tests_float32_hits(void)
×
756
{
757
    return g_sixel_dither_float32_diffusion_hits;
×
758
}
759

760
#define SIXEL_DITHER_FLOAT32_HIT()                                      \
761
    do {                                                                \
762
        ++g_sixel_dither_float32_diffusion_hits;                        \
763
    } while (0)
764
#else
765
#define SIXEL_DITHER_FLOAT32_HIT()                                      \
766
    do {                                                                \
767
    } while (0)
768
#endif
769

770
SIXELSTATUS
771
sixel_dither_apply_fixed_float32(sixel_dither_t *dither,
×
772
                                 sixel_dither_context_t *context)
UNCOV
773
{
×
774
#if _MSC_VER
775
    enum { max_channels = 4 };
776
#else
777
    const int max_channels = 4;
×
778
#endif
779
    SIXELSTATUS status;
780
    float *palette_float;
781
    float *new_palette_float;
782
    int float_depth;
783
    int serpentine;
784
    int y;
785
    int start;
786
    int end;
787
    int step;
788
    int direction;
789
    int x;
790
    int pos;
791
    size_t base;
792
    float *source_pixel;
UNCOV
793
    unsigned char quantized[max_channels];
×
UNCOV
794
    float snapshot[max_channels];
×
NEW
795
    float lookup_pixel_float[max_channels];
×
796
    int color_index;
797
    int output_index;
798
    int palette_value;
799
    float palette_value_float;
800
    float error;
801
    int n;
802
    float *data;
803
    unsigned char *palette;
804
    int float_index;
805
    int lookup_wants_float;
806
    int use_palette_float_lookup;
807
    int need_float_pixel;
808
    unsigned char const *lookup_pixel;
809
    int have_palette_float;
810
    int have_new_palette_float;
811
    diffuse_fixed_float_fn f_diffuse;
812
    int method_for_diffuse;
813

814
    palette_float = NULL;
×
815
    new_palette_float = NULL;
×
816
    float_depth = 0;
×
817

818
    if (dither == NULL || context == NULL) {
×
819
        return SIXEL_BAD_ARGUMENT;
×
820
    }
821
    data = context->pixels_float;
×
822
    if (data == NULL || context->palette == NULL) {
×
823
        return SIXEL_BAD_ARGUMENT;
×
824
    }
825
    if (context->result == NULL || context->new_palette == NULL) {
×
826
        return SIXEL_BAD_ARGUMENT;
×
827
    }
828
    if (context->migration_map == NULL || context->ncolors == NULL) {
×
829
        return SIXEL_BAD_ARGUMENT;
×
830
    }
831
    if (context->lookup == NULL) {
×
832
        return SIXEL_BAD_ARGUMENT;
×
833
    }
834

835
    palette = context->palette;
×
836
    palette_float = context->palette_float;
×
837
    new_palette_float = context->new_palette_float;
×
838
    float_depth = context->float_depth;
×
839
    if (context->depth > max_channels || context->depth != 3) {
×
840
        return SIXEL_BAD_ARGUMENT;
×
841
    }
842
    if (context->reqcolor < 1) {
×
843
        return SIXEL_BAD_ARGUMENT;
×
844
    }
845

846
    serpentine = (context->method_for_scan == SIXEL_SCAN_SERPENTINE);
×
NEW
847
    lookup_wants_float = (context->lookup_source_is_float != 0);
×
NEW
848
    use_palette_float_lookup = 0;
×
NEW
849
    if (context->prefer_palette_float_lookup != 0
×
NEW
850
            && palette_float != NULL
×
NEW
851
            && float_depth >= context->depth) {
×
NEW
852
        use_palette_float_lookup = 1;
×
853
    }
NEW
854
    need_float_pixel = lookup_wants_float || use_palette_float_lookup;
×
855

856
    /*
857
     * Remember whether each palette buffer exposes float32 components so
858
     * later loops can preserve precision instead of converting back to
859
     * bytes before computing the diffusion error.
860
     */
NEW
861
    if (palette_float != NULL && float_depth >= context->depth) {
×
NEW
862
        have_palette_float = 1;
×
863
    } else {
NEW
864
        have_palette_float = 0;
×
865
    }
NEW
866
    if (new_palette_float != NULL && float_depth >= context->depth) {
×
NEW
867
        have_new_palette_float = 1;
×
868
    } else {
NEW
869
        have_new_palette_float = 0;
×
870
    }
871

NEW
872
    method_for_diffuse = context->method_for_diffuse;
×
NEW
873
    switch (method_for_diffuse) {
×
NEW
874
    case SIXEL_DIFFUSE_NONE:
×
NEW
875
        f_diffuse = diffuse_none_float;
×
NEW
876
        break;
×
NEW
877
    case SIXEL_DIFFUSE_ATKINSON:
×
NEW
878
        f_diffuse = diffuse_atkinson_float;
×
NEW
879
        break;
×
NEW
880
    case SIXEL_DIFFUSE_JAJUNI:
×
NEW
881
        f_diffuse = diffuse_jajuni_float;
×
NEW
882
        break;
×
NEW
883
    case SIXEL_DIFFUSE_STUCKI:
×
NEW
884
        f_diffuse = diffuse_stucki_float;
×
NEW
885
        break;
×
NEW
886
    case SIXEL_DIFFUSE_BURKES:
×
NEW
887
        f_diffuse = diffuse_burkes_float;
×
NEW
888
        break;
×
NEW
889
    case SIXEL_DIFFUSE_SIERRA1:
×
NEW
890
        f_diffuse = diffuse_sierra1_float;
×
NEW
891
        break;
×
NEW
892
    case SIXEL_DIFFUSE_SIERRA2:
×
NEW
893
        f_diffuse = diffuse_sierra2_float;
×
NEW
894
        break;
×
NEW
895
    case SIXEL_DIFFUSE_SIERRA3:
×
NEW
896
        f_diffuse = diffuse_sierra3_float;
×
NEW
897
        break;
×
NEW
898
    case SIXEL_DIFFUSE_FS:
×
899
    default:
NEW
900
        f_diffuse = diffuse_fs_float;
×
NEW
901
        break;
×
902
    }
903

904
    if (context->optimize_palette) {
×
905
        *context->ncolors = 0;
×
906
        memset(context->new_palette, 0x00,
×
UNCOV
907
               (size_t)SIXEL_PALETTE_MAX * (size_t)context->depth);
×
908
        if (new_palette_float != NULL && float_depth > 0) {
×
909
            memset(new_palette_float, 0x00,
×
910
                   (size_t)SIXEL_PALETTE_MAX
UNCOV
911
                       * (size_t)float_depth * sizeof(float));
×
912
        }
913
        memset(context->migration_map, 0x00,
×
914
               sizeof(unsigned short) * (size_t)SIXEL_PALETTE_MAX);
915
    } else {
916
        *context->ncolors = context->reqcolor;
×
917
    }
918

919
    for (y = 0; y < context->height; ++y) {
×
920
        sixel_dither_scanline_params(serpentine, y, context->width,
×
921
                                     &start, &end, &step, &direction);
922
        for (x = start; x != end; x += step) {
×
923
            pos = y * context->width + x;
×
924
            base = (size_t)pos * (size_t)context->depth;
×
925
            source_pixel = data + base;
×
926

927
            for (n = 0; n < context->depth; ++n) {
×
928
                snapshot[n] = source_pixel[n];
×
NEW
929
                if (need_float_pixel) {
×
NEW
930
                    lookup_pixel_float[n] = source_pixel[n];
×
931
                }
NEW
932
                if (!lookup_wants_float && !use_palette_float_lookup) {
×
933
                    quantized[n]
NEW
934
                        = sixel_pixelformat_float_channel_to_byte(
×
935
                              context->pixelformat,
936
                              n,
NEW
937
                              source_pixel[n]);
×
938
                }
939
            }
940

NEW
941
            if (lookup_wants_float) {
×
NEW
942
                lookup_pixel = (unsigned char const *)(void const *)source_pixel;
×
NEW
943
                color_index = context->lookup(lookup_pixel,
×
944
                                              context->depth,
945
                                              palette,
946
                                              context->reqcolor,
947
                                              context->indextable,
948
                                              context->complexion);
NEW
949
            } else if (use_palette_float_lookup) {
×
NEW
950
                color_index = sixel_dither_lookup_palette_float32(
×
951
                    lookup_pixel_float,
952
                    context->depth,
953
                    palette_float,
954
                    context->reqcolor,
955
                    context->complexion);
956
            } else {
NEW
957
                lookup_pixel = quantized;
×
NEW
958
                color_index = context->lookup(lookup_pixel,
×
959
                                              context->depth,
960
                                              palette,
961
                                              context->reqcolor,
962
                                              context->indextable,
963
                                              context->complexion);
964
            }
965

966
            if (context->optimize_palette) {
×
967
                if (context->migration_map[color_index] == 0) {
×
968
                    output_index = *context->ncolors;
×
969
                    for (n = 0; n < context->depth; ++n) {
×
970
                        context->new_palette[output_index * context->depth + n]
×
971
                            = palette[color_index * context->depth + n];
×
972
                    }
973
                    if (palette_float != NULL
×
974
                            && new_palette_float != NULL
×
975
                            && float_depth > 0) {
×
976
                        for (float_index = 0;
×
977
                                float_index < float_depth;
×
978
                                ++float_index) {
×
979
                            new_palette_float[output_index * float_depth
×
980
                                              + float_index]
×
981
                                = palette_float[color_index * float_depth
×
982
                                                + float_index];
×
983
                        }
984
                    }
985
                    ++*context->ncolors;
×
986
                    context->migration_map[color_index] = *context->ncolors;
×
987
                } else {
988
                    output_index = context->migration_map[color_index] - 1;
×
989
                }
990
                context->result[pos] = output_index;
×
991
            } else {
992
                output_index = color_index;
×
993
                context->result[pos] = output_index;
×
994
            }
995

996
            for (n = 0; n < context->depth; ++n) {
×
997
                if (context->optimize_palette) {
×
NEW
998
                    if (have_new_palette_float) {
×
NEW
999
                        palette_value_float =
×
NEW
1000
                            new_palette_float[output_index * float_depth
×
NEW
1001
                                              + n];
×
1002
                    } else {
NEW
1003
                        palette_value =
×
NEW
1004
                            context->new_palette[output_index
×
NEW
1005
                                                 * context->depth + n];
×
1006
                        palette_value_float
NEW
1007
                            = sixel_pixelformat_byte_to_float(
×
1008
                                  context->pixelformat,
1009
                                  n,
NEW
1010
                                  (unsigned char)palette_value);
×
1011
                    }
1012
                } else {
NEW
1013
                    if (have_palette_float) {
×
NEW
1014
                        palette_value_float =
×
NEW
1015
                            palette_float[color_index * float_depth + n];
×
1016
                    } else {
NEW
1017
                        palette_value =
×
NEW
1018
                            palette[color_index * context->depth + n];
×
1019
                        palette_value_float
NEW
1020
                            = sixel_pixelformat_byte_to_float(
×
1021
                                  context->pixelformat,
1022
                                  n,
NEW
1023
                                  (unsigned char)palette_value);
×
1024
                    }
1025
                }
UNCOV
1026
                error = snapshot[n] - palette_value_float;
×
1027
                source_pixel[n] = palette_value_float;
×
NEW
1028
                f_diffuse(data + (size_t)n,
×
1029
                          context->width,
1030
                          context->height,
1031
                          x,
1032
                          y,
1033
                          context->depth,
1034
                          error,
1035
                          direction,
1036
                          context->pixelformat,
1037
                          n);
1038
            }
1039
        }
1040
        sixel_dither_pipeline_row_notify(dither, y);
×
1041
    }
1042

1043
    if (context->optimize_palette) {
×
1044
        memcpy(context->palette,
×
UNCOV
1045
               context->new_palette,
×
UNCOV
1046
               (size_t)(*context->ncolors * context->depth));
×
1047
        if (palette_float != NULL
×
1048
                && new_palette_float != NULL
×
1049
                && float_depth > 0) {
×
1050
            memcpy(palette_float,
×
1051
                   new_palette_float,
UNCOV
1052
                   (size_t)(*context->ncolors * float_depth)
×
1053
                       * sizeof(float));
1054
        }
1055
    }
1056

1057
    status = SIXEL_OK;
×
1058
    SIXEL_DITHER_FLOAT32_HIT();
×
1059
    return status;
×
1060
}
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