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

saitoha / libsixel / 22281294763

22 Feb 2026 04:53PM UTC coverage: 81.971% (-1.7%) from 83.691%
22281294763

push

github

saitoha
tests: enforce SIXEL_TEST_PYTHON contract for python tap runs

27660 of 54661 branches covered (50.6%)

45511 of 55521 relevant lines covered (81.97%)

2582260.61 hits per line

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

68.74
/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
#if defined(HAVE_CONFIG_H)
26
#include "config.h"
27
#endif
28

29
/* STDC_HEADERS */
30
#include <stdio.h>
31
#include <stdlib.h>
32

33
#if HAVE_MATH_H
34
# include <math.h>
35
#endif  /* HAVE_MATH_H */
36

37
#if HAVE_MEMORY_H
38
# include <memory.h>
39
#endif  /* HAVE_MEMORY_H */
40

41
#include <sixel.h>
42

43
#include "compat_stub.h"
44
#include "threading.h"
45
#include "pixelformat.h"
46

47
#define SIXEL_OKLAB_AB_FLOAT_MIN (-0.5f)
48
#define SIXEL_OKLAB_AB_FLOAT_MAX (0.5f)
49
#define SIXEL_CIELAB_AB_FLOAT_MIN (-1.5f)
50
#define SIXEL_CIELAB_AB_FLOAT_MAX (1.5f)
51
#define SIXEL_CIELAB_L_FLOAT_MIN  (0.0f)
52
#define SIXEL_CIELAB_L_FLOAT_MAX  (1.0f)
53
#define SIXEL_DIN99D_L_FLOAT_MIN  (0.0f)
54
#define SIXEL_DIN99D_L_FLOAT_MAX  (1.0f)
55
#define SIXEL_DIN99D_AB_FLOAT_MIN (-1.0f)
56
#define SIXEL_DIN99D_AB_FLOAT_MAX (1.0f)
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)
54,969,094✔
65
{
66
#if HAVE_MATH_H
67
    if (!isfinite(value)) {
54,969,094!
68
        value = 0.0f;
69
    }
70
#endif  /* HAVE_MATH_H */
71

72
    if (value <= 0.0f) {
54,969,094✔
73
        return 0;
8,740,418✔
74
    }
75
    if (value >= 1.0f) {
33,314,202✔
76
        return 255;
133,908✔
77
    }
78

79
    return (unsigned char)(value * 255.0f + 0.5f);
32,964,929✔
80
}
20,593,152✔
81

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

91
    if (value <= 0.0f) {
1,760,594!
92
        return 0;
93
    }
94
    if (value >= 1.0f) {
1,760,594!
95
        return 255;
96
    }
97

98
    return (unsigned char)(value * 255.0f + 0.5f);
1,760,594✔
99
}
638,600✔
100

101
static unsigned char
102
sixel_pixelformat_oklab_ab_to_byte(float value)
3,521,177✔
103
{
104
    float encoded;
3,521,177✔
105

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

112
    encoded = value + 0.5f;
5,765,154✔
113
    if (encoded <= 0.0f) {
3,521,177!
114
        return 0;
115
    }
116
    if (encoded >= 1.0f) {
3,521,177!
117
        return 255;
118
    }
119

120
    return (unsigned char)(encoded * 255.0f + 0.5f);
3,521,177✔
121
}
1,277,200✔
122

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

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

139
    return (unsigned char)(value * 255.0f + 0.5f);
540,672✔
140
}
196,608✔
141

142
static unsigned char
143
sixel_pixelformat_cielab_ab_to_byte(float value)
1,081,344✔
144
{
145
    float encoded;
1,081,344✔
146

147
#if HAVE_MATH_H
148
    if (!isfinite(value)) {
1,081,344!
149
        value = 0.0f;
393,216✔
150
    }
151
#endif  /* HAVE_MATH_H */
152

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

161
    return (unsigned char)(encoded * 255.0f + 0.5f);
1,081,344✔
162
}
393,216✔
163

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

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

180
    return (unsigned char)(value * 255.0f + 0.5f);
495,616✔
181
}
180,224✔
182

183
static unsigned char
184
sixel_pixelformat_din99d_ab_to_byte(float value)
991,232✔
185
{
186
    float encoded;
991,232✔
187

188
#if HAVE_MATH_H
189
    if (!isfinite(value)) {
991,232!
190
        value = 0.0f;
360,448✔
191
    }
192
#endif  /* HAVE_MATH_H */
193

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

202
    return (unsigned char)(encoded * 255.0f + 0.5f);
991,232✔
203
}
360,448✔
204

205
static float
206
sixel_pixelformat_float_channel_min_internal(int pixelformat,
62,399,402✔
207
                                             int channel)
208
{
209
    (void)channel;
62,399,402✔
210
    if (pixelformat == SIXEL_PIXELFORMAT_OKLABFLOAT32) {
62,399,402✔
211
        if (channel == 0) {
25,484,225✔
212
            return 0.0f;
3,172,324✔
213
        }
214
        return SIXEL_OKLAB_AB_FLOAT_MIN;
16,834,129✔
215
    }
216
    if (pixelformat == SIXEL_PIXELFORMAT_CIELABFLOAT32) {
36,915,177✔
217
        if (channel == 0) {
6,422,014✔
218
            return SIXEL_CIELAB_L_FLOAT_MIN;
780,472✔
219
        }
220
        return SIXEL_CIELAB_AB_FLOAT_MIN;
4,284,376✔
221
    }
222
    if (pixelformat == SIXEL_PIXELFORMAT_DIN99DFLOAT32) {
30,493,163✔
223
        if (channel == 0) {
5,759,001✔
224
            return SIXEL_DIN99D_L_FLOAT_MIN;
700,072✔
225
        }
226
        return SIXEL_DIN99D_AB_FLOAT_MIN;
3,842,908✔
227
    }
228
    return 0.0f;
8,961,600✔
229
}
22,728,700✔
230

231
float
232
sixel_pixelformat_float_channel_min(int pixelformat,
1,044✔
233
                                    int channel)
234
{
235
    return sixel_pixelformat_float_channel_min_internal(pixelformat,
1,332✔
236
                                                        channel);
288✔
237
}
238

239
static float
240
sixel_pixelformat_float_channel_max_internal(int pixelformat,
62,390,845✔
241
                                             int channel)
242
{
243
    (void)channel;
62,390,845✔
244
    if (pixelformat == SIXEL_PIXELFORMAT_OKLABFLOAT32) {
62,390,845✔
245
        if (channel == 0) {
25,451,718!
246
            return 1.0f;
3,172,308✔
247
        }
248
        return SIXEL_OKLAB_AB_FLOAT_MAX;
6,153,112✔
249
    }
250
    if (pixelformat == SIXEL_PIXELFORMAT_CIELABFLOAT32) {
36,939,127!
251
        if (channel == 0) {
6,416,518!
252
            return SIXEL_CIELAB_L_FLOAT_MAX;
780,456✔
253
        }
254
        return SIXEL_CIELAB_AB_FLOAT_MAX;
1,560,912✔
255
    }
256
    if (pixelformat == SIXEL_PIXELFORMAT_DIN99DFLOAT32) {
11,061,624✔
257
        if (channel == 0) {
2,100,168✔
258
            return SIXEL_DIN99D_L_FLOAT_MAX;
700,056✔
259
        }
260
        return SIXEL_DIN99D_AB_FLOAT_MAX;
1,400,112✔
261
    }
262
    return 1.0f;
8,961,456✔
263
}
22,728,412✔
264

265
float
266
sixel_pixelformat_float_channel_max(int pixelformat,
×
267
                                    int channel)
268
{
269
    return sixel_pixelformat_float_channel_max_internal(pixelformat,
×
270
                                                        channel);
271
}
272

273
float
274
sixel_pixelformat_float_channel_range(int pixelformat,
11,712✔
275
                                      int channel)
276
{
277
    float minimum;
11,712✔
278
    float maximum;
11,712✔
279
    float range;
11,712✔
280

281
    minimum = sixel_pixelformat_float_channel_min_internal(pixelformat,
14,424✔
282
                                                           channel);
2,712✔
283
    maximum = sixel_pixelformat_float_channel_max_internal(pixelformat,
14,424✔
284
                                                           channel);
2,712✔
285
    range = maximum - minimum;
11,712✔
286
    if (range <= 0.0f) {
11,712!
287
        range = 1.0f;
288
    }
289
    return range;
14,424✔
290
}
2,712✔
291

292
float
293
sixel_pixelformat_float_channel_clamp(int pixelformat,
62,379,133✔
294
                                      int channel,
295
                                      float value)
296
{
297
    float minimum;
62,379,133✔
298
    float maximum;
62,379,133✔
299

300
#if HAVE_MATH_H
301
    if (!isfinite(value)) {
62,379,133!
302
        value = 0.0f;
22,571,556✔
303
    }
304
#endif  /* HAVE_MATH_H */
305

306
    minimum = sixel_pixelformat_float_channel_min_internal(pixelformat,
85,104,833✔
307
                                                           channel);
22,725,700✔
308
    maximum = sixel_pixelformat_float_channel_max_internal(pixelformat,
85,104,833✔
309
                                                           channel);
22,725,700✔
310
    if (value < minimum) {
62,379,133✔
311
        return minimum;
179,424✔
312
    }
313
    if (value > maximum) {
61,868,754✔
314
        return maximum;
2,044✔
315
    }
316

317
    return value;
22,544,232✔
318
}
22,725,700✔
319

320
unsigned char
321
sixel_pixelformat_float_channel_to_byte(int pixelformat,
15,480,688✔
322
                                        int channel,
323
                                        float value)
324
{
325
    float clamped;
15,480,688✔
326

327
    clamped = sixel_pixelformat_float_channel_clamp(pixelformat,
21,097,012✔
328
                                                    channel,
5,616,324✔
329
                                                    value);
5,616,324✔
330
    if (pixelformat == SIXEL_PIXELFORMAT_OKLABFLOAT32) {
15,480,688✔
331
        if (channel == 0) {
5,011,433✔
332
            return sixel_pixelformat_oklab_L_to_byte(clamped);
1,670,479✔
333
        }
334
        return sixel_pixelformat_oklab_ab_to_byte(clamped);
3,340,954✔
335
    }
336
    if (pixelformat == SIXEL_PIXELFORMAT_CIELABFLOAT32) {
10,469,255✔
337
        if (channel == 0) {
1,622,016✔
338
            return sixel_pixelformat_cielab_L_to_byte(clamped);
540,672✔
339
        }
340
        return sixel_pixelformat_cielab_ab_to_byte(clamped);
1,081,344✔
341
    }
342
    if (pixelformat == SIXEL_PIXELFORMAT_DIN99DFLOAT32) {
8,847,239✔
343
        if (channel == 0) {
1,486,848✔
344
            return sixel_pixelformat_din99d_L_to_byte(clamped);
495,616✔
345
        }
346
        return sixel_pixelformat_din99d_ab_to_byte(clamped);
991,232✔
347
    }
348
    (void)channel;
7,360,391✔
349
    return sixel_pixelformat_float_to_byte(clamped);
7,360,391✔
350
}
5,616,324✔
351

352
float
353
sixel_pixelformat_byte_to_float(int pixelformat,
232,879,643✔
354
                                int channel,
355
                                unsigned char value)
356
{
357
    float decoded;
232,879,643✔
358

359
    if (pixelformat == SIXEL_PIXELFORMAT_OKLABFLOAT32) {
232,879,643✔
360
        if (channel == 0) {
4,881,357✔
361
            return (float)value / 255.0f;
1,657,218✔
362
        }
363
        decoded = (float)value / 255.0f;
3,224,139✔
364
        return decoded - 0.5f;
3,224,139✔
365
    }
366
    if (pixelformat == SIXEL_PIXELFORMAT_CIELABFLOAT32) {
227,998,286✔
367
        if (channel == 0) {
1,379,980✔
368
            return (float)value / 255.0f;
460,026✔
369
        }
370
        decoded = (float)value / 255.0f;
919,954✔
371
        decoded = (decoded - 0.5f)
1,227,154✔
372
                 * (2.0f * SIXEL_CIELAB_AB_FLOAT_MAX);
307,200✔
373
        return decoded;
919,954✔
374
    }
375
    if (pixelformat == SIXEL_PIXELFORMAT_DIN99DFLOAT32) {
226,618,306✔
376
        if (channel == 0) {
1,217,207✔
377
            return (float)value / 255.0f;
405,750✔
378
        }
379
        decoded = (float)value / 255.0f;
811,457✔
380
        decoded = (decoded - 0.5f)
1,081,793✔
381
                 * (2.0f * SIXEL_DIN99D_AB_FLOAT_MAX);
270,336✔
382
        return decoded;
811,457✔
383
    }
384
    (void)channel;
225,401,099✔
385
    return (float)value / 255.0f;
225,401,099✔
386
}
93,201,230✔
387

388
typedef void (*sixel_rgb_reader_t)(unsigned char const *data,
389
                                    unsigned char *r,
390
                                    unsigned char *g,
391
                                    unsigned char *b);
392

393

394
static unsigned int
395
sixel_rgb_read16(unsigned char const *data)
×
396
{
397
    unsigned int pixels;
×
398
#if SWAP_BYTES
399
    unsigned int low;
400
    unsigned int high;
401
#endif
402

403
    pixels = ((unsigned int)data[0] << 8) | (unsigned int)data[1];
×
404

405
#if SWAP_BYTES
406
    low = pixels & 0xff;
407
    high = (pixels >> 8) & 0xff;
408
    pixels = (low << 8) | high;
409
#endif
410

411
    return pixels;
×
412
}
413

414

415
static void
416
sixel_rgb_from_rgb555(unsigned char const *data,
×
417
                      unsigned char *r,
418
                      unsigned char *g,
419
                      unsigned char *b)
420
{
421
    unsigned int pixels;
×
422

423
    pixels = sixel_rgb_read16(data);
×
424

425
    *r = ((pixels >> 10) & 0x1f) << 3;
×
426
    *g = ((pixels >> 5) & 0x1f) << 3;
×
427
    *b = ((pixels >> 0) & 0x1f) << 3;
×
428
}
×
429

430

431
static void
432
sixel_rgb_from_rgb565(unsigned char const *data,
×
433
                      unsigned char *r,
434
                      unsigned char *g,
435
                      unsigned char *b)
436
{
437
    unsigned int pixels;
×
438

439
    pixels = sixel_rgb_read16(data);
×
440

441
    *r = ((pixels >> 11) & 0x1f) << 3;
×
442
    *g = ((pixels >> 5) & 0x3f) << 2;
×
443
    *b = ((pixels >> 0) & 0x1f) << 3;
×
444
}
×
445

446

447
static void
448
sixel_rgb_from_bgr555(unsigned char const *data,
×
449
                      unsigned char *r,
450
                      unsigned char *g,
451
                      unsigned char *b)
452
{
453
    unsigned int pixels;
×
454

455
    pixels = sixel_rgb_read16(data);
×
456

457
    *r = ((pixels >> 0) & 0x1f) << 3;
×
458
    *g = ((pixels >> 5) & 0x1f) << 3;
×
459
    *b = ((pixels >> 10) & 0x1f) << 3;
×
460
}
×
461

462

463
static void
464
sixel_rgb_from_bgr565(unsigned char const *data,
×
465
                      unsigned char *r,
466
                      unsigned char *g,
467
                      unsigned char *b)
468
{
469
    unsigned int pixels;
×
470

471
    pixels = sixel_rgb_read16(data);
×
472

473
    *r = ((pixels >> 0) & 0x1f) << 3;
×
474
    *g = ((pixels >> 5) & 0x3f) << 2;
×
475
    *b = ((pixels >> 11) & 0x1f) << 3;
×
476
}
×
477

478

479
static void
480
sixel_rgb_from_ga88(unsigned char const *data,
×
481
                    unsigned char *r,
482
                    unsigned char *g,
483
                    unsigned char *b)
484
{
485
    unsigned int pixels;
×
486

487
    pixels = sixel_rgb_read16(data);
×
488

489
    *r = (pixels >> 8) & 0xff;
×
490
    *g = (pixels >> 8) & 0xff;
×
491
    *b = (pixels >> 8) & 0xff;
×
492
}
×
493

494

495
static void
496
sixel_rgb_from_ag88(unsigned char const *data,
×
497
                    unsigned char *r,
498
                    unsigned char *g,
499
                    unsigned char *b)
500
{
501
    unsigned int pixels;
×
502

503
    pixels = sixel_rgb_read16(data);
×
504

505
    *r = pixels & 0xff;
×
506
    *g = pixels & 0xff;
×
507
    *b = pixels & 0xff;
×
508
}
×
509

510

511
static void
512
sixel_rgb_from_rgb888(unsigned char const *data,
×
513
                      unsigned char *r,
514
                      unsigned char *g,
515
                      unsigned char *b)
516
{
517
    *r = data[0];
×
518
    *g = data[1];
×
519
    *b = data[2];
×
520
}
×
521

522

523
static void
524
sixel_rgb_from_bgr888(unsigned char const *data,
×
525
                      unsigned char *r,
526
                      unsigned char *g,
527
                      unsigned char *b)
528
{
529
    *r = data[2];
×
530
    *g = data[1];
×
531
    *b = data[0];
×
532
}
×
533

534

535
static void
536
sixel_rgb_from_rgba8888(unsigned char const *data,
1,168,132✔
537
                        unsigned char *r,
538
                        unsigned char *g,
539
                        unsigned char *b)
540
{
541
    *r = data[0];
1,168,132✔
542
    *g = data[1];
1,168,132✔
543
    *b = data[2];
1,168,132✔
544
}
1,168,132✔
545

546

547
static void
548
sixel_rgb_from_argb8888(unsigned char const *data,
×
549
                        unsigned char *r,
550
                        unsigned char *g,
551
                        unsigned char *b)
552
{
553
    *r = data[1];
×
554
    *g = data[2];
×
555
    *b = data[3];
×
556
}
×
557

558

559
static void
560
sixel_rgb_from_bgra8888(unsigned char const *data,
×
561
                        unsigned char *r,
562
                        unsigned char *g,
563
                        unsigned char *b)
564
{
565
    *r = data[2];
×
566
    *g = data[1];
×
567
    *b = data[0];
×
568
}
×
569

570

571
static void
572
sixel_rgb_from_abgr8888(unsigned char const *data,
×
573
                        unsigned char *r,
574
                        unsigned char *g,
575
                        unsigned char *b)
576
{
577
    *r = data[3];
×
578
    *g = data[2];
×
579
    *b = data[1];
×
580
}
×
581

582

583
static void
584
sixel_rgb_from_g8(unsigned char const *data,
1,212,832✔
585
                  unsigned char *r,
586
                  unsigned char *g,
587
                  unsigned char *b)
588
{
589
    *r = data[0];
1,212,832✔
590
    *g = data[0];
1,212,832✔
591
    *b = data[0];
1,212,832✔
592
}
1,212,832✔
593

594

595
static void
596
sixel_rgb_from_rgbfloat32(unsigned char const *data,
15,869,525✔
597
                          unsigned char *r,
598
                          unsigned char *g,
599
                          unsigned char *b)
600
{
601
    float const *fpixels;
15,869,525✔
602

603
    fpixels = (float const *)(void const *)data;
15,869,525✔
604

605
    *r = sixel_pixelformat_float_to_byte(fpixels[0]);
15,869,525✔
606
    *g = sixel_pixelformat_float_to_byte(fpixels[1]);
15,869,525✔
607
    *b = sixel_pixelformat_float_to_byte(fpixels[2]);
15,869,525✔
608
}
15,869,525✔
609

610

611
static void
612
sixel_rgb_from_oklabfloat32(unsigned char const *data,
90,112✔
613
                            unsigned char *r,
614
                            unsigned char *g,
615
                            unsigned char *b)
616
{
617
    float const *fpixels;
90,112✔
618

619
    fpixels = (float const *)(void const *)data;
90,112✔
620

621
    *r = sixel_pixelformat_oklab_L_to_byte(fpixels[0]);
90,112✔
622
    *g = sixel_pixelformat_oklab_ab_to_byte(fpixels[1]);
90,112✔
623
    *b = sixel_pixelformat_oklab_ab_to_byte(fpixels[2]);
90,112✔
624
}
90,112✔
625

626

627
static void
628
sixel_rgb_from_cielabfloat32(unsigned char const *data,
×
629
                             unsigned char *r,
630
                             unsigned char *g,
631
                             unsigned char *b)
632
{
633
    float const *fpixels;
×
634

635
    fpixels = (float const *)(void const *)data;
×
636

637
    *r = sixel_pixelformat_cielab_L_to_byte(fpixels[0]);
×
638
    *g = sixel_pixelformat_cielab_ab_to_byte(fpixels[1]);
×
639
    *b = sixel_pixelformat_cielab_ab_to_byte(fpixels[2]);
×
640
}
×
641

642

643
static void
644
sixel_rgb_from_din99dfloat32(unsigned char const *data,
×
645
                             unsigned char *r,
646
                             unsigned char *g,
647
                             unsigned char *b)
648
{
649
    float const *fpixels;
×
650

651
    fpixels = (float const *)(void const *)data;
×
652

653
    *r = sixel_pixelformat_din99d_L_to_byte(fpixels[0]);
×
654
    *g = sixel_pixelformat_din99d_ab_to_byte(fpixels[1]);
×
655
    *b = sixel_pixelformat_din99d_ab_to_byte(fpixels[2]);
×
656
}
×
657

658

659
static void
660
sixel_rgb_from_unknown(unsigned char const *data,
×
661
                       unsigned char *r,
662
                       unsigned char *g,
663
                       unsigned char *b)
664
{
665
    (void)data;
×
666

667
    *r = 0;
×
668
    *g = 0;
×
669
    *b = 0;
×
670
}
×
671

672

673
static sixel_rgb_reader_t
674
sixel_select_rgb_reader(int pixelformat)
1,435✔
675
{
676
    switch (pixelformat) {
1,435!
677
    case SIXEL_PIXELFORMAT_RGB555:
678
        return sixel_rgb_from_rgb555;
679
    case SIXEL_PIXELFORMAT_RGB565:
680
        return sixel_rgb_from_rgb565;
×
681
    case SIXEL_PIXELFORMAT_RGB888:
682
        return sixel_rgb_from_rgb888;
×
683
    case SIXEL_PIXELFORMAT_RGBA8888:
251✔
684
        return sixel_rgb_from_rgba8888;
543✔
685
    case SIXEL_PIXELFORMAT_ARGB8888:
686
        return sixel_rgb_from_argb8888;
×
687
    case SIXEL_PIXELFORMAT_BGR555:
688
        return sixel_rgb_from_bgr555;
×
689
    case SIXEL_PIXELFORMAT_BGR565:
690
        return sixel_rgb_from_bgr565;
×
691
    case SIXEL_PIXELFORMAT_BGR888:
692
        return sixel_rgb_from_bgr888;
×
693
    case SIXEL_PIXELFORMAT_BGRA8888:
694
        return sixel_rgb_from_bgra8888;
×
695
    case SIXEL_PIXELFORMAT_ABGR8888:
696
        return sixel_rgb_from_abgr8888;
×
697
    case SIXEL_PIXELFORMAT_AG88:
698
        return sixel_rgb_from_ag88;
×
699
    case SIXEL_PIXELFORMAT_GA88:
700
        return sixel_rgb_from_ga88;
×
701
    case SIXEL_PIXELFORMAT_G8:
702
        return sixel_rgb_from_g8;
46✔
703
    case SIXEL_PIXELFORMAT_RGBFLOAT32:
498✔
704
    case SIXEL_PIXELFORMAT_LINEARRGBFLOAT32:
705
        return sixel_rgb_from_rgbfloat32;
824✔
706
    case SIXEL_PIXELFORMAT_OKLABFLOAT32:
14✔
707
        return sixel_rgb_from_oklabfloat32;
22✔
708
    case SIXEL_PIXELFORMAT_CIELABFLOAT32:
709
        return sixel_rgb_from_cielabfloat32;
×
710
    case SIXEL_PIXELFORMAT_DIN99DFLOAT32:
711
        return sixel_rgb_from_din99dfloat32;
×
712
    default:
713
        break;
×
714
    }
715

716
    return sixel_rgb_from_unknown;
×
717
}
672✔
718

719

720
SIXELAPI int
721
sixel_helper_compute_depth(int pixelformat)
42,384✔
722
{
723
    int depth = (-1);  /* unknown */
42,384✔
724

725
    switch (pixelformat) {
42,384!
726
    case SIXEL_PIXELFORMAT_ARGB8888:
111✔
727
    case SIXEL_PIXELFORMAT_RGBA8888:
728
    case SIXEL_PIXELFORMAT_ABGR8888:
729
    case SIXEL_PIXELFORMAT_BGRA8888:
730
        depth = 4;
257✔
731
        break;
257✔
732
    case SIXEL_PIXELFORMAT_RGB888:
13,082✔
733
    case SIXEL_PIXELFORMAT_BGR888:
734
        depth = 3;
22,617✔
735
        break;
22,617✔
736
    case SIXEL_PIXELFORMAT_RGB555:
737
    case SIXEL_PIXELFORMAT_RGB565:
738
    case SIXEL_PIXELFORMAT_BGR555:
739
    case SIXEL_PIXELFORMAT_BGR565:
740
    case SIXEL_PIXELFORMAT_AG88:
741
    case SIXEL_PIXELFORMAT_GA88:
742
        depth = 2;
×
743
        break;
×
744
    case SIXEL_PIXELFORMAT_G1:
144✔
745
    case SIXEL_PIXELFORMAT_G2:
746
    case SIXEL_PIXELFORMAT_G4:
747
    case SIXEL_PIXELFORMAT_G8:
748
    case SIXEL_PIXELFORMAT_PAL1:
749
    case SIXEL_PIXELFORMAT_PAL2:
750
    case SIXEL_PIXELFORMAT_PAL4:
751
    case SIXEL_PIXELFORMAT_PAL8:
752
        depth = 1;
392✔
753
        break;
392✔
754
    case SIXEL_PIXELFORMAT_RGBFLOAT32:
11,884✔
755
    case SIXEL_PIXELFORMAT_LINEARRGBFLOAT32:
756
    case SIXEL_PIXELFORMAT_OKLABFLOAT32:
757
    case SIXEL_PIXELFORMAT_CIELABFLOAT32:
758
    case SIXEL_PIXELFORMAT_DIN99DFLOAT32:
759
        depth = (int)(sizeof(float) * 3);
19,118✔
760
        break;
19,118✔
761
    default:
762
        break;
763
    }
764

765
    return depth;
59,547✔
766
}
17,163✔
767

768

769
static void
770
expand_rgb(unsigned char *restrict dst,
1,435✔
771
           unsigned char const *restrict src,
772
           int width, int height,
773
           int pixelformat, int depth)
774
{
775
    int x;
1,435✔
776
    int y;
1,435✔
777
    int dst_stride;
1,435✔
778
    int src_stride;
1,435✔
779
    sixel_rgb_reader_t reader;
1,435✔
780
    unsigned char const *src_row;
1,435✔
781
    unsigned char const *src_pixel;
1,435✔
782
    unsigned char *dst_row;
1,435✔
783
    unsigned char *dst_pixel;
1,435✔
784
    unsigned char r;
1,435✔
785
    unsigned char g;
1,435✔
786
    unsigned char b;
1,435✔
787

788
    /*
789
     * Select the reader once to avoid per-pixel branching. The lookup
790
     * maps each pixelformat to a dedicated decoder so the inner loop
791
     * only performs pointer math and byte stores.
792
     */
793
    reader = sixel_select_rgb_reader(pixelformat);
1,435✔
794

795
    /*
796
     * Pre-compute strides to avoid repeated multiplications in the
797
     * inner loop. The caller guarantees that the buffers are large
798
     * enough, so we can advance pointers by depth/3 bytes per pixel
799
     * instead of recalculating offsets each time.
800
     */
801
    dst_stride = width * 3;
1,435✔
802
    src_stride = width * depth;
1,435✔
803
    src_row = src;
1,435✔
804
    dst_row = dst;
1,435✔
805

806
    for (y = 0; y < height; y++) {
99,997✔
807
        src_pixel = src_row;
39,706✔
808
        dst_pixel = dst_row;
39,706✔
809
        for (x = 0; x < width; x++) {
18,434,434✔
810
            reader(src_pixel, &r, &g, &b);
18,335,872✔
811

812
            dst_pixel[0] = r;
18,335,872✔
813
            dst_pixel[1] = g;
18,335,872✔
814
            dst_pixel[2] = b;
18,335,872✔
815

816
            src_pixel += depth;
18,335,872✔
817
            dst_pixel += 3;
18,335,872✔
818
        }
7,517,504✔
819

820
        src_row += src_stride;
98,562✔
821
        dst_row += dst_stride;
98,562✔
822
    }
39,706✔
823
}
1,435✔
824

825

826
/*
827
 * Lookup tables for expanding packed palette indices. Each entry holds
828
 * the unpacked values for one input byte so the inner loops only copy
829
 * precomputed bytes instead of shifting each pixel.
830
 */
831
static unsigned char palette_table1[256][8];
832
static unsigned char palette_table2[256][4];
833
static unsigned char palette_table4[256][2];
834
static int palette_table_initialized;
835
static sixel_mutex_t palette_table_mutex;
836
static int palette_table_mutex_ready;
837

838

839
static int
840
sixel_init_palette_tables(void)
24✔
841
{
842
    char const *disable_tables;
24✔
843
    int value;
24✔
844
    int i;
24✔
845
    int init_result;
24✔
846

847
    /*
848
     * Allow tests to force the shift-based path by disabling table
849
     * initialization via SIXEL_PALETTE_DISABLE_TABLES. This exercises
850
     * the fallback without introducing additional code paths in
851
     * production builds.
852
     */
853
    disable_tables = sixel_compat_getenv(
24✔
854
            "SIXEL_PALETTE_DISABLE_TABLES");
855
    if (disable_tables != NULL && disable_tables[0] != '\0' &&
24!
856
            disable_tables[0] != '0') {
857
        return 0;
858
    }
859

860
    /*
861
     * Tables are generated once on first use to avoid increasing the
862
     * binary size with large static initializers.
863
     */
864
    if (palette_table_initialized) {
24!
865
        return 1;
866
    }
867

868
    if (palette_table_mutex_ready == 0) {
24!
869
        init_result = sixel_mutex_init(&palette_table_mutex);
24✔
870
        if (init_result == 0) {
24!
871
            palette_table_mutex_ready = 1;
24✔
872
        } else {
24✔
873
            palette_table_mutex_ready = -1;
×
874
        }
875
    }
24✔
876

877
    if (palette_table_mutex_ready < 0) {
24!
878
        /*
879
         * Without a mutex we cannot guarantee a race-free initialization.
880
         * Defer to the shift-based fallback path so multiple threads do not
881
         * write the static tables concurrently.
882
         */
883
        return 0;
884
    }
885

886
    if (palette_table_mutex_ready == 1) {
24!
887
        sixel_mutex_lock(&palette_table_mutex);
24✔
888
        if (palette_table_initialized) {
24!
889
            sixel_mutex_unlock(&palette_table_mutex);
×
890
            return 1;
×
891
        }
892
    }
24✔
893

894
    for (value = 0; value < 256; ++value) {
6,168!
895
        for (i = 0; i < 8; ++i) {
55,296!
896
            palette_table1[value][i] =
49,152✔
897
                (unsigned char)((value >> (7 - i)) & 0x01);
49,152✔
898
        }
49,152✔
899

900
        for (i = 0; i < 4; ++i) {
30,720!
901
            palette_table2[value][i] =
24,576✔
902
                (unsigned char)((value >> (6 - i * 2)) & 0x03);
24,576✔
903
        }
24,576✔
904

905
        for (i = 0; i < 2; ++i) {
18,432!
906
            palette_table4[value][i] =
12,288✔
907
                (unsigned char)((value >> (4 - i * 4)) & 0x0f);
12,288✔
908
        }
12,288✔
909
    }
6,144✔
910

911
    palette_table_initialized = 1;
24✔
912

913
    /*
914
     * Release the mutex after the single initialization pass so later calls
915
     * can reuse the tables without redundant locking.
916
     */
917
    sixel_mutex_unlock(&palette_table_mutex);
24✔
918

919
    return 1;
24✔
920
}
24✔
921

922

923
/*
924
 * Expand packed 1 bpp rows by copying a precomputed 8-pixel block per
925
 * source byte. A tiny tail loop handles the remainder when width is not
926
 * divisible by 8.
927
 */
928
static void
929
sixel_expand_palette_bpp1(unsigned char *restrict dst,
6✔
930
                          unsigned char const *restrict src,
931
                          int width, int height)
932
{
933
    int y;
6✔
934
    int x;
6✔
935
    int remainder;
6✔
936
    int byte_count;
6✔
937
    unsigned char const *table_entry;
6✔
938

939
    byte_count = width / 8;
6✔
940
    remainder = width - byte_count * 8;
6✔
941

942
    if (remainder == 0) {
6!
943
        /*
944
         * Fast path for byte-aligned rows. Removing the per-row
945
         * remainder branch keeps the steady-state inner loop tight.
946
         */
947
        for (y = 0; y < height; ++y) {
198!
948
            for (x = 0; x < byte_count; ++x) {
960!
949
                table_entry = palette_table1[src[0]];
768✔
950
                memcpy(dst, table_entry, 8);
768✔
951
                dst += 8;
768✔
952
                src += 1;
768✔
953
            }
768✔
954
        }
192✔
955
    } else {
6✔
956
        /*
957
         * Handle rows with a short tail. The main loop still copies a
958
         * precomputed 8-pixel block per byte while the tail is expanded
959
         * via a short memcpy so the steady-state loop remains branch free.
960
         */
961
        for (y = 0; y < height; ++y) {
×
962
            for (x = 0; x < byte_count; ++x) {
×
963
                table_entry = palette_table1[src[0]];
×
964
                memcpy(dst, table_entry, 8);
×
965
                dst += 8;
×
966
                src += 1;
×
967
            }
968

969
            table_entry = palette_table1[src[0]];
×
970
            memcpy(dst, table_entry, (size_t)remainder);
×
971
            dst += remainder;
×
972
            src += 1;
×
973
        }
974
    }
975
}
6✔
976

977

978
/*
979
 * Expand packed 2 bpp rows. Each lookup yields four pixels so the inner
980
 * loop becomes a memcpy per byte, followed by a small tail when the row
981
 * width leaves a remainder.
982
 */
983
static void
984
sixel_expand_palette_bpp2(unsigned char *restrict dst,
6✔
985
                          unsigned char const *restrict src,
986
                          int width, int height)
987
{
988
    int y;
6✔
989
    int x;
6✔
990
    int remainder;
6✔
991
    int byte_count;
6✔
992
    unsigned char const *table_entry;
6✔
993

994
    byte_count = width / 4;
6✔
995
    remainder = width - byte_count * 4;
6✔
996

997
    if (remainder == 0) {
6!
998
        /*
999
         * Width aligned to 4 pixels: skip the tail branch and keep the
1000
         * inner loop limited to memcpy plus pointer bumps.
1001
         */
1002
        for (y = 0; y < height; ++y) {
198!
1003
            for (x = 0; x < byte_count; ++x) {
1,728!
1004
                table_entry = palette_table2[src[0]];
1,536✔
1005
                memcpy(dst, table_entry, 4);
1,536✔
1006
                dst += 4;
1,536✔
1007
                src += 1;
1,536✔
1008
            }
1,536✔
1009
        }
192✔
1010
    } else {
6✔
1011
        /*
1012
         * Non-multiple-of-four widths still use the table for the bulk
1013
         * of each row and append the remaining pixels via a short memcpy.
1014
         */
1015
        for (y = 0; y < height; ++y) {
×
1016
            for (x = 0; x < byte_count; ++x) {
×
1017
                table_entry = palette_table2[src[0]];
×
1018
                memcpy(dst, table_entry, 4);
×
1019
                dst += 4;
×
1020
                src += 1;
×
1021
            }
1022

1023
            table_entry = palette_table2[src[0]];
×
1024
            memcpy(dst, table_entry, (size_t)remainder);
×
1025
            dst += remainder;
×
1026
            src += 1;
×
1027
        }
1028
    }
1029
}
6✔
1030

1031

1032
/*
1033
 * Expand packed 4 bpp rows using two-pixel lookup entries. Like the
1034
 * other helpers, the remainder loop only executes when the row width is
1035
 * odd.
1036
 */
1037
static void
1038
sixel_expand_palette_bpp4(unsigned char *restrict dst,
12✔
1039
                          unsigned char const *restrict src,
1040
                          int width, int height)
1041
{
1042
    int y;
12✔
1043
    int x;
12✔
1044
    int remainder;
12✔
1045
    int byte_count;
12✔
1046
    unsigned char const *table_entry;
12✔
1047

1048
    byte_count = width / 2;
12✔
1049
    remainder = width - byte_count * 2;
12✔
1050

1051
    if (remainder == 0) {
12!
1052
        /*
1053
         * When width is an even number of pixels the loop becomes a
1054
         * pure memcpy stream with no per-row branching.
1055
         */
1056
        for (y = 0; y < height; ++y) {
284!
1057
            for (x = 0; x < byte_count; ++x) {
28,464!
1058
                table_entry = palette_table4[src[0]];
28,188✔
1059
                memcpy(dst, table_entry, 2);
28,188✔
1060
                dst += 2;
28,188✔
1061
                src += 1;
28,188✔
1062
            }
28,188✔
1063
        }
276✔
1064
    } else {
8✔
1065
        /*
1066
         * Otherwise process the bulk via the lookup table and append the
1067
         * one remaining pixel with a short memcpy.
1068
         */
1069
        for (y = 0; y < height; ++y) {
60!
1070
            for (x = 0; x < byte_count; ++x) {
2,632!
1071
                table_entry = palette_table4[src[0]];
2,576✔
1072
                memcpy(dst, table_entry, 2);
2,576✔
1073
                dst += 2;
2,576✔
1074
                src += 1;
2,576✔
1075
            }
2,576✔
1076

1077
            table_entry = palette_table4[src[0]];
56✔
1078
            memcpy(dst, table_entry, (size_t)remainder);
56✔
1079
            dst += remainder;
56✔
1080
            src += 1;
56✔
1081
        }
56✔
1082
    }
1083
}
12✔
1084

1085

1086
/*
1087
 * Fallback path that mirrors the original shift-and-mask expansion for
1088
 * packed palette formats. This is selected when the lookup tables cannot be
1089
 * initialized, preserving correctness without concurrent writes to the
1090
 * static buffers.
1091
 */
1092
static void
1093
sixel_expand_palette_fallback(unsigned char *restrict dst,
×
1094
                              unsigned char const *restrict src,
1095
                              int width,
1096
                              int height,
1097
                              int bpp)
1098
{
1099
    int x;
×
1100
    int y;
×
1101
    int i;
×
1102
    int bytes_per_row;
×
1103
    int remainder;
×
1104
    int bits_per_byte;
×
1105
    int mask;
×
1106

1107
    bits_per_byte = 8 / bpp;
×
1108
    mask = (1 << bpp) - 1;
×
1109
    bytes_per_row = width * bpp / 8;
×
1110
    remainder = width - bytes_per_row * bits_per_byte;
×
1111

1112
    for (y = 0; y < height; ++y) {
×
1113
        for (x = 0; x < bytes_per_row; ++x) {
×
1114
            for (i = 0; i < bits_per_byte; ++i) {
×
1115
                *dst++ = (unsigned char)((src[0] >>
×
1116
                    (bits_per_byte - 1 - i) * bpp) & mask);
×
1117
            }
1118
            ++src;
×
1119
        }
1120

1121
        if (remainder > 0) {
×
1122
            for (i = 0; i < remainder; ++i) {
×
1123
                *dst++ = (unsigned char)((src[0] >>
×
1124
                    (bits_per_byte * bpp - (i + 1) * bpp)) & mask);
×
1125
            }
1126
            ++src;
×
1127
        }
1128
    }
1129
}
×
1130

1131

1132
static SIXELSTATUS
1133
expand_palette(unsigned char *restrict dst,
24✔
1134
               unsigned char const *restrict src,
1135
               int width, int height, int const pixelformat)
1136
{
1137
    SIXELSTATUS status = SIXEL_FALSE;
24✔
1138
    int bpp;  /* bit per plane */
24✔
1139
    int use_palette_tables;
24✔
1140
    int tables_ready;
24✔
1141
    size_t total_pixels;
24✔
1142

1143
    /*
1144
     * Reject empty dimensions early. An empty row or column would make the
1145
     * byte count calculations negative and does not represent a valid image
1146
     * to expand.
1147
     */
1148
    if (width <= 0 || height <= 0) {
24!
1149
        sixel_helper_set_additional_message(
×
1150
            "expand_palette: width and height must be positive.");
1151
        status = SIXEL_BAD_ARGUMENT;
×
1152
        goto end;
×
1153
    }
1154

1155
    use_palette_tables = 0;
24✔
1156
    tables_ready = 0;
24✔
1157

1158
    switch (pixelformat) {
24!
1159
    case SIXEL_PIXELFORMAT_PAL1:
1160
    case SIXEL_PIXELFORMAT_G1:
1161
        bpp = 1;
6✔
1162
        use_palette_tables = 1;
6✔
1163
        break;
6✔
1164
    case SIXEL_PIXELFORMAT_PAL2:
1165
    case SIXEL_PIXELFORMAT_G2:
1166
        bpp = 2;
6✔
1167
        use_palette_tables = 1;
6✔
1168
        break;
6✔
1169
    case SIXEL_PIXELFORMAT_PAL4:
1170
    case SIXEL_PIXELFORMAT_G4:
1171
        bpp = 4;
12✔
1172
        use_palette_tables = 1;
12✔
1173
        break;
12✔
1174
    case SIXEL_PIXELFORMAT_PAL8:
1175
    case SIXEL_PIXELFORMAT_G8:
1176
        total_pixels = (size_t)width * (size_t)height;
×
1177

1178
        /*
1179
         * Direct copy for already expanded 8 bpp sources. Using memcpy
1180
         * avoids the per-pixel loop overhead when the input is byte
1181
         * aligned and requires no bit unpacking.
1182
         */
1183
        memcpy(dst, src, total_pixels);
×
1184
        status = SIXEL_OK;
×
1185
        goto end;
×
1186
    default:
1187
        status = SIXEL_BAD_ARGUMENT;
×
1188
        sixel_helper_set_additional_message(
×
1189
            "expand_palette: invalid pixelformat.");
1190
        goto end;
×
1191
    }
1192

1193
    if (use_palette_tables) {
24!
1194
        /*
1195
         * Initialize lookup tables only when packed palette input is
1196
         * present. Formats that are already 8 bpp avoid the setup cost.
1197
         */
1198
        tables_ready = sixel_init_palette_tables();
24✔
1199
    }
24✔
1200

1201
#if HAVE_DEBUG
1202
    fprintf(stderr, "expanding PAL%d to PAL8...\n", bpp);
1203
#endif
1204

1205
    if (tables_ready) {
48!
1206
        /*
1207
         * Use lookup tables to unroll packed indices. Each path copies an
1208
         * entire byte of indices in one memcpy, leaving only a small
1209
         * remainder loop per row for widths that are not byte-aligned.
1210
         */
1211
        switch (bpp) {
24!
1212
        case 1:
1213
            sixel_expand_palette_bpp1(dst, src, width, height);
6✔
1214
            status = SIXEL_OK;
6✔
1215
            break;
6✔
1216
        case 2:
1217
            sixel_expand_palette_bpp2(dst, src, width, height);
6✔
1218
            status = SIXEL_OK;
6✔
1219
            break;
6✔
1220
        case 4:
1221
            sixel_expand_palette_bpp4(dst, src, width, height);
12✔
1222
            status = SIXEL_OK;
12✔
1223
            break;
12✔
1224
        default:
1225
            status = SIXEL_BAD_ARGUMENT;
1226
            break;
1227
        }
1228
    } else {
24✔
1229
        /*
1230
         * Mutex initialization failed or tables are unavailable.
1231
         * Fall back to the original shift-based expansion to avoid
1232
         * concurrent writes to the static lookup buffers.
1233
         */
1234
        sixel_expand_palette_fallback(dst, src, width, height, bpp);
×
1235
        status = SIXEL_OK;
×
1236
    }
1237

1238
end:
1239
    return status;
48✔
1240
}
24✔
1241

1242

1243
SIXELAPI SIXELSTATUS
1244
sixel_helper_normalize_pixelformat(
1,459✔
1245
    unsigned char       /* out */ *dst,             /* destination buffer */
1246
    int                 /* out */ *dst_pixelformat, /* converted pixelformat */
1247
    unsigned char const /* in */  *src,             /* source pixels */
1248
    int                 /* in */  src_pixelformat,  /* format of source image */
1249
    int                 /* in */  width,            /* width of source image */
1250
    int                 /* in */  height)           /* height of source image */
1251
{
1252
    SIXELSTATUS status = SIXEL_FALSE;
1,459✔
1253
    int depth;
1,459✔
1254

1255
    switch (src_pixelformat) {
1,459!
1256
    case SIXEL_PIXELFORMAT_G8:
1257
        expand_rgb(dst, src, width, height, src_pixelformat, 1);
46✔
1258
        *dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
46✔
1259
        break;
46✔
1260
    case SIXEL_PIXELFORMAT_RGB565:
1261
    case SIXEL_PIXELFORMAT_RGB555:
1262
    case SIXEL_PIXELFORMAT_BGR565:
1263
    case SIXEL_PIXELFORMAT_BGR555:
1264
    case SIXEL_PIXELFORMAT_GA88:
1265
    case SIXEL_PIXELFORMAT_AG88:
1266
        expand_rgb(dst, src, width, height, src_pixelformat, 2);
×
1267
        *dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
1268
        break;
×
1269
    case SIXEL_PIXELFORMAT_RGB888:
1270
    case SIXEL_PIXELFORMAT_BGR888:
1271
        expand_rgb(dst, src, width, height, src_pixelformat, 3);
×
1272
        *dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
1273
        break;
×
1274
    case SIXEL_PIXELFORMAT_RGBFLOAT32:
512✔
1275
    case SIXEL_PIXELFORMAT_LINEARRGBFLOAT32:
1276
    case SIXEL_PIXELFORMAT_OKLABFLOAT32:
1277
    case SIXEL_PIXELFORMAT_CIELABFLOAT32:
1278
    case SIXEL_PIXELFORMAT_DIN99DFLOAT32:
1279
        depth = sixel_helper_compute_depth(src_pixelformat);
846✔
1280
        if (depth <= 0) {
846!
1281
            status = SIXEL_BAD_ARGUMENT;
×
1282
            goto end;
×
1283
        }
1284
        expand_rgb(dst, src, width, height, src_pixelformat, depth);
846✔
1285
        *dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
846✔
1286
        break;
846✔
1287
    case SIXEL_PIXELFORMAT_RGBA8888:
251✔
1288
    case SIXEL_PIXELFORMAT_ARGB8888:
1289
    case SIXEL_PIXELFORMAT_BGRA8888:
1290
    case SIXEL_PIXELFORMAT_ABGR8888:
1291
        expand_rgb(dst, src, width, height, src_pixelformat, 4);
543✔
1292
        *dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
543✔
1293
        break;
543✔
1294
    case SIXEL_PIXELFORMAT_PAL1:
1295
    case SIXEL_PIXELFORMAT_PAL2:
1296
    case SIXEL_PIXELFORMAT_PAL4:
1297
        *dst_pixelformat = SIXEL_PIXELFORMAT_PAL8;
24✔
1298
        status = expand_palette(dst, src, width, height, src_pixelformat);
24✔
1299
        if (SIXEL_FAILED(status)) {
24!
1300
            goto end;
×
1301
        }
1302
        break;
24✔
1303
    case SIXEL_PIXELFORMAT_G1:
1304
    case SIXEL_PIXELFORMAT_G2:
1305
    case SIXEL_PIXELFORMAT_G4:
1306
        *dst_pixelformat = SIXEL_PIXELFORMAT_G8;
×
1307
        status = expand_palette(dst, src, width, height, src_pixelformat);
×
1308
        if (SIXEL_FAILED(status)) {
×
1309
            goto end;
×
1310
        }
1311
        break;
1312
    case SIXEL_PIXELFORMAT_PAL8:
1313
        memcpy(dst, src, (size_t)(width * height));
×
1314
        *dst_pixelformat = src_pixelformat;
×
1315
        break;
×
1316
    default:
1317
        status = SIXEL_BAD_ARGUMENT;
×
1318
        goto end;
×
1319
    }
1320

1321
    status = SIXEL_OK;
696✔
1322

1323
end:
763✔
1324
    return status;
2,155✔
1325
}
696✔
1326

1327

1328
/* Normalize RGB888 input without modification. */
1329

1330
/* emacs Local Variables:      */
1331
/* emacs mode: c               */
1332
/* emacs tab-width: 4          */
1333
/* emacs indent-tabs-mode: nil */
1334
/* emacs c-basic-offset: 4     */
1335
/* emacs End:                  */
1336
/* vim: set expandtab ts=4 sts=4 sw=4 : */
1337
/* 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