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

saitoha / libsixel / 19765269522

28 Nov 2025 01:30PM UTC coverage: 39.983% (-1.6%) from 41.616%
19765269522

push

github

web-flow
Merge pull request #214 from saitoha/codex/add-logging-to-resize-processing

Limit scale logging and add timeline window controls

9788 of 35562 branches covered (27.52%)

9 of 63 new or added lines in 1 file covered. (14.29%)

281 existing lines in 19 files now uncovered.

12991 of 32491 relevant lines covered (39.98%)

619662.16 hits per line

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

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

24
#include "config.h"
25

26
#include <math.h>
27
#include <stddef.h>
28

29
#include <sixel.h>
30

31
#include "colorspace.h"
32

33
#define SIXEL_COLORSPACE_LUT_SIZE 256
34
#define SIXEL_OKLAB_AB_OFFSET 0.5
35
#define SIXEL_OKLAB_AB_SCALE  255.0
36
#define SIXEL_CIELAB_AB_SCALE 128.0
37
#define SIXEL_CIELAB_L_SCALE  100.0
38
#define SIXEL_CIELAB_AB_LIMIT 1.5
39
#define SIXEL_DIN99D_L_SCALE  100.0
40
#define SIXEL_DIN99D_AB_RANGE 50.0
41

42
static const double sixel_linear_srgb_to_smptec_matrix[3][3] = {
43
    { 1.0651944799343782, -0.05539144537002962, -0.009975616485882548 },
44
    { -0.019633066659433226,  1.0363870284433383, -0.016731961783904975 },
45
    { 0.0016324889176928742,  0.004413466273704836,  0.994192644808602 }
46
};
47

48
static const double sixel_linear_smptec_to_srgb_matrix[3][3] = {
49
    { 0.9397048483892231,  0.05018036042570272,  0.010273409684415205 },
50
    { 0.01777536262173348, 0.9657705626655305,  0.01643197976410589 },
51
    { -0.0016219271954016755, -0.00436969856687614,  1.0057514450874723 }
52
};
53

54
static unsigned char gamma_to_linear_lut[SIXEL_COLORSPACE_LUT_SIZE];
55
static unsigned char linear_to_gamma_lut[SIXEL_COLORSPACE_LUT_SIZE];
56
static int tables_initialized = 0;
57

58
static inline double
59
sixel_clamp_unit(double value)
×
60
{
61
    if (value < 0.0) {
×
62
        return 0.0;
×
63
    }
64
    if (value > 1.0) {
×
65
        return 1.0;
×
66
    }
67
    return value;
×
68
}
69

70
static inline double
71
sixel_srgb_unit_to_linear(double value)
×
72
{
73
    double x;
74

75
    x = sixel_clamp_unit(value);
×
76
    if (x <= 0.04045) {
×
77
        return x / 12.92;
×
78
    }
79

80
    return pow((x + 0.055) / 1.055, 2.4);
×
81
}
82

83
static inline double
84
sixel_linear_to_srgb_unit(double value)
×
85
{
86
    double y;
87

88
    if (value <= 0.0) {
×
89
        return 0.0;
×
90
    }
91
    if (value >= 1.0) {
×
92
        return 1.0;
×
93
    }
94

95
    if (value <= 0.0031308) {
×
96
        y = value * 12.92;
×
97
    } else {
98
        y = 1.055 * pow(value, 1.0 / 2.4) - 0.055;
×
99
    }
100

101
    return sixel_clamp_unit(y);
×
102
}
103

104
static inline double
105
sixel_smptec_unit_to_linear(double value)
×
106
{
107
    double x;
108

109
    x = sixel_clamp_unit(value);
×
110
    if (x <= 0.0) {
×
111
        return 0.0;
×
112
    }
113
    if (x >= 1.0) {
×
114
        return 1.0;
×
115
    }
116

117
    return pow(x, 2.2);
×
118
}
119

120
static inline double
121
sixel_linear_to_smptec_unit(double value)
×
122
{
123
    double y;
124

125
    if (value <= 0.0) {
×
126
        return 0.0;
×
127
    }
128
    if (value >= 1.0) {
×
129
        return 1.0;
×
130
    }
131

132
    y = pow(value, 1.0 / 2.2);
×
133
    return sixel_clamp_unit(y);
×
134
}
135

136
static inline double
137
sixel_oklab_clamp_ab(double value)
×
138
{
139
    double lower;
140
    double upper;
141

142
    lower = -SIXEL_OKLAB_AB_OFFSET;
×
143
    upper = SIXEL_OKLAB_AB_OFFSET;
×
144
    if (value < lower) {
×
145
        return lower;
×
146
    }
147
    if (value > upper) {
×
148
        return upper;
×
149
    }
150

151
    return value;
×
152
}
153

154
static inline double
155
sixel_cielab_clamp_ab(double value)
×
156
{
157
    if (value < -SIXEL_CIELAB_AB_LIMIT) {
×
158
        return -SIXEL_CIELAB_AB_LIMIT;
×
159
    }
160
    if (value > SIXEL_CIELAB_AB_LIMIT) {
×
161
        return SIXEL_CIELAB_AB_LIMIT;
×
162
    }
163

164
    return value;
×
165
}
166

167
static inline double
168
sixel_din99d_clamp_ab_norm(double value)
×
169
{
170
    if (value < -1.0) {
×
171
        return -1.0;
×
172
    }
173
    if (value > 1.0) {
×
174
        return 1.0;
×
175
    }
176

177
    return value;
×
178
}
179

180
#if 0
181
static inline double
182
sixel_din99d_clamp_ab(double value)
183
{
184
    if (value < -SIXEL_DIN99D_AB_RANGE) {
185
        return -SIXEL_DIN99D_AB_RANGE;
186
    }
187
    if (value > SIXEL_DIN99D_AB_RANGE) {
188
        return SIXEL_DIN99D_AB_RANGE;
189
    }
190

191
    return value;
192
}
193
#endif
194

195
static unsigned char
196
sixel_colorspace_clamp(int value)
33,792✔
197
{
198
    if (value < 0) {
33,792!
199
        return 0;
×
200
    }
201
    if (value > 255) {
33,792!
202
        return 255;
×
203
    }
204
    return (unsigned char)value;
33,792✔
205
}
206

207
static inline double
208
sixel_srgb_to_linear_double(unsigned char v)
×
209
{
210
    double x = (double)v / 255.0;
×
211

212
    return sixel_srgb_unit_to_linear(x);
×
213
}
214

215
static inline unsigned char
216
sixel_linear_double_to_srgb(double v)
×
217
{
218
    double y;
219

220
    y = sixel_linear_to_srgb_unit(v);
×
221
    return sixel_colorspace_clamp((int)(y * 255.0 + 0.5));
×
222
}
223

224
static inline unsigned char
225
sixel_linear_double_to_byte(double v)
×
226
{
227
    if (v <= 0.0) {
×
228
        return 0;
×
229
    }
230
    if (v >= 1.0) {
×
231
        return 255;
×
232
    }
233

234
    return sixel_colorspace_clamp((int)(v * 255.0 + 0.5));
×
235
}
236

237
static inline double
238
sixel_smptec_to_linear_double(unsigned char v)
×
239
{
240
    double x = (double)v / 255.0;
×
241

242
    return sixel_smptec_unit_to_linear(x);
×
243
}
244

245
static inline unsigned char
246
sixel_linear_double_to_smptec(double v)
×
247
{
248
    double y;
249

250
    y = sixel_linear_to_smptec_unit(v);
×
251
    return sixel_colorspace_clamp((int)(y * 255.0 + 0.5));
×
252
}
253

254
static inline double
255
sixel_cielab_f(double t)
×
256
{
257
    double delta;
258

259
    delta = 6.0 / 29.0;
×
260
    if (t > delta * delta * delta) {
×
261
        return cbrt(t);
×
262
    }
263

264
    return (t / (3.0 * delta * delta)) + (4.0 / 29.0);
×
265
}
266

267
static inline double
268
sixel_cielab_f_inv(double t)
×
269
{
270
    double delta;
271

272
    delta = 6.0 / 29.0;
×
273
    if (t > delta) {
×
274
        return t * t * t;
×
275
    }
276

277
    return 3.0 * delta * delta * (t - (4.0 / 29.0));
×
278
}
279

280
static inline unsigned char
281
sixel_oklab_encode_L(double L)
×
282
{
283
    if (L < 0.0) {
×
284
        L = 0.0;
×
285
    } else if (L > 1.0) {
×
286
        L = 1.0;
×
287
    }
288

289
    return sixel_colorspace_clamp((int)(L * 255.0 + 0.5));
×
290
}
291

292
static inline unsigned char
293
sixel_oklab_encode_ab(double value)
×
294
{
295
    double encoded = value + SIXEL_OKLAB_AB_OFFSET;
×
296

297
    if (encoded < 0.0) {
×
298
        encoded = 0.0;
×
299
    } else if (encoded > 1.0) {
×
300
        encoded = 1.0;
×
301
    }
302

303
    return sixel_colorspace_clamp((int)(encoded * SIXEL_OKLAB_AB_SCALE + 0.5));
×
304
}
305

306
static inline double
307
sixel_oklab_decode_ab(unsigned char v)
×
308
{
309
    return (double)v / SIXEL_OKLAB_AB_SCALE - SIXEL_OKLAB_AB_OFFSET;
×
310
}
311

312
static inline unsigned char
313
sixel_cielab_encode_L(double L)
×
314
{
315
    double clamped;
316

317
    clamped = sixel_clamp_unit(L);
×
318
    return sixel_colorspace_clamp((int)(clamped * 255.0 + 0.5));
×
319
}
320

321
static inline unsigned char
322
sixel_cielab_encode_ab(double value)
×
323
{
324
    double shifted;
325
    double normalized;
326

327
    shifted = sixel_cielab_clamp_ab(value);
×
328
    normalized = (shifted / (2.0 * SIXEL_CIELAB_AB_LIMIT)) + 0.5;
×
329
    return sixel_colorspace_clamp((int)(normalized * 255.0 + 0.5));
×
330
}
331

332
static inline double
333
sixel_cielab_decode_ab(unsigned char v)
×
334
{
335
    double encoded;
336

337
    encoded = (double)v / 255.0;
×
338
    return (encoded - 0.5) * (2.0 * SIXEL_CIELAB_AB_LIMIT);
×
339
}
340

341
static inline unsigned char
342
sixel_din99d_encode_L(double L)
×
343
{
344
    double clamped;
345

346
    clamped = sixel_clamp_unit(L);
×
347
    return sixel_colorspace_clamp((int)(clamped * 255.0 + 0.5));
×
348
}
349

350
static inline unsigned char
351
sixel_din99d_encode_ab(double value)
×
352
{
353
    double normalized;
354

355
    normalized = (sixel_din99d_clamp_ab_norm(value) / 2.0) + 0.5;
×
356
    return sixel_colorspace_clamp((int)(normalized * 255.0 + 0.5));
×
357
}
358

359
static inline double
360
sixel_din99d_decode_ab(unsigned char v)
×
361
{
362
    double encoded;
363

364
    encoded = (double)v / 255.0;
×
365
    return sixel_din99d_clamp_ab_norm((encoded - 0.5) * 2.0);
×
366
}
367

368
static void
369
sixel_linear_to_cielab(double r, double g, double b,
×
370
                       double *L, double *A, double *B)
371
{
372
    const double Xn = 0.95047;
×
373
    const double Yn = 1.00000;
×
374
    const double Zn = 1.08883;
×
375
    double X;
376
    double Y;
377
    double Z;
378
    double fx;
379
    double fy;
380
    double fz;
381
    double L_component;
382
    double a_component;
383
    double b_component;
384

385
    X = 0.4124564 * r + 0.3575761 * g + 0.1804375 * b;
×
386
    Y = 0.2126729 * r + 0.7151522 * g + 0.0721750 * b;
×
387
    Z = 0.0193339 * r + 0.1191920 * g + 0.9503041 * b;
×
388

389
    fx = sixel_cielab_f(X / Xn);
×
390
    fy = sixel_cielab_f(Y / Yn);
×
391
    fz = sixel_cielab_f(Z / Zn);
×
392

393
    L_component = 116.0 * fy - 16.0;
×
394
    a_component = 500.0 * (fx - fy);
×
395
    b_component = 200.0 * (fy - fz);
×
396

397
    *L = sixel_clamp_unit(L_component / SIXEL_CIELAB_L_SCALE);
×
398
    *A = sixel_cielab_clamp_ab(a_component / SIXEL_CIELAB_AB_SCALE);
×
399
    *B = sixel_cielab_clamp_ab(b_component / SIXEL_CIELAB_AB_SCALE);
×
400
}
×
401

402
static void
403
sixel_cielab_to_linear(double L, double A, double B,
×
404
                       double *r, double *g, double *b)
405
{
406
    const double Xn = 0.95047;
×
407
    const double Yn = 1.00000;
×
408
    const double Zn = 1.08883;
×
409
    double L_component;
410
    double a_component;
411
    double b_component;
412
    double fx;
413
    double fy;
414
    double fz;
415
    double X;
416
    double Y;
417
    double Z;
418

419
    L_component = sixel_clamp_unit(L) * SIXEL_CIELAB_L_SCALE;
×
420
    a_component = sixel_cielab_clamp_ab(A) * SIXEL_CIELAB_AB_SCALE;
×
421
    b_component = sixel_cielab_clamp_ab(B) * SIXEL_CIELAB_AB_SCALE;
×
422

423
    fy = (L_component + 16.0) / 116.0;
×
424
    fx = fy + (a_component / 500.0);
×
425
    fz = fy - (b_component / 200.0);
×
426

427
    X = Xn * sixel_cielab_f_inv(fx);
×
428
    Y = Yn * sixel_cielab_f_inv(fy);
×
429
    Z = Zn * sixel_cielab_f_inv(fz);
×
430

431
    *r = 3.2404542 * X - 1.5371385 * Y - 0.4985314 * Z;
×
432
    *g = -0.9692660 * X + 1.8760108 * Y + 0.0415560 * Z;
×
433
    *b = 0.0556434 * X - 0.2040259 * Y + 1.0572252 * Z;
×
434

435
    *r = sixel_clamp_unit(*r);
×
436
    *g = sixel_clamp_unit(*g);
×
437
    *b = sixel_clamp_unit(*b);
×
438
}
×
439

440
static void
441
sixel_cielab_to_din99d(double L,
×
442
                       double a,
443
                       double b,
444
                       double *L99d,
445
                       double *A99d,
446
                       double *B99d)
447
{
448
    /* Convert from CIELAB to DIN99d using Cui et al. (2002) parameters. */
449
    const double c1 = 325.22;
×
450
    const double c2 = 0.0036;
×
451
    const double c3 = 50.0;
×
452
    const double c4 = 1.14;
×
453
    const double c5 = 22.5;
×
454
    const double c6 = 0.06;
×
455
    const double c7 = 50.0;
×
456
    const double c8 = 1.0;
×
457
    const double rad_per_degree = 3.14159265358979323846 / 180.0;
×
458
    double radians_c3;
459
    double radians_c7;
460
    double e;
461
    double f;
462
    double G;
463
    double h_ef;
464
    double C99;
465

466
    radians_c3 = c3 * rad_per_degree;
×
467
    radians_c7 = c7 * rad_per_degree;
×
468

469
    e = cos(radians_c3) * a + sin(radians_c3) * b;
×
470
    f = c4 * (-sin(radians_c3) * a + cos(radians_c3) * b);
×
471
    G = sqrt(e * e + f * f);
×
472
    h_ef = atan2(f, e) + radians_c7;
×
473

474
    C99 = c5 * (log1p(c6 * G)) / (c8);
×
475

476
    *A99d = C99 * cos(h_ef);
×
477
    *B99d = C99 * sin(h_ef);
×
478
    *L99d = c1 * log1p(c2 * L);
×
479
}
×
480

481
static void
482
sixel_din99d_to_cielab(double L99d,
×
483
                       double A99d,
484
                       double B99d,
485
                       double *L,
486
                       double *a,
487
                       double *b)
488
{
489
    /* Convert from DIN99d back to absolute CIELAB coordinates. */
490
    const double c1 = 325.22;
×
491
    const double c2 = 0.0036;
×
492
    const double c3 = 50.0;
×
493
    const double c4 = 1.14;
×
494
    const double c5 = 22.5;
×
495
    const double c6 = 0.06;
×
496
    const double c7 = 50.0;
×
497
    const double c8 = 1.0;
×
498
    const double rad_per_degree = 3.14159265358979323846 / 180.0;
×
499
    double radians_c3;
500
    double radians_c7;
501
    double h99;
502
    double C99;
503
    double G;
504
    double e;
505
    double f;
506

507
    radians_c3 = c3 * rad_per_degree;
×
508
    radians_c7 = c7 * rad_per_degree;
×
509

510
    h99 = atan2(B99d, A99d) - radians_c7;
×
511
    C99 = hypot(A99d, B99d);
×
512
    G = expm1((c8 / c5) * C99) / c6;
×
513

514
    e = G * cos(h99);
×
515
    f = G * sin(h99);
×
516

517
    *a = e * cos(radians_c3) - (f / c4) * sin(radians_c3);
×
518
    *b = e * sin(radians_c3) + (f / c4) * cos(radians_c3);
×
519
    *L = expm1(L99d / c1) / c2;
×
520
}
×
521

522
static void
523
sixel_linear_to_din99d(double r,
×
524
                       double g,
525
                       double b,
526
                       double *L99d_norm,
527
                       double *A99d_norm,
528
                       double *B99d_norm)
529
{
530
    double L;
531
    double A;
532
    double B;
533
    double L_star;
534
    double a_star;
535
    double b_star;
536
    double L99d;
537
    double A99d;
538
    double B99d;
539

540
    sixel_linear_to_cielab(r, g, b, &L, &A, &B);
×
541

542
    L_star = L * SIXEL_CIELAB_L_SCALE;
×
543
    a_star = A * SIXEL_CIELAB_AB_SCALE;
×
544
    b_star = B * SIXEL_CIELAB_AB_SCALE;
×
545

546
    sixel_cielab_to_din99d(L_star, a_star, b_star, &L99d, &A99d, &B99d);
×
547

548
    *L99d_norm = sixel_clamp_unit(L99d / SIXEL_DIN99D_L_SCALE);
×
549
    *A99d_norm = sixel_din99d_clamp_ab_norm(
×
550
        A99d / SIXEL_DIN99D_AB_RANGE);
551
    *B99d_norm = sixel_din99d_clamp_ab_norm(
×
552
        B99d / SIXEL_DIN99D_AB_RANGE);
553
}
×
554

555
static void
556
sixel_din99d_to_linear(double L99d_norm,
×
557
                       double A99d_norm,
558
                       double B99d_norm,
559
                       double *r,
560
                       double *g,
561
                       double *b)
562
{
563
    double L_star;
564
    double a_star;
565
    double b_star;
566
    double L;
567
    double A;
568
    double B;
569

570
    L = sixel_clamp_unit(L99d_norm) * SIXEL_DIN99D_L_SCALE;
×
571
    A = sixel_din99d_clamp_ab_norm(A99d_norm) * SIXEL_DIN99D_AB_RANGE;
×
572
    B = sixel_din99d_clamp_ab_norm(B99d_norm) * SIXEL_DIN99D_AB_RANGE;
×
573

574
    sixel_din99d_to_cielab(L, A, B, &L_star, &a_star, &b_star);
×
575

576
    L_star = sixel_clamp_unit(L_star / SIXEL_CIELAB_L_SCALE);
×
577
    a_star = sixel_cielab_clamp_ab(a_star / SIXEL_CIELAB_AB_SCALE);
×
578
    b_star = sixel_cielab_clamp_ab(b_star / SIXEL_CIELAB_AB_SCALE);
×
579

580
    sixel_cielab_to_linear(L_star, a_star, b_star, r, g, b);
×
581
}
×
582

583
static void
584
sixel_linear_to_oklab(double r, double g, double b,
×
585
                      double *L, double *A, double *B)
586
{
587
    double l;
588
    double m;
589
    double s;
590
    double l_;
591
    double m_;
592
    double s_;
593

594
    l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;
×
595
    m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b;
×
596
    s = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b;
×
597

598
    l_ = cbrt(l);
×
599
    m_ = cbrt(m);
×
600
    s_ = cbrt(s);
×
601

602
    *L = 0.2104542553 * l_ + 0.7936177850 * m_ - 0.0040720468 * s_;
×
603
    *A = 1.9779984951 * l_ - 2.4285922050 * m_ + 0.4505937099 * s_;
×
604
    *B = 0.0259040371 * l_ + 0.7827717662 * m_ - 0.8086757660 * s_;
×
605
}
×
606

607
static void
608
sixel_oklab_to_linear(double L, double A, double B,
×
609
                      double *r, double *g, double *b)
610
{
611
    double l_;
612
    double m_;
613
    double s_;
614
    double l;
615
    double m;
616
    double s;
617

618
    l_ = L + 0.3963377774 * A + 0.2158037573 * B;
×
619
    m_ = L - 0.1055613458 * A - 0.0638541728 * B;
×
620
    s_ = L - 0.0894841775 * A - 1.2914855480 * B;
×
621

622
    l = l_ * l_ * l_;
×
623
    m = m_ * m_ * m_;
×
624
    s = s_ * s_ * s_;
×
625

626
    *r = 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s;
×
627
    *g = -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s;
×
628
    *b = -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s;
×
629

630
    if (*r < 0.0) {
×
631
        *r = 0.0;
×
632
    }
633
    if (*g < 0.0) {
×
634
        *g = 0.0;
×
635
    }
636
    if (*b < 0.0) {
×
637
        *b = 0.0;
×
638
    }
639
}
×
640

641
static void
642
sixel_linear_srgb_to_smptec(double r, double g, double b,
×
643
                            double *rs, double *gs, double *bs)
644
{
645
    double sr;
646
    double sg;
647
    double sb;
648

649
    sr = sixel_linear_srgb_to_smptec_matrix[0][0] * r
×
650
       + sixel_linear_srgb_to_smptec_matrix[0][1] * g
×
651
       + sixel_linear_srgb_to_smptec_matrix[0][2] * b;
×
652
    sg = sixel_linear_srgb_to_smptec_matrix[1][0] * r
×
653
       + sixel_linear_srgb_to_smptec_matrix[1][1] * g
×
654
       + sixel_linear_srgb_to_smptec_matrix[1][2] * b;
×
655
    sb = sixel_linear_srgb_to_smptec_matrix[2][0] * r
×
656
       + sixel_linear_srgb_to_smptec_matrix[2][1] * g
×
657
       + sixel_linear_srgb_to_smptec_matrix[2][2] * b;
×
658

659
    *rs = sixel_clamp_unit(sr);
×
660
    *gs = sixel_clamp_unit(sg);
×
661
    *bs = sixel_clamp_unit(sb);
×
662
}
×
663

664
static void
665
sixel_linear_smptec_to_srgb(double rs, double gs, double bs,
×
666
                            double *r, double *g, double *b)
667
{
668
    double r_lin;
669
    double g_lin;
670
    double b_lin;
671

672
    r_lin = sixel_linear_smptec_to_srgb_matrix[0][0] * rs
×
673
          + sixel_linear_smptec_to_srgb_matrix[0][1] * gs
×
674
          + sixel_linear_smptec_to_srgb_matrix[0][2] * bs;
×
675
    g_lin = sixel_linear_smptec_to_srgb_matrix[1][0] * rs
×
676
          + sixel_linear_smptec_to_srgb_matrix[1][1] * gs
×
677
          + sixel_linear_smptec_to_srgb_matrix[1][2] * bs;
×
678
    b_lin = sixel_linear_smptec_to_srgb_matrix[2][0] * rs
×
679
          + sixel_linear_smptec_to_srgb_matrix[2][1] * gs
×
680
          + sixel_linear_smptec_to_srgb_matrix[2][2] * bs;
×
681

682
    *r = sixel_clamp_unit(r_lin);
×
683
    *g = sixel_clamp_unit(g_lin);
×
684
    *b = sixel_clamp_unit(b_lin);
×
685
}
×
686

687
static void
688
sixel_colorspace_init_tables(void)
66✔
689
{
690
    int i;
691
    double gamma_value;
692
    double linear_value;
693
    double converted;
694

695
    if (tables_initialized) {
66!
696
        return;
×
697
    }
698

699
    for (i = 0; i < SIXEL_COLORSPACE_LUT_SIZE; ++i) {
16,962!
700
        gamma_value = (double)i / 255.0;
16,896✔
701
        if (gamma_value <= 0.04045) {
16,896!
702
            converted = gamma_value / 12.92;
726✔
703
        } else {
704
            converted = pow((gamma_value + 0.055) / 1.055, 2.4);
16,170✔
705
        }
706
        gamma_to_linear_lut[i] =
16,896✔
707
            sixel_colorspace_clamp((int)(converted * 255.0 + 0.5));
16,896✔
708
    }
709

710
    for (i = 0; i < SIXEL_COLORSPACE_LUT_SIZE; ++i) {
16,962!
711
        linear_value = (double)i / 255.0;
16,896✔
712
        if (linear_value <= 0.0031308) {
16,896!
713
            converted = linear_value * 12.92;
66✔
714
        } else {
715
            converted = 1.055 * pow(linear_value, 1.0 / 2.4) - 0.055;
16,830✔
716
        }
717
        linear_to_gamma_lut[i] =
16,896✔
718
            sixel_colorspace_clamp((int)(converted * 255.0 + 0.5));
16,896✔
719
    }
720

721
    tables_initialized = 1;
66✔
722
}
723

724
static void
725
sixel_decode_linear_from_colorspace(int colorspace,
×
726
                                    unsigned char r8,
727
                                    unsigned char g8,
728
                                    unsigned char b8,
729
                                    double *r_lin,
730
                                    double *g_lin,
731
                                    double *b_lin)
732
{
733
    switch (colorspace) {
×
UNCOV
734
    case SIXEL_COLORSPACE_GAMMA:
×
735
        *r_lin = sixel_srgb_to_linear_double(r8);
×
736
        *g_lin = sixel_srgb_to_linear_double(g8);
×
737
        *b_lin = sixel_srgb_to_linear_double(b8);
×
738
        break;
×
UNCOV
739
    case SIXEL_COLORSPACE_LINEAR:
×
740
        *r_lin = (double)r8 / 255.0;
×
741
        *g_lin = (double)g8 / 255.0;
×
742
        *b_lin = (double)b8 / 255.0;
×
743
        break;
×
UNCOV
744
    case SIXEL_COLORSPACE_OKLAB:
×
745
    {
746
        double L = (double)r8 / 255.0;
×
747
        double A = sixel_oklab_decode_ab(g8);
×
748
        double B = sixel_oklab_decode_ab(b8);
×
749
        sixel_oklab_to_linear(L, A, B, r_lin, g_lin, b_lin);
×
750
        break;
×
751
    }
UNCOV
752
    case SIXEL_COLORSPACE_CIELAB:
×
753
    {
754
        double L;
755
        double A;
756
        double B;
757

758
        L = (double)r8 / 255.0;
×
759
        A = sixel_cielab_decode_ab(g8);
×
760
        B = sixel_cielab_decode_ab(b8);
×
761
        sixel_cielab_to_linear(L, A, B, r_lin, g_lin, b_lin);
×
762
        break;
×
763
    }
UNCOV
764
    case SIXEL_COLORSPACE_DIN99D:
×
765
    {
766
        double L;
767
        double A;
768
        double B;
769

770
        L = (double)r8 / 255.0;
×
771
        A = sixel_din99d_decode_ab(g8);
×
772
        B = sixel_din99d_decode_ab(b8);
×
773
        sixel_din99d_to_linear(L, A, B, r_lin, g_lin, b_lin);
×
774
        break;
×
775
    }
UNCOV
776
    case SIXEL_COLORSPACE_SMPTEC:
×
777
    {
778
        double r_smptec = sixel_smptec_to_linear_double(r8);
×
779
        double g_smptec = sixel_smptec_to_linear_double(g8);
×
780
        double b_smptec = sixel_smptec_to_linear_double(b8);
×
781
        sixel_linear_smptec_to_srgb(r_smptec, g_smptec, b_smptec,
×
782
                                    r_lin, g_lin, b_lin);
783
        break;
×
784
    }
UNCOV
785
    default:
×
786
        *r_lin = (double)r8 / 255.0;
×
787
        *g_lin = (double)g8 / 255.0;
×
788
        *b_lin = (double)b8 / 255.0;
×
789
        break;
×
790
    }
791
}
×
792

793
/*
794
 * Float variant of the colorspace decoder so RGBFLOAT32 buffers can skip the
795
 * byte quantisation that the legacy helper performs.
796
 */
797
static void
798
sixel_decode_linear_from_colorspace_float(int colorspace,
×
799
                                          float r_value,
800
                                          float g_value,
801
                                          float b_value,
802
                                          double *r_lin,
803
                                          double *g_lin,
804
                                          double *b_lin)
805
{
806
    double r;
807
    double g;
808
    double b;
809

810
    r = (double)r_value;
×
811
    g = (double)g_value;
×
812
    b = (double)b_value;
×
813

814
    switch (colorspace) {
×
UNCOV
815
    case SIXEL_COLORSPACE_GAMMA:
×
816
        *r_lin = sixel_srgb_unit_to_linear(r);
×
817
        *g_lin = sixel_srgb_unit_to_linear(g);
×
818
        *b_lin = sixel_srgb_unit_to_linear(b);
×
819
        break;
×
UNCOV
820
    case SIXEL_COLORSPACE_LINEAR:
×
821
        *r_lin = sixel_clamp_unit(r);
×
822
        *g_lin = sixel_clamp_unit(g);
×
823
        *b_lin = sixel_clamp_unit(b);
×
824
        break;
×
UNCOV
825
    case SIXEL_COLORSPACE_OKLAB:
×
826
        sixel_oklab_to_linear(r, g, b, r_lin, g_lin, b_lin);
×
827
        break;
×
UNCOV
828
    case SIXEL_COLORSPACE_CIELAB:
×
829
        sixel_cielab_to_linear(r, g, b, r_lin, g_lin, b_lin);
×
830
        break;
×
UNCOV
831
    case SIXEL_COLORSPACE_DIN99D:
×
832
        sixel_din99d_to_linear(r, g, b, r_lin, g_lin, b_lin);
×
833
        break;
×
UNCOV
834
    case SIXEL_COLORSPACE_SMPTEC:
×
835
    {
836
        double r_smptec;
837
        double g_smptec;
838
        double b_smptec;
839

840
        r_smptec = sixel_smptec_unit_to_linear(r);
×
841
        g_smptec = sixel_smptec_unit_to_linear(g);
×
842
        b_smptec = sixel_smptec_unit_to_linear(b);
×
843
        sixel_linear_smptec_to_srgb(r_smptec,
×
844
                                    g_smptec,
845
                                    b_smptec,
846
                                    r_lin,
847
                                    g_lin,
848
                                    b_lin);
849
        break;
×
850
    }
UNCOV
851
    default:
×
852
        *r_lin = sixel_clamp_unit(r);
×
853
        *g_lin = sixel_clamp_unit(g);
×
854
        *b_lin = sixel_clamp_unit(b);
×
855
        break;
×
856
    }
857
}
×
858

859
static void
860
sixel_encode_linear_to_colorspace(int colorspace,
×
861
                                  double r_lin,
862
                                  double g_lin,
863
                                  double b_lin,
864
                                  unsigned char *r8,
865
                                  unsigned char *g8,
866
                                  unsigned char *b8)
867
{
868
    double L;
869
    double A;
870
    double B;
871

872
    switch (colorspace) {
×
UNCOV
873
    case SIXEL_COLORSPACE_GAMMA:
×
874
        *r8 = sixel_linear_double_to_srgb(r_lin);
×
875
        *g8 = sixel_linear_double_to_srgb(g_lin);
×
876
        *b8 = sixel_linear_double_to_srgb(b_lin);
×
877
        break;
×
UNCOV
878
    case SIXEL_COLORSPACE_LINEAR:
×
879
        *r8 = sixel_linear_double_to_byte(r_lin);
×
880
        *g8 = sixel_linear_double_to_byte(g_lin);
×
881
        *b8 = sixel_linear_double_to_byte(b_lin);
×
882
        break;
×
UNCOV
883
    case SIXEL_COLORSPACE_OKLAB:
×
884
        sixel_linear_to_oklab(r_lin, g_lin, b_lin, &L, &A, &B);
×
885
        *r8 = sixel_oklab_encode_L(L);
×
886
        *g8 = sixel_oklab_encode_ab(A);
×
887
        *b8 = sixel_oklab_encode_ab(B);
×
888
        break;
×
UNCOV
889
    case SIXEL_COLORSPACE_CIELAB:
×
890
        sixel_linear_to_cielab(r_lin, g_lin, b_lin, &L, &A, &B);
×
891
        *r8 = sixel_cielab_encode_L(L);
×
892
        *g8 = sixel_cielab_encode_ab(A);
×
893
        *b8 = sixel_cielab_encode_ab(B);
×
894
        break;
×
UNCOV
895
    case SIXEL_COLORSPACE_DIN99D:
×
896
        sixel_linear_to_din99d(r_lin, g_lin, b_lin, &L, &A, &B);
×
897
        *r8 = sixel_din99d_encode_L(L);
×
898
        *g8 = sixel_din99d_encode_ab(A);
×
899
        *b8 = sixel_din99d_encode_ab(B);
×
900
        break;
×
UNCOV
901
    case SIXEL_COLORSPACE_SMPTEC:
×
902
    {
903
        double r_smptec;
904
        double g_smptec;
905
        double b_smptec;
906

907
        sixel_linear_srgb_to_smptec(r_lin, g_lin, b_lin,
×
908
                                     &r_smptec, &g_smptec, &b_smptec);
909

910
        *r8 = sixel_linear_double_to_smptec(r_smptec);
×
911
        *g8 = sixel_linear_double_to_smptec(g_smptec);
×
912
        *b8 = sixel_linear_double_to_smptec(b_smptec);
×
913
        break;
×
914
    }
UNCOV
915
    default:
×
916
        *r8 = sixel_linear_double_to_byte(r_lin);
×
917
        *g8 = sixel_linear_double_to_byte(g_lin);
×
918
        *b8 = sixel_linear_double_to_byte(b_lin);
×
919
        break;
×
920
    }
921
}
×
922

923
static void
924
sixel_encode_linear_to_colorspace_float(int colorspace,
×
925
                                        double r_lin,
926
                                        double g_lin,
927
                                        double b_lin,
928
                                        float *r_value,
929
                                        float *g_value,
930
                                        float *b_value)
931
{
932
    double r;
933
    double g;
934
    double b;
935

936
    switch (colorspace) {
×
UNCOV
937
    case SIXEL_COLORSPACE_GAMMA:
×
938
        r = sixel_linear_to_srgb_unit(r_lin);
×
939
        g = sixel_linear_to_srgb_unit(g_lin);
×
940
        b = sixel_linear_to_srgb_unit(b_lin);
×
941
        break;
×
UNCOV
942
    case SIXEL_COLORSPACE_LINEAR:
×
943
        r = sixel_clamp_unit(r_lin);
×
944
        g = sixel_clamp_unit(g_lin);
×
945
        b = sixel_clamp_unit(b_lin);
×
946
        break;
×
UNCOV
947
    case SIXEL_COLORSPACE_OKLAB:
×
948
        sixel_linear_to_oklab(r_lin, g_lin, b_lin, &r, &g, &b);
×
949
        r = sixel_clamp_unit(r);
×
950
        g = sixel_oklab_clamp_ab(g);
×
951
        b = sixel_oklab_clamp_ab(b);
×
952
        break;
×
UNCOV
953
    case SIXEL_COLORSPACE_CIELAB:
×
954
        sixel_linear_to_cielab(r_lin, g_lin, b_lin, &r, &g, &b);
×
955
        r = sixel_clamp_unit(r);
×
956
        g = sixel_cielab_clamp_ab(g);
×
957
        b = sixel_cielab_clamp_ab(b);
×
958
        break;
×
UNCOV
959
    case SIXEL_COLORSPACE_DIN99D:
×
960
        sixel_linear_to_din99d(r_lin, g_lin, b_lin, &r, &g, &b);
×
961
        r = sixel_clamp_unit(r);
×
962
        g = sixel_din99d_clamp_ab_norm(g);
×
963
        b = sixel_din99d_clamp_ab_norm(b);
×
964
        break;
×
UNCOV
965
    case SIXEL_COLORSPACE_SMPTEC:
×
966
    {
967
        double r_smptec;
968
        double g_smptec;
969
        double b_smptec;
970

971
        sixel_linear_srgb_to_smptec(r_lin,
×
972
                                     g_lin,
973
                                     b_lin,
974
                                     &r_smptec,
975
                                     &g_smptec,
976
                                     &b_smptec);
977
        r = sixel_linear_to_smptec_unit(r_smptec);
×
978
        g = sixel_linear_to_smptec_unit(g_smptec);
×
979
        b = sixel_linear_to_smptec_unit(b_smptec);
×
980
        break;
×
981
    }
UNCOV
982
    default:
×
983
        r = sixel_clamp_unit(r_lin);
×
984
        g = sixel_clamp_unit(g_lin);
×
985
        b = sixel_clamp_unit(b_lin);
×
986
        break;
×
987
    }
988

989
    *r_value = (float)r;
×
990
    *g_value = (float)g;
×
991
    *b_value = (float)b;
×
992
}
×
993

994
static SIXELSTATUS
995
sixel_convert_pixels_via_linear(unsigned char *pixels,
×
996
                                size_t size,
997
                                int pixelformat,
998
                                int colorspace_src,
999
                                int colorspace_dst)
1000
{
1001
    size_t i;
1002
    int step;
1003
    int index_r;
1004
    int index_g;
1005
    int index_b;
1006

1007
    if (colorspace_src == colorspace_dst) {
×
1008
        return SIXEL_OK;
×
1009
    }
1010

1011
    switch (pixelformat) {
×
UNCOV
1012
    case SIXEL_PIXELFORMAT_RGB888:
×
1013
        step = 3;
×
1014
        index_r = 0;
×
1015
        index_g = 1;
×
1016
        index_b = 2;
×
1017
        break;
×
UNCOV
1018
    case SIXEL_PIXELFORMAT_BGR888:
×
1019
        step = 3;
×
1020
        index_r = 2;
×
1021
        index_g = 1;
×
1022
        index_b = 0;
×
1023
        break;
×
UNCOV
1024
    case SIXEL_PIXELFORMAT_RGBA8888:
×
1025
        step = 4;
×
1026
        index_r = 0;
×
1027
        index_g = 1;
×
1028
        index_b = 2;
×
1029
        break;
×
UNCOV
1030
    case SIXEL_PIXELFORMAT_BGRA8888:
×
1031
        step = 4;
×
1032
        index_r = 2;
×
1033
        index_g = 1;
×
1034
        index_b = 0;
×
1035
        break;
×
UNCOV
1036
    case SIXEL_PIXELFORMAT_ARGB8888:
×
1037
        step = 4;
×
1038
        index_r = 1;
×
1039
        index_g = 2;
×
1040
        index_b = 3;
×
1041
        break;
×
UNCOV
1042
    case SIXEL_PIXELFORMAT_ABGR8888:
×
1043
        step = 4;
×
1044
        index_r = 3;
×
1045
        index_g = 2;
×
1046
        index_b = 1;
×
1047
        break;
×
UNCOV
1048
    case SIXEL_PIXELFORMAT_G8:
×
1049
        step = 1;
×
1050
        index_r = 0;
×
1051
        index_g = 0;
×
1052
        index_b = 0;
×
1053
        break;
×
UNCOV
1054
    case SIXEL_PIXELFORMAT_GA88:
×
1055
        step = 2;
×
1056
        index_r = 0;
×
1057
        index_g = 0;
×
1058
        index_b = 0;
×
1059
        break;
×
UNCOV
1060
    case SIXEL_PIXELFORMAT_AG88:
×
1061
        step = 2;
×
1062
        index_r = 1;
×
1063
        index_g = 1;
×
1064
        index_b = 1;
×
1065
        break;
×
UNCOV
1066
    default:
×
1067
        return SIXEL_BAD_INPUT;
×
1068
    }
1069

1070
    if (size % (size_t)step != 0) {
×
1071
        return SIXEL_BAD_INPUT;
×
1072
    }
1073

1074
    for (i = 0; i < size; i += (size_t)step) {
×
1075
        unsigned char *pr = pixels + i + (size_t)index_r;
×
1076
        unsigned char *pg = pixels + i + (size_t)index_g;
×
1077
        unsigned char *pb = pixels + i + (size_t)index_b;
×
1078
        double r_lin;
1079
        double g_lin;
1080
        double b_lin;
1081

1082
        sixel_decode_linear_from_colorspace(colorspace_src,
×
1083
                                            *pr,
×
1084
                                            *pg,
×
1085
                                            *pb,
×
1086
                                            &r_lin,
1087
                                            &g_lin,
1088
                                            &b_lin);
1089

1090
        sixel_encode_linear_to_colorspace(colorspace_dst,
×
1091
                                          r_lin,
1092
                                          g_lin,
1093
                                          b_lin,
1094
                                          pr,
1095
                                          pg,
1096
                                          pb);
1097
    }
1098

1099
    return SIXEL_OK;
×
1100
}
1101

1102
/*
1103
 * Convert RGBFLOAT32 buffers in-place by round-tripping through linear space
1104
 * with double intermediates.  This keeps OKLab/linear conversions precise
1105
 * while sharing the same matrices as the byte implementation.
1106
 */
1107
static SIXELSTATUS
1108
sixel_convert_pixels_via_linear_float(float *pixels,
×
1109
                                      size_t size,
1110
                                      int colorspace_src,
1111
                                      int colorspace_dst)
1112
{
1113
    size_t pixel_total;
1114
    size_t index;
1115
    size_t base;
1116
    double r_lin;
1117
    double g_lin;
1118
    double b_lin;
1119
    float *pr;
1120
    float *pg;
1121
    float *pb;
1122

1123
    if (colorspace_src == colorspace_dst) {
×
1124
        return SIXEL_OK;
×
1125
    }
1126

1127
    if (size % (3U * sizeof(float)) != 0U) {
×
1128
        return SIXEL_BAD_INPUT;
×
1129
    }
1130

1131
    pixel_total = size / (3U * sizeof(float));
×
1132
    for (index = 0U; index < pixel_total; ++index) {
×
1133
        base = index * 3U;
×
1134
        pr = pixels + base + 0U;
×
1135
        pg = pixels + base + 1U;
×
1136
        pb = pixels + base + 2U;
×
1137

1138
        sixel_decode_linear_from_colorspace_float(colorspace_src,
×
1139
                                                  *pr,
1140
                                                  *pg,
1141
                                                  *pb,
1142
                                                  &r_lin,
1143
                                                  &g_lin,
1144
                                                  &b_lin);
1145

1146
        sixel_encode_linear_to_colorspace_float(colorspace_dst,
×
1147
                                                r_lin,
1148
                                                g_lin,
1149
                                                b_lin,
1150
                                                pr,
1151
                                                pg,
1152
                                                pb);
1153
    }
1154

1155
    return SIXEL_OK;
×
1156
}
1157

1158
static unsigned char
1159
sixel_colorspace_convert_component(unsigned char value,
17,850✔
1160
                                   int colorspace_src,
1161
                                   int colorspace_dst)
1162
{
1163
    if (colorspace_src == colorspace_dst) {
17,850!
1164
        return value;
×
1165
    }
1166

1167
    if (colorspace_src == SIXEL_COLORSPACE_GAMMA &&
17,850!
1168
            colorspace_dst == SIXEL_COLORSPACE_LINEAR) {
1169
        return gamma_to_linear_lut[value];
×
1170
    }
1171

1172
    if (colorspace_src == SIXEL_COLORSPACE_LINEAR &&
17,850!
1173
            colorspace_dst == SIXEL_COLORSPACE_GAMMA) {
1174
        return linear_to_gamma_lut[value];
×
1175
    }
1176

1177
    return value;
17,850✔
1178
}
1179

1180
int
1181
sixel_colorspace_supports_pixelformat(int pixelformat)
66✔
1182
{
1183
    switch (pixelformat) {
66!
1184
    case SIXEL_PIXELFORMAT_RGB888:
66✔
1185
    case SIXEL_PIXELFORMAT_BGR888:
1186
    case SIXEL_PIXELFORMAT_RGBA8888:
1187
    case SIXEL_PIXELFORMAT_ARGB8888:
1188
    case SIXEL_PIXELFORMAT_BGRA8888:
1189
    case SIXEL_PIXELFORMAT_ABGR8888:
1190
    case SIXEL_PIXELFORMAT_G8:
1191
    case SIXEL_PIXELFORMAT_GA88:
1192
    case SIXEL_PIXELFORMAT_AG88:
1193
    case SIXEL_PIXELFORMAT_RGBFLOAT32:
1194
    case SIXEL_PIXELFORMAT_LINEARRGBFLOAT32:
1195
    case SIXEL_PIXELFORMAT_OKLABFLOAT32:
1196
    case SIXEL_PIXELFORMAT_CIELABFLOAT32:
1197
    case SIXEL_PIXELFORMAT_DIN99DFLOAT32:
1198
        return 1;
66✔
UNCOV
1199
    default:
×
1200
        break;
×
1201
    }
1202

1203
    return 0;
×
1204
}
1205

1206
SIXELAPI SIXELSTATUS
1207
sixel_helper_convert_colorspace(unsigned char *pixels,
66✔
1208
                                size_t size,
1209
                                int pixelformat,
1210
                                int colorspace_src,
1211
                                int colorspace_dst)
1212
{
1213
    size_t i;
1214

1215
    if (pixels == NULL) {
66!
1216
        sixel_helper_set_additional_message(
×
1217
            "sixel_helper_convert_colorspace: pixels is null.");
1218
        return SIXEL_BAD_ARGUMENT;
×
1219
    }
1220

1221
    if (colorspace_src == colorspace_dst) {
66!
1222
        return SIXEL_OK;
×
1223
    }
1224

1225
    if (!sixel_colorspace_supports_pixelformat(pixelformat)) {
66!
1226
        sixel_helper_set_additional_message(
×
1227
            "sixel_helper_convert_colorspace: unsupported pixelformat.");
1228
        return SIXEL_BAD_INPUT;
×
1229
    }
1230

1231
    sixel_colorspace_init_tables();
66✔
1232

1233
    if (SIXEL_PIXELFORMAT_IS_FLOAT32(pixelformat)) {
66!
1234
        return sixel_convert_pixels_via_linear_float((float *)pixels,
×
1235
                                                     size,
1236
                                                     colorspace_src,
1237
                                                     colorspace_dst);
1238
    }
1239

1240
    if (colorspace_src == SIXEL_COLORSPACE_OKLAB ||
66!
1241
            colorspace_dst == SIXEL_COLORSPACE_OKLAB ||
66!
1242
            colorspace_src == SIXEL_COLORSPACE_CIELAB ||
66!
1243
            colorspace_dst == SIXEL_COLORSPACE_CIELAB ||
66!
1244
            colorspace_src == SIXEL_COLORSPACE_DIN99D ||
66!
1245
            colorspace_dst == SIXEL_COLORSPACE_DIN99D ||
66!
1246
            colorspace_src == SIXEL_COLORSPACE_SMPTEC ||
66!
1247
            colorspace_dst == SIXEL_COLORSPACE_SMPTEC) {
1248
        SIXELSTATUS status = sixel_convert_pixels_via_linear(pixels,
×
1249
                                                             size,
1250
                                                             pixelformat,
1251
                                                             colorspace_src,
1252
                                                             colorspace_dst);
1253
        if (SIXEL_FAILED(status)) {
×
1254
            sixel_helper_set_additional_message(
×
1255
                "sixel_helper_convert_colorspace: unsupported pixelformat for conversion.");
1256
        }
1257
        return status;
×
1258
    }
1259

1260
    switch (pixelformat) {
66!
1261
    case SIXEL_PIXELFORMAT_RGB888:
66✔
1262
        if (size % 3 != 0) {
66!
1263
            sixel_helper_set_additional_message(
×
1264
                "sixel_helper_convert_colorspace: invalid data size.");
1265
            return SIXEL_BAD_INPUT;
×
1266
        }
1267
        for (i = 0; i + 2 < size; i += 3) {
6,016!
1268
            pixels[i + 0] = sixel_colorspace_convert_component(
11,900✔
1269
                pixels[i + 0], colorspace_src, colorspace_dst);
5,950✔
1270
            pixels[i + 1] = sixel_colorspace_convert_component(
11,900✔
1271
                pixels[i + 1], colorspace_src, colorspace_dst);
5,950✔
1272
            pixels[i + 2] = sixel_colorspace_convert_component(
5,950✔
1273
                pixels[i + 2], colorspace_src, colorspace_dst);
5,950✔
1274
        }
1275
        break;
66✔
UNCOV
1276
    case SIXEL_PIXELFORMAT_BGR888:
×
1277
        if (size % 3 != 0) {
×
1278
            sixel_helper_set_additional_message(
×
1279
                "sixel_helper_convert_colorspace: invalid data size.");
1280
            return SIXEL_BAD_INPUT;
×
1281
        }
1282
        for (i = 0; i + 2 < size; i += 3) {
×
1283
            pixels[i + 0] = sixel_colorspace_convert_component(
×
1284
                pixels[i + 0], colorspace_src, colorspace_dst);
×
1285
            pixels[i + 1] = sixel_colorspace_convert_component(
×
1286
                pixels[i + 1], colorspace_src, colorspace_dst);
×
1287
            pixels[i + 2] = sixel_colorspace_convert_component(
×
1288
                pixels[i + 2], colorspace_src, colorspace_dst);
×
1289
        }
1290
        break;
×
UNCOV
1291
    case SIXEL_PIXELFORMAT_RGBA8888:
×
1292
        if (size % 4 != 0) {
×
1293
            sixel_helper_set_additional_message(
×
1294
                "sixel_helper_convert_colorspace: invalid data size.");
1295
            return SIXEL_BAD_INPUT;
×
1296
        }
1297
        for (i = 0; i + 3 < size; i += 4) {
×
1298
            pixels[i + 0] = sixel_colorspace_convert_component(
×
1299
                pixels[i + 0], colorspace_src, colorspace_dst);
×
1300
            pixels[i + 1] = sixel_colorspace_convert_component(
×
1301
                pixels[i + 1], colorspace_src, colorspace_dst);
×
1302
            pixels[i + 2] = sixel_colorspace_convert_component(
×
1303
                pixels[i + 2], colorspace_src, colorspace_dst);
×
1304
        }
1305
        break;
×
UNCOV
1306
    case SIXEL_PIXELFORMAT_ARGB8888:
×
1307
        if (size % 4 != 0) {
×
1308
            sixel_helper_set_additional_message(
×
1309
                "sixel_helper_convert_colorspace: invalid data size.");
1310
            return SIXEL_BAD_INPUT;
×
1311
        }
1312
        for (i = 0; i + 3 < size; i += 4) {
×
1313
            pixels[i + 1] = sixel_colorspace_convert_component(
×
1314
                pixels[i + 1], colorspace_src, colorspace_dst);
×
1315
            pixels[i + 2] = sixel_colorspace_convert_component(
×
1316
                pixels[i + 2], colorspace_src, colorspace_dst);
×
1317
            pixels[i + 3] = sixel_colorspace_convert_component(
×
1318
                pixels[i + 3], colorspace_src, colorspace_dst);
×
1319
        }
1320
        break;
×
UNCOV
1321
    case SIXEL_PIXELFORMAT_BGRA8888:
×
1322
        if (size % 4 != 0) {
×
1323
            sixel_helper_set_additional_message(
×
1324
                "sixel_helper_convert_colorspace: invalid data size.");
1325
            return SIXEL_BAD_INPUT;
×
1326
        }
1327
        for (i = 0; i + 3 < size; i += 4) {
×
1328
            pixels[i + 0] = sixel_colorspace_convert_component(
×
1329
                pixels[i + 0], colorspace_src, colorspace_dst);
×
1330
            pixels[i + 1] = sixel_colorspace_convert_component(
×
1331
                pixels[i + 1], colorspace_src, colorspace_dst);
×
1332
            pixels[i + 2] = sixel_colorspace_convert_component(
×
1333
                pixels[i + 2], colorspace_src, colorspace_dst);
×
1334
        }
1335
        break;
×
UNCOV
1336
    case SIXEL_PIXELFORMAT_ABGR8888:
×
1337
        if (size % 4 != 0) {
×
1338
            sixel_helper_set_additional_message(
×
1339
                "sixel_helper_convert_colorspace: invalid data size.");
1340
            return SIXEL_BAD_INPUT;
×
1341
        }
1342
        for (i = 0; i + 3 < size; i += 4) {
×
1343
            pixels[i + 1] = sixel_colorspace_convert_component(
×
1344
                pixels[i + 1], colorspace_src, colorspace_dst);
×
1345
            pixels[i + 2] = sixel_colorspace_convert_component(
×
1346
                pixels[i + 2], colorspace_src, colorspace_dst);
×
1347
            pixels[i + 3] = sixel_colorspace_convert_component(
×
1348
                pixels[i + 3], colorspace_src, colorspace_dst);
×
1349
        }
1350
        break;
×
UNCOV
1351
    case SIXEL_PIXELFORMAT_G8:
×
1352
        for (i = 0; i < size; ++i) {
×
1353
            pixels[i] = sixel_colorspace_convert_component(
×
1354
                pixels[i], colorspace_src, colorspace_dst);
×
1355
        }
1356
        break;
×
UNCOV
1357
    case SIXEL_PIXELFORMAT_GA88:
×
1358
        if (size % 2 != 0) {
×
1359
            sixel_helper_set_additional_message(
×
1360
                "sixel_helper_convert_colorspace: invalid data size.");
1361
            return SIXEL_BAD_INPUT;
×
1362
        }
1363
        for (i = 0; i + 1 < size; i += 2) {
×
1364
            pixels[i + 0] = sixel_colorspace_convert_component(
×
1365
                pixels[i + 0], colorspace_src, colorspace_dst);
×
1366
        }
1367
        break;
×
UNCOV
1368
    case SIXEL_PIXELFORMAT_AG88:
×
1369
        if (size % 2 != 0) {
×
1370
            sixel_helper_set_additional_message(
×
1371
                "sixel_helper_convert_colorspace: invalid data size.");
1372
            return SIXEL_BAD_INPUT;
×
1373
        }
1374
        for (i = 0; i + 1 < size; i += 2) {
×
1375
            pixels[i + 1] = sixel_colorspace_convert_component(
×
1376
                pixels[i + 1], colorspace_src, colorspace_dst);
×
1377
        }
1378
        break;
×
UNCOV
1379
    default:
×
1380
        sixel_helper_set_additional_message(
×
1381
            "sixel_helper_convert_colorspace: unsupported pixelformat.");
1382
        return SIXEL_BAD_INPUT;
×
1383
    }
1384

1385
    return SIXEL_OK;
66✔
1386
}
1387

1388
/* emacs Local Variables:      */
1389
/* emacs mode: c               */
1390
/* emacs tab-width: 4          */
1391
/* emacs indent-tabs-mode: nil */
1392
/* emacs c-basic-offset: 4     */
1393
/* emacs End:                  */
1394
/* vim: set expandtab ts=4 sts=4 sw=4 : */
1395
/* 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