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

saitoha / libsixel / 19541344273

20 Nov 2025 03:02PM UTC coverage: 40.773% (-0.4%) from 41.21%
19541344273

push

github

saitoha
feat: initial prototyping for parallel dithering

9711 of 33880 branches covered (28.66%)

55 of 483 new or added lines in 10 files covered. (11.39%)

12 existing lines in 4 files now uncovered.

12720 of 31197 relevant lines covered (40.77%)

656879.66 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
#include "config.h"
26

27
#if HAVE_MATH_H
28
# include <math.h>
29
#endif  /* HAVE_MATH_H */
30
#include <string.h>
31

32
#include "dither-positional-float32.h"
33
#include "dither-common-pipeline.h"
34
#include "pixelformat.h"
35

36
static void
37
sixel_dither_scanline_params(int serpentine,
×
38
                             int index,
39
                             int limit,
40
                             int *start,
41
                             int *end,
42
                             int *step,
43
                             int *direction)
44
{
45
    if (serpentine && (index & 1)) {
×
46
        *start = limit - 1;
×
47
        *end = -1;
×
48
        *step = -1;
×
49
        *direction = -1;
×
50
    } else {
51
        *start = 0;
×
52
        *end = limit;
×
53
        *step = 1;
×
54
        *direction = 1;
×
55
    }
56
}
×
57

58
static float
59
mask_a(int x, int y, int c)
×
60
{
61
    return ((((x + c * 67) + y * 236) * 119) & 255) / 128.0f - 1.0f;
×
62
}
63

64
static float
65
mask_x(int x, int y, int c)
×
66
{
67
    return ((((x + c * 29) ^ (y * 149)) * 1234) & 511) / 256.0f - 1.0f;
×
68
}
69

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

95
    palette_float = NULL;
×
96
    new_palette_float = NULL;
×
97
    float_depth = 0;
×
98
    quantized = NULL;
×
99
    lookup_wants_float = 0;
×
100

101
    if (dither == NULL || context == NULL) {
×
102
        return SIXEL_BAD_ARGUMENT;
×
103
    }
104
    if (context->pixels_float == NULL || context->scratch == NULL) {
×
105
        return SIXEL_BAD_ARGUMENT;
×
106
    }
107
    if (context->palette == NULL || context->result == NULL) {
×
108
        return SIXEL_BAD_ARGUMENT;
×
109
    }
110

111
    switch (context->method_for_diffuse) {
×
112
    case SIXEL_DIFFUSE_A_DITHER:
×
113
        f_mask = mask_a;
×
114
        break;
×
115
    case SIXEL_DIFFUSE_X_DITHER:
×
116
    default:
117
        f_mask = mask_x;
×
118
        break;
×
119
    }
120

121
    serpentine = (context->method_for_scan == SIXEL_SCAN_SERPENTINE);
×
122
    jitter_scale = 32.0f / 255.0f;
×
123
    palette_float = context->palette_float;
×
124
    new_palette_float = context->new_palette_float;
×
125
    float_depth = context->float_depth;
×
126
    quantized = context->scratch;
×
127
    lookup_wants_float = (context->lookup_source_is_float != 0);
×
128
    use_palette_float_lookup = 0;
×
129
    if (context->prefer_palette_float_lookup != 0
×
130
            && palette_float != NULL
×
131
            && float_depth >= context->depth) {
×
132
        use_palette_float_lookup = 1;
×
133
    }
134
    need_float_pixel = lookup_wants_float || use_palette_float_lookup;
×
135

136
    if (context->optimize_palette) {
×
137
        int x;
138

139
        *context->ncolors = 0;
×
140
        memset(context->new_palette, 0x00,
×
141
               (size_t)SIXEL_PALETTE_MAX * (size_t)context->depth);
×
142
        if (new_palette_float != NULL && float_depth > 0) {
×
143
            memset(new_palette_float, 0x00,
×
144
                   (size_t)SIXEL_PALETTE_MAX
145
                       * (size_t)float_depth * sizeof(float));
×
146
        }
147
        memset(context->migration_map, 0x00,
×
148
               sizeof(unsigned short) * (size_t)SIXEL_PALETTE_MAX);
149
        for (y = 0; y < context->height; ++y) {
×
NEW
150
            absolute_y = context->band_origin + y;
×
151
            int start;
152
            int end;
153
            int step;
154
            int direction;
155

NEW
156
            sixel_dither_scanline_params(serpentine, absolute_y,
×
157
                                         context->width,
158
                                         &start, &end, &step, &direction);
159
            (void)direction;
160
            for (x = start; x != end; x += step) {
×
161
                int pos;
162
                int d;
163
                int color_index;
164

165
                pos = y * context->width + x;
×
166
                for (d = 0; d < context->depth; ++d) {
×
167
                    float val;
168

169
                    val = context->pixels_float[pos * context->depth + d]
×
170
                        + f_mask(x, y, d) * jitter_scale;
×
171
                    val = sixel_pixelformat_float_channel_clamp(
×
172
                        context->pixelformat,
173
                        d,
174
                        val);
175
                    if (need_float_pixel) {
×
176
                        lookup_pixel_float[d] = val;
×
177
                    }
178
                    if (!lookup_wants_float && !use_palette_float_lookup) {
×
179
                        quantized[d]
×
180
                            = sixel_pixelformat_float_channel_to_byte(
×
181
                                  context->pixelformat,
182
                                  d,
183
                                  val);
184
                    }
185
                }
186
                if (lookup_wants_float) {
×
187
                    lookup_pixel = (unsigned char const *)(void const *)
×
188
                        lookup_pixel_float;
189
                    color_index = context->lookup(lookup_pixel,
×
190
                                                  context->depth,
191
                                                  context->palette,
×
192
                                                  context->reqcolor,
193
                                                  context->indextable,
194
                                                  context->complexion);
195
                } else if (use_palette_float_lookup) {
×
196
                    color_index = sixel_dither_lookup_palette_float32(
×
197
                        lookup_pixel_float,
198
                        context->depth,
199
                        palette_float,
200
                        context->reqcolor,
201
                        context->complexion);
202
                } else {
203
                    lookup_pixel = quantized;
×
204
                    color_index = context->lookup(lookup_pixel,
×
205
                                                  context->depth,
206
                                                  context->palette,
×
207
                                                  context->reqcolor,
208
                                                  context->indextable,
209
                                                  context->complexion);
210
                }
211
                if (context->migration_map[color_index] == 0) {
×
NEW
212
                    if (absolute_y >= context->output_start) {
×
NEW
213
                        context->result[pos] = *context->ncolors;
×
214
                    }
215
                    for (d = 0; d < context->depth; ++d) {
×
216
                        context->new_palette[*context->ncolors
×
217
                                             * context->depth + d]
×
218
                            = context->palette[color_index
×
219
                                               * context->depth + d];
×
220
                    }
221
                    if (palette_float != NULL
×
222
                            && new_palette_float != NULL
×
223
                            && float_depth > 0) {
×
224
                        for (float_index = 0;
×
225
                                float_index < float_depth;
×
226
                                ++float_index) {
×
227
                            new_palette_float[*context->ncolors
×
228
                                               * float_depth
×
229
                                               + float_index]
×
230
                                = palette_float[color_index * float_depth
×
231
                                                + float_index];
×
232
                        }
233
                    }
234
                    ++*context->ncolors;
×
235
                    context->migration_map[color_index] = *context->ncolors;
×
236
                } else {
NEW
237
                    if (absolute_y >= context->output_start) {
×
NEW
238
                        context->result[pos] =
×
NEW
239
                            context->migration_map[color_index] - 1;
×
240
                    }
241
                }
242
            }
NEW
243
            if (absolute_y >= context->output_start) {
×
NEW
244
                sixel_dither_pipeline_row_notify(dither, absolute_y);
×
245
            }
246
        }
247
        memcpy(context->palette, context->new_palette,
×
248
               (size_t)(*context->ncolors * context->depth));
×
249
        if (palette_float != NULL
×
250
                && new_palette_float != NULL
×
251
                && float_depth > 0) {
×
252
            memcpy(palette_float,
×
253
                   new_palette_float,
254
                   (size_t)(*context->ncolors * float_depth)
×
255
                       * sizeof(float));
256
        }
257
    } else {
258
        int x;
259

260
        for (y = 0; y < context->height; ++y) {
×
NEW
261
            absolute_y = context->band_origin + y;
×
262
            int start;
263
            int end;
264
            int step;
265
            int direction;
266

NEW
267
            sixel_dither_scanline_params(serpentine, absolute_y,
×
268
                                         context->width,
269
                                         &start, &end, &step, &direction);
270
            (void)direction;
271
            for (x = start; x != end; x += step) {
×
272
                int pos;
273
                int d;
274

275
                pos = y * context->width + x;
×
276
                for (d = 0; d < context->depth; ++d) {
×
277
                    float val;
278

279
                    val = context->pixels_float[pos * context->depth + d]
×
280
                        + f_mask(x, y, d) * jitter_scale;
×
281
                    val = sixel_pixelformat_float_channel_clamp(
×
282
                        context->pixelformat,
283
                        d,
284
                        val);
285
                    if (need_float_pixel) {
×
286
                        lookup_pixel_float[d] = val;
×
287
                    }
288
                    if (!lookup_wants_float && !use_palette_float_lookup) {
×
289
                        quantized[d]
×
290
                            = sixel_pixelformat_float_channel_to_byte(
×
291
                                  context->pixelformat,
292
                                  d,
293
                                  val);
294
                    }
295
                }
NEW
296
                if (absolute_y >= context->output_start) {
×
NEW
297
                    if (lookup_wants_float) {
×
NEW
298
                        lookup_pixel = (unsigned char const *)(void const *)
×
299
                            lookup_pixel_float;
NEW
300
                        context->result[pos] = context->lookup(
×
301
                            lookup_pixel,
302
                            context->depth,
NEW
303
                            context->palette,
×
304
                            context->reqcolor,
305
                            context->indextable,
306
                            context->complexion);
NEW
307
                    } else if (use_palette_float_lookup) {
×
NEW
308
                        context->result[pos] =
×
NEW
309
                            sixel_dither_lookup_palette_float32(
×
310
                                lookup_pixel_float,
311
                                context->depth,
312
                                palette_float,
313
                                context->reqcolor,
314
                                context->complexion);
315
                    } else {
NEW
316
                        lookup_pixel = quantized;
×
NEW
317
                        context->result[pos] = context->lookup(
×
318
                            lookup_pixel,
319
                            context->depth,
NEW
320
                            context->palette,
×
321
                            context->reqcolor,
322
                            context->indextable,
323
                            context->complexion);
324
                    }
325
                }
326
            }
NEW
327
            if (absolute_y >= context->output_start) {
×
NEW
328
                sixel_dither_pipeline_row_notify(dither, absolute_y);
×
329
            }
330
        }
331
        *context->ncolors = context->reqcolor;
×
332
    }
333

334
    return SIXEL_OK;
×
335
}
336

337
/* emacs Local Variables:      */
338
/* emacs mode: c               */
339
/* emacs tab-width: 4          */
340
/* emacs indent-tabs-mode: nil */
341
/* emacs c-basic-offset: 4     */
342
/* emacs End:                  */
343
/* vim: set expandtab ts=4 sts=4 sw=4 : */
344
/* 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