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

saitoha / libsixel / 20120680111

11 Dec 2025 03:12AM UTC coverage: 43.656% (-0.2%) from 43.832%
20120680111

push

github

saitoha
colorspace: add YUV colorspace pipeline support

11824 of 41570 branches covered (28.44%)

3 of 124 new or added lines in 7 files covered. (2.42%)

9 existing lines in 4 files now uncovered.

16289 of 37312 relevant lines covered (43.66%)

3242315.89 hits per line

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

2.32
/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_Y_FLOAT_MIN     (0.0f)
54
#define SIXEL_YUV_Y_FLOAT_MAX     (1.0f)
55
#define SIXEL_YUV_U_FLOAT_MIN     (-0.436f)
56
#define SIXEL_YUV_U_FLOAT_MAX     (0.436f)
57
#define SIXEL_YUV_V_FLOAT_MIN     (-0.615f)
58
#define SIXEL_YUV_V_FLOAT_MAX     (0.615f)
59

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

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

81
    return (unsigned char)(value * 255.0f + 0.5f);
×
82
}
83

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

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

100
    return (unsigned char)(value * 255.0f + 0.5f);
×
101
}
102

103
static unsigned char
104
sixel_pixelformat_oklab_ab_to_byte(float value)
×
105
{
106
    float encoded;
×
107

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

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

122
    return (unsigned char)(encoded * 255.0f + 0.5f);
×
123
}
124

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

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

141
    return (unsigned char)(value * 255.0f + 0.5f);
×
142
}
143

144
static unsigned char
145
sixel_pixelformat_cielab_ab_to_byte(float value)
×
146
{
147
    float encoded;
×
148

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

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

163
    return (unsigned char)(encoded * 255.0f + 0.5f);
×
164
}
165

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

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

182
    return (unsigned char)(value * 255.0f + 0.5f);
×
183
}
184

185
static unsigned char
186
sixel_pixelformat_din99d_ab_to_byte(float value)
×
187
{
188
    float encoded;
×
189

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

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

204
    return (unsigned char)(encoded * 255.0f + 0.5f);
×
205
}
206

207
static unsigned char
NEW
208
sixel_pixelformat_yuv_chroma_to_byte(float value, float range)
×
209
{
NEW
210
    float encoded;
×
211

212
#if HAVE_MATH_H
NEW
213
    if (!isfinite(value)) {
×
NEW
214
        value = 0.0f;
×
215
    }
216
#endif  /* HAVE_MATH_H */
217

NEW
218
    encoded = (value / (2.0f * range)) + 0.5f;
×
NEW
219
    if (encoded <= 0.0f) {
×
220
        return 0;
221
    }
NEW
222
    if (encoded >= 1.0f) {
×
223
        return 255;
224
    }
225

NEW
226
    return (unsigned char)(encoded * 255.0f + 0.5f);
×
227
}
228

229
static float
NEW
230
sixel_pixelformat_yuv_chroma_from_byte(unsigned char value, float range)
×
231
{
NEW
232
    float encoded;
×
233

NEW
234
    encoded = (float)value / 255.0f;
×
NEW
235
    return (encoded - 0.5f) * (2.0f * range);
×
236
}
237

238
static float
239
sixel_pixelformat_float_channel_min_internal(int pixelformat,
×
240
                                             int channel)
241
{
242
    (void)channel;
×
243
    if (pixelformat == SIXEL_PIXELFORMAT_OKLABFLOAT32) {
×
244
        if (channel == 0) {
×
245
            return 0.0f;
246
        }
247
        return SIXEL_OKLAB_AB_FLOAT_MIN;
×
248
    }
249
    if (pixelformat == SIXEL_PIXELFORMAT_CIELABFLOAT32) {
×
250
        if (channel == 0) {
×
251
            return SIXEL_CIELAB_L_FLOAT_MIN;
252
        }
253
        return SIXEL_CIELAB_AB_FLOAT_MIN;
×
254
    }
255
    if (pixelformat == SIXEL_PIXELFORMAT_DIN99DFLOAT32) {
×
256
        if (channel == 0) {
×
257
            return SIXEL_DIN99D_L_FLOAT_MIN;
258
        }
259
        return SIXEL_DIN99D_AB_FLOAT_MIN;
×
260
    }
NEW
261
    if (pixelformat == SIXEL_PIXELFORMAT_YUVFLOAT32) {
×
NEW
262
        if (channel == 0) {
×
263
            return SIXEL_YUV_Y_FLOAT_MIN;
264
        }
NEW
265
        if (channel == 1) {
×
266
            return SIXEL_YUV_U_FLOAT_MIN;
267
        }
NEW
268
        return SIXEL_YUV_V_FLOAT_MIN;
×
269
    }
270
    return 0.0f;
271
}
272

273
static float
274
sixel_pixelformat_float_channel_max_internal(int pixelformat,
×
275
                                             int channel)
276
{
277
    (void)channel;
×
278
    if (pixelformat == SIXEL_PIXELFORMAT_OKLABFLOAT32) {
×
279
        if (channel == 0) {
×
280
            return 1.0f;
281
        }
UNCOV
282
        return SIXEL_OKLAB_AB_FLOAT_MAX;
×
283
    }
284
    if (pixelformat == SIXEL_PIXELFORMAT_CIELABFLOAT32) {
×
285
        if (channel == 0) {
×
286
            return SIXEL_CIELAB_L_FLOAT_MAX;
287
        }
UNCOV
288
        return SIXEL_CIELAB_AB_FLOAT_MAX;
×
289
    }
UNCOV
290
    if (pixelformat == SIXEL_PIXELFORMAT_DIN99DFLOAT32) {
×
291
        if (channel == 0) {
×
292
            return SIXEL_DIN99D_L_FLOAT_MAX;
293
        }
294
        return SIXEL_DIN99D_AB_FLOAT_MAX;
295
    }
NEW
296
    if (pixelformat == SIXEL_PIXELFORMAT_YUVFLOAT32) {
×
NEW
297
        if (channel == 0) {
×
298
            return SIXEL_YUV_Y_FLOAT_MAX;
299
        }
NEW
300
        if (channel == 1) {
×
301
            return SIXEL_YUV_U_FLOAT_MAX;
302
        }
NEW
303
        return SIXEL_YUV_V_FLOAT_MAX;
×
304
    }
305
    return 1.0f;
306
}
307

308
float
309
sixel_pixelformat_float_channel_clamp(int pixelformat,
×
310
                                      int channel,
311
                                      float value)
312
{
313
    float minimum;
×
314
    float maximum;
×
315

316
#if HAVE_MATH_H
317
    if (!isfinite(value)) {
×
318
        value = 0.0f;
×
319
    }
320
#endif  /* HAVE_MATH_H */
321

322
    minimum = sixel_pixelformat_float_channel_min_internal(pixelformat,
×
323
                                                           channel);
324
    maximum = sixel_pixelformat_float_channel_max_internal(pixelformat,
×
325
                                                           channel);
326
    if (value < minimum) {
×
327
        return minimum;
328
    }
329
    if (value > maximum) {
×
330
        return maximum;
331
    }
332

333
    return value;
334
}
335

336
unsigned char
337
sixel_pixelformat_float_channel_to_byte(int pixelformat,
×
338
                                        int channel,
339
                                        float value)
340
{
341
    float clamped;
×
342

343
    clamped = sixel_pixelformat_float_channel_clamp(pixelformat,
×
344
                                                    channel,
345
                                                    value);
346
    if (pixelformat == SIXEL_PIXELFORMAT_OKLABFLOAT32) {
×
347
        if (channel == 0) {
×
348
            return sixel_pixelformat_oklab_L_to_byte(clamped);
×
349
        }
350
        return sixel_pixelformat_oklab_ab_to_byte(clamped);
×
351
    }
352
    if (pixelformat == SIXEL_PIXELFORMAT_CIELABFLOAT32) {
×
353
        if (channel == 0) {
×
354
            return sixel_pixelformat_cielab_L_to_byte(clamped);
×
355
        }
356
        return sixel_pixelformat_cielab_ab_to_byte(clamped);
×
357
    }
358
    if (pixelformat == SIXEL_PIXELFORMAT_DIN99DFLOAT32) {
×
359
        if (channel == 0) {
×
360
            return sixel_pixelformat_din99d_L_to_byte(clamped);
×
361
        }
362
        return sixel_pixelformat_din99d_ab_to_byte(clamped);
×
363
    }
NEW
364
    if (pixelformat == SIXEL_PIXELFORMAT_YUVFLOAT32) {
×
NEW
365
        if (channel == 0) {
×
NEW
366
            return sixel_pixelformat_float_to_byte(clamped);
×
367
        }
NEW
368
        if (channel == 1) {
×
NEW
369
            return sixel_pixelformat_yuv_chroma_to_byte(
×
370
                clamped,
371
                SIXEL_YUV_U_FLOAT_MAX);
372
        }
NEW
373
        return sixel_pixelformat_yuv_chroma_to_byte(clamped,
×
374
                                                    SIXEL_YUV_V_FLOAT_MAX);
375
    }
376

377
    (void)channel;
×
378
    return sixel_pixelformat_float_to_byte(clamped);
×
379
}
380

381
float
382
sixel_pixelformat_byte_to_float(int pixelformat,
×
383
                                int channel,
384
                                unsigned char value)
385
{
386
    float decoded;
×
387

388
    if (pixelformat == SIXEL_PIXELFORMAT_OKLABFLOAT32) {
×
389
        if (channel == 0) {
×
390
            return (float)value / 255.0f;
×
391
        }
392
        decoded = (float)value / 255.0f;
×
393
        return decoded - 0.5f;
×
394
    }
395
    if (pixelformat == SIXEL_PIXELFORMAT_CIELABFLOAT32) {
×
396
        if (channel == 0) {
×
397
            return (float)value / 255.0f;
×
398
        }
399
        decoded = (float)value / 255.0f;
×
400
        decoded = (decoded - 0.5f)
×
401
                 * (2.0f * SIXEL_CIELAB_AB_FLOAT_MAX);
402
        return decoded;
×
403
    }
404
    if (pixelformat == SIXEL_PIXELFORMAT_DIN99DFLOAT32) {
×
405
        if (channel == 0) {
×
406
            return (float)value / 255.0f;
×
407
        }
408
        decoded = (float)value / 255.0f;
×
409
        decoded = (decoded - 0.5f)
×
410
                 * (2.0f * SIXEL_DIN99D_AB_FLOAT_MAX);
411
        return decoded;
×
412
    }
NEW
413
    if (pixelformat == SIXEL_PIXELFORMAT_YUVFLOAT32) {
×
NEW
414
        if (channel == 0) {
×
NEW
415
            return (float)value / 255.0f;
×
416
        }
NEW
417
        if (channel == 1) {
×
NEW
418
            return sixel_pixelformat_yuv_chroma_from_byte(
×
419
                value,
420
                SIXEL_YUV_U_FLOAT_MAX);
421
        }
NEW
422
        return sixel_pixelformat_yuv_chroma_from_byte(value,
×
423
                                                      SIXEL_YUV_V_FLOAT_MAX);
424
    }
425

426
    (void)channel;
×
427
    return (float)value / 255.0f;
×
428
}
429

430
static void
431
get_rgb(unsigned char const *data,
×
432
        int const pixelformat,
433
        int depth,
434
        unsigned char *r,
435
        unsigned char *g,
436
        unsigned char *b)
437
{
438
    unsigned int pixels = 0;
×
439
#if SWAP_BYTES
440
    unsigned int low;
441
    unsigned int high;
442
#endif
443
    int count = 0;
×
444

445
    if (pixelformat == SIXEL_PIXELFORMAT_RGBFLOAT32
×
446
            || pixelformat == SIXEL_PIXELFORMAT_LINEARRGBFLOAT32) {
×
447
        float const *fpixels = (float const *)(void const *)data;
×
448

449
        *r = sixel_pixelformat_float_to_byte(fpixels[0]);
×
450
        *g = sixel_pixelformat_float_to_byte(fpixels[1]);
×
451
        *b = sixel_pixelformat_float_to_byte(fpixels[2]);
×
452
        return;
×
453
    }
454
    if (pixelformat == SIXEL_PIXELFORMAT_OKLABFLOAT32) {
1!
455
        float const *fpixels = (float const *)(void const *)data;
×
456

457
        *r = sixel_pixelformat_oklab_L_to_byte(fpixels[0]);
×
458
        *g = sixel_pixelformat_oklab_ab_to_byte(fpixels[1]);
×
459
        *b = sixel_pixelformat_oklab_ab_to_byte(fpixels[2]);
×
460
        return;
×
461
    }
462
    if (pixelformat == SIXEL_PIXELFORMAT_CIELABFLOAT32) {
1!
463
        float const *fpixels = (float const *)(void const *)data;
×
464

465
        *r = sixel_pixelformat_cielab_L_to_byte(fpixels[0]);
×
466
        *g = sixel_pixelformat_cielab_ab_to_byte(fpixels[1]);
×
467
        *b = sixel_pixelformat_cielab_ab_to_byte(fpixels[2]);
×
468
        return;
×
469
    }
470
    if (pixelformat == SIXEL_PIXELFORMAT_DIN99DFLOAT32) {
1!
471
        float const *fpixels = (float const *)(void const *)data;
×
472

473
        *r = sixel_pixelformat_din99d_L_to_byte(fpixels[0]);
×
474
        *g = sixel_pixelformat_din99d_ab_to_byte(fpixels[1]);
×
475
        *b = sixel_pixelformat_din99d_ab_to_byte(fpixels[2]);
×
476
        return;
×
477
    }
478
    if (pixelformat == SIXEL_PIXELFORMAT_YUVFLOAT32) {
1!
NEW
479
        float const *fpixels = (float const *)(void const *)data;
×
480

NEW
481
        *r = sixel_pixelformat_float_to_byte(fpixels[0]);
×
NEW
482
        *g = sixel_pixelformat_yuv_chroma_to_byte(fpixels[1],
×
483
                                                  SIXEL_YUV_U_FLOAT_MAX);
NEW
484
        *b = sixel_pixelformat_yuv_chroma_to_byte(fpixels[2],
×
485
                                                  SIXEL_YUV_V_FLOAT_MAX);
NEW
486
        return;
×
487
    }
488

489
    while (count < depth) {
×
490
        pixels = *(data + count) | (pixels << 8);
×
491
        count++;
×
492
    }
493

494
    /* TODO: we should swap bytes (only necessary on LSByte first hardware?) */
495
#if SWAP_BYTES
496
    if (depth == 2) {
497
        low    = pixels & 0xff;
498
        high   = (pixels >> 8) & 0xff;
499
        pixels = (low << 8) | high;
500
    }
501
#endif
502

503
    switch (pixelformat) {
×
504
    case SIXEL_PIXELFORMAT_RGB555:
×
505
        *r = ((pixels >> 10) & 0x1f) << 3;
×
506
        *g = ((pixels >>  5) & 0x1f) << 3;
×
507
        *b = ((pixels >>  0) & 0x1f) << 3;
×
508
        break;
×
509
    case SIXEL_PIXELFORMAT_RGB565:
×
510
        *r = ((pixels >> 11) & 0x1f) << 3;
×
511
        *g = ((pixels >>  5) & 0x3f) << 2;
×
512
        *b = ((pixels >>  0) & 0x1f) << 3;
×
513
        break;
×
514
    case SIXEL_PIXELFORMAT_RGB888:
×
515
        *r = (pixels >> 16) & 0xff;
×
516
        *g = (pixels >>  8) & 0xff;
×
517
        *b = (pixels >>  0) & 0xff;
×
518
        break;
×
519
    case SIXEL_PIXELFORMAT_BGR555:
×
520
        *r = ((pixels >>  0) & 0x1f) << 3;
×
521
        *g = ((pixels >>  5) & 0x1f) << 3;
×
522
        *b = ((pixels >> 10) & 0x1f) << 3;
×
523
        break;
×
524
    case SIXEL_PIXELFORMAT_BGR565:
×
525
        *r = ((pixels >>  0) & 0x1f) << 3;
×
526
        *g = ((pixels >>  5) & 0x3f) << 2;
×
527
        *b = ((pixels >> 11) & 0x1f) << 3;
×
528
        break;
×
529
    case SIXEL_PIXELFORMAT_BGR888:
×
530
        *r = (pixels >>  0) & 0xff;
×
531
        *g = (pixels >>  8) & 0xff;
×
532
        *b = (pixels >> 16) & 0xff;
×
533
        break;
×
534
    case SIXEL_PIXELFORMAT_RGBA8888:
×
535
        *r = (pixels >> 24) & 0xff;
×
536
        *g = (pixels >> 16) & 0xff;
×
537
        *b = (pixels >>  8) & 0xff;
×
538
        break;
×
539
    case SIXEL_PIXELFORMAT_ARGB8888:
×
540
        *r = (pixels >> 16) & 0xff;
×
541
        *g = (pixels >>  8) & 0xff;
×
542
        *b = (pixels >>  0) & 0xff;
×
543
        break;
×
544
    case SIXEL_PIXELFORMAT_BGRA8888:
×
545
        *r = (pixels >>  8) & 0xff;
×
546
        *g = (pixels >> 16) & 0xff;
×
547
        *b = (pixels >> 24) & 0xff;
×
548
        break;
×
549
    case SIXEL_PIXELFORMAT_ABGR8888:
×
550
        *r = (pixels >>  0) & 0xff;
×
551
        *g = (pixels >>  8) & 0xff;
×
552
        *b = (pixels >> 16) & 0xff;
×
553
        break;
×
554
    case SIXEL_PIXELFORMAT_GA88:
×
555
        *r = *g = *b = (pixels >> 8) & 0xff;
×
556
        break;
×
557
    case SIXEL_PIXELFORMAT_G8:
×
558
    case SIXEL_PIXELFORMAT_AG88:
559
        *r = *g = *b = pixels & 0xff;
×
560
        break;
×
561
    default:
×
562
        *r = *g = *b = 0;
×
563
        break;
×
564
    }
565
}
1!
566

567

568
SIXELAPI int
569
sixel_helper_compute_depth(int pixelformat)
2,283✔
570
{
571
    int depth = (-1);  /* unknown */
2,283✔
572

573
    switch (pixelformat) {
2,283!
574
    case SIXEL_PIXELFORMAT_ARGB8888:
×
575
    case SIXEL_PIXELFORMAT_RGBA8888:
576
    case SIXEL_PIXELFORMAT_ABGR8888:
577
    case SIXEL_PIXELFORMAT_BGRA8888:
578
        depth = 4;
×
579
        break;
×
580
    case SIXEL_PIXELFORMAT_RGB888:
1,572✔
581
    case SIXEL_PIXELFORMAT_BGR888:
582
        depth = 3;
1,572✔
583
        break;
1,572✔
584
    case SIXEL_PIXELFORMAT_RGB555:
×
585
    case SIXEL_PIXELFORMAT_RGB565:
586
    case SIXEL_PIXELFORMAT_BGR555:
587
    case SIXEL_PIXELFORMAT_BGR565:
588
    case SIXEL_PIXELFORMAT_AG88:
589
    case SIXEL_PIXELFORMAT_GA88:
590
        depth = 2;
×
591
        break;
×
592
    case SIXEL_PIXELFORMAT_G1:
231✔
593
    case SIXEL_PIXELFORMAT_G2:
594
    case SIXEL_PIXELFORMAT_G4:
595
    case SIXEL_PIXELFORMAT_G8:
596
    case SIXEL_PIXELFORMAT_PAL1:
597
    case SIXEL_PIXELFORMAT_PAL2:
598
    case SIXEL_PIXELFORMAT_PAL4:
599
    case SIXEL_PIXELFORMAT_PAL8:
600
        depth = 1;
231✔
601
        break;
231✔
602
    case SIXEL_PIXELFORMAT_RGBFLOAT32:
480✔
603
    case SIXEL_PIXELFORMAT_LINEARRGBFLOAT32:
604
    case SIXEL_PIXELFORMAT_OKLABFLOAT32:
605
    case SIXEL_PIXELFORMAT_CIELABFLOAT32:
606
    case SIXEL_PIXELFORMAT_DIN99DFLOAT32:
607
    case SIXEL_PIXELFORMAT_YUVFLOAT32:
608
        depth = (int)(sizeof(float) * 3);
480✔
609
        break;
480✔
610
    default:
611
        break;
612
    }
613

614
    return depth;
2,283✔
615
}
616

617

618
static void
619
expand_rgb(unsigned char *dst,
×
620
           unsigned char const *src,
621
           int width, int height,
622
           int pixelformat, int depth)
623
{
624
    int x;
×
625
    int y;
×
626
    int dst_offset;
×
627
    int src_offset;
×
628
    unsigned char r, g, b;
×
629

630
    for (y = 0; y < height; y++) {
×
631
        for (x = 0; x < width; x++) {
×
632
            src_offset = depth * (y * width + x);
×
633
            dst_offset = 3 * (y * width + x);
×
634
            get_rgb(src + src_offset, pixelformat, depth, &r, &g, &b);
×
635

636
            *(dst + dst_offset + 0) = r;
×
637
            *(dst + dst_offset + 1) = g;
×
638
            *(dst + dst_offset + 2) = b;
×
639
        }
640
    }
641
}
×
642

643

644
static SIXELSTATUS
645
expand_palette(unsigned char *dst, unsigned char const *src,
×
646
               int width, int height, int const pixelformat)
647
{
648
    SIXELSTATUS status = SIXEL_FALSE;
×
649
    int x;
×
650
    int y;
×
651
    int i;
×
652
    int bpp;  /* bit per plane */
×
653

654
    switch (pixelformat) {
×
655
    case SIXEL_PIXELFORMAT_PAL1:
656
    case SIXEL_PIXELFORMAT_G1:
657
        bpp = 1;
658
        break;
659
    case SIXEL_PIXELFORMAT_PAL2:
×
660
    case SIXEL_PIXELFORMAT_G2:
661
        bpp = 2;
×
662
        break;
×
663
    case SIXEL_PIXELFORMAT_PAL4:
×
664
    case SIXEL_PIXELFORMAT_G4:
665
        bpp = 4;
×
666
        break;
×
667
    case SIXEL_PIXELFORMAT_PAL8:
668
    case SIXEL_PIXELFORMAT_G8:
669
        for (i = 0; i < width * height; ++i, ++src) {
×
670
            *dst++ = *src;
×
671
        }
672
        status = SIXEL_OK;
×
673
        goto end;
×
674
    default:
×
675
        status = SIXEL_BAD_ARGUMENT;
×
676
        sixel_helper_set_additional_message(
×
677
            "expand_palette: invalid pixelformat.");
678
        goto end;
×
679
    }
680

681
#if HAVE_DEBUG
682
    fprintf(stderr, "expanding PAL%d to PAL8...\n", bpp);
×
683
#endif
684

685
    for (y = 0; y < height; ++y) {
×
686
        for (x = 0; x < width * bpp / 8; ++x) {
×
687
            for (i = 0; i < 8 / bpp; ++i) {
×
688
                *dst++ = *src >> (8 / bpp - 1 - i) * bpp & ((1 << bpp) - 1);
×
689
            }
690
            src++;
×
691
        }
692
        x = width - x * 8 / bpp;
×
693
        if (x > 0) {
×
694
            for (i = 0; i < x; ++i) {
×
695
                *dst++ = *src >> (8 - (i + 1) * bpp) & ((1 << bpp) - 1);
×
696
            }
697
            src++;
×
698
        }
699
    }
700

701
    status = SIXEL_OK;
702

703
end:
×
704
    return status;
×
705
}
706

707

708
SIXELAPI SIXELSTATUS
709
sixel_helper_normalize_pixelformat(
×
710
    unsigned char       /* out */ *dst,             /* destination buffer */
711
    int                 /* out */ *dst_pixelformat, /* converted pixelformat */
712
    unsigned char const /* in */  *src,             /* source pixels */
713
    int                 /* in */  src_pixelformat,  /* format of source image */
714
    int                 /* in */  width,            /* width of source image */
715
    int                 /* in */  height)           /* height of source image */
716
{
717
    SIXELSTATUS status = SIXEL_FALSE;
×
718
    int depth;
×
719

720
    switch (src_pixelformat) {
×
721
    case SIXEL_PIXELFORMAT_G8:
×
722
        expand_rgb(dst, src, width, height, src_pixelformat, 1);
×
723
        *dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
724
        break;
×
725
    case SIXEL_PIXELFORMAT_RGB565:
×
726
    case SIXEL_PIXELFORMAT_RGB555:
727
    case SIXEL_PIXELFORMAT_BGR565:
728
    case SIXEL_PIXELFORMAT_BGR555:
729
    case SIXEL_PIXELFORMAT_GA88:
730
    case SIXEL_PIXELFORMAT_AG88:
731
        expand_rgb(dst, src, width, height, src_pixelformat, 2);
×
732
        *dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
733
        break;
×
734
    case SIXEL_PIXELFORMAT_RGB888:
×
735
    case SIXEL_PIXELFORMAT_BGR888:
736
        expand_rgb(dst, src, width, height, src_pixelformat, 3);
×
737
        *dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
738
        break;
×
739
    case SIXEL_PIXELFORMAT_RGBFLOAT32:
×
740
    case SIXEL_PIXELFORMAT_LINEARRGBFLOAT32:
741
    case SIXEL_PIXELFORMAT_OKLABFLOAT32:
742
    case SIXEL_PIXELFORMAT_CIELABFLOAT32:
743
    case SIXEL_PIXELFORMAT_DIN99DFLOAT32:
744
        depth = sixel_helper_compute_depth(src_pixelformat);
×
745
        if (depth <= 0) {
×
746
            status = SIXEL_BAD_ARGUMENT;
×
747
            goto end;
×
748
        }
749
        expand_rgb(dst, src, width, height, src_pixelformat, depth);
×
750
        *dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
751
        break;
×
752
    case SIXEL_PIXELFORMAT_RGBA8888:
×
753
    case SIXEL_PIXELFORMAT_ARGB8888:
754
    case SIXEL_PIXELFORMAT_BGRA8888:
755
    case SIXEL_PIXELFORMAT_ABGR8888:
756
        expand_rgb(dst, src, width, height, src_pixelformat, 4);
×
757
        *dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
758
        break;
×
759
    case SIXEL_PIXELFORMAT_PAL1:
×
760
    case SIXEL_PIXELFORMAT_PAL2:
761
    case SIXEL_PIXELFORMAT_PAL4:
762
        *dst_pixelformat = SIXEL_PIXELFORMAT_PAL8;
×
763
        status = expand_palette(dst, src, width, height, src_pixelformat);
×
764
        if (SIXEL_FAILED(status)) {
×
765
            goto end;
×
766
        }
767
        break;
768
    case SIXEL_PIXELFORMAT_G1:
×
769
    case SIXEL_PIXELFORMAT_G2:
770
    case SIXEL_PIXELFORMAT_G4:
771
        *dst_pixelformat = SIXEL_PIXELFORMAT_G8;
×
772
        status = expand_palette(dst, src, width, height, src_pixelformat);
×
773
        if (SIXEL_FAILED(status)) {
×
774
            goto end;
×
775
        }
776
        break;
777
    case SIXEL_PIXELFORMAT_PAL8:
×
778
        memcpy(dst, src, (size_t)(width * height));
×
779
        *dst_pixelformat = src_pixelformat;
×
780
        break;
×
781
    default:
×
782
        status = SIXEL_BAD_ARGUMENT;
×
783
        goto end;
×
784
    }
785

786
    status = SIXEL_OK;
787

788
end:
×
789
    return status;
×
790
}
791

792

793
#if HAVE_TESTS
794
static int
795
test1(void)
×
796
{
797
    unsigned char dst[3];
×
798
    int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
799
    int src_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
800
    unsigned char src[] = { 0x46, 0xf3, 0xe5 };
×
801
    int ret = 0;
×
802

803
    int nret = EXIT_FAILURE;
×
804

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

822
error:
×
823
    perror("test1");
×
824
    return nret;
×
825
}
826

827

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

837
    int nret = EXIT_FAILURE;
×
838

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

856
error:
×
857
    perror("test2");
×
858
    return nret;
×
859
}
860

861

862
static int
863
test3(void)
×
864
{
865
    unsigned char dst[3];
×
866
    int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
867
    int src_pixelformat = SIXEL_PIXELFORMAT_RGB565;
×
868
    unsigned char src[] = { 0x47, 0x9c };
×
869
    int ret = 0;
×
870

871
    int nret = EXIT_FAILURE;
×
872

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

890
error:
×
891
    perror("test3");
×
892
    return nret;
×
893
}
894

895

896
static int
897
test4(void)
×
898
{
899
    unsigned char dst[3];
×
900
    int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
901
    int src_pixelformat = SIXEL_PIXELFORMAT_BGR888;
×
902
    unsigned char src[] = { 0x46, 0xf3, 0xe5 };
×
903
    int ret = 0;
×
904

905
    int nret = EXIT_FAILURE;
×
906

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

924
error:
×
925
    perror("test4");
×
926
    return nret;
×
927
}
928

929

930
static int
931
test5(void)
×
932
{
933
    unsigned char dst[3];
×
934
    int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
935
    int src_pixelformat = SIXEL_PIXELFORMAT_BGR555;
×
936
    unsigned char src[] = { 0x23, 0xc8 };
×
937
    int ret = 0;
×
938

939
    int nret = EXIT_FAILURE;
×
940

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

958
error:
×
959
    perror("test5");
×
960
    return nret;
×
961
}
962

963

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

973
    int nret = EXIT_FAILURE;
×
974

975
    ret = sixel_helper_normalize_pixelformat(dst,
×
976
                                             &dst_pixelformat,
977
                                             src,
978
                                             src_pixelformat,
979
                                             1,
980
                                             1);
981
    if (ret != 0) {
×
982
        goto error;
×
983
    }
984
    if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
×
985
        goto error;
×
986
    }
987
    if ((dst[2] >> 3 << 11 | dst[1] >> 2 << 5 | dst[0] >> 3) != (src[0] << 8 | src[1])) {
×
988
        goto error;
×
989
    }
990
    return EXIT_SUCCESS;
991

992
error:
×
993
    perror("test6");
×
994
    return nret;
×
995
}
996

997

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

1007
    int nret = EXIT_FAILURE;
×
1008

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

1026
error:
×
1027
    perror("test7");
×
1028
    return nret;
×
1029
}
1030

1031

1032
static int
1033
test8(void)
×
1034
{
1035
    unsigned char dst[3];
×
1036
    int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
1037
    int src_pixelformat = SIXEL_PIXELFORMAT_GA88;
×
1038
    unsigned char src[] = { 0x47, 0x88 };
×
1039
    int ret = 0;
×
1040

1041
    int nret = EXIT_FAILURE;
×
1042

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

1060
error:
×
1061
    perror("test8");
×
1062
    return nret;
×
1063
}
1064

1065

1066
static int
1067
test9(void)
×
1068
{
1069
    unsigned char dst[3];
×
1070
    int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
1071
    int src_pixelformat = SIXEL_PIXELFORMAT_RGBA8888;
×
1072
    unsigned char src[] = { 0x46, 0xf3, 0xe5, 0xf0 };
×
1073
    int ret = 0;
×
1074

1075
    int nret = EXIT_FAILURE;
×
1076

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

1100
error:
×
1101
    perror("test8");
×
1102
    return nret;
×
1103
}
1104

1105

1106
static int
1107
test10(void)
×
1108
{
1109
    unsigned char dst[3];
×
1110
    int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
1111
    int src_pixelformat = SIXEL_PIXELFORMAT_ARGB8888;
×
1112
    unsigned char src[] = { 0x46, 0xf3, 0xe5, 0xf0 };
×
1113
    int ret = 0;
×
1114

1115
    int nret = EXIT_FAILURE;
×
1116

1117
    ret = sixel_helper_normalize_pixelformat(dst,
×
1118
                                             &dst_pixelformat,
1119
                                             src,
1120
                                             src_pixelformat,
1121
                                             1,
1122
                                             1);
1123
    if (ret != 0) {
×
1124
        goto error;
×
1125
    }
1126
    if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
×
1127
        goto error;
×
1128
    }
1129
    if (dst[0] != src[1]) {
×
1130
        goto error;
×
1131
    }
1132
    if (dst[1] != src[2]) {
×
1133
        goto error;
×
1134
    }
1135
    if (dst[2] != src[3]) {
×
1136
        goto error;
×
1137
    }
1138
    return EXIT_SUCCESS;
1139

1140
error:
×
1141
    perror("test8");
×
1142
    return nret;
×
1143
}
1144

1145

1146
static int
1147
test11(void)
×
1148
{
1149
    unsigned char dst[3];
×
1150
    int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
1151
    int src_pixelformat = SIXEL_PIXELFORMAT_RGBFLOAT32;
×
1152
    float srcf[] = { 0.0f, 0.5f, 1.0f };
×
1153
    unsigned char const *src = (unsigned char const *)srcf;
×
1154
    int ret = 0;
×
1155
    int depth;
×
1156

1157
    int nret = EXIT_FAILURE;
×
1158

1159
    ret = sixel_helper_normalize_pixelformat(dst,
×
1160
                                             &dst_pixelformat,
1161
                                             src,
1162
                                             src_pixelformat,
1163
                                             1,
1164
                                             1);
1165
    if (ret != 0) {
×
1166
        goto error;
×
1167
    }
1168
    if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
×
1169
        goto error;
×
1170
    }
1171
    if (dst[0] != 0 || dst[1] != 128 || dst[2] != 255) {
×
1172
        goto error;
×
1173
    }
1174
    depth = sixel_helper_compute_depth(src_pixelformat);
×
1175
    if (depth != (int)(sizeof(float) * 3)) {
×
1176
        goto error;
×
1177
    }
1178
    return EXIT_SUCCESS;
1179

1180
error:
×
1181
    perror("test11");
×
1182
    return nret;
×
1183
}
1184

1185

1186
SIXELAPI int
1187
sixel_pixelformat_tests_main(void)
×
1188
{
1189
    int nret = EXIT_FAILURE;
×
1190
    size_t i;
×
1191
    typedef int (* testcase)(void);
×
1192

1193
    static testcase const testcases[] = {
×
1194
        test1,
1195
        test2,
1196
        test3,
1197
        test4,
1198
        test5,
1199
        test6,
1200
        test7,
1201
        test8,
1202
        test9,
1203
        test10,
1204
        test11,
1205
    };
1206

1207
    for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) {
×
1208
        nret = testcases[i]();
×
1209
        if (nret != EXIT_SUCCESS) {
×
1210
            goto error;
×
1211
        }
1212
    }
1213

1214
    nret = EXIT_SUCCESS;
1215

1216
error:
×
1217
    return nret;
×
1218
}
1219
#endif  /* HAVE_TESTS */
1220

1221
/* emacs Local Variables:      */
1222
/* emacs mode: c               */
1223
/* emacs tab-width: 4          */
1224
/* emacs indent-tabs-mode: nil */
1225
/* emacs c-basic-offset: 4     */
1226
/* emacs End:                  */
1227
/* vim: set expandtab ts=4 sts=4 sw=4 : */
1228
/* 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