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

saitoha / libsixel / 20109299434

10 Dec 2025 06:31PM UTC coverage: 41.113% (-0.07%) from 41.187%
20109299434

push

github

saitoha
colorspace: add YUV colorspace and pixel format support

10856 of 41095 branches covered (26.42%)

3 of 83 new or added lines in 3 files covered. (3.61%)

2 existing lines in 2 files now uncovered.

14884 of 36203 relevant lines covered (41.11%)

2711647.75 hits per line

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

2.36
/src/pixelformat.c
1
/*
2
 * SPDX-License-Identifier: MIT
3
 *
4
 * Copyright (c) 2021-2025 libsixel developers. See `AUTHORS`.
5
 * Copyright (c) 2014-2019 Hayaki Saito
6
 *
7
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
8
 * this software and associated documentation files (the "Software"), to deal in
9
 * the Software without restriction, including without limitation the rights to
10
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11
 * the Software, and to permit persons to whom the Software is furnished to do so,
12
 * subject to the following conditions:
13
 *
14
 * The above copyright notice and this permission notice shall be included in all
15
 * copies or substantial portions of the Software.
16
 *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
 */
24

25
#include "config.h"
26

27
/* STDC_HEADERS */
28
#include <stdio.h>
29
#include <stdlib.h>
30

31
#if HAVE_MATH_H
32
# include <math.h>
33
#endif  /* HAVE_MATH_H */
34

35
#if HAVE_MEMORY_H
36
# include <memory.h>
37
#endif  /* HAVE_MEMORY_H */
38

39
#include <sixel.h>
40

41
#include "pixelformat.h"
42

43
#define SIXEL_OKLAB_AB_FLOAT_MIN (-0.5f)
44
#define SIXEL_OKLAB_AB_FLOAT_MAX (0.5f)
45
#define SIXEL_CIELAB_AB_FLOAT_MIN (-1.5f)
46
#define SIXEL_CIELAB_AB_FLOAT_MAX (1.5f)
47
#define SIXEL_CIELAB_L_FLOAT_MIN  (0.0f)
48
#define SIXEL_CIELAB_L_FLOAT_MAX  (1.0f)
49
#define SIXEL_DIN99D_L_FLOAT_MIN  (0.0f)
50
#define SIXEL_DIN99D_L_FLOAT_MAX  (1.0f)
51
#define SIXEL_DIN99D_AB_FLOAT_MIN (-1.0f)
52
#define SIXEL_DIN99D_AB_FLOAT_MAX (1.0f)
53
#define SIXEL_YUV_LUMA_FLOAT_MIN  (0.0f)
54
#define SIXEL_YUV_LUMA_FLOAT_MAX  (1.0f)
55
#define SIXEL_YUV_CHROMA_FLOAT_MIN (-0.5f)
56
#define SIXEL_YUV_CHROMA_FLOAT_MAX (0.5f)
57

58
/*
59
 * Normalize a float32 channel stored in the 0.0-1.0 range and convert
60
 * the value to an 8-bit sample. Out-of-range or NaN inputs are clamped
61
 * to sane defaults so downstream conversions always receive valid bytes.
62
 */
63
static unsigned char
64
sixel_pixelformat_float_to_byte(float value)
×
65
{
66
#if HAVE_MATH_H
67
    if (!isfinite(value)) {
×
68
        value = 0.0f;
69
    }
70
#endif  /* HAVE_MATH_H */
71

72
    if (value <= 0.0f) {
×
73
        return 0;
74
    }
75
    if (value >= 1.0f) {
×
76
        return 255;
77
    }
78

79
    return (unsigned char)(value * 255.0f + 0.5f);
×
80
}
81

82
static unsigned char
83
sixel_pixelformat_oklab_L_to_byte(float value)
×
84
{
85
#if HAVE_MATH_H
86
    if (!isfinite(value)) {
×
87
        value = 0.0f;
88
    }
89
#endif  /* HAVE_MATH_H */
90

91
    if (value <= 0.0f) {
×
92
        return 0;
93
    }
94
    if (value >= 1.0f) {
×
95
        return 255;
96
    }
97

98
    return (unsigned char)(value * 255.0f + 0.5f);
×
99
}
100

101
static unsigned char
102
sixel_pixelformat_oklab_ab_to_byte(float value)
×
103
{
104
    float encoded;
×
105

106
#if HAVE_MATH_H
107
    if (!isfinite(value)) {
×
108
        value = 0.0f;
109
    }
110
#endif  /* HAVE_MATH_H */
111

112
    encoded = value + 0.5f;
×
113
    if (encoded <= 0.0f) {
×
114
        return 0;
115
    }
116
    if (encoded >= 1.0f) {
×
117
        return 255;
118
    }
119

120
    return (unsigned char)(encoded * 255.0f + 0.5f);
×
121
}
122

123
static unsigned char
124
sixel_pixelformat_cielab_L_to_byte(float value)
×
125
{
126
#if HAVE_MATH_H
127
    if (!isfinite(value)) {
×
128
        value = 0.0f;
129
    }
130
#endif  /* HAVE_MATH_H */
131

132
    if (value <= 0.0f) {
×
133
        return 0;
134
    }
135
    if (value >= 1.0f) {
×
136
        return 255;
137
    }
138

139
    return (unsigned char)(value * 255.0f + 0.5f);
×
140
}
141

142
static unsigned char
143
sixel_pixelformat_cielab_ab_to_byte(float value)
×
144
{
145
    float encoded;
×
146

147
#if HAVE_MATH_H
148
    if (!isfinite(value)) {
×
149
        value = 0.0f;
×
150
    }
151
#endif  /* HAVE_MATH_H */
152

153
    encoded = (value / (2.0f * SIXEL_CIELAB_AB_FLOAT_MAX)) + 0.5f;
×
154
    if (encoded <= 0.0f) {
×
155
        return 0;
156
    }
157
    if (encoded >= 1.0f) {
×
158
        return 255;
159
    }
160

161
    return (unsigned char)(encoded * 255.0f + 0.5f);
×
162
}
163

164
static unsigned char
165
sixel_pixelformat_din99d_L_to_byte(float value)
×
166
{
167
#if HAVE_MATH_H
168
    if (!isfinite(value)) {
×
169
        value = 0.0f;
170
    }
171
#endif  /* HAVE_MATH_H */
172

173
    if (value <= 0.0f) {
×
174
        return 0;
175
    }
176
    if (value >= 1.0f) {
×
177
        return 255;
178
    }
179

180
    return (unsigned char)(value * 255.0f + 0.5f);
×
181
}
182

183
static unsigned char
184
sixel_pixelformat_din99d_ab_to_byte(float value)
×
185
{
186
    float encoded;
×
187

188
#if HAVE_MATH_H
189
    if (!isfinite(value)) {
×
190
        value = 0.0f;
×
191
    }
192
#endif  /* HAVE_MATH_H */
193

194
    encoded = (value / (2.0f * SIXEL_DIN99D_AB_FLOAT_MAX)) + 0.5f;
×
195
    if (encoded <= 0.0f) {
×
196
        return 0;
197
    }
198
    if (encoded >= 1.0f) {
×
199
        return 255;
200
    }
201

202
    return (unsigned char)(encoded * 255.0f + 0.5f);
×
203
}
204

205
static float
206
sixel_pixelformat_float_channel_min_internal(int pixelformat,
×
207
                                             int channel)
208
{
209
    (void)channel;
×
210
    if (pixelformat == SIXEL_PIXELFORMAT_OKLABFLOAT32) {
×
211
        if (channel == 0) {
×
212
            return 0.0f;
213
        }
214
        return SIXEL_OKLAB_AB_FLOAT_MIN;
×
215
    }
216
    if (pixelformat == SIXEL_PIXELFORMAT_CIELABFLOAT32) {
×
217
        if (channel == 0) {
×
218
            return SIXEL_CIELAB_L_FLOAT_MIN;
219
        }
220
        return SIXEL_CIELAB_AB_FLOAT_MIN;
×
221
    }
222
    if (pixelformat == SIXEL_PIXELFORMAT_DIN99DFLOAT32) {
×
223
        if (channel == 0) {
×
224
            return SIXEL_DIN99D_L_FLOAT_MIN;
225
        }
226
        return SIXEL_DIN99D_AB_FLOAT_MIN;
×
227
    }
228
    return 0.0f;
229
}
230

231
static float
232
sixel_pixelformat_float_channel_max_internal(int pixelformat,
×
233
                                             int channel)
234
{
235
    (void)channel;
×
236
    if (pixelformat == SIXEL_PIXELFORMAT_OKLABFLOAT32) {
×
237
        if (channel == 0) {
×
238
            return 1.0f;
239
        }
240
        return SIXEL_OKLAB_AB_FLOAT_MAX;
241
    }
242
    if (pixelformat == SIXEL_PIXELFORMAT_CIELABFLOAT32) {
×
243
        if (channel == 0) {
×
244
            return SIXEL_CIELAB_L_FLOAT_MAX;
245
        }
246
        return SIXEL_CIELAB_AB_FLOAT_MAX;
247
    }
248
    if (pixelformat == SIXEL_PIXELFORMAT_DIN99DFLOAT32) {
×
249
        if (channel == 0) {
×
250
            return SIXEL_DIN99D_L_FLOAT_MAX;
251
        }
252
        return SIXEL_DIN99D_AB_FLOAT_MAX;
253
    }
254
    return 1.0f;
255
}
256

257
float
258
sixel_pixelformat_float_channel_clamp(int pixelformat,
×
259
                                      int channel,
260
                                      float value)
261
{
262
    float minimum;
×
263
    float maximum;
×
264

265
#if HAVE_MATH_H
266
    if (!isfinite(value)) {
×
267
        value = 0.0f;
×
268
    }
269
#endif  /* HAVE_MATH_H */
270

271
    minimum = sixel_pixelformat_float_channel_min_internal(pixelformat,
×
272
                                                           channel);
273
    maximum = sixel_pixelformat_float_channel_max_internal(pixelformat,
×
274
                                                           channel);
275
    if (value < minimum) {
×
276
        return minimum;
277
    }
278
    if (value > maximum) {
×
279
        return maximum;
280
    }
281

282
    return value;
283
}
284

285
unsigned char
286
sixel_pixelformat_float_channel_to_byte(int pixelformat,
×
287
                                        int channel,
288
                                        float value)
289
{
290
    float clamped;
×
291

292
    clamped = sixel_pixelformat_float_channel_clamp(pixelformat,
×
293
                                                    channel,
294
                                                    value);
295
    if (pixelformat == SIXEL_PIXELFORMAT_OKLABFLOAT32) {
×
296
        if (channel == 0) {
×
297
            return sixel_pixelformat_oklab_L_to_byte(clamped);
×
298
        }
299
        return sixel_pixelformat_oklab_ab_to_byte(clamped);
×
300
    }
301
    if (pixelformat == SIXEL_PIXELFORMAT_CIELABFLOAT32) {
×
302
        if (channel == 0) {
×
303
            return sixel_pixelformat_cielab_L_to_byte(clamped);
×
304
        }
305
        return sixel_pixelformat_cielab_ab_to_byte(clamped);
×
306
    }
307
    if (pixelformat == SIXEL_PIXELFORMAT_DIN99DFLOAT32) {
×
308
        if (channel == 0) {
×
309
            return sixel_pixelformat_din99d_L_to_byte(clamped);
×
310
        }
311
        return sixel_pixelformat_din99d_ab_to_byte(clamped);
×
312
    }
313

314
    (void)channel;
×
315
    return sixel_pixelformat_float_to_byte(clamped);
×
316
}
317

318
float
319
sixel_pixelformat_byte_to_float(int pixelformat,
×
320
                                int channel,
321
                                unsigned char value)
322
{
323
    float decoded;
×
324

325
    if (pixelformat == SIXEL_PIXELFORMAT_OKLABFLOAT32) {
×
326
        if (channel == 0) {
×
327
            return (float)value / 255.0f;
×
328
        }
329
        decoded = (float)value / 255.0f;
×
330
        return decoded - 0.5f;
×
331
    }
332
    if (pixelformat == SIXEL_PIXELFORMAT_CIELABFLOAT32) {
×
333
        if (channel == 0) {
×
334
            return (float)value / 255.0f;
×
335
        }
336
        decoded = (float)value / 255.0f;
×
337
        decoded = (decoded - 0.5f)
×
338
                 * (2.0f * SIXEL_CIELAB_AB_FLOAT_MAX);
339
        return decoded;
×
340
    }
341
    if (pixelformat == SIXEL_PIXELFORMAT_DIN99DFLOAT32) {
×
342
        if (channel == 0) {
×
343
            return (float)value / 255.0f;
×
344
        }
345
        decoded = (float)value / 255.0f;
×
346
        decoded = (decoded - 0.5f)
×
347
                 * (2.0f * SIXEL_DIN99D_AB_FLOAT_MAX);
348
        return decoded;
×
349
    }
350

351
    (void)channel;
×
352
    return (float)value / 255.0f;
×
353
}
354

355
static void
356
get_rgb(unsigned char const *data,
×
357
        int const pixelformat,
358
        int depth,
359
        unsigned char *r,
360
        unsigned char *g,
361
        unsigned char *b)
362
{
363
    unsigned int pixels = 0;
×
364
#if SWAP_BYTES
365
    unsigned int low;
366
    unsigned int high;
367
#endif
368
    int count = 0;
×
369

370
    if (pixelformat == SIXEL_PIXELFORMAT_RGBFLOAT32
×
371
            || pixelformat == SIXEL_PIXELFORMAT_LINEARRGBFLOAT32) {
×
372
        float const *fpixels = (float const *)(void const *)data;
×
373

374
        *r = sixel_pixelformat_float_to_byte(fpixels[0]);
×
375
        *g = sixel_pixelformat_float_to_byte(fpixels[1]);
×
376
        *b = sixel_pixelformat_float_to_byte(fpixels[2]);
×
377
        return;
×
378
    }
379
    if (pixelformat == SIXEL_PIXELFORMAT_OKLABFLOAT32) {
1!
380
        float const *fpixels = (float const *)(void const *)data;
×
381

382
        *r = sixel_pixelformat_oklab_L_to_byte(fpixels[0]);
×
383
        *g = sixel_pixelformat_oklab_ab_to_byte(fpixels[1]);
×
384
        *b = sixel_pixelformat_oklab_ab_to_byte(fpixels[2]);
×
385
        return;
×
386
    }
387
    if (pixelformat == SIXEL_PIXELFORMAT_CIELABFLOAT32) {
1!
388
        float const *fpixels = (float const *)(void const *)data;
×
389

390
        *r = sixel_pixelformat_cielab_L_to_byte(fpixels[0]);
×
391
        *g = sixel_pixelformat_cielab_ab_to_byte(fpixels[1]);
×
392
        *b = sixel_pixelformat_cielab_ab_to_byte(fpixels[2]);
×
393
        return;
×
394
    }
395
    if (pixelformat == SIXEL_PIXELFORMAT_DIN99DFLOAT32) {
1!
396
        float const *fpixels = (float const *)(void const *)data;
×
397

398
        *r = sixel_pixelformat_din99d_L_to_byte(fpixels[0]);
×
399
        *g = sixel_pixelformat_din99d_ab_to_byte(fpixels[1]);
×
400
        *b = sixel_pixelformat_din99d_ab_to_byte(fpixels[2]);
×
401
        return;
×
402
    }
403
    if (pixelformat == SIXEL_PIXELFORMAT_YUVFLOAT32) {
1!
NEW
404
        float const *fpixels = (float const *)(void const *)data;
×
NEW
405
        double y;
×
NEW
406
        double u;
×
NEW
407
        double v;
×
NEW
408
        double r_value;
×
NEW
409
        double g_value;
×
NEW
410
        double b_value;
×
411

NEW
412
        y = (double)fpixels[0];
×
NEW
413
        u = (double)fpixels[1];
×
NEW
414
        v = (double)fpixels[2];
×
415
#if HAVE_MATH_H
NEW
416
        if (!isfinite(y)) {
×
NEW
417
            y = 0.0;
×
418
        }
NEW
419
        if (!isfinite(u)) {
×
NEW
420
            u = 0.0;
×
421
        }
NEW
422
        if (!isfinite(v)) {
×
NEW
423
            v = 0.0;
×
424
        }
425
#endif  /* HAVE_MATH_H */
NEW
426
        if (y < SIXEL_YUV_LUMA_FLOAT_MIN) {
×
427
            y = SIXEL_YUV_LUMA_FLOAT_MIN;
NEW
428
        } else if (y > SIXEL_YUV_LUMA_FLOAT_MAX) {
×
429
            y = SIXEL_YUV_LUMA_FLOAT_MAX;
430
        }
NEW
431
        if (u < SIXEL_YUV_CHROMA_FLOAT_MIN) {
×
432
            u = SIXEL_YUV_CHROMA_FLOAT_MIN;
NEW
433
        } else if (u > SIXEL_YUV_CHROMA_FLOAT_MAX) {
×
434
            u = SIXEL_YUV_CHROMA_FLOAT_MAX;
435
        }
NEW
436
        if (v < SIXEL_YUV_CHROMA_FLOAT_MIN) {
×
437
            v = SIXEL_YUV_CHROMA_FLOAT_MIN;
NEW
438
        } else if (v > SIXEL_YUV_CHROMA_FLOAT_MAX) {
×
439
            v = SIXEL_YUV_CHROMA_FLOAT_MAX;
440
        }
441

NEW
442
        r_value = y + 1.13983 * v;
×
NEW
443
        g_value = y - 0.39465 * u - 0.58060 * v;
×
NEW
444
        b_value = y + 2.03211 * u;
×
445

NEW
446
        *r = sixel_pixelformat_float_to_byte((float)r_value);
×
NEW
447
        *g = sixel_pixelformat_float_to_byte((float)g_value);
×
NEW
448
        *b = sixel_pixelformat_float_to_byte((float)b_value);
×
NEW
449
        return;
×
450
    }
451

452
    while (count < depth) {
×
453
        pixels = *(data + count) | (pixels << 8);
×
454
        count++;
×
455
    }
456

457
    /* TODO: we should swap bytes (only necessary on LSByte first hardware?) */
458
#if SWAP_BYTES
459
    if (depth == 2) {
460
        low    = pixels & 0xff;
461
        high   = (pixels >> 8) & 0xff;
462
        pixels = (low << 8) | high;
463
    }
464
#endif
465

466
    switch (pixelformat) {
×
467
    case SIXEL_PIXELFORMAT_RGB555:
×
468
        *r = ((pixels >> 10) & 0x1f) << 3;
×
469
        *g = ((pixels >>  5) & 0x1f) << 3;
×
470
        *b = ((pixels >>  0) & 0x1f) << 3;
×
471
        break;
×
472
    case SIXEL_PIXELFORMAT_RGB565:
×
473
        *r = ((pixels >> 11) & 0x1f) << 3;
×
474
        *g = ((pixels >>  5) & 0x3f) << 2;
×
475
        *b = ((pixels >>  0) & 0x1f) << 3;
×
476
        break;
×
477
    case SIXEL_PIXELFORMAT_RGB888:
×
478
        *r = (pixels >> 16) & 0xff;
×
479
        *g = (pixels >>  8) & 0xff;
×
480
        *b = (pixels >>  0) & 0xff;
×
481
        break;
×
482
    case SIXEL_PIXELFORMAT_BGR555:
×
483
        *r = ((pixels >>  0) & 0x1f) << 3;
×
484
        *g = ((pixels >>  5) & 0x1f) << 3;
×
485
        *b = ((pixels >> 10) & 0x1f) << 3;
×
486
        break;
×
487
    case SIXEL_PIXELFORMAT_BGR565:
×
488
        *r = ((pixels >>  0) & 0x1f) << 3;
×
489
        *g = ((pixels >>  5) & 0x3f) << 2;
×
490
        *b = ((pixels >> 11) & 0x1f) << 3;
×
491
        break;
×
492
    case SIXEL_PIXELFORMAT_BGR888:
×
493
        *r = (pixels >>  0) & 0xff;
×
494
        *g = (pixels >>  8) & 0xff;
×
495
        *b = (pixels >> 16) & 0xff;
×
496
        break;
×
497
    case SIXEL_PIXELFORMAT_RGBA8888:
×
498
        *r = (pixels >> 24) & 0xff;
×
499
        *g = (pixels >> 16) & 0xff;
×
500
        *b = (pixels >>  8) & 0xff;
×
501
        break;
×
502
    case SIXEL_PIXELFORMAT_ARGB8888:
×
503
        *r = (pixels >> 16) & 0xff;
×
504
        *g = (pixels >>  8) & 0xff;
×
505
        *b = (pixels >>  0) & 0xff;
×
506
        break;
×
507
    case SIXEL_PIXELFORMAT_BGRA8888:
×
508
        *r = (pixels >>  8) & 0xff;
×
509
        *g = (pixels >> 16) & 0xff;
×
510
        *b = (pixels >> 24) & 0xff;
×
511
        break;
×
512
    case SIXEL_PIXELFORMAT_ABGR8888:
×
513
        *r = (pixels >>  0) & 0xff;
×
514
        *g = (pixels >>  8) & 0xff;
×
515
        *b = (pixels >> 16) & 0xff;
×
516
        break;
×
517
    case SIXEL_PIXELFORMAT_GA88:
×
518
        *r = *g = *b = (pixels >> 8) & 0xff;
×
519
        break;
×
520
    case SIXEL_PIXELFORMAT_G8:
×
521
    case SIXEL_PIXELFORMAT_AG88:
522
        *r = *g = *b = pixels & 0xff;
×
523
        break;
×
524
    default:
×
525
        *r = *g = *b = 0;
×
526
        break;
×
527
    }
528
}
1!
529

530

531
SIXELAPI int
532
sixel_helper_compute_depth(int pixelformat)
2,025✔
533
{
534
    int depth = (-1);  /* unknown */
2,025✔
535

536
    switch (pixelformat) {
2,025!
537
    case SIXEL_PIXELFORMAT_ARGB8888:
×
538
    case SIXEL_PIXELFORMAT_RGBA8888:
539
    case SIXEL_PIXELFORMAT_ABGR8888:
540
    case SIXEL_PIXELFORMAT_BGRA8888:
541
        depth = 4;
×
542
        break;
×
543
    case SIXEL_PIXELFORMAT_RGB888:
1,341✔
544
    case SIXEL_PIXELFORMAT_BGR888:
545
        depth = 3;
1,341✔
546
        break;
1,341✔
547
    case SIXEL_PIXELFORMAT_RGB555:
×
548
    case SIXEL_PIXELFORMAT_RGB565:
549
    case SIXEL_PIXELFORMAT_BGR555:
550
    case SIXEL_PIXELFORMAT_BGR565:
551
    case SIXEL_PIXELFORMAT_AG88:
552
    case SIXEL_PIXELFORMAT_GA88:
553
        depth = 2;
×
554
        break;
×
555
    case SIXEL_PIXELFORMAT_G1:
228✔
556
    case SIXEL_PIXELFORMAT_G2:
557
    case SIXEL_PIXELFORMAT_G4:
558
    case SIXEL_PIXELFORMAT_G8:
559
    case SIXEL_PIXELFORMAT_PAL1:
560
    case SIXEL_PIXELFORMAT_PAL2:
561
    case SIXEL_PIXELFORMAT_PAL4:
562
    case SIXEL_PIXELFORMAT_PAL8:
563
        depth = 1;
228✔
564
        break;
228✔
565
    case SIXEL_PIXELFORMAT_RGBFLOAT32:
456✔
566
    case SIXEL_PIXELFORMAT_LINEARRGBFLOAT32:
567
    case SIXEL_PIXELFORMAT_OKLABFLOAT32:
568
    case SIXEL_PIXELFORMAT_CIELABFLOAT32:
569
    case SIXEL_PIXELFORMAT_DIN99DFLOAT32:
570
    case SIXEL_PIXELFORMAT_YUVFLOAT32:
571
        depth = (int)(sizeof(float) * 3);
456✔
572
        break;
456✔
573
    default:
574
        break;
575
    }
576

577
    return depth;
2,025✔
578
}
579

580

581
static void
582
expand_rgb(unsigned char *dst,
×
583
           unsigned char const *src,
584
           int width, int height,
585
           int pixelformat, int depth)
586
{
587
    int x;
×
588
    int y;
×
589
    int dst_offset;
×
590
    int src_offset;
×
591
    unsigned char r, g, b;
×
592

593
    for (y = 0; y < height; y++) {
×
594
        for (x = 0; x < width; x++) {
×
595
            src_offset = depth * (y * width + x);
×
596
            dst_offset = 3 * (y * width + x);
×
597
            get_rgb(src + src_offset, pixelformat, depth, &r, &g, &b);
×
598

599
            *(dst + dst_offset + 0) = r;
×
600
            *(dst + dst_offset + 1) = g;
×
601
            *(dst + dst_offset + 2) = b;
×
602
        }
603
    }
604
}
×
605

606

607
static SIXELSTATUS
608
expand_palette(unsigned char *dst, unsigned char const *src,
×
609
               int width, int height, int const pixelformat)
610
{
611
    SIXELSTATUS status = SIXEL_FALSE;
×
612
    int x;
×
613
    int y;
×
614
    int i;
×
615
    int bpp;  /* bit per plane */
×
616

617
    switch (pixelformat) {
×
618
    case SIXEL_PIXELFORMAT_PAL1:
619
    case SIXEL_PIXELFORMAT_G1:
620
        bpp = 1;
621
        break;
622
    case SIXEL_PIXELFORMAT_PAL2:
×
623
    case SIXEL_PIXELFORMAT_G2:
624
        bpp = 2;
×
625
        break;
×
626
    case SIXEL_PIXELFORMAT_PAL4:
×
627
    case SIXEL_PIXELFORMAT_G4:
628
        bpp = 4;
×
629
        break;
×
630
    case SIXEL_PIXELFORMAT_PAL8:
631
    case SIXEL_PIXELFORMAT_G8:
632
        for (i = 0; i < width * height; ++i, ++src) {
×
633
            *dst++ = *src;
×
634
        }
635
        status = SIXEL_OK;
×
636
        goto end;
×
637
    default:
×
638
        status = SIXEL_BAD_ARGUMENT;
×
639
        sixel_helper_set_additional_message(
×
640
            "expand_palette: invalid pixelformat.");
641
        goto end;
×
642
    }
643

644
#if HAVE_DEBUG
645
    fprintf(stderr, "expanding PAL%d to PAL8...\n", bpp);
×
646
#endif
647

648
    for (y = 0; y < height; ++y) {
×
649
        for (x = 0; x < width * bpp / 8; ++x) {
×
650
            for (i = 0; i < 8 / bpp; ++i) {
×
651
                *dst++ = *src >> (8 / bpp - 1 - i) * bpp & ((1 << bpp) - 1);
×
652
            }
653
            src++;
×
654
        }
655
        x = width - x * 8 / bpp;
×
656
        if (x > 0) {
×
657
            for (i = 0; i < x; ++i) {
×
658
                *dst++ = *src >> (8 - (i + 1) * bpp) & ((1 << bpp) - 1);
×
659
            }
660
            src++;
×
661
        }
662
    }
663

664
    status = SIXEL_OK;
665

666
end:
×
667
    return status;
×
668
}
669

670

671
SIXELAPI SIXELSTATUS
672
sixel_helper_normalize_pixelformat(
×
673
    unsigned char       /* out */ *dst,             /* destination buffer */
674
    int                 /* out */ *dst_pixelformat, /* converted pixelformat */
675
    unsigned char const /* in */  *src,             /* source pixels */
676
    int                 /* in */  src_pixelformat,  /* format of source image */
677
    int                 /* in */  width,            /* width of source image */
678
    int                 /* in */  height)           /* height of source image */
679
{
680
    SIXELSTATUS status = SIXEL_FALSE;
×
681
    int depth;
×
682

683
    switch (src_pixelformat) {
×
684
    case SIXEL_PIXELFORMAT_G8:
×
685
        expand_rgb(dst, src, width, height, src_pixelformat, 1);
×
686
        *dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
687
        break;
×
688
    case SIXEL_PIXELFORMAT_RGB565:
×
689
    case SIXEL_PIXELFORMAT_RGB555:
690
    case SIXEL_PIXELFORMAT_BGR565:
691
    case SIXEL_PIXELFORMAT_BGR555:
692
    case SIXEL_PIXELFORMAT_GA88:
693
    case SIXEL_PIXELFORMAT_AG88:
694
        expand_rgb(dst, src, width, height, src_pixelformat, 2);
×
695
        *dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
696
        break;
×
697
    case SIXEL_PIXELFORMAT_RGB888:
×
698
    case SIXEL_PIXELFORMAT_BGR888:
699
        expand_rgb(dst, src, width, height, src_pixelformat, 3);
×
700
        *dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
701
        break;
×
702
    case SIXEL_PIXELFORMAT_RGBFLOAT32:
×
703
    case SIXEL_PIXELFORMAT_LINEARRGBFLOAT32:
704
    case SIXEL_PIXELFORMAT_OKLABFLOAT32:
705
    case SIXEL_PIXELFORMAT_CIELABFLOAT32:
706
    case SIXEL_PIXELFORMAT_DIN99DFLOAT32:
707
    case SIXEL_PIXELFORMAT_YUVFLOAT32:
708
        depth = sixel_helper_compute_depth(src_pixelformat);
×
709
        if (depth <= 0) {
×
710
            status = SIXEL_BAD_ARGUMENT;
×
711
            goto end;
×
712
        }
713
        expand_rgb(dst, src, width, height, src_pixelformat, depth);
×
714
        *dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
715
        break;
×
716
    case SIXEL_PIXELFORMAT_RGBA8888:
×
717
    case SIXEL_PIXELFORMAT_ARGB8888:
718
    case SIXEL_PIXELFORMAT_BGRA8888:
719
    case SIXEL_PIXELFORMAT_ABGR8888:
720
        expand_rgb(dst, src, width, height, src_pixelformat, 4);
×
721
        *dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
722
        break;
×
723
    case SIXEL_PIXELFORMAT_PAL1:
×
724
    case SIXEL_PIXELFORMAT_PAL2:
725
    case SIXEL_PIXELFORMAT_PAL4:
726
        *dst_pixelformat = SIXEL_PIXELFORMAT_PAL8;
×
727
        status = expand_palette(dst, src, width, height, src_pixelformat);
×
728
        if (SIXEL_FAILED(status)) {
×
729
            goto end;
×
730
        }
731
        break;
732
    case SIXEL_PIXELFORMAT_G1:
×
733
    case SIXEL_PIXELFORMAT_G2:
734
    case SIXEL_PIXELFORMAT_G4:
735
        *dst_pixelformat = SIXEL_PIXELFORMAT_G8;
×
736
        status = expand_palette(dst, src, width, height, src_pixelformat);
×
737
        if (SIXEL_FAILED(status)) {
×
738
            goto end;
×
739
        }
740
        break;
741
    case SIXEL_PIXELFORMAT_PAL8:
×
742
        memcpy(dst, src, (size_t)(width * height));
×
743
        *dst_pixelformat = src_pixelformat;
×
744
        break;
×
745
    default:
×
746
        status = SIXEL_BAD_ARGUMENT;
×
747
        goto end;
×
748
    }
749

750
    status = SIXEL_OK;
751

752
end:
×
753
    return status;
×
754
}
755

756

757
#if HAVE_TESTS
758
static int
759
test1(void)
×
760
{
761
    unsigned char dst[3];
×
762
    int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
763
    int src_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
764
    unsigned char src[] = { 0x46, 0xf3, 0xe5 };
×
765
    int ret = 0;
×
766

767
    int nret = EXIT_FAILURE;
×
768

769
    ret = sixel_helper_normalize_pixelformat(dst,
×
770
                                             &dst_pixelformat,
771
                                             src,
772
                                             src_pixelformat,
773
                                             1,
774
                                             1);
775
    if (ret != 0) {
×
776
        goto error;
×
777
    }
778
    if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
×
779
        goto error;
×
780
    }
781
    if ((dst[0] << 16 | dst[1] << 8 | dst[2]) != (src[0] << 16 | src[1] << 8 | src[2])) {
×
782
        goto error;
×
783
    }
784
    return EXIT_SUCCESS;
785

786
error:
×
787
    perror("test1");
×
788
    return nret;
×
789
}
790

791

792
static int
793
test2(void)
×
794
{
795
    unsigned char dst[3];
×
796
    int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
797
    int src_pixelformat = SIXEL_PIXELFORMAT_RGB555;
×
798
    unsigned char src[] = { 0x47, 0x9c };
×
799
    int ret = 0;
×
800

801
    int nret = EXIT_FAILURE;
×
802

803
    ret = sixel_helper_normalize_pixelformat(dst,
×
804
                                             &dst_pixelformat,
805
                                             src,
806
                                             src_pixelformat,
807
                                             1,
808
                                             1);
809
    if (ret != 0) {
×
810
        goto error;
×
811
    }
812
    if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
×
813
        goto error;
×
814
    }
815
    if ((dst[0] >> 3 << 10 | dst[1] >> 3 << 5 | dst[2] >> 3) != (src[0] << 8 | src[1])) {
×
816
        goto error;
×
817
    }
818
    return EXIT_SUCCESS;
819

820
error:
×
821
    perror("test2");
×
822
    return nret;
×
823
}
824

825

826
static int
827
test3(void)
×
828
{
829
    unsigned char dst[3];
×
830
    int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
831
    int src_pixelformat = SIXEL_PIXELFORMAT_RGB565;
×
832
    unsigned char src[] = { 0x47, 0x9c };
×
833
    int ret = 0;
×
834

835
    int nret = EXIT_FAILURE;
×
836

837
    ret = sixel_helper_normalize_pixelformat(dst,
×
838
                                             &dst_pixelformat,
839
                                             src,
840
                                             src_pixelformat,
841
                                             1,
842
                                             1);
843
    if (ret != 0) {
×
844
        goto error;
×
845
    }
846
    if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
×
847
        goto error;
×
848
    }
849
    if ((dst[0] >> 3 << 11 | dst[1] >> 2 << 5 | dst[2] >> 3) != (src[0] << 8 | src[1])) {
×
850
        goto error;
×
851
    }
852
    return EXIT_SUCCESS;
853

854
error:
×
855
    perror("test3");
×
856
    return nret;
×
857
}
858

859

860
static int
861
test4(void)
×
862
{
863
    unsigned char dst[3];
×
864
    int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
865
    int src_pixelformat = SIXEL_PIXELFORMAT_BGR888;
×
866
    unsigned char src[] = { 0x46, 0xf3, 0xe5 };
×
867
    int ret = 0;
×
868

869
    int nret = EXIT_FAILURE;
×
870

871
    ret = sixel_helper_normalize_pixelformat(dst,
×
872
                                             &dst_pixelformat,
873
                                             src,
874
                                             src_pixelformat,
875
                                             1,
876
                                             1);
877
    if (ret != 0) {
×
878
        goto error;
×
879
    }
880
    if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
×
881
        goto error;
×
882
    }
883
    if ((dst[2] << 16 | dst[1] << 8 | dst[0]) != (src[0] << 16 | src[1] << 8 | src[2])) {
×
884
        goto error;
×
885
    }
886
    return EXIT_SUCCESS;
887

888
error:
×
889
    perror("test4");
×
890
    return nret;
×
891
}
892

893

894
static int
895
test5(void)
×
896
{
897
    unsigned char dst[3];
×
898
    int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
899
    int src_pixelformat = SIXEL_PIXELFORMAT_BGR555;
×
900
    unsigned char src[] = { 0x23, 0xc8 };
×
901
    int ret = 0;
×
902

903
    int nret = EXIT_FAILURE;
×
904

905
    ret = sixel_helper_normalize_pixelformat(dst,
×
906
                                             &dst_pixelformat,
907
                                             src,
908
                                             src_pixelformat,
909
                                             1,
910
                                             1);
911
    if (ret != 0) {
×
912
        goto error;
×
913
    }
914
    if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
×
915
        goto error;
×
916
    }
917
    if ((dst[2] >> 3 << 10 | dst[1] >> 3 << 5 | dst[0] >> 3) != (src[0] << 8 | src[1])) {
×
918
        goto error;
×
919
    }
920
    return EXIT_SUCCESS;
921

922
error:
×
923
    perror("test5");
×
924
    return nret;
×
925
}
926

927

928
static int
929
test6(void)
×
930
{
931
    unsigned char dst[3];
×
932
    int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
933
    int src_pixelformat = SIXEL_PIXELFORMAT_BGR565;
×
934
    unsigned char src[] = { 0x47, 0x88 };
×
935
    int ret = 0;
×
936

937
    int nret = EXIT_FAILURE;
×
938

939
    ret = sixel_helper_normalize_pixelformat(dst,
×
940
                                             &dst_pixelformat,
941
                                             src,
942
                                             src_pixelformat,
943
                                             1,
944
                                             1);
945
    if (ret != 0) {
×
946
        goto error;
×
947
    }
948
    if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
×
949
        goto error;
×
950
    }
951
    if ((dst[2] >> 3 << 11 | dst[1] >> 2 << 5 | dst[0] >> 3) != (src[0] << 8 | src[1])) {
×
952
        goto error;
×
953
    }
954
    return EXIT_SUCCESS;
955

956
error:
×
957
    perror("test6");
×
958
    return nret;
×
959
}
960

961

962
static int
963
test7(void)
×
964
{
965
    unsigned char dst[3];
×
966
    int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
967
    int src_pixelformat = SIXEL_PIXELFORMAT_AG88;
×
968
    unsigned char src[] = { 0x47, 0x88 };
×
969
    int ret = 0;
×
970

971
    int nret = EXIT_FAILURE;
×
972

973
    ret = sixel_helper_normalize_pixelformat(dst,
×
974
                                             &dst_pixelformat,
975
                                             src,
976
                                             src_pixelformat,
977
                                             1,
978
                                             1);
979
    if (ret != 0) {
×
980
        goto error;
×
981
    }
982
    if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
×
983
        goto error;
×
984
    }
985
    if (dst[0] != src[1]) {
×
986
        goto error;
×
987
    }
988
    return EXIT_SUCCESS;
989

990
error:
×
991
    perror("test7");
×
992
    return nret;
×
993
}
994

995

996
static int
997
test8(void)
×
998
{
999
    unsigned char dst[3];
×
1000
    int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
1001
    int src_pixelformat = SIXEL_PIXELFORMAT_GA88;
×
1002
    unsigned char src[] = { 0x47, 0x88 };
×
1003
    int ret = 0;
×
1004

1005
    int nret = EXIT_FAILURE;
×
1006

1007
    ret = sixel_helper_normalize_pixelformat(dst,
×
1008
                                             &dst_pixelformat,
1009
                                             src,
1010
                                             src_pixelformat,
1011
                                             1,
1012
                                             1);
1013
    if (ret != 0) {
×
1014
        goto error;
×
1015
    }
1016
    if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
×
1017
        goto error;
×
1018
    }
1019
    if (dst[0] != src[0]) {
×
1020
        goto error;
×
1021
    }
1022
    return EXIT_SUCCESS;
1023

1024
error:
×
1025
    perror("test8");
×
1026
    return nret;
×
1027
}
1028

1029

1030
static int
1031
test9(void)
×
1032
{
1033
    unsigned char dst[3];
×
1034
    int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
1035
    int src_pixelformat = SIXEL_PIXELFORMAT_RGBA8888;
×
1036
    unsigned char src[] = { 0x46, 0xf3, 0xe5, 0xf0 };
×
1037
    int ret = 0;
×
1038

1039
    int nret = EXIT_FAILURE;
×
1040

1041
    ret = sixel_helper_normalize_pixelformat(dst,
×
1042
                                             &dst_pixelformat,
1043
                                             src,
1044
                                             src_pixelformat,
1045
                                             1,
1046
                                             1);
1047
    if (ret != 0) {
×
1048
        goto error;
×
1049
    }
1050
    if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
×
1051
        goto error;
×
1052
    }
1053
    if (dst[0] != src[0]) {
×
1054
        goto error;
×
1055
    }
1056
    if (dst[1] != src[1]) {
×
1057
        goto error;
×
1058
    }
1059
    if (dst[2] != src[2]) {
×
1060
        goto error;
×
1061
    }
1062
    return EXIT_SUCCESS;
1063

1064
error:
×
1065
    perror("test8");
×
1066
    return nret;
×
1067
}
1068

1069

1070
static int
1071
test10(void)
×
1072
{
1073
    unsigned char dst[3];
×
1074
    int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
1075
    int src_pixelformat = SIXEL_PIXELFORMAT_ARGB8888;
×
1076
    unsigned char src[] = { 0x46, 0xf3, 0xe5, 0xf0 };
×
1077
    int ret = 0;
×
1078

1079
    int nret = EXIT_FAILURE;
×
1080

1081
    ret = sixel_helper_normalize_pixelformat(dst,
×
1082
                                             &dst_pixelformat,
1083
                                             src,
1084
                                             src_pixelformat,
1085
                                             1,
1086
                                             1);
1087
    if (ret != 0) {
×
1088
        goto error;
×
1089
    }
1090
    if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
×
1091
        goto error;
×
1092
    }
1093
    if (dst[0] != src[1]) {
×
1094
        goto error;
×
1095
    }
1096
    if (dst[1] != src[2]) {
×
1097
        goto error;
×
1098
    }
1099
    if (dst[2] != src[3]) {
×
1100
        goto error;
×
1101
    }
1102
    return EXIT_SUCCESS;
1103

1104
error:
×
1105
    perror("test8");
×
1106
    return nret;
×
1107
}
1108

1109

1110
static int
1111
test11(void)
×
1112
{
1113
    unsigned char dst[3];
×
1114
    int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
1115
    int src_pixelformat = SIXEL_PIXELFORMAT_RGBFLOAT32;
×
1116
    float srcf[] = { 0.0f, 0.5f, 1.0f };
×
1117
    unsigned char const *src = (unsigned char const *)srcf;
×
1118
    int ret = 0;
×
1119
    int depth;
×
1120

1121
    int nret = EXIT_FAILURE;
×
1122

1123
    ret = sixel_helper_normalize_pixelformat(dst,
×
1124
                                             &dst_pixelformat,
1125
                                             src,
1126
                                             src_pixelformat,
1127
                                             1,
1128
                                             1);
1129
    if (ret != 0) {
×
1130
        goto error;
×
1131
    }
1132
    if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
×
1133
        goto error;
×
1134
    }
1135
    if (dst[0] != 0 || dst[1] != 128 || dst[2] != 255) {
×
1136
        goto error;
×
1137
    }
1138
    depth = sixel_helper_compute_depth(src_pixelformat);
×
1139
    if (depth != (int)(sizeof(float) * 3)) {
×
1140
        goto error;
×
1141
    }
1142
    return EXIT_SUCCESS;
1143

1144
error:
×
1145
    perror("test11");
×
1146
    return nret;
×
1147
}
1148

1149

1150
SIXELAPI int
1151
sixel_pixelformat_tests_main(void)
×
1152
{
1153
    int nret = EXIT_FAILURE;
×
1154
    size_t i;
×
1155
    typedef int (* testcase)(void);
×
1156

1157
    static testcase const testcases[] = {
×
1158
        test1,
1159
        test2,
1160
        test3,
1161
        test4,
1162
        test5,
1163
        test6,
1164
        test7,
1165
        test8,
1166
        test9,
1167
        test10,
1168
        test11,
1169
    };
1170

1171
    for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) {
×
1172
        nret = testcases[i]();
×
1173
        if (nret != EXIT_SUCCESS) {
×
1174
            goto error;
×
1175
        }
1176
    }
1177

1178
    nret = EXIT_SUCCESS;
1179

1180
error:
×
1181
    return nret;
×
1182
}
1183
#endif  /* HAVE_TESTS */
1184

1185
/* emacs Local Variables:      */
1186
/* emacs mode: c               */
1187
/* emacs tab-width: 4          */
1188
/* emacs indent-tabs-mode: nil */
1189
/* emacs c-basic-offset: 4     */
1190
/* emacs End:                  */
1191
/* vim: set expandtab ts=4 sts=4 sw=4 : */
1192
/* 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