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

saitoha / libsixel / 20609368106

31 Dec 2025 12:57AM UTC coverage: 52.011% (-6.3%) from 58.281%
20609368106

push

github

saitoha
tests: split converter option tap suites

14741 of 45141 branches covered (32.66%)

21394 of 41134 relevant lines covered (52.01%)

3932390.77 hits per line

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

0.0
/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,
×
41
                             int index,
42
                             int limit,
43
                             int *start,
44
                             int *end,
45
                             int *step,
46
                             int *direction)
47
{
48
    if (serpentine && (index & 1)) {
×
49
        *start = limit - 1;
×
50
        *end = -1;
×
51
        *step = -1;
×
52
        *direction = -1;
×
53
    } else {
54
        *start = 0;
55
        *end = limit;
56
        *step = 1;
57
        *direction = 1;
×
58
    }
59
}
60

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

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

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

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

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

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

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

143
    if (context->optimize_palette) {
×
144
        int x;
×
145

146
        *context->ncolors = 0;
×
147
        memset(context->new_palette, 0x00,
×
148
               (size_t)SIXEL_PALETTE_MAX * (size_t)context->depth);
×
149
        if (new_palette_float != NULL && float_depth > 0) {
×
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,
×
155
               sizeof(unsigned short) * (size_t)SIXEL_PALETTE_MAX);
156
        for (y = 0; y < context->height; ++y) {
×
157
            absolute_y = context->band_origin + y;
×
158
            int start;
×
159
            int end;
×
160
            int step;
×
161
            int direction;
×
162

163
            sixel_dither_scanline_params_positional_float32(serpentine, absolute_y,
×
164
                                         context->width,
165
                                         &start, &end, &step, &direction);
166
            (void)direction;
×
167
            for (x = start; x != end; x += step) {
×
168
                int pos;
×
169
                int d;
×
170
                int color_index;
×
171

172
                pos = y * context->width + x;
×
173
                for (d = 0; d < context->depth; ++d) {
×
174
                    float val;
×
175

176
                    val = context->pixels_float[pos * context->depth + d]
×
177
                        + f_mask(x, y, d) * jitter_scale;
×
178
                    val = sixel_pixelformat_float_channel_clamp(
×
179
                        context->pixelformat,
180
                        d,
181
                        val);
182
                    if (need_float_pixel) {
×
183
                        lookup_pixel_float[d] = val;
×
184
                    }
185
                    if (!lookup_wants_float && !use_palette_float_lookup) {
×
186
                        quantized[d]
×
187
                            = sixel_pixelformat_float_channel_to_byte(
×
188
                                  context->pixelformat,
189
                                  d,
190
                                  val);
191
                    }
192
                }
193
                if (lookup_wants_float) {
×
194
                    lookup_pixel = (unsigned char const *)(void const *)
×
195
                        lookup_pixel_float;
196
                    if (use_fast_lut) {
×
197
                        color_index = sixel_lut_map_pixel(fast_lut,
×
198
                                                         lookup_pixel);
199
                    } else {
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) {
×
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) {
×
230
                    if (absolute_y >= context->output_start) {
×
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]
×
236
                            = (sixel_index_t)(*context->ncolors);
×
237
                    }
238
                    for (d = 0; d < context->depth; ++d) {
×
239
                        context->new_palette[*context->ncolors
×
240
                                             * context->depth + d]
×
241
                            = context->palette[color_index
×
242
                                               * context->depth + d];
×
243
                    }
244
                    if (palette_float != NULL
×
245
                            && new_palette_float != NULL
×
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;
×
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]
×
263
                        = (unsigned short)(*context->ncolors);
×
264
                } else {
265
                    if (absolute_y >= context->output_start) {
×
266
                        context->result[pos]
×
267
                            = (sixel_index_t)(context->migration_map[
×
268
                                  color_index] - 1);
269
                    }
270
                }
271
            }
272
            if (absolute_y >= context->output_start) {
×
273
                sixel_dither_pipeline_row_notify(dither, absolute_y);
×
274
            }
275
        }
276
        memcpy(context->palette, context->new_palette,
×
277
               (size_t)(*context->ncolors * context->depth));
×
278
        if (palette_float != NULL
×
279
                && new_palette_float != NULL
×
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 {
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;
371
}
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