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

saitoha / libsixel / 21335896708

25 Jan 2026 04:33PM UTC coverage: 76.581% (-2.3%) from 78.904%
21335896708

push

github

saitoha
meson: set build type to plain

20012 of 44638 branches covered (44.83%)

36354 of 47471 relevant lines covered (76.58%)

13461842.27 hits per line

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

72.92
/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 <string.h>
33

34
#include "dither-positional-float32.h"
35
#include "dither-common-pipeline.h"
36
#include "pixelformat.h"
37
#include "lookup-common.h"
38

39
static void
40
sixel_dither_scanline_params_positional_float32(int serpentine,
6,168✔
41
                             int index,
42
                             int limit,
43
                             int *start,
44
                             int *end,
45
                             int *step,
46
                             int *direction)
47
{
48
    if (serpentine && (index & 1)) {
5,784!
49
        *start = limit - 1;
2,700✔
50
        *end = -1;
2,700✔
51
        *step = -1;
2,700✔
52
        *direction = -1;
2,700✔
53
    } else {
675✔
54
        *start = 0;
1,734✔
55
        *end = limit;
1,734✔
56
        *step = 1;
1,734✔
57
        *direction = 1;
4,818✔
58
    }
59
}
3,084✔
60

61
static float
62
positional_mask_a_float32(int x, int y, int c)
147,456✔
63
{
64
    return ((((x + c * 67) + y * 236) * 119) & 255) / 128.0f - 1.0f;
147,456✔
65
}
66

67
static float
68
positional_mask_x_float32(int x, int y, int c)
9,720,000✔
69
{
70
    return ((((x + c * 29) ^ (y * 149)) * 1234) & 511) / 256.0f - 1.0f;
9,720,000✔
71
}
72

73
SIXELSTATUS
74
sixel_dither_apply_positional_float32(sixel_dither_t *dither,
24✔
75
                                      sixel_dither_context_t *context)
76
{
18✔
77
#if _MSC_VER
78
    enum { max_channels = 4 };
79
#else
80
    const int max_channels = 4;
24✔
81
#endif
82
    int serpentine;
14✔
83
    int y;
14✔
84
    int absolute_y;
14✔
85
    float (*f_mask)(int x, int y, int c);
14✔
86
    float jitter_scale;
14✔
87
    float *palette_float;
14✔
88
    float *new_palette_float;
14✔
89
    int float_depth;
14✔
90
    int float_index;
14✔
91
    unsigned char *quantized;
14✔
92
    float lookup_pixel_float[max_channels];
24✔
93
    unsigned char const *lookup_pixel;
14✔
94
    sixel_lut_t *fast_lut;
14✔
95
    int use_fast_lut;
14✔
96
    int lookup_wants_float;
14✔
97
    int use_palette_float_lookup;
14✔
98
    int need_float_pixel;
14✔
99

100
    palette_float = NULL;
24✔
101
    new_palette_float = NULL;
24✔
102
    float_depth = 0;
24✔
103
    quantized = NULL;
24✔
104
    lookup_wants_float = 0;
24✔
105

106
    if (dither == NULL || context == NULL) {
24!
107
        return SIXEL_BAD_ARGUMENT;
108
    }
109
    if (context->pixels_float == NULL || context->scratch == NULL) {
24!
110
        return SIXEL_BAD_ARGUMENT;
111
    }
112
    if (context->palette == NULL || context->result == NULL) {
24!
113
        return SIXEL_BAD_ARGUMENT;
114
    }
115

116
    switch (context->method_for_diffuse) {
24!
117
    case SIXEL_DIFFUSE_A_DITHER:
3✔
118
        f_mask = positional_mask_a_float32;
6✔
119
        break;
6✔
120
    case SIXEL_DIFFUSE_X_DITHER:
12✔
121
    default:
122
        f_mask = positional_mask_x_float32;
12✔
123
        break;
12✔
124
    }
125

126
    serpentine = (context->method_for_scan == SIXEL_SCAN_SERPENTINE);
24✔
127
    jitter_scale = 32.0f / 255.0f;
24✔
128
    palette_float = context->palette_float;
24✔
129
    new_palette_float = context->new_palette_float;
24✔
130
    float_depth = context->float_depth;
24✔
131
    quantized = context->scratch;
24✔
132
    fast_lut = context->lut;
24✔
133
    use_fast_lut = (fast_lut != NULL);
24✔
134
    lookup_wants_float = (context->lookup_source_is_float != 0);
24✔
135
    use_palette_float_lookup = 0;
24✔
136
    if (context->prefer_palette_float_lookup != 0
24!
137
            && palette_float != NULL
6!
138
            && float_depth >= context->depth) {
×
139
        use_palette_float_lookup = 1;
12✔
140
    }
141
    need_float_pixel = lookup_wants_float || use_palette_float_lookup;
24!
142

143
    if (context->optimize_palette) {
24!
144
        int x;
14✔
145

146
        *context->ncolors = 0;
24✔
147
        memset(context->new_palette, 0x00,
26✔
148
               (size_t)SIXEL_PALETTE_MAX * (size_t)context->depth);
20!
149
        if (new_palette_float != NULL && float_depth > 0) {
24!
150
            memset(new_palette_float, 0x00,
×
151
                   (size_t)SIXEL_PALETTE_MAX
152
                       * (size_t)float_depth * sizeof(float));
153
        }
154
        memset(context->migration_map, 0x00,
24✔
155
               sizeof(unsigned short) * (size_t)SIXEL_PALETTE_MAX);
156
        for (y = 0; y < context->height; ++y) {
6,192✔
157
            absolute_y = context->band_origin + y;
6,168✔
158
            int start;
3,598✔
159
            int end;
3,598✔
160
            int step;
3,598✔
161
            int direction;
3,598✔
162

163
            sixel_dither_scanline_params_positional_float32(serpentine, absolute_y,
7,710✔
164
                                         context->width,
1,542✔
165
                                         &start, &end, &step, &direction);
166
            (void)direction;
4,626✔
167
            for (x = start; x != end; x += step) {
3,295,320✔
168
                int pos;
1,918,672✔
169
                int d;
1,918,672✔
170
                int color_index;
1,918,672✔
171

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

176
                    val = context->pixels_float[pos * context->depth + d]
19,734,912✔
177
                        + f_mask(x, y, d) * jitter_scale;
9,867,456✔
178
                    val = sixel_pixelformat_float_channel_clamp(
9,867,456✔
179
                        context->pixelformat,
2,466,864✔
180
                        d,
2,466,864✔
181
                        val);
2,466,864✔
182
                    if (need_float_pixel) {
9,867,456!
183
                        lookup_pixel_float[d] = val;
9,867,456✔
184
                    }
2,466,864✔
185
                    if (!lookup_wants_float && !use_palette_float_lookup) {
9,867,456!
186
                        quantized[d]
×
187
                            = sixel_pixelformat_float_channel_to_byte(
×
188
                                  context->pixelformat,
189
                                  d,
190
                                  val);
191
                    }
192
                }
2,466,864✔
193
                if (lookup_wants_float) {
3,289,152!
194
                    lookup_pixel = (unsigned char const *)(void const *)
3,289,152✔
195
                        lookup_pixel_float;
196
                    if (use_fast_lut) {
3,289,152!
197
                        color_index = sixel_lut_map_pixel(fast_lut,
4,111,440✔
198
                                                         lookup_pixel);
822,288✔
199
                    } else {
822,288✔
200
                        color_index = context->lookup(lookup_pixel,
×
201
                                                      context->depth,
202
                                                      context->palette,
×
203
                                                      context->reqcolor,
204
                                                      context->indextable,
205
                                                      context->complexion);
206
                    }
207
                } else if (use_palette_float_lookup) {
822,288!
208
                    color_index = sixel_dither_lookup_palette_float32(
×
209
                        lookup_pixel_float,
210
                        context->depth,
211
                        palette_float,
212
                        context->reqcolor,
213
                        context->complexion,
214
                        0);
215
                } else {
216
                    lookup_pixel = quantized;
×
217
                    if (use_fast_lut) {
×
218
                        color_index = sixel_lut_map_pixel(fast_lut,
×
219
                                                         lookup_pixel);
220
                    } else {
221
                        color_index = context->lookup(lookup_pixel,
×
222
                                                      context->depth,
223
                                                      context->palette,
×
224
                                                      context->reqcolor,
225
                                                      context->indextable,
226
                                                      context->complexion);
227
                    }
228
                }
229
                if (context->migration_map[color_index] == 0) {
3,289,152✔
230
                    if (absolute_y >= context->output_start) {
2,564!
231
                        /*
232
                         * Palette indices never exceed SIXEL_PALETTE_MAX, so
233
                         * the cast to sixel_index_t (unsigned char) is safe.
234
                         */
235
                        context->result[pos]
2,564✔
236
                            = (sixel_index_t)(*context->ncolors);
3,193✔
237
                    }
629✔
238
                    for (d = 0; d < context->depth; ++d) {
10,256✔
239
                        context->new_palette[*context->ncolors
9,579✔
240
                                             * context->depth + d]
7,692✔
241
                            = context->palette[color_index
11,466✔
242
                                               * context->depth + d];
7,692✔
243
                    }
1,887✔
244
                    if (palette_float != NULL
2,564!
245
                            && new_palette_float != NULL
1,919!
246
                            && float_depth > 0) {
×
247
                        for (float_index = 0;
×
248
                                float_index < float_depth;
×
249
                                ++float_index) {
×
250
                            new_palette_float[*context->ncolors
×
251
                                               * float_depth
×
252
                                               + float_index]
×
253
                                = palette_float[color_index * float_depth
×
254
                                                + float_index];
×
255
                        }
256
                    }
257
                    ++*context->ncolors;
2,564✔
258
                    /*
259
                     * Migration map entries are limited to the palette size
260
                     * (<= 256), so storing them as unsigned short is safe.
261
                     */
262
                    context->migration_map[color_index]
2,564✔
263
                        = (unsigned short)(*context->ncolors);
3,193✔
264
                } else {
629✔
265
                    if (absolute_y >= context->output_start) {
3,286,588!
266
                        context->result[pos]
3,286,588✔
267
                            = (sixel_index_t)(context->migration_map[
5,751,565✔
268
                                  color_index] - 1);
1,643,318✔
269
                    }
821,659✔
270
                }
271
            }
822,288✔
272
            if (absolute_y >= context->output_start) {
6,168!
273
                sixel_dither_pipeline_row_notify(dither, absolute_y);
6,168✔
274
            }
1,542✔
275
        }
1,542✔
276
        memcpy(context->palette, context->new_palette,
26✔
277
               (size_t)(*context->ncolors * context->depth));
20!
278
        if (palette_float != NULL
24!
279
                && new_palette_float != NULL
18!
280
                && float_depth > 0) {
×
281
            memcpy(palette_float,
×
282
                   new_palette_float,
283
                   (size_t)(*context->ncolors * float_depth)
284
                       * sizeof(float));
285
        }
286
    } else {
6✔
287
        int x;
288

289
        for (y = 0; y < context->height; ++y) {
×
290
            absolute_y = context->band_origin + y;
×
291
            int start;
292
            int end;
293
            int step;
294
            int direction;
295

296
            sixel_dither_scanline_params_positional_float32(serpentine, absolute_y,
×
297
                                         context->width,
298
                                         &start, &end, &step, &direction);
299
            (void)direction;
300
            for (x = start; x != end; x += step) {
×
301
                int pos;
302
                int d;
303

304
                pos = y * context->width + x;
×
305
                for (d = 0; d < context->depth; ++d) {
×
306
                    float val;
307

308
                    val = context->pixels_float[pos * context->depth + d]
×
309
                        + f_mask(x, y, d) * jitter_scale;
×
310
                    val = sixel_pixelformat_float_channel_clamp(
×
311
                        context->pixelformat,
312
                        d,
313
                        val);
314
                    if (need_float_pixel) {
×
315
                        lookup_pixel_float[d] = val;
×
316
                    }
317
                    if (!lookup_wants_float && !use_palette_float_lookup) {
×
318
                        quantized[d]
×
319
                            = sixel_pixelformat_float_channel_to_byte(
×
320
                                  context->pixelformat,
321
                                  d,
322
                                  val);
323
                    }
324
                }
325
                if (absolute_y >= context->output_start) {
×
326
                    /*
327
                     * Palette indices never exceed SIXEL_PALETTE_MAX, so
328
                     * narrowing to sixel_index_t (unsigned char) is safe.
329
                     */
330
                    if (lookup_wants_float) {
×
331
                        lookup_pixel = (unsigned char const *)(void const *)
×
332
                            lookup_pixel_float;
333
                        context->result[pos] = (sixel_index_t)
×
334
                            context->lookup(
×
335
                                lookup_pixel,
336
                                context->depth,
337
                                context->palette,
×
338
                                context->reqcolor,
339
                                context->indextable,
340
                                context->complexion);
341
                    } else if (use_palette_float_lookup) {
×
342
                        context->result[pos] = (sixel_index_t)
×
343
                            sixel_dither_lookup_palette_float32(
×
344
                                lookup_pixel_float,
345
                                context->depth,
346
                                palette_float,
347
                                context->reqcolor,
348
                                context->complexion,
349
                                0);
350
                    } else {
351
                        lookup_pixel = quantized;
×
352
                        context->result[pos] = (sixel_index_t)
×
353
                            context->lookup(
×
354
                                lookup_pixel,
355
                                context->depth,
356
                                context->palette,
×
357
                                context->reqcolor,
358
                                context->indextable,
359
                                context->complexion);
360
                    }
361
                }
362
            }
363
            if (absolute_y >= context->output_start) {
×
364
                sixel_dither_pipeline_row_notify(dither, absolute_y);
×
365
            }
366
        }
367
        *context->ncolors = context->reqcolor;
×
368
    }
369

370
    return SIXEL_OK;
12✔
371
}
6✔
372

373
/* emacs Local Variables:      */
374
/* emacs mode: c               */
375
/* emacs tab-width: 4          */
376
/* emacs indent-tabs-mode: nil */
377
/* emacs c-basic-offset: 4     */
378
/* emacs End:                  */
379
/* vim: set expandtab ts=4 sts=4 sw=4 : */
380
/* 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