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

saitoha / libsixel / 19776286632

29 Nov 2025 12:20AM UTC coverage: 41.017% (-0.3%) from 41.338%
19776286632

push

github

saitoha
build: remove unused status in reversible snap test

9964 of 36344 branches covered (27.42%)

13002 of 31699 relevant lines covered (41.02%)

1178071.22 hits per line

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

0.0
/src/palette-common-snap.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
/*
26
 * Safe-tone snapping helpers for palettes.  The implementation mirrors the
27
 * reversible tone logic historically embedded inside palette.c but now lives in
28
 * a dedicated module so other translation units can reuse it without dragging
29
 * in unrelated merge infrastructure.
30
 */
31

32
#include "config.h"
33

34
#include <stddef.h>
35
#include <stdlib.h>
36
#include <string.h>
37

38
#include "colorspace.h"
39
#include "lookup-common.h"
40
#include "palette-common-snap.h"
41
#include "pixelformat.h"
42

43
static int
44
sixel_palette_determine_colorspace(int pixelformat);
45
static void
46
sixel_palette_clamp_float_triplet(float *components, int pixelformat);
47
static SIXELSTATUS
48
sixel_palette_snap_float_triplet(float *components,
49
                                 int use_reversible,
50
                                 int pixelformat);
51

52
static int
53
sixel_palette_determine_colorspace(int pixelformat)
×
54
{
55
    switch (pixelformat) {
×
56
    case SIXEL_PIXELFORMAT_LINEARRGBFLOAT32:
×
57
        return SIXEL_COLORSPACE_LINEAR;
×
58
    case SIXEL_PIXELFORMAT_OKLABFLOAT32:
×
59
        return SIXEL_COLORSPACE_OKLAB;
×
60
    case SIXEL_PIXELFORMAT_CIELABFLOAT32:
×
61
        return SIXEL_COLORSPACE_CIELAB;
×
62
    case SIXEL_PIXELFORMAT_DIN99DFLOAT32:
×
63
        return SIXEL_COLORSPACE_DIN99D;
×
64
    default:
×
65
        return SIXEL_COLORSPACE_GAMMA;
×
66
    }
67
}
68

69
void
70
sixel_palette_reversible_palette(unsigned char *palette,
×
71
                                 unsigned int colors,
72
                                 int pixelformat)
73
{
74
    SIXELSTATUS status;
75
    unsigned char *working;
76
    size_t palette_bytes;
77
    size_t color_index;
78
    int depth;
79
    int channel;
80
    int colorspace;
81

82
    status = SIXEL_OK;
×
83
    working = NULL;
×
84
    palette_bytes = 0U;
×
85
    color_index = 0U;
×
86
    depth = sixel_helper_compute_depth(pixelformat);
×
87
    channel = 0;
×
88
    colorspace = sixel_palette_determine_colorspace(pixelformat);
×
89
    if (SIXEL_PIXELFORMAT_IS_FLOAT32(pixelformat)) {
×
90
        sixel_palette_reversible_palette_float((float *)palette,
×
91
                                               colors,
92
                                               pixelformat);
93
        return;
×
94
    }
95
    if (palette == NULL || colors == 0U || depth <= 0) {
×
96
        return;
×
97
    }
98

99
    /*
100
     * Snap in gamma-corrected sRGB space.  Byte palettes that already live in
101
     * gamma space are processed in-place; others are converted to sRGB, snapped
102
     * on the safe-tone grid, then converted back to the original colorspace.
103
     */
104
    palette_bytes = (size_t)colors * (size_t)depth;
×
105
    if (colorspace != SIXEL_COLORSPACE_GAMMA) {
×
106
        working = (unsigned char *)malloc(palette_bytes);
×
107
        if (working == NULL) {
×
108
            return;
×
109
        }
110
        memcpy(working, palette, palette_bytes);
×
111
        status = sixel_helper_convert_colorspace(working,
×
112
                                                 palette_bytes,
113
                                                 pixelformat,
114
                                                 colorspace,
115
                                                 SIXEL_COLORSPACE_GAMMA);
116
        if (SIXEL_FAILED(status)) {
×
117
            free(working);
×
118
            return;
×
119
        }
120
    } else {
121
        working = palette;
×
122
    }
123

124
    for (color_index = 0U; color_index < (size_t)colors; ++color_index) {
×
125
        for (channel = 0; channel < depth; ++channel) {
×
126
            size_t index;
127

128
            index = color_index * (size_t)depth + (size_t)channel;
×
129
            working[index]
×
130
                = sixel_palette_reversible_value(working[index]);
×
131
        }
132
    }
133

134
    if (colorspace != SIXEL_COLORSPACE_GAMMA) {
×
135
        status = sixel_helper_convert_colorspace(working,
×
136
                                                 palette_bytes,
137
                                                 pixelformat,
138
                                                 SIXEL_COLORSPACE_GAMMA,
139
                                                 colorspace);
140
        if (SIXEL_FAILED(status)) {
×
141
            free(working);
×
142
            return;
×
143
        }
144
        memcpy(palette, working, palette_bytes);
×
145
        free(working);
×
146
    }
147
}
148

149
void
150
sixel_palette_reversible_palette_float(float *palette,
×
151
                                       unsigned int colors,
152
                                       int pixelformat)
153
{
154
    SIXELSTATUS status;
155
    float *working_palette;
156
    unsigned char *snapped_bytes;
157
    size_t palette_channels;
158
    size_t palette_bytes_len;
159
    size_t index;
160
    size_t color_index;
161
    int colorspace;
162
    int channel_count;
163
    int channel;
164

165
    status = SIXEL_OK;
×
166
    working_palette = NULL;
×
167
    snapped_bytes = NULL;
×
168
    palette_channels = 0U;
×
169
    palette_bytes_len = 0U;
×
170
    index = 0U;
×
171
    color_index = 0U;
×
172
    colorspace = sixel_palette_determine_colorspace(pixelformat);
×
173
    channel_count = 0;
×
174
    channel = 0;
×
175

176
    if (palette == NULL || colors == 0U) {
×
177
        return;
×
178
    }
179

180
    /*
181
     * Preserve reversible snapping for OKLab palettes by round-tripping
182
     * through sRGB gamma before re-encoding onto the 101-tone grid.
183
     */
184
    if (colorspace == SIXEL_COLORSPACE_OKLAB) {
×
185
        palette_channels = (size_t)colors * 3U;
×
186
        if (palette_channels > SIZE_MAX / sizeof(float)) {
×
187
            return;
×
188
        }
189
        palette_bytes_len = palette_channels * sizeof(float);
×
190
        working_palette = (float *)malloc(palette_bytes_len);
×
191
        if (working_palette == NULL) {
×
192
            return;
×
193
        }
194
        snapped_bytes = (unsigned char *)malloc(palette_channels);
×
195
        if (snapped_bytes == NULL) {
×
196
            free(working_palette);
×
197
            return;
×
198
        }
199

200
        memcpy(working_palette, palette, palette_bytes_len);
×
201
        sixel_palette_clamp_float_triplet(working_palette, pixelformat);
×
202
        status = sixel_helper_convert_colorspace(
×
203
            (unsigned char *)working_palette,
204
            palette_bytes_len,
205
            SIXEL_PIXELFORMAT_OKLABFLOAT32,
206
            SIXEL_COLORSPACE_OKLAB,
207
            SIXEL_COLORSPACE_GAMMA);
208
        if (SIXEL_FAILED(status)) {
×
209
            goto cleanup;
×
210
        }
211

212
        for (index = 0U; index < palette_channels; ++index) {
×
213
            channel = (int)(index % 3U);
×
214
            snapped_bytes[index]
×
215
                = sixel_pixelformat_float_channel_to_byte(
×
216
                    SIXEL_PIXELFORMAT_RGBFLOAT32,
217
                    channel,
218
                    working_palette[index]);
×
219
            snapped_bytes[index]
×
220
                = sixel_palette_reversible_value(snapped_bytes[index]);
×
221
            working_palette[index]
×
222
                = sixel_pixelformat_byte_to_float(
×
223
                    SIXEL_PIXELFORMAT_RGBFLOAT32,
224
                    channel,
225
                    snapped_bytes[index]);
×
226
        }
227

228
        status = sixel_helper_convert_colorspace(
×
229
            (unsigned char *)working_palette,
230
            palette_bytes_len,
231
            SIXEL_PIXELFORMAT_RGBFLOAT32,
232
            SIXEL_COLORSPACE_GAMMA,
233
            SIXEL_COLORSPACE_OKLAB);
234
        if (SIXEL_FAILED(status)) {
×
235
            goto cleanup;
×
236
        }
237

238
        sixel_palette_clamp_float_triplet(working_palette, pixelformat);
×
239
        memcpy(palette, working_palette, palette_bytes_len);
×
240

241
cleanup:
×
242
        free(snapped_bytes);
×
243
        free(working_palette);
×
244
        return;
×
245
    }
246

247
    channel_count = sixel_helper_compute_depth(pixelformat)
×
248
        / (int)sizeof(float);
249
    if (channel_count <= 0) {
×
250
        return;
×
251
    }
252
    for (color_index = 0U; color_index < (size_t)colors; ++color_index) {
×
253
        index = color_index * (size_t)channel_count;
×
254
        status = sixel_palette_snap_float_triplet(&palette[index],
×
255
                                                  1,
256
                                                  pixelformat);
257
        if (SIXEL_FAILED(status)) {
×
258
            return;
×
259
        }
260
    }
261
}
262

263
static SIXELSTATUS
264
sixel_palette_snap_float_triplet(float *components,
×
265
                                 int use_reversible,
266
                                 int pixelformat)
267
{
268
    SIXELSTATUS status;
269
    float working_palette[3];
270
    unsigned char snapped_bytes[3];
271
    int colorspace;
272
    int original_pixelformat;
273
    int channel;
274

275
    status = SIXEL_OK;
×
276
    colorspace = sixel_palette_determine_colorspace(pixelformat);
×
277
    original_pixelformat = pixelformat;
×
278
    if (components == NULL) {
×
279
        return SIXEL_BAD_ARGUMENT;
×
280
    }
281

282
    if (!use_reversible) {
×
283
        for (channel = 0; channel < 3; ++channel) {
×
284
            components[channel]
×
285
                = sixel_pixelformat_float_channel_clamp(pixelformat,
×
286
                                                        channel,
287
                                                        components[channel]);
×
288
        }
289

290
        return SIXEL_OK;
×
291
    }
292

293
    memcpy(working_palette, components, sizeof(working_palette));
×
294
    sixel_palette_clamp_float_triplet(working_palette,
×
295
                                      original_pixelformat);
296
    if (colorspace != SIXEL_COLORSPACE_GAMMA) {
×
297
        status = sixel_helper_convert_colorspace(
×
298
            (unsigned char *)working_palette,
299
            sizeof(working_palette),
300
            pixelformat,
301
            colorspace,
302
            SIXEL_COLORSPACE_GAMMA);
303
        if (SIXEL_FAILED(status)) {
×
304
            return status;
×
305
        }
306
        pixelformat = SIXEL_PIXELFORMAT_RGBFLOAT32;
×
307
    }
308

309
    for (channel = 0; channel < 3; ++channel) {
×
310
        snapped_bytes[channel]
311
            = sixel_pixelformat_float_channel_to_byte(pixelformat,
×
312
                                                      channel,
313
                                                      working_palette[channel]);
314
        snapped_bytes[channel]
315
            = sixel_palette_reversible_value(snapped_bytes[channel]);
×
316
        working_palette[channel]
317
            = sixel_pixelformat_byte_to_float(pixelformat,
×
318
                                              channel,
319
                                              snapped_bytes[channel]);
×
320
    }
321

322
    if (colorspace != SIXEL_COLORSPACE_GAMMA) {
×
323
        status = sixel_helper_convert_colorspace(
×
324
            (unsigned char *)working_palette,
325
            sizeof(working_palette),
326
            SIXEL_PIXELFORMAT_RGBFLOAT32,
327
            SIXEL_COLORSPACE_GAMMA,
328
            colorspace);
329
        if (SIXEL_FAILED(status)) {
×
330
            return status;
×
331
        }
332
    }
333

334
    sixel_palette_clamp_float_triplet(working_palette,
×
335
                                      original_pixelformat);
336
    memcpy(components, working_palette, sizeof(working_palette));
×
337

338
    return SIXEL_OK;
×
339
}
340

341
static void
342
sixel_palette_clamp_float_triplet(float *components, int pixelformat)
×
343
{
344
    int channel;
345

346
    for (channel = 0; channel < 3; ++channel) {
×
347
        components[channel] = sixel_pixelformat_float_channel_clamp(
×
348
            pixelformat, channel, components[channel]);
×
349
    }
350
}
×
351

352
double
353
sixel_palette_snap_double(double value,
×
354
                          int use_reversible,
355
                          int pixelformat,
356
                          int channel)
357
{
358
    double clamped;
359
    double snapped;
360
    unsigned char sample;
361

362
    clamped = value;
×
363
    snapped = value;
×
364
    sample = 0U;
×
365
    if (SIXEL_PIXELFORMAT_IS_FLOAT32(pixelformat)) {
×
366
        if (!use_reversible) {
×
367
            return (double)sixel_pixelformat_float_channel_clamp(
×
368
                pixelformat, channel, (float)value);
369
        }
370
        sample = sixel_pixelformat_float_channel_to_byte(pixelformat,
×
371
                                                         channel,
372
                                                         (float)value);
373
        if (use_reversible) {
×
374
            sample = sixel_palette_reversible_value(sample);
×
375
        }
376
        snapped = (double)sixel_pixelformat_byte_to_float(pixelformat,
×
377
                                                          channel,
378
                                                          sample);
379

380
        return snapped;
×
381
    }
382
    if (clamped < 0.0) {
×
383
        clamped = 0.0;
×
384
    }
385
    if (clamped > 255.0) {
×
386
        clamped = 255.0;
×
387
    }
388
    if (!use_reversible) {
×
389
        return clamped;
×
390
    }
391
    sample = (unsigned char)(clamped + 0.5);
×
392
    snapped = (double)sixel_palette_reversible_value((unsigned int)sample);
×
393

394
    return snapped;
×
395
}
396

397
void
398
sixel_palette_snap_triple(double *components,
×
399
                          int use_reversible,
400
                          int pixelformat)
401
{
402
    SIXELSTATUS status;
403
    float working[3];
404
    unsigned char bytes[3];
405
    int channel;
406

407
    status = SIXEL_OK;
×
408
    if (components == NULL) {
×
409
        return;
×
410
    }
411

412
    /*
413
     * Float palettes are round-tripped through sRGB bytes so the reversible
414
     * grid logic always works on the canonical 0-255 space.
415
     */
416
    if (SIXEL_PIXELFORMAT_IS_FLOAT32(pixelformat)) {
×
417
        for (channel = 0; channel < 3; ++channel) {
×
418
            working[channel] = (float)components[channel];
×
419
        }
420
        status = sixel_palette_snap_float_triplet(
×
421
            working, use_reversible, pixelformat);
422
        if (SIXEL_FAILED(status)) {
×
423
            return;
×
424
        }
425
        for (channel = 0; channel < 3; ++channel) {
×
426
            components[channel] = (double)working[channel];
×
427
        }
428

429
        return;
×
430
    }
431

432
    /*
433
     * Byte palettes stay in-place; clamp to 0-255 and snap directly on the
434
     * reversible tone grid without passing through float conversions.
435
     */
436
    for (channel = 0; channel < 3; ++channel) {
×
437
        if (components[channel] < 0.0) {
×
438
            components[channel] = 0.0;
×
439
        }
440
        if (components[channel] > 255.0) {
×
441
            components[channel] = 255.0;
×
442
        }
443
        bytes[channel] = (unsigned char)(components[channel] + 0.5);
×
444
        if (use_reversible) {
×
445
            bytes[channel] = sixel_palette_reversible_value(bytes[channel]);
×
446
        }
447
        components[channel] = (double)bytes[channel];
×
448
    }
449
}
450

451
/* emacs Local Variables:      */
452
/* emacs mode: c               */
453
/* emacs tab-width: 4          */
454
/* emacs indent-tabs-mode: nil */
455
/* emacs c-basic-offset: 4     */
456
/* emacs End:                  */
457
/* vim: set expandtab ts=4 sts=4 sw=4 : */
458
/* 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