Coverage Report

Created: 2024-09-14 07:19

/src/skia/modules/skcms/skcms.cc
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2018 Google Inc.
3
 *
4
 * Use of this source code is governed by a BSD-style license that can be
5
 * found in the LICENSE file.
6
 */
7
8
#include "src/skcms_public.h"  // NO_G3_REWRITE
9
#include "src/skcms_internals.h"  // NO_G3_REWRITE
10
#include "src/skcms_Transform.h"  // NO_G3_REWRITE
11
#include <assert.h>
12
#include <float.h>
13
#include <limits.h>
14
#include <stdlib.h>
15
#include <string.h>
16
17
#if defined(__ARM_NEON)
18
    #include <arm_neon.h>
19
#elif defined(__SSE__)
20
    #include <immintrin.h>
21
22
    #if defined(__clang__)
23
        // That #include <immintrin.h> is usually enough, but Clang's headers
24
        // "helpfully" skip including the whole kitchen sink when _MSC_VER is
25
        // defined, because lots of programs on Windows would include that and
26
        // it'd be a lot slower.  But we want all those headers included so we
27
        // can use their features after runtime checks later.
28
        #include <smmintrin.h>
29
        #include <avxintrin.h>
30
        #include <avx2intrin.h>
31
        #include <avx512fintrin.h>
32
        #include <avx512dqintrin.h>
33
    #endif
34
#endif
35
36
using namespace skcms_private;
37
38
static bool sAllowRuntimeCPUDetection = true;
39
40
0
void skcms_DisableRuntimeCPUDetection() {
41
0
    sAllowRuntimeCPUDetection = false;
42
0
}
43
44
2.04M
static float log2f_(float x) {
45
    // The first approximation of log2(x) is its exponent 'e', minus 127.
46
2.04M
    int32_t bits;
47
2.04M
    memcpy(&bits, &x, sizeof(bits));
48
49
2.04M
    float e = (float)bits * (1.0f / (1<<23));
50
51
    // If we use the mantissa too we can refine the error signficantly.
52
2.04M
    int32_t m_bits = (bits & 0x007fffff) | 0x3f000000;
53
2.04M
    float m;
54
2.04M
    memcpy(&m, &m_bits, sizeof(m));
55
56
2.04M
    return (e - 124.225514990f
57
2.04M
              -   1.498030302f*m
58
2.04M
              -   1.725879990f/(0.3520887068f + m));
59
2.04M
}
60
0
static float logf_(float x) {
61
0
    const float ln2 = 0.69314718f;
62
0
    return ln2*log2f_(x);
63
0
}
64
65
2.04M
static float exp2f_(float x) {
66
2.04M
    if (x > 128.0f) {
67
1.40k
        return INFINITY_;
68
2.04M
    } else if (x < -127.0f) {
69
2.38k
        return 0.0f;
70
2.38k
    }
71
2.04M
    float fract = x - floorf_(x);
72
73
2.04M
    float fbits = (1.0f * (1<<23)) * (x + 121.274057500f
74
2.04M
                                        -   1.490129070f*fract
75
2.04M
                                        +  27.728023300f/(4.84252568f - fract));
76
77
    // Before we cast fbits to int32_t, check for out of range values to pacify UBSAN.
78
    // INT_MAX is not exactly representable as a float, so exclude it as effectively infinite.
79
    // Negative values are effectively underflow - we'll end up returning a (different) negative
80
    // value, which makes no sense. So clamp to zero.
81
2.04M
    if (fbits >= (float)INT_MAX) {
82
0
        return INFINITY_;
83
2.04M
    } else if (fbits < 0) {
84
1
        return 0;
85
1
    }
86
87
2.04M
    int32_t bits = (int32_t)fbits;
88
2.04M
    memcpy(&x, &bits, sizeof(x));
89
2.04M
    return x;
90
2.04M
}
91
92
// Not static, as it's used by some test tools.
93
2.82M
float powf_(float x, float y) {
94
2.82M
    if (x <= 0.f) {
95
33.3k
        return 0.f;
96
33.3k
    }
97
2.79M
    if (x == 1.f) {
98
743k
        return 1.f;
99
743k
    }
100
2.04M
    return exp2f_(log2f_(x) * y);
101
2.79M
}
102
103
0
static float expf_(float x) {
104
0
    const float log2_e = 1.4426950408889634074f;
105
0
    return exp2f_(log2_e * x);
106
0
}
107
108
343k
static float fmaxf_(float x, float y) { return x > y ? x : y; }
109
207k
static float fminf_(float x, float y) { return x < y ? x : y; }
110
111
6.67M
static bool isfinitef_(float x) { return 0 == x*0; }
112
113
167k
static float minus_1_ulp(float x) {
114
167k
    int32_t bits;
115
167k
    memcpy(&bits, &x, sizeof(bits));
116
167k
    bits = bits - 1;
117
167k
    memcpy(&x, &bits, sizeof(bits));
118
167k
    return x;
119
167k
}
120
121
// Most transfer functions we work with are sRGBish.
122
// For exotic HDR transfer functions, we encode them using a tf.g that makes no sense,
123
// and repurpose the other fields to hold the parameters of the HDR functions.
124
struct TF_PQish  { float A,B,C,D,E,F; };
125
struct TF_HLGish { float R,G,a,b,c,K_minus_1; };
126
// We didn't originally support a scale factor K for HLG, and instead just stored 0 in
127
// the unused `f` field of skcms_TransferFunction for HLGish and HLGInvish transfer functions.
128
// By storing f=K-1, those old unusued f=0 values now mean K=1, a noop scale factor.
129
130
9
static float TFKind_marker(skcms_TFType kind) {
131
    // We'd use different NaNs, but those aren't guaranteed to be preserved by WASM.
132
9
    return -(float)kind;
133
9
}
134
135
static skcms_TFType classify(const skcms_TransferFunction& tf, TF_PQish*   pq = nullptr
136
3.72M
                                                             , TF_HLGish* hlg = nullptr) {
137
3.72M
    if (tf.g < 0) {
138
        // Negative "g" is mapped to enum values; large negative are for sure invalid.
139
93
        if (tf.g < -128) {
140
62
            return skcms_TFType_Invalid;
141
62
        }
142
31
        int enum_g = -static_cast<int>(tf.g);
143
        // Non-whole "g" values are invalid as well.
144
31
        if (static_cast<float>(-enum_g) != tf.g) {
145
2
            return skcms_TFType_Invalid;
146
2
        }
147
        // TODO: soundness checks for PQ/HLG like we do for sRGBish?
148
29
        switch (enum_g) {
149
6
            case skcms_TFType_PQish:
150
6
                if (pq) {
151
2
                    memcpy(pq , &tf.a, sizeof(*pq ));
152
2
                }
153
6
                return skcms_TFType_PQish;
154
5
            case skcms_TFType_HLGish:
155
5
                if (hlg) {
156
2
                    memcpy(hlg, &tf.a, sizeof(*hlg));
157
2
                }
158
5
                return skcms_TFType_HLGish;
159
15
            case skcms_TFType_HLGinvish:
160
15
                if (hlg) {
161
5
                    memcpy(hlg, &tf.a, sizeof(*hlg));
162
5
                }
163
15
                return skcms_TFType_HLGinvish;
164
29
        }
165
3
        return skcms_TFType_Invalid;
166
29
    }
167
168
    // Basic soundness checks for sRGBish transfer functions.
169
3.72M
    if (isfinitef_(tf.a + tf.b + tf.c + tf.d + tf.e + tf.f + tf.g)
170
            // a,c,d,g should be non-negative to make any sense.
171
3.72M
            && tf.a >= 0
172
3.72M
            && tf.c >= 0
173
3.72M
            && tf.d >= 0
174
3.72M
            && tf.g >= 0
175
            // Raising a negative value to a fractional tf->g produces complex numbers.
176
3.72M
            && tf.a * tf.d + tf.b >= 0) {
177
3.72M
        return skcms_TFType_sRGBish;
178
3.72M
    }
179
180
290
    return skcms_TFType_Invalid;
181
3.72M
}
182
183
43.4k
skcms_TFType skcms_TransferFunction_getType(const skcms_TransferFunction* tf) {
184
43.4k
    return classify(*tf);
185
43.4k
}
186
1.95k
bool skcms_TransferFunction_isSRGBish(const skcms_TransferFunction* tf) {
187
1.95k
    return classify(*tf) == skcms_TFType_sRGBish;
188
1.95k
}
189
0
bool skcms_TransferFunction_isPQish(const skcms_TransferFunction* tf) {
190
0
    return classify(*tf) == skcms_TFType_PQish;
191
0
}
192
0
bool skcms_TransferFunction_isHLGish(const skcms_TransferFunction* tf) {
193
0
    return classify(*tf) == skcms_TFType_HLGish;
194
0
}
195
196
bool skcms_TransferFunction_makePQish(skcms_TransferFunction* tf,
197
                                      float A, float B, float C,
198
0
                                      float D, float E, float F) {
199
0
    *tf = { TFKind_marker(skcms_TFType_PQish), A,B,C,D,E,F };
200
0
    assert(skcms_TransferFunction_isPQish(tf));
201
0
    return true;
202
0
}
203
204
bool skcms_TransferFunction_makeScaledHLGish(skcms_TransferFunction* tf,
205
                                             float K, float R, float G,
206
0
                                             float a, float b, float c) {
207
0
    *tf = { TFKind_marker(skcms_TFType_HLGish), R,G, a,b,c, K-1.0f };
208
0
    assert(skcms_TransferFunction_isHLGish(tf));
209
0
    return true;
210
0
}
211
212
852k
float skcms_TransferFunction_eval(const skcms_TransferFunction* tf, float x) {
213
852k
    float sign = x < 0 ? -1.0f : 1.0f;
214
852k
    x *= sign;
215
216
852k
    TF_PQish  pq;
217
852k
    TF_HLGish hlg;
218
852k
    switch (classify(*tf, &pq, &hlg)) {
219
0
        case skcms_TFType_Invalid: break;
220
221
0
        case skcms_TFType_HLGish: {
222
0
            const float K = hlg.K_minus_1 + 1.0f;
223
0
            return K * sign * (x*hlg.R <= 1 ? powf_(x*hlg.R, hlg.G)
224
0
                                            : expf_((x-hlg.c)*hlg.a) + hlg.b);
225
0
        }
226
227
        // skcms_TransferFunction_invert() inverts R, G, and a for HLGinvish so this math is fast.
228
0
        case skcms_TFType_HLGinvish: {
229
0
            const float K = hlg.K_minus_1 + 1.0f;
230
0
            x /= K;
231
0
            return sign * (x <= 1 ? hlg.R * powf_(x, hlg.G)
232
0
                                  : hlg.a * logf_(x - hlg.b) + hlg.c);
233
0
        }
234
235
852k
        case skcms_TFType_sRGBish:
236
852k
            return sign * (x < tf->d ?       tf->c * x + tf->f
237
852k
                                     : powf_(tf->a * x + tf->b, tf->g) + tf->e);
238
239
0
        case skcms_TFType_PQish:
240
0
            return sign *
241
0
                   powf_((pq.A + pq.B * powf_(x, pq.C)) / (pq.D + pq.E * powf_(x, pq.C)), pq.F);
242
852k
    }
243
0
    return 0;
244
852k
}
245
246
247
177k
static float eval_curve(const skcms_Curve* curve, float x) {
248
177k
    if (curve->table_entries == 0) {
249
9.72k
        return skcms_TransferFunction_eval(&curve->parametric, x);
250
9.72k
    }
251
252
167k
    float ix = fmaxf_(0, fminf_(x, 1)) * static_cast<float>(curve->table_entries - 1);
253
167k
    int   lo = (int)                   ix        ,
254
167k
          hi = (int)(float)minus_1_ulp(ix + 1.0f);
255
167k
    float t = ix - (float)lo;
256
257
167k
    float l, h;
258
167k
    if (curve->table_8) {
259
35.8k
        l = curve->table_8[lo] * (1/255.0f);
260
35.8k
        h = curve->table_8[hi] * (1/255.0f);
261
131k
    } else {
262
131k
        uint16_t be_l, be_h;
263
131k
        memcpy(&be_l, curve->table_16 + 2*lo, 2);
264
131k
        memcpy(&be_h, curve->table_16 + 2*hi, 2);
265
131k
        uint16_t le_l = ((be_l << 8) | (be_l >> 8)) & 0xffff;
266
131k
        uint16_t le_h = ((be_h << 8) | (be_h >> 8)) & 0xffff;
267
131k
        l = le_l * (1/65535.0f);
268
131k
        h = le_h * (1/65535.0f);
269
131k
    }
270
167k
    return l + (h-l)*t;
271
177k
}
272
273
370
float skcms_MaxRoundtripError(const skcms_Curve* curve, const skcms_TransferFunction* inv_tf) {
274
370
    uint32_t N = curve->table_entries > 256 ? curve->table_entries : 256;
275
370
    const float dx = 1.0f / static_cast<float>(N - 1);
276
370
    float err = 0;
277
136k
    for (uint32_t i = 0; i < N; i++) {
278
135k
        float x = static_cast<float>(i) * dx,
279
135k
              y = eval_curve(curve, x);
280
135k
        err = fmaxf_(err, fabsf_(x - skcms_TransferFunction_eval(inv_tf, y)));
281
135k
    }
282
370
    return err;
283
370
}
284
285
370
bool skcms_AreApproximateInverses(const skcms_Curve* curve, const skcms_TransferFunction* inv_tf) {
286
370
    return skcms_MaxRoundtripError(curve, inv_tf) < (1/512.0f);
287
370
}
288
289
// Additional ICC signature values that are only used internally
290
enum {
291
    // File signature
292
    skcms_Signature_acsp = 0x61637370,
293
294
    // Tag signatures
295
    skcms_Signature_rTRC = 0x72545243,
296
    skcms_Signature_gTRC = 0x67545243,
297
    skcms_Signature_bTRC = 0x62545243,
298
    skcms_Signature_kTRC = 0x6B545243,
299
300
    skcms_Signature_rXYZ = 0x7258595A,
301
    skcms_Signature_gXYZ = 0x6758595A,
302
    skcms_Signature_bXYZ = 0x6258595A,
303
304
    skcms_Signature_A2B0 = 0x41324230,
305
    skcms_Signature_B2A0 = 0x42324130,
306
307
    skcms_Signature_CHAD = 0x63686164,
308
    skcms_Signature_WTPT = 0x77747074,
309
310
    skcms_Signature_CICP = 0x63696370,
311
312
    // Type signatures
313
    skcms_Signature_curv = 0x63757276,
314
    skcms_Signature_mft1 = 0x6D667431,
315
    skcms_Signature_mft2 = 0x6D667432,
316
    skcms_Signature_mAB  = 0x6D414220,
317
    skcms_Signature_mBA  = 0x6D424120,
318
    skcms_Signature_para = 0x70617261,
319
    skcms_Signature_sf32 = 0x73663332,
320
    // XYZ is also a PCS signature, so it's defined in skcms.h
321
    // skcms_Signature_XYZ = 0x58595A20,
322
};
323
324
3.02k
static uint16_t read_big_u16(const uint8_t* ptr) {
325
3.02k
    uint16_t be;
326
3.02k
    memcpy(&be, ptr, sizeof(be));
327
#if defined(_MSC_VER)
328
    return _byteswap_ushort(be);
329
#else
330
3.02k
    return __builtin_bswap16(be);
331
3.02k
#endif
332
3.02k
}
333
334
360k
static uint32_t read_big_u32(const uint8_t* ptr) {
335
360k
    uint32_t be;
336
360k
    memcpy(&be, ptr, sizeof(be));
337
#if defined(_MSC_VER)
338
    return _byteswap_ulong(be);
339
#else
340
360k
    return __builtin_bswap32(be);
341
360k
#endif
342
360k
}
343
344
33.6k
static int32_t read_big_i32(const uint8_t* ptr) {
345
33.6k
    return (int32_t)read_big_u32(ptr);
346
33.6k
}
347
348
33.6k
static float read_big_fixed(const uint8_t* ptr) {
349
33.6k
    return static_cast<float>(read_big_i32(ptr)) * (1.0f / 65536.0f);
350
33.6k
}
351
352
// Maps to an in-memory profile so that fields line up to the locations specified
353
// in ICC.1:2010, section 7.2
354
typedef struct {
355
    uint8_t size                [ 4];
356
    uint8_t cmm_type            [ 4];
357
    uint8_t version             [ 4];
358
    uint8_t profile_class       [ 4];
359
    uint8_t data_color_space    [ 4];
360
    uint8_t pcs                 [ 4];
361
    uint8_t creation_date_time  [12];
362
    uint8_t signature           [ 4];
363
    uint8_t platform            [ 4];
364
    uint8_t flags               [ 4];
365
    uint8_t device_manufacturer [ 4];
366
    uint8_t device_model        [ 4];
367
    uint8_t device_attributes   [ 8];
368
    uint8_t rendering_intent    [ 4];
369
    uint8_t illuminant_X        [ 4];
370
    uint8_t illuminant_Y        [ 4];
371
    uint8_t illuminant_Z        [ 4];
372
    uint8_t creator             [ 4];
373
    uint8_t profile_id          [16];
374
    uint8_t reserved            [28];
375
    uint8_t tag_count           [ 4]; // Technically not part of header, but required
376
} header_Layout;
377
378
typedef struct {
379
    uint8_t signature [4];
380
    uint8_t offset    [4];
381
    uint8_t size      [4];
382
} tag_Layout;
383
384
28.5k
static const tag_Layout* get_tag_table(const skcms_ICCProfile* profile) {
385
28.5k
    return (const tag_Layout*)(profile->buffer + SAFE_SIZEOF(header_Layout));
386
28.5k
}
387
388
// s15Fixed16ArrayType is technically variable sized, holding N values. However, the only valid
389
// use of the type is for the CHAD tag that stores exactly nine values.
390
typedef struct {
391
    uint8_t type     [ 4];
392
    uint8_t reserved [ 4];
393
    uint8_t values   [36];
394
} sf32_Layout;
395
396
0
bool skcms_GetCHAD(const skcms_ICCProfile* profile, skcms_Matrix3x3* m) {
397
0
    skcms_ICCTag tag;
398
0
    if (!skcms_GetTagBySignature(profile, skcms_Signature_CHAD, &tag)) {
399
0
        return false;
400
0
    }
401
402
0
    if (tag.type != skcms_Signature_sf32 || tag.size < SAFE_SIZEOF(sf32_Layout)) {
403
0
        return false;
404
0
    }
405
406
0
    const sf32_Layout* sf32Tag = (const sf32_Layout*)tag.buf;
407
0
    const uint8_t* values = sf32Tag->values;
408
0
    for (int r = 0; r < 3; ++r)
409
0
    for (int c = 0; c < 3; ++c, values += 4) {
410
0
        m->vals[r][c] = read_big_fixed(values);
411
0
    }
412
0
    return true;
413
0
}
414
415
// XYZType is technically variable sized, holding N XYZ triples. However, the only valid uses of
416
// the type are for tags/data that store exactly one triple.
417
typedef struct {
418
    uint8_t type     [4];
419
    uint8_t reserved [4];
420
    uint8_t X        [4];
421
    uint8_t Y        [4];
422
    uint8_t Z        [4];
423
} XYZ_Layout;
424
425
3.88k
static bool read_tag_xyz(const skcms_ICCTag* tag, float* x, float* y, float* z) {
426
3.88k
    if (tag->type != skcms_Signature_XYZ || tag->size < SAFE_SIZEOF(XYZ_Layout)) {
427
720
        return false;
428
720
    }
429
430
3.16k
    const XYZ_Layout* xyzTag = (const XYZ_Layout*)tag->buf;
431
432
3.16k
    *x = read_big_fixed(xyzTag->X);
433
3.16k
    *y = read_big_fixed(xyzTag->Y);
434
3.16k
    *z = read_big_fixed(xyzTag->Z);
435
3.16k
    return true;
436
3.88k
}
437
438
0
bool skcms_GetWTPT(const skcms_ICCProfile* profile, float xyz[3]) {
439
0
    skcms_ICCTag tag;
440
0
    return skcms_GetTagBySignature(profile, skcms_Signature_WTPT, &tag) &&
441
0
           read_tag_xyz(&tag, &xyz[0], &xyz[1], &xyz[2]);
442
0
}
443
444
0
static int data_color_space_channel_count(uint32_t data_color_space) {
445
0
    switch (data_color_space) {
446
0
        case skcms_Signature_CMYK:   return 4;
447
0
        case skcms_Signature_Gray:   return 1;
448
0
        case skcms_Signature_RGB:    return 3;
449
0
        case skcms_Signature_Lab:    return 3;
450
0
        case skcms_Signature_XYZ:    return 3;
451
0
        case skcms_Signature_CIELUV: return 3;
452
0
        case skcms_Signature_YCbCr:  return 3;
453
0
        case skcms_Signature_CIEYxy: return 3;
454
0
        case skcms_Signature_HSV:    return 3;
455
0
        case skcms_Signature_HLS:    return 3;
456
0
        case skcms_Signature_CMY:    return 3;
457
0
        case skcms_Signature_2CLR:   return 2;
458
0
        case skcms_Signature_3CLR:   return 3;
459
0
        case skcms_Signature_4CLR:   return 4;
460
0
        case skcms_Signature_5CLR:   return 5;
461
0
        case skcms_Signature_6CLR:   return 6;
462
0
        case skcms_Signature_7CLR:   return 7;
463
0
        case skcms_Signature_8CLR:   return 8;
464
0
        case skcms_Signature_9CLR:   return 9;
465
0
        case skcms_Signature_10CLR:  return 10;
466
0
        case skcms_Signature_11CLR:  return 11;
467
0
        case skcms_Signature_12CLR:  return 12;
468
0
        case skcms_Signature_13CLR:  return 13;
469
0
        case skcms_Signature_14CLR:  return 14;
470
0
        case skcms_Signature_15CLR:  return 15;
471
0
        default:                     return -1;
472
0
    }
473
0
}
474
475
0
int skcms_GetInputChannelCount(const skcms_ICCProfile* profile) {
476
0
    int a2b_count = 0;
477
0
    if (profile->has_A2B) {
478
0
        a2b_count = profile->A2B.input_channels != 0
479
0
                        ? static_cast<int>(profile->A2B.input_channels)
480
0
                        : 3;
481
0
    }
482
483
0
    skcms_ICCTag tag;
484
0
    int trc_count = 0;
485
0
    if (skcms_GetTagBySignature(profile, skcms_Signature_kTRC, &tag)) {
486
0
        trc_count = 1;
487
0
    } else if (profile->has_trc) {
488
0
        trc_count = 3;
489
0
    }
490
491
0
    int dcs_count = data_color_space_channel_count(profile->data_color_space);
492
493
0
    if (dcs_count < 0) {
494
0
        return -1;
495
0
    }
496
497
0
    if (a2b_count > 0 && a2b_count != dcs_count) {
498
0
        return -1;
499
0
    }
500
0
    if (trc_count > 0 && trc_count != dcs_count) {
501
0
        return -1;
502
0
    }
503
504
0
    return dcs_count;
505
0
}
506
507
static bool read_to_XYZD50(const skcms_ICCTag* rXYZ, const skcms_ICCTag* gXYZ,
508
1.54k
                           const skcms_ICCTag* bXYZ, skcms_Matrix3x3* toXYZ) {
509
1.54k
    return read_tag_xyz(rXYZ, &toXYZ->vals[0][0], &toXYZ->vals[1][0], &toXYZ->vals[2][0]) &&
510
1.54k
           read_tag_xyz(gXYZ, &toXYZ->vals[0][1], &toXYZ->vals[1][1], &toXYZ->vals[2][1]) &&
511
1.54k
           read_tag_xyz(bXYZ, &toXYZ->vals[0][2], &toXYZ->vals[1][2], &toXYZ->vals[2][2]);
512
1.54k
}
513
514
typedef struct {
515
    uint8_t type          [4];
516
    uint8_t reserved_a    [4];
517
    uint8_t function_type [2];
518
    uint8_t reserved_b    [2];
519
    uint8_t variable      [1/*variable*/];  // 1, 3, 4, 5, or 7 s15.16, depending on function_type
520
} para_Layout;
521
522
static bool read_curve_para(const uint8_t* buf, uint32_t size,
523
2.11k
                            skcms_Curve* curve, uint32_t* curve_size) {
524
2.11k
    if (size < SAFE_FIXED_SIZE(para_Layout)) {
525
3
        return false;
526
3
    }
527
528
2.11k
    const para_Layout* paraTag = (const para_Layout*)buf;
529
530
2.11k
    enum { kG = 0, kGAB = 1, kGABC = 2, kGABCD = 3, kGABCDEF = 4 };
531
2.11k
    uint16_t function_type = read_big_u16(paraTag->function_type);
532
2.11k
    if (function_type > kGABCDEF) {
533
94
        return false;
534
94
    }
535
536
2.01k
    static const uint32_t curve_bytes[] = { 4, 12, 16, 20, 28 };
537
2.01k
    if (size < SAFE_FIXED_SIZE(para_Layout) + curve_bytes[function_type]) {
538
52
        return false;
539
52
    }
540
541
1.96k
    if (curve_size) {
542
666
        *curve_size = SAFE_FIXED_SIZE(para_Layout) + curve_bytes[function_type];
543
666
    }
544
545
1.96k
    curve->table_entries = 0;
546
1.96k
    curve->parametric.a  = 1.0f;
547
1.96k
    curve->parametric.b  = 0.0f;
548
1.96k
    curve->parametric.c  = 0.0f;
549
1.96k
    curve->parametric.d  = 0.0f;
550
1.96k
    curve->parametric.e  = 0.0f;
551
1.96k
    curve->parametric.f  = 0.0f;
552
1.96k
    curve->parametric.g  = read_big_fixed(paraTag->variable);
553
554
1.96k
    switch (function_type) {
555
98
        case kGAB:
556
98
            curve->parametric.a = read_big_fixed(paraTag->variable + 4);
557
98
            curve->parametric.b = read_big_fixed(paraTag->variable + 8);
558
98
            if (curve->parametric.a == 0) {
559
8
                return false;
560
8
            }
561
90
            curve->parametric.d = -curve->parametric.b / curve->parametric.a;
562
90
            break;
563
54
        case kGABC:
564
54
            curve->parametric.a = read_big_fixed(paraTag->variable + 4);
565
54
            curve->parametric.b = read_big_fixed(paraTag->variable + 8);
566
54
            curve->parametric.e = read_big_fixed(paraTag->variable + 12);
567
54
            if (curve->parametric.a == 0) {
568
3
                return false;
569
3
            }
570
51
            curve->parametric.d = -curve->parametric.b / curve->parametric.a;
571
51
            curve->parametric.f = curve->parametric.e;
572
51
            break;
573
948
        case kGABCD:
574
948
            curve->parametric.a = read_big_fixed(paraTag->variable + 4);
575
948
            curve->parametric.b = read_big_fixed(paraTag->variable + 8);
576
948
            curve->parametric.c = read_big_fixed(paraTag->variable + 12);
577
948
            curve->parametric.d = read_big_fixed(paraTag->variable + 16);
578
948
            break;
579
222
        case kGABCDEF:
580
222
            curve->parametric.a = read_big_fixed(paraTag->variable + 4);
581
222
            curve->parametric.b = read_big_fixed(paraTag->variable + 8);
582
222
            curve->parametric.c = read_big_fixed(paraTag->variable + 12);
583
222
            curve->parametric.d = read_big_fixed(paraTag->variable + 16);
584
222
            curve->parametric.e = read_big_fixed(paraTag->variable + 20);
585
222
            curve->parametric.f = read_big_fixed(paraTag->variable + 24);
586
222
            break;
587
1.96k
    }
588
1.95k
    return skcms_TransferFunction_isSRGBish(&curve->parametric);
589
1.96k
}
590
591
typedef struct {
592
    uint8_t type          [4];
593
    uint8_t reserved      [4];
594
    uint8_t value_count   [4];
595
    uint8_t variable      [1/*variable*/];  // value_count, 8.8 if 1, uint16 (n*65535) if > 1
596
} curv_Layout;
597
598
static bool read_curve_curv(const uint8_t* buf, uint32_t size,
599
2.57k
                            skcms_Curve* curve, uint32_t* curve_size) {
600
2.57k
    if (size < SAFE_FIXED_SIZE(curv_Layout)) {
601
49
        return false;
602
49
    }
603
604
2.53k
    const curv_Layout* curvTag = (const curv_Layout*)buf;
605
606
2.53k
    uint32_t value_count = read_big_u32(curvTag->value_count);
607
2.53k
    if (size < SAFE_FIXED_SIZE(curv_Layout) + value_count * SAFE_SIZEOF(uint16_t)) {
608
130
        return false;
609
130
    }
610
611
2.40k
    if (curve_size) {
612
31
        *curve_size = SAFE_FIXED_SIZE(curv_Layout) + value_count * SAFE_SIZEOF(uint16_t);
613
31
    }
614
615
2.40k
    if (value_count < 2) {
616
1.58k
        curve->table_entries = 0;
617
1.58k
        curve->parametric.a  = 1.0f;
618
1.58k
        curve->parametric.b  = 0.0f;
619
1.58k
        curve->parametric.c  = 0.0f;
620
1.58k
        curve->parametric.d  = 0.0f;
621
1.58k
        curve->parametric.e  = 0.0f;
622
1.58k
        curve->parametric.f  = 0.0f;
623
1.58k
        if (value_count == 0) {
624
            // Empty tables are a shorthand for an identity curve
625
906
            curve->parametric.g = 1.0f;
626
906
        } else {
627
            // Single entry tables are a shorthand for simple gamma
628
681
            curve->parametric.g = read_big_u16(curvTag->variable) * (1.0f / 256.0f);
629
681
        }
630
1.58k
    } else {
631
813
        curve->table_8       = nullptr;
632
813
        curve->table_16      = curvTag->variable;
633
813
        curve->table_entries = value_count;
634
813
    }
635
636
2.40k
    return true;
637
2.53k
}
638
639
// Parses both curveType and parametricCurveType data. Ensures that at most 'size' bytes are read.
640
// If curve_size is not nullptr, writes the number of bytes used by the curve in (*curve_size).
641
static bool read_curve(const uint8_t* buf, uint32_t size,
642
4.96k
                       skcms_Curve* curve, uint32_t* curve_size) {
643
4.96k
    if (!buf || size < 4 || !curve) {
644
18
        return false;
645
18
    }
646
647
4.94k
    uint32_t type = read_big_u32(buf);
648
4.94k
    if (type == skcms_Signature_para) {
649
2.11k
        return read_curve_para(buf, size, curve, curve_size);
650
2.83k
    } else if (type == skcms_Signature_curv) {
651
2.57k
        return read_curve_curv(buf, size, curve, curve_size);
652
2.57k
    }
653
654
253
    return false;
655
4.94k
}
656
657
// mft1 and mft2 share a large chunk of data
658
typedef struct {
659
    uint8_t type                 [ 4];
660
    uint8_t reserved_a           [ 4];
661
    uint8_t input_channels       [ 1];
662
    uint8_t output_channels      [ 1];
663
    uint8_t grid_points          [ 1];
664
    uint8_t reserved_b           [ 1];
665
    uint8_t matrix               [36];
666
} mft_CommonLayout;
667
668
typedef struct {
669
    mft_CommonLayout common      [1];
670
671
    uint8_t variable             [1/*variable*/];
672
} mft1_Layout;
673
674
typedef struct {
675
    mft_CommonLayout common      [1];
676
677
    uint8_t input_table_entries  [2];
678
    uint8_t output_table_entries [2];
679
    uint8_t variable             [1/*variable*/];
680
} mft2_Layout;
681
682
141
static bool read_mft_common(const mft_CommonLayout* mftTag, skcms_A2B* a2b) {
683
    // MFT matrices are applied before the first set of curves, but must be identity unless the
684
    // input is PCSXYZ. We don't support PCSXYZ profiles, so we ignore this matrix. Note that the
685
    // matrix in skcms_A2B is applied later in the pipe, so supporting this would require another
686
    // field/flag.
687
141
    a2b->matrix_channels = 0;
688
141
    a2b-> input_channels = mftTag-> input_channels[0];
689
141
    a2b->output_channels = mftTag->output_channels[0];
690
691
    // We require exactly three (ie XYZ/Lab/RGB) output channels
692
141
    if (a2b->output_channels != ARRAY_COUNT(a2b->output_curves)) {
693
4
        return false;
694
4
    }
695
    // We require at least one, and no more than four (ie CMYK) input channels
696
137
    if (a2b->input_channels < 1 || a2b->input_channels > ARRAY_COUNT(a2b->input_curves)) {
697
9
        return false;
698
9
    }
699
700
481
    for (uint32_t i = 0; i < a2b->input_channels; ++i) {
701
353
        a2b->grid_points[i] = mftTag->grid_points[0];
702
353
    }
703
    // The grid only makes sense with at least two points along each axis
704
128
    if (a2b->grid_points[0] < 2) {
705
3
        return false;
706
3
    }
707
125
    return true;
708
128
}
709
710
// All as the A2B version above, except where noted.
711
79
static bool read_mft_common(const mft_CommonLayout* mftTag, skcms_B2A* b2a) {
712
    // Same as A2B.
713
79
    b2a->matrix_channels = 0;
714
79
    b2a-> input_channels = mftTag-> input_channels[0];
715
79
    b2a->output_channels = mftTag->output_channels[0];
716
717
718
    // For B2A, exactly 3 input channels (XYZ) and 3 (RGB) or 4 (CMYK) output channels.
719
79
    if (b2a->input_channels != ARRAY_COUNT(b2a->input_curves)) {
720
4
        return false;
721
4
    }
722
75
    if (b2a->output_channels < 3 || b2a->output_channels > ARRAY_COUNT(b2a->output_curves)) {
723
9
        return false;
724
9
    }
725
726
    // Same as A2B.
727
264
    for (uint32_t i = 0; i < b2a->input_channels; ++i) {
728
198
        b2a->grid_points[i] = mftTag->grid_points[0];
729
198
    }
730
66
    if (b2a->grid_points[0] < 2) {
731
2
        return false;
732
2
    }
733
64
    return true;
734
66
}
735
736
template <typename A2B_or_B2A>
737
static bool init_tables(const uint8_t* table_base, uint64_t max_tables_len, uint32_t byte_width,
738
                        uint32_t input_table_entries, uint32_t output_table_entries,
739
169
                        A2B_or_B2A* out) {
740
    // byte_width is 1 or 2, [input|output]_table_entries are in [2, 4096], so no overflow
741
169
    uint32_t byte_len_per_input_table  = input_table_entries * byte_width;
742
169
    uint32_t byte_len_per_output_table = output_table_entries * byte_width;
743
744
    // [input|output]_channels are <= 4, so still no overflow
745
169
    uint32_t byte_len_all_input_tables  = out->input_channels * byte_len_per_input_table;
746
169
    uint32_t byte_len_all_output_tables = out->output_channels * byte_len_per_output_table;
747
748
169
    uint64_t grid_size = out->output_channels * byte_width;
749
654
    for (uint32_t axis = 0; axis < out->input_channels; ++axis) {
750
485
        grid_size *= out->grid_points[axis];
751
485
    }
752
753
169
    if (max_tables_len < byte_len_all_input_tables + grid_size + byte_len_all_output_tables) {
754
34
        return false;
755
34
    }
756
757
523
    for (uint32_t i = 0; i < out->input_channels; ++i) {
758
388
        out->input_curves[i].table_entries = input_table_entries;
759
388
        if (byte_width == 1) {
760
181
            out->input_curves[i].table_8  = table_base + i * byte_len_per_input_table;
761
181
            out->input_curves[i].table_16 = nullptr;
762
207
        } else {
763
207
            out->input_curves[i].table_8  = nullptr;
764
207
            out->input_curves[i].table_16 = table_base + i * byte_len_per_input_table;
765
207
        }
766
388
    }
767
768
135
    if (byte_width == 1) {
769
59
        out->grid_8  = table_base + byte_len_all_input_tables;
770
59
        out->grid_16 = nullptr;
771
76
    } else {
772
76
        out->grid_8  = nullptr;
773
76
        out->grid_16 = table_base + byte_len_all_input_tables;
774
76
    }
775
776
135
    const uint8_t* output_table_base = table_base + byte_len_all_input_tables + grid_size;
777
556
    for (uint32_t i = 0; i < out->output_channels; ++i) {
778
421
        out->output_curves[i].table_entries = output_table_entries;
779
421
        if (byte_width == 1) {
780
178
            out->output_curves[i].table_8  = output_table_base + i * byte_len_per_output_table;
781
178
            out->output_curves[i].table_16 = nullptr;
782
243
        } else {
783
243
            out->output_curves[i].table_8  = nullptr;
784
243
            out->output_curves[i].table_16 = output_table_base + i * byte_len_per_output_table;
785
243
        }
786
421
    }
787
788
135
    return true;
789
169
}
skcms.cc:bool init_tables<skcms_A2B>(unsigned char const*, unsigned long, unsigned int, unsigned int, unsigned int, skcms_A2B*)
Line
Count
Source
739
113
                        A2B_or_B2A* out) {
740
    // byte_width is 1 or 2, [input|output]_table_entries are in [2, 4096], so no overflow
741
113
    uint32_t byte_len_per_input_table  = input_table_entries * byte_width;
742
113
    uint32_t byte_len_per_output_table = output_table_entries * byte_width;
743
744
    // [input|output]_channels are <= 4, so still no overflow
745
113
    uint32_t byte_len_all_input_tables  = out->input_channels * byte_len_per_input_table;
746
113
    uint32_t byte_len_all_output_tables = out->output_channels * byte_len_per_output_table;
747
748
113
    uint64_t grid_size = out->output_channels * byte_width;
749
430
    for (uint32_t axis = 0; axis < out->input_channels; ++axis) {
750
317
        grid_size *= out->grid_points[axis];
751
317
    }
752
753
113
    if (max_tables_len < byte_len_all_input_tables + grid_size + byte_len_all_output_tables) {
754
20
        return false;
755
20
    }
756
757
355
    for (uint32_t i = 0; i < out->input_channels; ++i) {
758
262
        out->input_curves[i].table_entries = input_table_entries;
759
262
        if (byte_width == 1) {
760
118
            out->input_curves[i].table_8  = table_base + i * byte_len_per_input_table;
761
118
            out->input_curves[i].table_16 = nullptr;
762
144
        } else {
763
144
            out->input_curves[i].table_8  = nullptr;
764
144
            out->input_curves[i].table_16 = table_base + i * byte_len_per_input_table;
765
144
        }
766
262
    }
767
768
93
    if (byte_width == 1) {
769
38
        out->grid_8  = table_base + byte_len_all_input_tables;
770
38
        out->grid_16 = nullptr;
771
55
    } else {
772
55
        out->grid_8  = nullptr;
773
55
        out->grid_16 = table_base + byte_len_all_input_tables;
774
55
    }
775
776
93
    const uint8_t* output_table_base = table_base + byte_len_all_input_tables + grid_size;
777
372
    for (uint32_t i = 0; i < out->output_channels; ++i) {
778
279
        out->output_curves[i].table_entries = output_table_entries;
779
279
        if (byte_width == 1) {
780
114
            out->output_curves[i].table_8  = output_table_base + i * byte_len_per_output_table;
781
114
            out->output_curves[i].table_16 = nullptr;
782
165
        } else {
783
165
            out->output_curves[i].table_8  = nullptr;
784
165
            out->output_curves[i].table_16 = output_table_base + i * byte_len_per_output_table;
785
165
        }
786
279
    }
787
788
93
    return true;
789
113
}
skcms.cc:bool init_tables<skcms_B2A>(unsigned char const*, unsigned long, unsigned int, unsigned int, unsigned int, skcms_B2A*)
Line
Count
Source
739
56
                        A2B_or_B2A* out) {
740
    // byte_width is 1 or 2, [input|output]_table_entries are in [2, 4096], so no overflow
741
56
    uint32_t byte_len_per_input_table  = input_table_entries * byte_width;
742
56
    uint32_t byte_len_per_output_table = output_table_entries * byte_width;
743
744
    // [input|output]_channels are <= 4, so still no overflow
745
56
    uint32_t byte_len_all_input_tables  = out->input_channels * byte_len_per_input_table;
746
56
    uint32_t byte_len_all_output_tables = out->output_channels * byte_len_per_output_table;
747
748
56
    uint64_t grid_size = out->output_channels * byte_width;
749
224
    for (uint32_t axis = 0; axis < out->input_channels; ++axis) {
750
168
        grid_size *= out->grid_points[axis];
751
168
    }
752
753
56
    if (max_tables_len < byte_len_all_input_tables + grid_size + byte_len_all_output_tables) {
754
14
        return false;
755
14
    }
756
757
168
    for (uint32_t i = 0; i < out->input_channels; ++i) {
758
126
        out->input_curves[i].table_entries = input_table_entries;
759
126
        if (byte_width == 1) {
760
63
            out->input_curves[i].table_8  = table_base + i * byte_len_per_input_table;
761
63
            out->input_curves[i].table_16 = nullptr;
762
63
        } else {
763
63
            out->input_curves[i].table_8  = nullptr;
764
63
            out->input_curves[i].table_16 = table_base + i * byte_len_per_input_table;
765
63
        }
766
126
    }
767
768
42
    if (byte_width == 1) {
769
21
        out->grid_8  = table_base + byte_len_all_input_tables;
770
21
        out->grid_16 = nullptr;
771
21
    } else {
772
21
        out->grid_8  = nullptr;
773
21
        out->grid_16 = table_base + byte_len_all_input_tables;
774
21
    }
775
776
42
    const uint8_t* output_table_base = table_base + byte_len_all_input_tables + grid_size;
777
184
    for (uint32_t i = 0; i < out->output_channels; ++i) {
778
142
        out->output_curves[i].table_entries = output_table_entries;
779
142
        if (byte_width == 1) {
780
64
            out->output_curves[i].table_8  = output_table_base + i * byte_len_per_output_table;
781
64
            out->output_curves[i].table_16 = nullptr;
782
78
        } else {
783
78
            out->output_curves[i].table_8  = nullptr;
784
78
            out->output_curves[i].table_16 = output_table_base + i * byte_len_per_output_table;
785
78
        }
786
142
    }
787
788
42
    return true;
789
56
}
790
791
template <typename A2B_or_B2A>
792
95
static bool read_tag_mft1(const skcms_ICCTag* tag, A2B_or_B2A* out) {
793
95
    if (tag->size < SAFE_FIXED_SIZE(mft1_Layout)) {
794
5
        return false;
795
5
    }
796
797
90
    const mft1_Layout* mftTag = (const mft1_Layout*)tag->buf;
798
90
    if (!read_mft_common(mftTag->common, out)) {
799
16
        return false;
800
16
    }
801
802
74
    uint32_t input_table_entries  = 256;
803
74
    uint32_t output_table_entries = 256;
804
805
74
    return init_tables(mftTag->variable, tag->size - SAFE_FIXED_SIZE(mft1_Layout), 1,
806
74
                       input_table_entries, output_table_entries, out);
807
90
}
skcms.cc:bool read_tag_mft1<skcms_A2B>(skcms_ICCTag const*, skcms_A2B*)
Line
Count
Source
792
57
static bool read_tag_mft1(const skcms_ICCTag* tag, A2B_or_B2A* out) {
793
57
    if (tag->size < SAFE_FIXED_SIZE(mft1_Layout)) {
794
2
        return false;
795
2
    }
796
797
55
    const mft1_Layout* mftTag = (const mft1_Layout*)tag->buf;
798
55
    if (!read_mft_common(mftTag->common, out)) {
799
8
        return false;
800
8
    }
801
802
47
    uint32_t input_table_entries  = 256;
803
47
    uint32_t output_table_entries = 256;
804
805
47
    return init_tables(mftTag->variable, tag->size - SAFE_FIXED_SIZE(mft1_Layout), 1,
806
47
                       input_table_entries, output_table_entries, out);
807
55
}
skcms.cc:bool read_tag_mft1<skcms_B2A>(skcms_ICCTag const*, skcms_B2A*)
Line
Count
Source
792
38
static bool read_tag_mft1(const skcms_ICCTag* tag, A2B_or_B2A* out) {
793
38
    if (tag->size < SAFE_FIXED_SIZE(mft1_Layout)) {
794
3
        return false;
795
3
    }
796
797
35
    const mft1_Layout* mftTag = (const mft1_Layout*)tag->buf;
798
35
    if (!read_mft_common(mftTag->common, out)) {
799
8
        return false;
800
8
    }
801
802
27
    uint32_t input_table_entries  = 256;
803
27
    uint32_t output_table_entries = 256;
804
805
27
    return init_tables(mftTag->variable, tag->size - SAFE_FIXED_SIZE(mft1_Layout), 1,
806
27
                       input_table_entries, output_table_entries, out);
807
35
}
808
809
template <typename A2B_or_B2A>
810
134
static bool read_tag_mft2(const skcms_ICCTag* tag, A2B_or_B2A* out) {
811
134
    if (tag->size < SAFE_FIXED_SIZE(mft2_Layout)) {
812
4
        return false;
813
4
    }
814
815
130
    const mft2_Layout* mftTag = (const mft2_Layout*)tag->buf;
816
130
    if (!read_mft_common(mftTag->common, out)) {
817
15
        return false;
818
15
    }
819
820
115
    uint32_t input_table_entries = read_big_u16(mftTag->input_table_entries);
821
115
    uint32_t output_table_entries = read_big_u16(mftTag->output_table_entries);
822
823
    // ICC spec mandates that 2 <= table_entries <= 4096
824
115
    if (input_table_entries < 2 || input_table_entries > 4096 ||
825
115
        output_table_entries < 2 || output_table_entries > 4096) {
826
20
        return false;
827
20
    }
828
829
95
    return init_tables(mftTag->variable, tag->size - SAFE_FIXED_SIZE(mft2_Layout), 2,
830
95
                       input_table_entries, output_table_entries, out);
831
115
}
skcms.cc:bool read_tag_mft2<skcms_A2B>(skcms_ICCTag const*, skcms_A2B*)
Line
Count
Source
810
88
static bool read_tag_mft2(const skcms_ICCTag* tag, A2B_or_B2A* out) {
811
88
    if (tag->size < SAFE_FIXED_SIZE(mft2_Layout)) {
812
2
        return false;
813
2
    }
814
815
86
    const mft2_Layout* mftTag = (const mft2_Layout*)tag->buf;
816
86
    if (!read_mft_common(mftTag->common, out)) {
817
8
        return false;
818
8
    }
819
820
78
    uint32_t input_table_entries = read_big_u16(mftTag->input_table_entries);
821
78
    uint32_t output_table_entries = read_big_u16(mftTag->output_table_entries);
822
823
    // ICC spec mandates that 2 <= table_entries <= 4096
824
78
    if (input_table_entries < 2 || input_table_entries > 4096 ||
825
78
        output_table_entries < 2 || output_table_entries > 4096) {
826
12
        return false;
827
12
    }
828
829
66
    return init_tables(mftTag->variable, tag->size - SAFE_FIXED_SIZE(mft2_Layout), 2,
830
66
                       input_table_entries, output_table_entries, out);
831
78
}
skcms.cc:bool read_tag_mft2<skcms_B2A>(skcms_ICCTag const*, skcms_B2A*)
Line
Count
Source
810
46
static bool read_tag_mft2(const skcms_ICCTag* tag, A2B_or_B2A* out) {
811
46
    if (tag->size < SAFE_FIXED_SIZE(mft2_Layout)) {
812
2
        return false;
813
2
    }
814
815
44
    const mft2_Layout* mftTag = (const mft2_Layout*)tag->buf;
816
44
    if (!read_mft_common(mftTag->common, out)) {
817
7
        return false;
818
7
    }
819
820
37
    uint32_t input_table_entries = read_big_u16(mftTag->input_table_entries);
821
37
    uint32_t output_table_entries = read_big_u16(mftTag->output_table_entries);
822
823
    // ICC spec mandates that 2 <= table_entries <= 4096
824
37
    if (input_table_entries < 2 || input_table_entries > 4096 ||
825
37
        output_table_entries < 2 || output_table_entries > 4096) {
826
8
        return false;
827
8
    }
828
829
29
    return init_tables(mftTag->variable, tag->size - SAFE_FIXED_SIZE(mft2_Layout), 2,
830
29
                       input_table_entries, output_table_entries, out);
831
37
}
832
833
static bool read_curves(const uint8_t* buf, uint32_t size, uint32_t curve_offset,
834
440
                        uint32_t num_curves, skcms_Curve* curves) {
835
1.13k
    for (uint32_t i = 0; i < num_curves; ++i) {
836
865
        if (curve_offset > size) {
837
85
            return false;
838
85
        }
839
840
780
        uint32_t curve_bytes;
841
780
        if (!read_curve(buf + curve_offset, size - curve_offset, &curves[i], &curve_bytes)) {
842
86
            return false;
843
86
        }
844
845
694
        if (curve_bytes > UINT32_MAX - 3) {
846
0
            return false;
847
0
        }
848
694
        curve_bytes = (curve_bytes + 3) & ~3U;
849
850
694
        uint64_t new_offset_64 = (uint64_t)curve_offset + curve_bytes;
851
694
        curve_offset = (uint32_t)new_offset_64;
852
694
        if (new_offset_64 != curve_offset) {
853
0
            return false;
854
0
        }
855
694
    }
856
857
269
    return true;
858
440
}
859
860
// mAB and mBA tags use the same encoding, including color lookup tables.
861
typedef struct {
862
    uint8_t type                 [ 4];
863
    uint8_t reserved_a           [ 4];
864
    uint8_t input_channels       [ 1];
865
    uint8_t output_channels      [ 1];
866
    uint8_t reserved_b           [ 2];
867
    uint8_t b_curve_offset       [ 4];
868
    uint8_t matrix_offset        [ 4];
869
    uint8_t m_curve_offset       [ 4];
870
    uint8_t clut_offset          [ 4];
871
    uint8_t a_curve_offset       [ 4];
872
} mAB_or_mBA_Layout;
873
874
typedef struct {
875
    uint8_t grid_points          [16];
876
    uint8_t grid_byte_width      [ 1];
877
    uint8_t reserved             [ 3];
878
    uint8_t variable             [1/*variable*/];
879
} CLUT_Layout;
880
881
280
static bool read_tag_mab(const skcms_ICCTag* tag, skcms_A2B* a2b, bool pcs_is_xyz) {
882
280
    if (tag->size < SAFE_SIZEOF(mAB_or_mBA_Layout)) {
883
16
        return false;
884
16
    }
885
886
264
    const mAB_or_mBA_Layout* mABTag = (const mAB_or_mBA_Layout*)tag->buf;
887
888
264
    a2b->input_channels  = mABTag->input_channels[0];
889
264
    a2b->output_channels = mABTag->output_channels[0];
890
891
    // We require exactly three (ie XYZ/Lab/RGB) output channels
892
264
    if (a2b->output_channels != ARRAY_COUNT(a2b->output_curves)) {
893
21
        return false;
894
21
    }
895
    // We require no more than four (ie CMYK) input channels
896
243
    if (a2b->input_channels > ARRAY_COUNT(a2b->input_curves)) {
897
4
        return false;
898
4
    }
899
900
239
    uint32_t b_curve_offset = read_big_u32(mABTag->b_curve_offset);
901
239
    uint32_t matrix_offset  = read_big_u32(mABTag->matrix_offset);
902
239
    uint32_t m_curve_offset = read_big_u32(mABTag->m_curve_offset);
903
239
    uint32_t clut_offset    = read_big_u32(mABTag->clut_offset);
904
239
    uint32_t a_curve_offset = read_big_u32(mABTag->a_curve_offset);
905
906
    // "B" curves must be present
907
239
    if (0 == b_curve_offset) {
908
7
        return false;
909
7
    }
910
911
232
    if (!read_curves(tag->buf, tag->size, b_curve_offset, a2b->output_channels,
912
232
                     a2b->output_curves)) {
913
111
        return false;
914
111
    }
915
916
    // "M" curves and Matrix must be used together
917
121
    if (0 != m_curve_offset) {
918
95
        if (0 == matrix_offset) {
919
3
            return false;
920
3
        }
921
92
        a2b->matrix_channels = a2b->output_channels;
922
92
        if (!read_curves(tag->buf, tag->size, m_curve_offset, a2b->matrix_channels,
923
92
                         a2b->matrix_curves)) {
924
20
            return false;
925
20
        }
926
927
        // Read matrix, which is stored as a row-major 3x3, followed by the fourth column
928
72
        if (tag->size < matrix_offset + 12 * SAFE_SIZEOF(uint32_t)) {
929
4
            return false;
930
4
        }
931
68
        float encoding_factor = pcs_is_xyz ? (65535 / 32768.0f) : 1.0f;
932
68
        const uint8_t* mtx_buf = tag->buf + matrix_offset;
933
68
        a2b->matrix.vals[0][0] = encoding_factor * read_big_fixed(mtx_buf +  0);
934
68
        a2b->matrix.vals[0][1] = encoding_factor * read_big_fixed(mtx_buf +  4);
935
68
        a2b->matrix.vals[0][2] = encoding_factor * read_big_fixed(mtx_buf +  8);
936
68
        a2b->matrix.vals[1][0] = encoding_factor * read_big_fixed(mtx_buf + 12);
937
68
        a2b->matrix.vals[1][1] = encoding_factor * read_big_fixed(mtx_buf + 16);
938
68
        a2b->matrix.vals[1][2] = encoding_factor * read_big_fixed(mtx_buf + 20);
939
68
        a2b->matrix.vals[2][0] = encoding_factor * read_big_fixed(mtx_buf + 24);
940
68
        a2b->matrix.vals[2][1] = encoding_factor * read_big_fixed(mtx_buf + 28);
941
68
        a2b->matrix.vals[2][2] = encoding_factor * read_big_fixed(mtx_buf + 32);
942
68
        a2b->matrix.vals[0][3] = encoding_factor * read_big_fixed(mtx_buf + 36);
943
68
        a2b->matrix.vals[1][3] = encoding_factor * read_big_fixed(mtx_buf + 40);
944
68
        a2b->matrix.vals[2][3] = encoding_factor * read_big_fixed(mtx_buf + 44);
945
68
    } else {
946
26
        if (0 != matrix_offset) {
947
3
            return false;
948
3
        }
949
23
        a2b->matrix_channels = 0;
950
23
    }
951
952
    // "A" curves and CLUT must be used together
953
91
    if (0 != a_curve_offset) {
954
85
        if (0 == clut_offset) {
955
1
            return false;
956
1
        }
957
84
        if (!read_curves(tag->buf, tag->size, a_curve_offset, a2b->input_channels,
958
84
                         a2b->input_curves)) {
959
8
            return false;
960
8
        }
961
962
76
        if (tag->size < clut_offset + SAFE_FIXED_SIZE(CLUT_Layout)) {
963
8
            return false;
964
8
        }
965
68
        const CLUT_Layout* clut = (const CLUT_Layout*)(tag->buf + clut_offset);
966
967
68
        if (clut->grid_byte_width[0] == 1) {
968
41
            a2b->grid_8  = clut->variable;
969
41
            a2b->grid_16 = nullptr;
970
41
        } else if (clut->grid_byte_width[0] == 2) {
971
23
            a2b->grid_8  = nullptr;
972
23
            a2b->grid_16 = clut->variable;
973
23
        } else {
974
4
            return false;
975
4
        }
976
977
64
        uint64_t grid_size = a2b->output_channels * clut->grid_byte_width[0];  // the payload
978
123
        for (uint32_t i = 0; i < a2b->input_channels; ++i) {
979
61
            a2b->grid_points[i] = clut->grid_points[i];
980
            // The grid only makes sense with at least two points along each axis
981
61
            if (a2b->grid_points[i] < 2) {
982
2
                return false;
983
2
            }
984
59
            grid_size *= a2b->grid_points[i];
985
59
        }
986
62
        if (tag->size < clut_offset + SAFE_FIXED_SIZE(CLUT_Layout) + grid_size) {
987
2
            return false;
988
2
        }
989
62
    } else {
990
6
        if (0 != clut_offset) {
991
3
            return false;
992
3
        }
993
994
        // If there is no CLUT, the number of input and output channels must match
995
3
        if (a2b->input_channels != a2b->output_channels) {
996
1
            return false;
997
1
        }
998
999
        // Zero out the number of input channels to signal that we're skipping this stage
1000
2
        a2b->input_channels = 0;
1001
2
    }
1002
1003
62
    return true;
1004
91
}
1005
1006
// Exactly the same as read_tag_mab(), except where there are comments.
1007
// TODO: refactor the two to eliminate common code?
1008
61
static bool read_tag_mba(const skcms_ICCTag* tag, skcms_B2A* b2a, bool pcs_is_xyz) {
1009
61
    if (tag->size < SAFE_SIZEOF(mAB_or_mBA_Layout)) {
1010
4
        return false;
1011
4
    }
1012
1013
57
    const mAB_or_mBA_Layout* mBATag = (const mAB_or_mBA_Layout*)tag->buf;
1014
1015
57
    b2a->input_channels  = mBATag->input_channels[0];
1016
57
    b2a->output_channels = mBATag->output_channels[0];
1017
1018
    // Require exactly 3 inputs (XYZ) and 3 (RGB) or 4 (CMYK) outputs.
1019
57
    if (b2a->input_channels != ARRAY_COUNT(b2a->input_curves)) {
1020
9
        return false;
1021
9
    }
1022
48
    if (b2a->output_channels < 3 || b2a->output_channels > ARRAY_COUNT(b2a->output_curves)) {
1023
12
        return false;
1024
12
    }
1025
1026
36
    uint32_t b_curve_offset = read_big_u32(mBATag->b_curve_offset);
1027
36
    uint32_t matrix_offset  = read_big_u32(mBATag->matrix_offset);
1028
36
    uint32_t m_curve_offset = read_big_u32(mBATag->m_curve_offset);
1029
36
    uint32_t clut_offset    = read_big_u32(mBATag->clut_offset);
1030
36
    uint32_t a_curve_offset = read_big_u32(mBATag->a_curve_offset);
1031
1032
36
    if (0 == b_curve_offset) {
1033
4
        return false;
1034
4
    }
1035
1036
    // "B" curves are our inputs, not outputs.
1037
32
    if (!read_curves(tag->buf, tag->size, b_curve_offset, b2a->input_channels,
1038
32
                     b2a->input_curves)) {
1039
32
        return false;
1040
32
    }
1041
1042
0
    if (0 != m_curve_offset) {
1043
0
        if (0 == matrix_offset) {
1044
0
            return false;
1045
0
        }
1046
        // Matrix channels is tied to input_channels (3), not output_channels.
1047
0
        b2a->matrix_channels = b2a->input_channels;
1048
1049
0
        if (!read_curves(tag->buf, tag->size, m_curve_offset, b2a->matrix_channels,
1050
0
                         b2a->matrix_curves)) {
1051
0
            return false;
1052
0
        }
1053
1054
0
        if (tag->size < matrix_offset + 12 * SAFE_SIZEOF(uint32_t)) {
1055
0
            return false;
1056
0
        }
1057
0
        float encoding_factor = pcs_is_xyz ? (32768 / 65535.0f) : 1.0f;  // TODO: understand
1058
0
        const uint8_t* mtx_buf = tag->buf + matrix_offset;
1059
0
        b2a->matrix.vals[0][0] = encoding_factor * read_big_fixed(mtx_buf +  0);
1060
0
        b2a->matrix.vals[0][1] = encoding_factor * read_big_fixed(mtx_buf +  4);
1061
0
        b2a->matrix.vals[0][2] = encoding_factor * read_big_fixed(mtx_buf +  8);
1062
0
        b2a->matrix.vals[1][0] = encoding_factor * read_big_fixed(mtx_buf + 12);
1063
0
        b2a->matrix.vals[1][1] = encoding_factor * read_big_fixed(mtx_buf + 16);
1064
0
        b2a->matrix.vals[1][2] = encoding_factor * read_big_fixed(mtx_buf + 20);
1065
0
        b2a->matrix.vals[2][0] = encoding_factor * read_big_fixed(mtx_buf + 24);
1066
0
        b2a->matrix.vals[2][1] = encoding_factor * read_big_fixed(mtx_buf + 28);
1067
0
        b2a->matrix.vals[2][2] = encoding_factor * read_big_fixed(mtx_buf + 32);
1068
0
        b2a->matrix.vals[0][3] = encoding_factor * read_big_fixed(mtx_buf + 36);
1069
0
        b2a->matrix.vals[1][3] = encoding_factor * read_big_fixed(mtx_buf + 40);
1070
0
        b2a->matrix.vals[2][3] = encoding_factor * read_big_fixed(mtx_buf + 44);
1071
0
    } else {
1072
0
        if (0 != matrix_offset) {
1073
0
            return false;
1074
0
        }
1075
0
        b2a->matrix_channels = 0;
1076
0
    }
1077
1078
0
    if (0 != a_curve_offset) {
1079
0
        if (0 == clut_offset) {
1080
0
            return false;
1081
0
        }
1082
1083
        // "A" curves are our output, not input.
1084
0
        if (!read_curves(tag->buf, tag->size, a_curve_offset, b2a->output_channels,
1085
0
                         b2a->output_curves)) {
1086
0
            return false;
1087
0
        }
1088
1089
0
        if (tag->size < clut_offset + SAFE_FIXED_SIZE(CLUT_Layout)) {
1090
0
            return false;
1091
0
        }
1092
0
        const CLUT_Layout* clut = (const CLUT_Layout*)(tag->buf + clut_offset);
1093
1094
0
        if (clut->grid_byte_width[0] == 1) {
1095
0
            b2a->grid_8  = clut->variable;
1096
0
            b2a->grid_16 = nullptr;
1097
0
        } else if (clut->grid_byte_width[0] == 2) {
1098
0
            b2a->grid_8  = nullptr;
1099
0
            b2a->grid_16 = clut->variable;
1100
0
        } else {
1101
0
            return false;
1102
0
        }
1103
1104
0
        uint64_t grid_size = b2a->output_channels * clut->grid_byte_width[0];
1105
0
        for (uint32_t i = 0; i < b2a->input_channels; ++i) {
1106
0
            b2a->grid_points[i] = clut->grid_points[i];
1107
0
            if (b2a->grid_points[i] < 2) {
1108
0
                return false;
1109
0
            }
1110
0
            grid_size *= b2a->grid_points[i];
1111
0
        }
1112
0
        if (tag->size < clut_offset + SAFE_FIXED_SIZE(CLUT_Layout) + grid_size) {
1113
0
            return false;
1114
0
        }
1115
0
    } else {
1116
0
        if (0 != clut_offset) {
1117
0
            return false;
1118
0
        }
1119
1120
0
        if (b2a->input_channels != b2a->output_channels) {
1121
0
            return false;
1122
0
        }
1123
1124
        // Zero out *output* channels to skip this stage.
1125
0
        b2a->output_channels = 0;
1126
0
    }
1127
0
    return true;
1128
0
}
1129
1130
// If you pass f, we'll fit a possibly-non-zero value for *f.
1131
// If you pass nullptr, we'll assume you want *f to be treated as zero.
1132
static int fit_linear(const skcms_Curve* curve, int N, float tol,
1133
826
                      float* c, float* d, float* f = nullptr) {
1134
826
    assert(N > 1);
1135
    // We iteratively fit the first points to the TF's linear piece.
1136
    // We want the cx + f line to pass through the first and last points we fit exactly.
1137
    //
1138
    // As we walk along the points we find the minimum and maximum slope of the line before the
1139
    // error would exceed our tolerance.  We stop when the range [slope_min, slope_max] becomes
1140
    // emtpy, when we definitely can't add any more points.
1141
    //
1142
    // Some points' error intervals may intersect the running interval but not lie fully
1143
    // within it.  So we keep track of the last point we saw that is a valid end point candidate,
1144
    // and once the search is done, back up to build the line through *that* point.
1145
826
    const float dx = 1.0f / static_cast<float>(N - 1);
1146
1147
826
    int lin_points = 1;
1148
1149
826
    float f_zero = 0.0f;
1150
826
    if (f) {
1151
826
        *f = eval_curve(curve, 0);
1152
826
    } else {
1153
0
        f = &f_zero;
1154
0
    }
1155
1156
1157
826
    float slope_min = -INFINITY_;
1158
826
    float slope_max = +INFINITY_;
1159
40.8k
    for (int i = 1; i < N; ++i) {
1160
40.5k
        float x = static_cast<float>(i) * dx;
1161
40.5k
        float y = eval_curve(curve, x);
1162
1163
40.5k
        float slope_max_i = (y + tol - *f) / x,
1164
40.5k
              slope_min_i = (y - tol - *f) / x;
1165
40.5k
        if (slope_max_i < slope_min || slope_max < slope_min_i) {
1166
            // Slope intervals would no longer overlap.
1167
573
            break;
1168
573
        }
1169
39.9k
        slope_max = fminf_(slope_max, slope_max_i);
1170
39.9k
        slope_min = fmaxf_(slope_min, slope_min_i);
1171
1172
39.9k
        float cur_slope = (y - *f) / x;
1173
39.9k
        if (slope_min <= cur_slope && cur_slope <= slope_max) {
1174
38.2k
            lin_points = i + 1;
1175
38.2k
            *c = cur_slope;
1176
38.2k
        }
1177
39.9k
    }
1178
1179
    // Set D to the last point that met our tolerance.
1180
826
    *d = static_cast<float>(lin_points - 1) * dx;
1181
826
    return lin_points;
1182
826
}
1183
1184
// If this skcms_Curve holds an identity table, rewrite it as an identity skcms_TransferFunction.
1185
1.18k
static void canonicalize_identity(skcms_Curve* curve) {
1186
1.18k
    if (curve->table_entries && curve->table_entries <= (uint32_t)INT_MAX) {
1187
826
        int N = (int)curve->table_entries;
1188
1189
826
        float c = 0.0f, d = 0.0f, f = 0.0f;
1190
826
        if (N == fit_linear(curve, N, 1.0f/static_cast<float>(2*N), &c,&d,&f)
1191
826
            && c == 1.0f
1192
826
            && f == 0.0f) {
1193
25
            curve->table_entries = 0;
1194
25
            curve->table_8       = nullptr;
1195
25
            curve->table_16      = nullptr;
1196
25
            curve->parametric    = skcms_TransferFunction{1,1,0,0,0,0,0};
1197
25
        }
1198
826
    }
1199
1.18k
}
1200
1201
532
static bool read_a2b(const skcms_ICCTag* tag, skcms_A2B* a2b, bool pcs_is_xyz) {
1202
532
    bool ok = false;
1203
532
    if (tag->type == skcms_Signature_mft1) { ok = read_tag_mft1(tag, a2b); }
1204
532
    if (tag->type == skcms_Signature_mft2) { ok = read_tag_mft2(tag, a2b); }
1205
532
    if (tag->type == skcms_Signature_mAB ) { ok = read_tag_mab(tag, a2b, pcs_is_xyz); }
1206
532
    if (!ok) {
1207
377
        return false;
1208
377
    }
1209
1210
155
    if (a2b->input_channels > 0) { canonicalize_identity(a2b->input_curves + 0); }
1211
155
    if (a2b->input_channels > 1) { canonicalize_identity(a2b->input_curves + 1); }
1212
155
    if (a2b->input_channels > 2) { canonicalize_identity(a2b->input_curves + 2); }
1213
155
    if (a2b->input_channels > 3) { canonicalize_identity(a2b->input_curves + 3); }
1214
1215
155
    if (a2b->matrix_channels > 0) { canonicalize_identity(a2b->matrix_curves + 0); }
1216
155
    if (a2b->matrix_channels > 1) { canonicalize_identity(a2b->matrix_curves + 1); }
1217
155
    if (a2b->matrix_channels > 2) { canonicalize_identity(a2b->matrix_curves + 2); }
1218
1219
155
    if (a2b->output_channels > 0) { canonicalize_identity(a2b->output_curves + 0); }
1220
155
    if (a2b->output_channels > 1) { canonicalize_identity(a2b->output_curves + 1); }
1221
155
    if (a2b->output_channels > 2) { canonicalize_identity(a2b->output_curves + 2); }
1222
1223
155
    return true;
1224
532
}
1225
1226
209
static bool read_b2a(const skcms_ICCTag* tag, skcms_B2A* b2a, bool pcs_is_xyz) {
1227
209
    bool ok = false;
1228
209
    if (tag->type == skcms_Signature_mft1) { ok = read_tag_mft1(tag, b2a); }
1229
209
    if (tag->type == skcms_Signature_mft2) { ok = read_tag_mft2(tag, b2a); }
1230
209
    if (tag->type == skcms_Signature_mBA ) { ok = read_tag_mba(tag, b2a, pcs_is_xyz); }
1231
209
    if (!ok) {
1232
167
        return false;
1233
167
    }
1234
1235
42
    if (b2a->input_channels > 0) { canonicalize_identity(b2a->input_curves + 0); }
1236
42
    if (b2a->input_channels > 1) { canonicalize_identity(b2a->input_curves + 1); }
1237
42
    if (b2a->input_channels > 2) { canonicalize_identity(b2a->input_curves + 2); }
1238
1239
42
    if (b2a->matrix_channels > 0) { canonicalize_identity(b2a->matrix_curves + 0); }
1240
42
    if (b2a->matrix_channels > 1) { canonicalize_identity(b2a->matrix_curves + 1); }
1241
42
    if (b2a->matrix_channels > 2) { canonicalize_identity(b2a->matrix_curves + 2); }
1242
1243
42
    if (b2a->output_channels > 0) { canonicalize_identity(b2a->output_curves + 0); }
1244
42
    if (b2a->output_channels > 1) { canonicalize_identity(b2a->output_curves + 1); }
1245
42
    if (b2a->output_channels > 2) { canonicalize_identity(b2a->output_curves + 2); }
1246
42
    if (b2a->output_channels > 3) { canonicalize_identity(b2a->output_curves + 3); }
1247
1248
42
    return true;
1249
209
}
1250
1251
typedef struct {
1252
    uint8_t type                     [4];
1253
    uint8_t reserved                 [4];
1254
    uint8_t color_primaries          [1];
1255
    uint8_t transfer_characteristics [1];
1256
    uint8_t matrix_coefficients      [1];
1257
    uint8_t video_full_range_flag    [1];
1258
} CICP_Layout;
1259
1260
17
static bool read_cicp(const skcms_ICCTag* tag, skcms_CICP* cicp) {
1261
17
    if (tag->type != skcms_Signature_CICP || tag->size < SAFE_SIZEOF(CICP_Layout)) {
1262
16
        return false;
1263
16
    }
1264
1265
1
    const CICP_Layout* cicpTag = (const CICP_Layout*)tag->buf;
1266
1267
1
    cicp->color_primaries          = cicpTag->color_primaries[0];
1268
1
    cicp->transfer_characteristics = cicpTag->transfer_characteristics[0];
1269
1
    cicp->matrix_coefficients      = cicpTag->matrix_coefficients[0];
1270
1
    cicp->video_full_range_flag    = cicpTag->video_full_range_flag[0];
1271
1
    return true;
1272
17
}
1273
1274
0
void skcms_GetTagByIndex(const skcms_ICCProfile* profile, uint32_t idx, skcms_ICCTag* tag) {
1275
0
    if (!profile || !profile->buffer || !tag) { return; }
1276
0
    if (idx > profile->tag_count) { return; }
1277
0
    const tag_Layout* tags = get_tag_table(profile);
1278
0
    tag->signature = read_big_u32(tags[idx].signature);
1279
0
    tag->size      = read_big_u32(tags[idx].size);
1280
0
    tag->buf       = read_big_u32(tags[idx].offset) + profile->buffer;
1281
0
    tag->type      = read_big_u32(tag->buf);
1282
0
}
1283
1284
24.2k
bool skcms_GetTagBySignature(const skcms_ICCProfile* profile, uint32_t sig, skcms_ICCTag* tag) {
1285
24.2k
    if (!profile || !profile->buffer || !tag) { return false; }
1286
24.2k
    const tag_Layout* tags = get_tag_table(profile);
1287
188k
    for (uint32_t i = 0; i < profile->tag_count; ++i) {
1288
176k
        if (read_big_u32(tags[i].signature) == sig) {
1289
11.9k
            tag->signature = sig;
1290
11.9k
            tag->size      = read_big_u32(tags[i].size);
1291
11.9k
            tag->buf       = read_big_u32(tags[i].offset) + profile->buffer;
1292
11.9k
            tag->type      = read_big_u32(tag->buf);
1293
11.9k
            return true;
1294
11.9k
        }
1295
176k
    }
1296
12.2k
    return false;
1297
24.2k
}
1298
1299
1.74k
static bool usable_as_src(const skcms_ICCProfile* profile) {
1300
1.74k
    return profile->has_A2B
1301
1.74k
       || (profile->has_trc && profile->has_toXYZD50);
1302
1.74k
}
1303
1304
bool skcms_ParseWithA2BPriority(const void* buf, size_t len,
1305
                                const int priority[], const int priorities,
1306
5.42k
                                skcms_ICCProfile* profile) {
1307
5.42k
    static_assert(SAFE_SIZEOF(header_Layout) == 132, "need to update header code");
1308
1309
5.42k
    if (!profile) {
1310
0
        return false;
1311
0
    }
1312
5.42k
    memset(profile, 0, SAFE_SIZEOF(*profile));
1313
1314
5.42k
    if (len < SAFE_SIZEOF(header_Layout)) {
1315
121
        return false;
1316
121
    }
1317
1318
    // Byte-swap all header fields
1319
5.30k
    const header_Layout* header  = (const header_Layout*)buf;
1320
5.30k
    profile->buffer              = (const uint8_t*)buf;
1321
5.30k
    profile->size                = read_big_u32(header->size);
1322
5.30k
    uint32_t version             = read_big_u32(header->version);
1323
5.30k
    profile->data_color_space    = read_big_u32(header->data_color_space);
1324
5.30k
    profile->pcs                 = read_big_u32(header->pcs);
1325
5.30k
    uint32_t signature           = read_big_u32(header->signature);
1326
5.30k
    float illuminant_X           = read_big_fixed(header->illuminant_X);
1327
5.30k
    float illuminant_Y           = read_big_fixed(header->illuminant_Y);
1328
5.30k
    float illuminant_Z           = read_big_fixed(header->illuminant_Z);
1329
5.30k
    profile->tag_count           = read_big_u32(header->tag_count);
1330
1331
    // Validate signature, size (smaller than buffer, large enough to hold tag table),
1332
    // and major version
1333
5.30k
    uint64_t tag_table_size = profile->tag_count * SAFE_SIZEOF(tag_Layout);
1334
5.30k
    if (signature != skcms_Signature_acsp ||
1335
5.30k
        profile->size > len ||
1336
5.30k
        profile->size < SAFE_SIZEOF(header_Layout) + tag_table_size ||
1337
5.30k
        (version >> 24) > 4) {
1338
378
        return false;
1339
378
    }
1340
1341
    // Validate that illuminant is D50 white
1342
4.92k
    if (fabsf_(illuminant_X - 0.9642f) > 0.0100f ||
1343
4.92k
        fabsf_(illuminant_Y - 1.0000f) > 0.0100f ||
1344
4.92k
        fabsf_(illuminant_Z - 0.8249f) > 0.0100f) {
1345
566
        return false;
1346
566
    }
1347
1348
    // Validate that all tag entries have sane offset + size
1349
4.36k
    const tag_Layout* tags = get_tag_table(profile);
1350
41.0k
    for (uint32_t i = 0; i < profile->tag_count; ++i) {
1351
37.1k
        uint32_t tag_offset = read_big_u32(tags[i].offset);
1352
37.1k
        uint32_t tag_size   = read_big_u32(tags[i].size);
1353
37.1k
        uint64_t tag_end    = (uint64_t)tag_offset + (uint64_t)tag_size;
1354
37.1k
        if (tag_size < 4 || tag_end > profile->size) {
1355
413
            return false;
1356
413
        }
1357
37.1k
    }
1358
1359
3.94k
    if (profile->pcs != skcms_Signature_XYZ && profile->pcs != skcms_Signature_Lab) {
1360
100
        return false;
1361
100
    }
1362
1363
3.84k
    bool pcs_is_xyz = profile->pcs == skcms_Signature_XYZ;
1364
1365
    // Pre-parse commonly used tags.
1366
3.84k
    skcms_ICCTag kTRC;
1367
3.84k
    if (profile->data_color_space == skcms_Signature_Gray &&
1368
3.84k
        skcms_GetTagBySignature(profile, skcms_Signature_kTRC, &kTRC)) {
1369
289
        if (!read_curve(kTRC.buf, kTRC.size, &profile->trc[0], nullptr)) {
1370
            // Malformed tag
1371
104
            return false;
1372
104
        }
1373
185
        profile->trc[1] = profile->trc[0];
1374
185
        profile->trc[2] = profile->trc[0];
1375
185
        profile->has_trc = true;
1376
1377
185
        if (pcs_is_xyz) {
1378
185
            profile->toXYZD50.vals[0][0] = illuminant_X;
1379
185
            profile->toXYZD50.vals[1][1] = illuminant_Y;
1380
185
            profile->toXYZD50.vals[2][2] = illuminant_Z;
1381
185
            profile->has_toXYZD50 = true;
1382
185
        }
1383
3.55k
    } else {
1384
3.55k
        skcms_ICCTag rTRC, gTRC, bTRC;
1385
3.55k
        if (skcms_GetTagBySignature(profile, skcms_Signature_rTRC, &rTRC) &&
1386
3.55k
            skcms_GetTagBySignature(profile, skcms_Signature_gTRC, &gTRC) &&
1387
3.55k
            skcms_GetTagBySignature(profile, skcms_Signature_bTRC, &bTRC)) {
1388
1.77k
            if (!read_curve(rTRC.buf, rTRC.size, &profile->trc[0], nullptr) ||
1389
1.77k
                !read_curve(gTRC.buf, gTRC.size, &profile->trc[1], nullptr) ||
1390
1.77k
                !read_curve(bTRC.buf, bTRC.size, &profile->trc[2], nullptr)) {
1391
                // Malformed TRC tags
1392
720
                return false;
1393
720
            }
1394
1.05k
            profile->has_trc = true;
1395
1.05k
        }
1396
1397
2.83k
        skcms_ICCTag rXYZ, gXYZ, bXYZ;
1398
2.83k
        if (skcms_GetTagBySignature(profile, skcms_Signature_rXYZ, &rXYZ) &&
1399
2.83k
            skcms_GetTagBySignature(profile, skcms_Signature_gXYZ, &gXYZ) &&
1400
2.83k
            skcms_GetTagBySignature(profile, skcms_Signature_bXYZ, &bXYZ)) {
1401
1.54k
            if (!read_to_XYZD50(&rXYZ, &gXYZ, &bXYZ, &profile->toXYZD50)) {
1402
                // Malformed XYZ tags
1403
720
                return false;
1404
720
            }
1405
823
            profile->has_toXYZD50 = true;
1406
823
        }
1407
2.83k
    }
1408
1409
5.96k
    for (int i = 0; i < priorities; i++) {
1410
        // enum { perceptual, relative_colormetric, saturation }
1411
4.18k
        if (priority[i] < 0 || priority[i] > 2) {
1412
0
            return false;
1413
0
        }
1414
4.18k
        uint32_t sig = skcms_Signature_A2B0 + static_cast<uint32_t>(priority[i]);
1415
4.18k
        skcms_ICCTag tag;
1416
4.18k
        if (skcms_GetTagBySignature(profile, sig, &tag)) {
1417
532
            if (!read_a2b(&tag, &profile->A2B, pcs_is_xyz)) {
1418
                // Malformed A2B tag
1419
377
                return false;
1420
377
            }
1421
155
            profile->has_A2B = true;
1422
155
            break;
1423
532
        }
1424
4.18k
    }
1425
1426
5.45k
    for (int i = 0; i < priorities; i++) {
1427
        // enum { perceptual, relative_colormetric, saturation }
1428
3.73k
        if (priority[i] < 0 || priority[i] > 2) {
1429
0
            return false;
1430
0
        }
1431
3.73k
        uint32_t sig = skcms_Signature_B2A0 + static_cast<uint32_t>(priority[i]);
1432
3.73k
        skcms_ICCTag tag;
1433
3.73k
        if (skcms_GetTagBySignature(profile, sig, &tag)) {
1434
209
            if (!read_b2a(&tag, &profile->B2A, pcs_is_xyz)) {
1435
                // Malformed B2A tag
1436
167
                return false;
1437
167
            }
1438
42
            profile->has_B2A = true;
1439
42
            break;
1440
209
        }
1441
3.73k
    }
1442
1443
1.75k
    skcms_ICCTag cicp_tag;
1444
1.75k
    if (skcms_GetTagBySignature(profile, skcms_Signature_CICP, &cicp_tag)) {
1445
17
        if (!read_cicp(&cicp_tag, &profile->CICP)) {
1446
            // Malformed CICP tag
1447
16
            return false;
1448
16
        }
1449
1
        profile->has_CICP = true;
1450
1
    }
1451
1452
1.74k
    return usable_as_src(profile);
1453
1.75k
}
1454
1455
1456
1.48M
const skcms_ICCProfile* skcms_sRGB_profile() {
1457
1.48M
    static const skcms_ICCProfile sRGB_profile = {
1458
1.48M
        nullptr,               // buffer, moot here
1459
1460
1.48M
        0,                     // size, moot here
1461
1.48M
        skcms_Signature_RGB,   // data_color_space
1462
1.48M
        skcms_Signature_XYZ,   // pcs
1463
1.48M
        0,                     // tag count, moot here
1464
1465
        // We choose to represent sRGB with its canonical transfer function,
1466
        // and with its canonical XYZD50 gamut matrix.
1467
1.48M
        true,  // has_trc, followed by the 3 trc curves
1468
1.48M
        {
1469
1.48M
            {{0, {2.4f, (float)(1/1.055), (float)(0.055/1.055), (float)(1/12.92), 0.04045f, 0, 0}}},
1470
1.48M
            {{0, {2.4f, (float)(1/1.055), (float)(0.055/1.055), (float)(1/12.92), 0.04045f, 0, 0}}},
1471
1.48M
            {{0, {2.4f, (float)(1/1.055), (float)(0.055/1.055), (float)(1/12.92), 0.04045f, 0, 0}}},
1472
1.48M
        },
1473
1474
1.48M
        true,  // has_toXYZD50, followed by 3x3 toXYZD50 matrix
1475
1.48M
        {{
1476
1.48M
            { 0.436065674f, 0.385147095f, 0.143066406f },
1477
1.48M
            { 0.222488403f, 0.716873169f, 0.060607910f },
1478
1.48M
            { 0.013916016f, 0.097076416f, 0.714096069f },
1479
1.48M
        }},
1480
1481
1.48M
        false, // has_A2B, followed by A2B itself, which we don't care about.
1482
1.48M
        {
1483
1.48M
            0,
1484
1.48M
            {
1485
1.48M
                {{0, {0,0, 0,0,0,0,0}}},
1486
1.48M
                {{0, {0,0, 0,0,0,0,0}}},
1487
1.48M
                {{0, {0,0, 0,0,0,0,0}}},
1488
1.48M
                {{0, {0,0, 0,0,0,0,0}}},
1489
1.48M
            },
1490
1.48M
            {0,0,0,0},
1491
1.48M
            nullptr,
1492
1.48M
            nullptr,
1493
1494
1.48M
            0,
1495
1.48M
            {
1496
1.48M
                {{0, {0,0, 0,0,0,0,0}}},
1497
1.48M
                {{0, {0,0, 0,0,0,0,0}}},
1498
1.48M
                {{0, {0,0, 0,0,0,0,0}}},
1499
1.48M
            },
1500
1.48M
            {{
1501
1.48M
                { 0,0,0,0 },
1502
1.48M
                { 0,0,0,0 },
1503
1.48M
                { 0,0,0,0 },
1504
1.48M
            }},
1505
1506
1.48M
            0,
1507
1.48M
            {
1508
1.48M
                {{0, {0,0, 0,0,0,0,0}}},
1509
1.48M
                {{0, {0,0, 0,0,0,0,0}}},
1510
1.48M
                {{0, {0,0, 0,0,0,0,0}}},
1511
1.48M
            },
1512
1.48M
        },
1513
1514
1.48M
        false, // has_B2A, followed by B2A itself, which we also don't care about.
1515
1.48M
        {
1516
1.48M
            0,
1517
1.48M
            {
1518
1.48M
                {{0, {0,0, 0,0,0,0,0}}},
1519
1.48M
                {{0, {0,0, 0,0,0,0,0}}},
1520
1.48M
                {{0, {0,0, 0,0,0,0,0}}},
1521
1.48M
            },
1522
1523
1.48M
            0,
1524
1.48M
            {{
1525
1.48M
                { 0,0,0,0 },
1526
1.48M
                { 0,0,0,0 },
1527
1.48M
                { 0,0,0,0 },
1528
1.48M
            }},
1529
1.48M
            {
1530
1.48M
                {{0, {0,0, 0,0,0,0,0}}},
1531
1.48M
                {{0, {0,0, 0,0,0,0,0}}},
1532
1.48M
                {{0, {0,0, 0,0,0,0,0}}},
1533
1.48M
            },
1534
1535
1.48M
            0,
1536
1.48M
            {0,0,0,0},
1537
1.48M
            nullptr,
1538
1.48M
            nullptr,
1539
1.48M
            {
1540
1.48M
                {{0, {0,0, 0,0,0,0,0}}},
1541
1.48M
                {{0, {0,0, 0,0,0,0,0}}},
1542
1.48M
                {{0, {0,0, 0,0,0,0,0}}},
1543
1.48M
                {{0, {0,0, 0,0,0,0,0}}},
1544
1.48M
            },
1545
1.48M
        },
1546
1547
1.48M
        false, // has_CICP, followed by cicp itself which we don't care about.
1548
1.48M
        { 0, 0, 0, 0 },
1549
1.48M
    };
1550
1.48M
    return &sRGB_profile;
1551
1.48M
}
1552
1553
5.45k
const skcms_ICCProfile* skcms_XYZD50_profile() {
1554
    // Just like sRGB above, but with identity transfer functions and toXYZD50 matrix.
1555
5.45k
    static const skcms_ICCProfile XYZD50_profile = {
1556
5.45k
        nullptr,               // buffer, moot here
1557
1558
5.45k
        0,                     // size, moot here
1559
5.45k
        skcms_Signature_RGB,   // data_color_space
1560
5.45k
        skcms_Signature_XYZ,   // pcs
1561
5.45k
        0,                     // tag count, moot here
1562
1563
5.45k
        true,  // has_trc, followed by the 3 trc curves
1564
5.45k
        {
1565
5.45k
            {{0, {1,1, 0,0,0,0,0}}},
1566
5.45k
            {{0, {1,1, 0,0,0,0,0}}},
1567
5.45k
            {{0, {1,1, 0,0,0,0,0}}},
1568
5.45k
        },
1569
1570
5.45k
        true,  // has_toXYZD50, followed by 3x3 toXYZD50 matrix
1571
5.45k
        {{
1572
5.45k
            { 1,0,0 },
1573
5.45k
            { 0,1,0 },
1574
5.45k
            { 0,0,1 },
1575
5.45k
        }},
1576
1577
5.45k
        false, // has_A2B, followed by A2B itself, which we don't care about.
1578
5.45k
        {
1579
5.45k
            0,
1580
5.45k
            {
1581
5.45k
                {{0, {0,0, 0,0,0,0,0}}},
1582
5.45k
                {{0, {0,0, 0,0,0,0,0}}},
1583
5.45k
                {{0, {0,0, 0,0,0,0,0}}},
1584
5.45k
                {{0, {0,0, 0,0,0,0,0}}},
1585
5.45k
            },
1586
5.45k
            {0,0,0,0},
1587
5.45k
            nullptr,
1588
5.45k
            nullptr,
1589
1590
5.45k
            0,
1591
5.45k
            {
1592
5.45k
                {{0, {0,0, 0,0,0,0,0}}},
1593
5.45k
                {{0, {0,0, 0,0,0,0,0}}},
1594
5.45k
                {{0, {0,0, 0,0,0,0,0}}},
1595
5.45k
            },
1596
5.45k
            {{
1597
5.45k
                { 0,0,0,0 },
1598
5.45k
                { 0,0,0,0 },
1599
5.45k
                { 0,0,0,0 },
1600
5.45k
            }},
1601
1602
5.45k
            0,
1603
5.45k
            {
1604
5.45k
                {{0, {0,0, 0,0,0,0,0}}},
1605
5.45k
                {{0, {0,0, 0,0,0,0,0}}},
1606
5.45k
                {{0, {0,0, 0,0,0,0,0}}},
1607
5.45k
            },
1608
5.45k
        },
1609
1610
5.45k
        false, // has_B2A, followed by B2A itself, which we also don't care about.
1611
5.45k
        {
1612
5.45k
            0,
1613
5.45k
            {
1614
5.45k
                {{0, {0,0, 0,0,0,0,0}}},
1615
5.45k
                {{0, {0,0, 0,0,0,0,0}}},
1616
5.45k
                {{0, {0,0, 0,0,0,0,0}}},
1617
5.45k
            },
1618
1619
5.45k
            0,
1620
5.45k
            {{
1621
5.45k
                { 0,0,0,0 },
1622
5.45k
                { 0,0,0,0 },
1623
5.45k
                { 0,0,0,0 },
1624
5.45k
            }},
1625
5.45k
            {
1626
5.45k
                {{0, {0,0, 0,0,0,0,0}}},
1627
5.45k
                {{0, {0,0, 0,0,0,0,0}}},
1628
5.45k
                {{0, {0,0, 0,0,0,0,0}}},
1629
5.45k
            },
1630
1631
5.45k
            0,
1632
5.45k
            {0,0,0,0},
1633
5.45k
            nullptr,
1634
5.45k
            nullptr,
1635
5.45k
            {
1636
5.45k
                {{0, {0,0, 0,0,0,0,0}}},
1637
5.45k
                {{0, {0,0, 0,0,0,0,0}}},
1638
5.45k
                {{0, {0,0, 0,0,0,0,0}}},
1639
5.45k
                {{0, {0,0, 0,0,0,0,0}}},
1640
5.45k
            },
1641
5.45k
        },
1642
1643
5.45k
        false, // has_CICP, followed by cicp itself which we don't care about.
1644
5.45k
        { 0, 0, 0, 0 },
1645
5.45k
    };
1646
1647
5.45k
    return &XYZD50_profile;
1648
5.45k
}
1649
1650
8.34k
const skcms_TransferFunction* skcms_sRGB_TransferFunction() {
1651
8.34k
    return &skcms_sRGB_profile()->trc[0].parametric;
1652
8.34k
}
1653
1654
485
const skcms_TransferFunction* skcms_sRGB_Inverse_TransferFunction() {
1655
485
    static const skcms_TransferFunction sRGB_inv =
1656
485
        {0.416666657f, 1.137283325f, -0.0f, 12.920000076f, 0.003130805f, -0.054969788f, -0.0f};
1657
485
    return &sRGB_inv;
1658
485
}
1659
1660
0
const skcms_TransferFunction* skcms_Identity_TransferFunction() {
1661
0
    static const skcms_TransferFunction identity = {1,1,0,0,0,0,0};
1662
0
    return &identity;
1663
0
}
1664
1665
const uint8_t skcms_252_random_bytes[] = {
1666
    8, 179, 128, 204, 253, 38, 134, 184, 68, 102, 32, 138, 99, 39, 169, 215,
1667
    119, 26, 3, 223, 95, 239, 52, 132, 114, 74, 81, 234, 97, 116, 244, 205, 30,
1668
    154, 173, 12, 51, 159, 122, 153, 61, 226, 236, 178, 229, 55, 181, 220, 191,
1669
    194, 160, 126, 168, 82, 131, 18, 180, 245, 163, 22, 246, 69, 235, 252, 57,
1670
    108, 14, 6, 152, 240, 255, 171, 242, 20, 227, 177, 238, 96, 85, 16, 211,
1671
    70, 200, 149, 155, 146, 127, 145, 100, 151, 109, 19, 165, 208, 195, 164,
1672
    137, 254, 182, 248, 64, 201, 45, 209, 5, 147, 207, 210, 113, 162, 83, 225,
1673
    9, 31, 15, 231, 115, 37, 58, 53, 24, 49, 197, 56, 120, 172, 48, 21, 214,
1674
    129, 111, 11, 50, 187, 196, 34, 60, 103, 71, 144, 47, 203, 77, 80, 232,
1675
    140, 222, 250, 206, 166, 247, 139, 249, 221, 72, 106, 27, 199, 117, 54,
1676
    219, 135, 118, 40, 79, 41, 251, 46, 93, 212, 92, 233, 148, 28, 121, 63,
1677
    123, 158, 105, 59, 29, 42, 143, 23, 0, 107, 176, 87, 104, 183, 156, 193,
1678
    189, 90, 188, 65, 190, 17, 198, 7, 186, 161, 1, 124, 78, 125, 170, 133,
1679
    174, 218, 67, 157, 75, 101, 89, 217, 62, 33, 141, 228, 25, 35, 91, 230, 4,
1680
    2, 13, 73, 86, 167, 237, 84, 243, 44, 185, 66, 130, 110, 150, 142, 216, 88,
1681
    112, 36, 224, 136, 202, 76, 94, 98, 175, 213
1682
};
1683
1684
67.6k
bool skcms_ApproximatelyEqualProfiles(const skcms_ICCProfile* A, const skcms_ICCProfile* B) {
1685
    // Test for exactly equal profiles first.
1686
67.6k
    if (A == B || 0 == memcmp(A,B, sizeof(skcms_ICCProfile))) {
1687
64.9k
        return true;
1688
64.9k
    }
1689
1690
    // For now this is the essentially the same strategy we use in test_only.c
1691
    // for our skcms_Transform() smoke tests:
1692
    //    1) transform A to XYZD50
1693
    //    2) transform B to XYZD50
1694
    //    3) return true if they're similar enough
1695
    // Our current criterion in 3) is maximum 1 bit error per XYZD50 byte.
1696
1697
    // skcms_252_random_bytes are 252 of a random shuffle of all possible bytes.
1698
    // 252 is evenly divisible by 3 and 4.  Only 192, 10, 241, and 43 are missing.
1699
1700
    // We want to allow otherwise equivalent profiles tagged as grayscale and RGB
1701
    // to be treated as equal.  But CMYK profiles are a totally different ballgame.
1702
2.72k
    const auto CMYK = skcms_Signature_CMYK;
1703
2.72k
    if ((A->data_color_space == CMYK) != (B->data_color_space == CMYK)) {
1704
0
        return false;
1705
0
    }
1706
1707
    // Interpret as RGB_888 if data color space is RGB or GRAY, RGBA_8888 if CMYK.
1708
    // TODO: working with RGBA_8888 either way is probably fastest.
1709
2.72k
    skcms_PixelFormat fmt = skcms_PixelFormat_RGB_888;
1710
2.72k
    size_t npixels = 84;
1711
2.72k
    if (A->data_color_space == skcms_Signature_CMYK) {
1712
0
        fmt = skcms_PixelFormat_RGBA_8888;
1713
0
        npixels = 63;
1714
0
    }
1715
1716
    // TODO: if A or B is a known profile (skcms_sRGB_profile, skcms_XYZD50_profile),
1717
    // use pre-canned results and skip that skcms_Transform() call?
1718
2.72k
    uint8_t dstA[252],
1719
2.72k
            dstB[252];
1720
2.72k
    if (!skcms_Transform(
1721
2.72k
                skcms_252_random_bytes,     fmt, skcms_AlphaFormat_Unpremul, A,
1722
2.72k
                dstA, skcms_PixelFormat_RGB_888, skcms_AlphaFormat_Unpremul, skcms_XYZD50_profile(),
1723
2.72k
                npixels)) {
1724
0
        return false;
1725
0
    }
1726
2.72k
    if (!skcms_Transform(
1727
2.72k
                skcms_252_random_bytes,     fmt, skcms_AlphaFormat_Unpremul, B,
1728
2.72k
                dstB, skcms_PixelFormat_RGB_888, skcms_AlphaFormat_Unpremul, skcms_XYZD50_profile(),
1729
2.72k
                npixels)) {
1730
0
        return false;
1731
0
    }
1732
1733
    // TODO: make sure this final check has reasonable codegen.
1734
125k
    for (size_t i = 0; i < 252; i++) {
1735
125k
        if (abs((int)dstA[i] - (int)dstB[i]) > 1) {
1736
2.27k
            return false;
1737
2.27k
        }
1738
125k
    }
1739
451
    return true;
1740
2.72k
}
1741
1742
bool skcms_TRCs_AreApproximateInverse(const skcms_ICCProfile* profile,
1743
370
                                      const skcms_TransferFunction* inv_tf) {
1744
370
    if (!profile || !profile->has_trc) {
1745
0
        return false;
1746
0
    }
1747
1748
370
    return skcms_AreApproximateInverses(&profile->trc[0], inv_tf) &&
1749
370
           skcms_AreApproximateInverses(&profile->trc[1], inv_tf) &&
1750
370
           skcms_AreApproximateInverses(&profile->trc[2], inv_tf);
1751
370
}
1752
1753
2.95k
static bool is_zero_to_one(float x) {
1754
2.95k
    return 0 <= x && x <= 1;
1755
2.95k
}
1756
1757
typedef struct { float vals[3]; } skcms_Vector3;
1758
1759
885
static skcms_Vector3 mv_mul(const skcms_Matrix3x3* m, const skcms_Vector3* v) {
1760
885
    skcms_Vector3 dst = {{0,0,0}};
1761
3.54k
    for (int row = 0; row < 3; ++row) {
1762
2.65k
        dst.vals[row] = m->vals[row][0] * v->vals[0]
1763
2.65k
                      + m->vals[row][1] * v->vals[1]
1764
2.65k
                      + m->vals[row][2] * v->vals[2];
1765
2.65k
    }
1766
885
    return dst;
1767
885
}
1768
1769
bool skcms_AdaptToXYZD50(float wx, float wy,
1770
295
                         skcms_Matrix3x3* toXYZD50) {
1771
295
    if (!is_zero_to_one(wx) || !is_zero_to_one(wy) ||
1772
295
        !toXYZD50) {
1773
0
        return false;
1774
0
    }
1775
1776
    // Assumes that Y is 1.0f.
1777
295
    skcms_Vector3 wXYZ = { { wx / wy, 1, (1 - wx - wy) / wy } };
1778
1779
    // Now convert toXYZ matrix to toXYZD50.
1780
295
    skcms_Vector3 wXYZD50 = { { 0.96422f, 1.0f, 0.82521f } };
1781
1782
    // Calculate the chromatic adaptation matrix.  We will use the Bradford method, thus
1783
    // the matrices below.  The Bradford method is used by Adobe and is widely considered
1784
    // to be the best.
1785
295
    skcms_Matrix3x3 xyz_to_lms = {{
1786
295
        {  0.8951f,  0.2664f, -0.1614f },
1787
295
        { -0.7502f,  1.7135f,  0.0367f },
1788
295
        {  0.0389f, -0.0685f,  1.0296f },
1789
295
    }};
1790
295
    skcms_Matrix3x3 lms_to_xyz = {{
1791
295
        {  0.9869929f, -0.1470543f, 0.1599627f },
1792
295
        {  0.4323053f,  0.5183603f, 0.0492912f },
1793
295
        { -0.0085287f,  0.0400428f, 0.9684867f },
1794
295
    }};
1795
1796
295
    skcms_Vector3 srcCone = mv_mul(&xyz_to_lms, &wXYZ);
1797
295
    skcms_Vector3 dstCone = mv_mul(&xyz_to_lms, &wXYZD50);
1798
1799
295
    *toXYZD50 = {{
1800
295
        { dstCone.vals[0] / srcCone.vals[0], 0, 0 },
1801
295
        { 0, dstCone.vals[1] / srcCone.vals[1], 0 },
1802
295
        { 0, 0, dstCone.vals[2] / srcCone.vals[2] },
1803
295
    }};
1804
295
    *toXYZD50 = skcms_Matrix3x3_concat(toXYZD50, &xyz_to_lms);
1805
295
    *toXYZD50 = skcms_Matrix3x3_concat(&lms_to_xyz, toXYZD50);
1806
1807
295
    return true;
1808
295
}
1809
1810
bool skcms_PrimariesToXYZD50(float rx, float ry,
1811
                             float gx, float gy,
1812
                             float bx, float by,
1813
                             float wx, float wy,
1814
295
                             skcms_Matrix3x3* toXYZD50) {
1815
295
    if (!is_zero_to_one(rx) || !is_zero_to_one(ry) ||
1816
295
        !is_zero_to_one(gx) || !is_zero_to_one(gy) ||
1817
295
        !is_zero_to_one(bx) || !is_zero_to_one(by) ||
1818
295
        !is_zero_to_one(wx) || !is_zero_to_one(wy) ||
1819
295
        !toXYZD50) {
1820
0
        return false;
1821
0
    }
1822
1823
    // First, we need to convert xy values (primaries) to XYZ.
1824
295
    skcms_Matrix3x3 primaries = {{
1825
295
        { rx, gx, bx },
1826
295
        { ry, gy, by },
1827
295
        { 1 - rx - ry, 1 - gx - gy, 1 - bx - by },
1828
295
    }};
1829
295
    skcms_Matrix3x3 primaries_inv;
1830
295
    if (!skcms_Matrix3x3_invert(&primaries, &primaries_inv)) {
1831
0
        return false;
1832
0
    }
1833
1834
    // Assumes that Y is 1.0f.
1835
295
    skcms_Vector3 wXYZ = { { wx / wy, 1, (1 - wx - wy) / wy } };
1836
295
    skcms_Vector3 XYZ = mv_mul(&primaries_inv, &wXYZ);
1837
1838
295
    skcms_Matrix3x3 toXYZ = {{
1839
295
        { XYZ.vals[0],           0,           0 },
1840
295
        {           0, XYZ.vals[1],           0 },
1841
295
        {           0,           0, XYZ.vals[2] },
1842
295
    }};
1843
295
    toXYZ = skcms_Matrix3x3_concat(&primaries, &toXYZ);
1844
1845
295
    skcms_Matrix3x3 DXtoD50;
1846
295
    if (!skcms_AdaptToXYZD50(wx, wy, &DXtoD50)) {
1847
0
        return false;
1848
0
    }
1849
1850
295
    *toXYZD50 = skcms_Matrix3x3_concat(&DXtoD50, &toXYZ);
1851
295
    return true;
1852
295
}
1853
1854
1855
227k
bool skcms_Matrix3x3_invert(const skcms_Matrix3x3* src, skcms_Matrix3x3* dst) {
1856
227k
    double a00 = src->vals[0][0],
1857
227k
           a01 = src->vals[1][0],
1858
227k
           a02 = src->vals[2][0],
1859
227k
           a10 = src->vals[0][1],
1860
227k
           a11 = src->vals[1][1],
1861
227k
           a12 = src->vals[2][1],
1862
227k
           a20 = src->vals[0][2],
1863
227k
           a21 = src->vals[1][2],
1864
227k
           a22 = src->vals[2][2];
1865
1866
227k
    double b0 = a00*a11 - a01*a10,
1867
227k
           b1 = a00*a12 - a02*a10,
1868
227k
           b2 = a01*a12 - a02*a11,
1869
227k
           b3 = a20,
1870
227k
           b4 = a21,
1871
227k
           b5 = a22;
1872
1873
227k
    double determinant = b0*b5
1874
227k
                       - b1*b4
1875
227k
                       + b2*b3;
1876
1877
227k
    if (determinant == 0) {
1878
133
        return false;
1879
133
    }
1880
1881
227k
    double invdet = 1.0 / determinant;
1882
227k
    if (invdet > +FLT_MAX || invdet < -FLT_MAX || !isfinitef_((float)invdet)) {
1883
31
        return false;
1884
31
    }
1885
1886
227k
    b0 *= invdet;
1887
227k
    b1 *= invdet;
1888
227k
    b2 *= invdet;
1889
227k
    b3 *= invdet;
1890
227k
    b4 *= invdet;
1891
227k
    b5 *= invdet;
1892
1893
227k
    dst->vals[0][0] = (float)( a11*b5 - a12*b4 );
1894
227k
    dst->vals[1][0] = (float)( a02*b4 - a01*b5 );
1895
227k
    dst->vals[2][0] = (float)(        +     b2 );
1896
227k
    dst->vals[0][1] = (float)( a12*b3 - a10*b5 );
1897
227k
    dst->vals[1][1] = (float)( a00*b5 - a02*b3 );
1898
227k
    dst->vals[2][1] = (float)(        -     b1 );
1899
227k
    dst->vals[0][2] = (float)( a10*b4 - a11*b3 );
1900
227k
    dst->vals[1][2] = (float)( a01*b3 - a00*b4 );
1901
227k
    dst->vals[2][2] = (float)(        +     b0 );
1902
1903
910k
    for (int r = 0; r < 3; ++r)
1904
2.73M
    for (int c = 0; c < 3; ++c) {
1905
2.04M
        if (!isfinitef_(dst->vals[r][c])) {
1906
24
            return false;
1907
24
        }
1908
2.04M
    }
1909
227k
    return true;
1910
227k
}
1911
1912
226k
skcms_Matrix3x3 skcms_Matrix3x3_concat(const skcms_Matrix3x3* A, const skcms_Matrix3x3* B) {
1913
226k
    skcms_Matrix3x3 m = { { { 0,0,0 },{ 0,0,0 },{ 0,0,0 } } };
1914
905k
    for (int r = 0; r < 3; r++)
1915
2.71M
        for (int c = 0; c < 3; c++) {
1916
2.03M
            m.vals[r][c] = A->vals[r][0] * B->vals[0][c]
1917
2.03M
                         + A->vals[r][1] * B->vals[1][c]
1918
2.03M
                         + A->vals[r][2] * B->vals[2][c];
1919
2.03M
        }
1920
226k
    return m;
1921
226k
}
1922
1923
#if defined(__clang__)
1924
    [[clang::no_sanitize("float-divide-by-zero")]]  // Checked for by classify() on the way out.
1925
#endif
1926
676k
bool skcms_TransferFunction_invert(const skcms_TransferFunction* src, skcms_TransferFunction* dst) {
1927
676k
    TF_PQish  pq;
1928
676k
    TF_HLGish hlg;
1929
676k
    switch (classify(*src, &pq, &hlg)) {
1930
0
        case skcms_TFType_Invalid: return false;
1931
676k
        case skcms_TFType_sRGBish: break;  // handled below
1932
1933
2
        case skcms_TFType_PQish:
1934
2
            *dst = { TFKind_marker(skcms_TFType_PQish), -pq.A,  pq.D, 1.0f/pq.F
1935
2
                                                      ,  pq.B, -pq.E, 1.0f/pq.C};
1936
2
            return true;
1937
1938
2
        case skcms_TFType_HLGish:
1939
2
            *dst = { TFKind_marker(skcms_TFType_HLGinvish), 1.0f/hlg.R, 1.0f/hlg.G
1940
2
                                                          , 1.0f/hlg.a, hlg.b, hlg.c
1941
2
                                                          , hlg.K_minus_1 };
1942
2
            return true;
1943
1944
5
        case skcms_TFType_HLGinvish:
1945
5
            *dst = { TFKind_marker(skcms_TFType_HLGish), 1.0f/hlg.R, 1.0f/hlg.G
1946
5
                                                       , 1.0f/hlg.a, hlg.b, hlg.c
1947
5
                                                       , hlg.K_minus_1 };
1948
5
            return true;
1949
676k
    }
1950
1951
676k
    assert (classify(*src) == skcms_TFType_sRGBish);
1952
1953
    // We're inverting this function, solving for x in terms of y.
1954
    //   y = (cx + f)         x < d
1955
    //       (ax + b)^g + e   x ≥ d
1956
    // The inverse of this function can be expressed in the same piecewise form.
1957
676k
    skcms_TransferFunction inv = {0,0,0,0,0,0,0};
1958
1959
    // We'll start by finding the new threshold inv.d.
1960
    // In principle we should be able to find that by solving for y at x=d from either side.
1961
    // (If those two d values aren't the same, it's a discontinuous transfer function.)
1962
676k
    float d_l =       src->c * src->d + src->f,
1963
676k
          d_r = powf_(src->a * src->d + src->b, src->g) + src->e;
1964
676k
    if (fabsf_(d_l - d_r) > 1/512.0f) {
1965
64
        return false;
1966
64
    }
1967
676k
    inv.d = d_l;  // TODO(mtklein): better in practice to choose d_r?
1968
1969
    // When d=0, the linear section collapses to a point.  We leave c,d,f all zero in that case.
1970
676k
    if (inv.d > 0) {
1971
        // Inverting the linear section is pretty straightfoward:
1972
        //        y       = cx + f
1973
        //        y - f   = cx
1974
        //   (1/c)y - f/c = x
1975
643k
        inv.c =    1.0f/src->c;
1976
643k
        inv.f = -src->f/src->c;
1977
643k
    }
1978
1979
    // The interesting part is inverting the nonlinear section:
1980
    //         y                = (ax + b)^g + e.
1981
    //         y - e            = (ax + b)^g
1982
    //        (y - e)^1/g       =  ax + b
1983
    //        (y - e)^1/g - b   =  ax
1984
    //   (1/a)(y - e)^1/g - b/a =   x
1985
    //
1986
    // To make that fit our form, we need to move the (1/a) term inside the exponentiation:
1987
    //   let k = (1/a)^g
1988
    //   (1/a)( y -  e)^1/g - b/a = x
1989
    //        (ky - ke)^1/g - b/a = x
1990
1991
676k
    float k = powf_(src->a, -src->g);  // (1/a)^g == a^-g
1992
676k
    inv.g = 1.0f / src->g;
1993
676k
    inv.a = k;
1994
676k
    inv.b = -k * src->e;
1995
676k
    inv.e = -src->b / src->a;
1996
1997
    // We need to enforce the same constraints here that we do when fitting a curve,
1998
    // a >= 0 and ad+b >= 0.  These constraints are checked by classify(), so they're true
1999
    // of the source function if we're here.
2000
2001
    // Just like when fitting the curve, there's really no way to rescue a < 0.
2002
676k
    if (inv.a < 0) {
2003
0
        return false;
2004
0
    }
2005
    // On the other hand we can rescue an ad+b that's gone slightly negative here.
2006
676k
    if (inv.a * inv.d + inv.b < 0) {
2007
8
        inv.b = -inv.a * inv.d;
2008
8
    }
2009
2010
    // That should usually make classify(inv) == sRGBish true, but there are a couple situations
2011
    // where we might still fail here, like non-finite parameter values.
2012
676k
    if (classify(inv) != skcms_TFType_sRGBish) {
2013
33
        return false;
2014
33
    }
2015
2016
676k
    assert (inv.a >= 0);
2017
676k
    assert (inv.a * inv.d + inv.b >= 0);
2018
2019
    // Now in principle we're done.
2020
    // But to preserve the valuable invariant inv(src(1.0f)) == 1.0f, we'll tweak
2021
    // e or f of the inverse, depending on which segment contains src(1.0f).
2022
676k
    float s = skcms_TransferFunction_eval(src, 1.0f);
2023
676k
    if (!isfinitef_(s)) {
2024
4
        return false;
2025
4
    }
2026
2027
676k
    float sign = s < 0 ? -1.0f : 1.0f;
2028
676k
    s *= sign;
2029
676k
    if (s < inv.d) {
2030
4
        inv.f = 1.0f - sign * inv.c * s;
2031
676k
    } else {
2032
676k
        inv.e = 1.0f - sign * powf_(inv.a * s + inv.b, inv.g);
2033
676k
    }
2034
2035
676k
    *dst = inv;
2036
676k
    return classify(*dst) == skcms_TFType_sRGBish;
2037
676k
}
2038
2039
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
2040
2041
// From here below we're approximating an skcms_Curve with an skcms_TransferFunction{g,a,b,c,d,e,f}:
2042
//
2043
//   tf(x) =  cx + f          x < d
2044
//   tf(x) = (ax + b)^g + e   x ≥ d
2045
//
2046
// When fitting, we add the additional constraint that both pieces meet at d:
2047
//
2048
//   cd + f = (ad + b)^g + e
2049
//
2050
// Solving for e and folding it through gives an alternate formulation of the non-linear piece:
2051
//
2052
//   tf(x) =                           cx + f   x < d
2053
//   tf(x) = (ax + b)^g - (ad + b)^g + cd + f   x ≥ d
2054
//
2055
// Our overall strategy is then:
2056
//    For a couple tolerances,
2057
//       - fit_linear():    fit c,d,f iteratively to as many points as our tolerance allows
2058
//       - invert c,d,f
2059
//       - fit_nonlinear(): fit g,a,b using Gauss-Newton given those inverted c,d,f
2060
//                          (and by constraint, inverted e) to the inverse of the table.
2061
//    Return the parameters with least maximum error.
2062
//
2063
// To run Gauss-Newton to find g,a,b, we'll also need the gradient of the residuals
2064
// of round-trip f_inv(x), the inverse of the non-linear piece of f(x).
2065
//
2066
//    let y = Table(x)
2067
//    r(x) = x - f_inv(y)
2068
//
2069
//    ∂r/∂g = ln(ay + b)*(ay + b)^g
2070
//          - ln(ad + b)*(ad + b)^g
2071
//    ∂r/∂a = yg(ay + b)^(g-1)
2072
//          - dg(ad + b)^(g-1)
2073
//    ∂r/∂b =  g(ay + b)^(g-1)
2074
//          -  g(ad + b)^(g-1)
2075
2076
// Return the residual of roundtripping skcms_Curve(x) through f_inv(y) with parameters P,
2077
// and fill out the gradient of the residual into dfdP.
2078
static float rg_nonlinear(float x,
2079
                          const skcms_Curve* curve,
2080
                          const skcms_TransferFunction* tf,
2081
0
                          float dfdP[3]) {
2082
0
    const float y = eval_curve(curve, x);
2083
2084
0
    const float g = tf->g, a = tf->a, b = tf->b,
2085
0
                c = tf->c, d = tf->d, f = tf->f;
2086
2087
0
    const float Y = fmaxf_(a*y + b, 0.0f),
2088
0
                D =        a*d + b;
2089
0
    assert (D >= 0);
2090
2091
    // The gradient.
2092
0
    dfdP[0] = logf_(Y)*powf_(Y, g)
2093
0
            - logf_(D)*powf_(D, g);
2094
0
    dfdP[1] = y*g*powf_(Y, g-1)
2095
0
            - d*g*powf_(D, g-1);
2096
0
    dfdP[2] =   g*powf_(Y, g-1)
2097
0
            -   g*powf_(D, g-1);
2098
2099
    // The residual.
2100
0
    const float f_inv = powf_(Y, g)
2101
0
                      - powf_(D, g)
2102
0
                      + c*d + f;
2103
0
    return x - f_inv;
2104
0
}
2105
2106
static bool gauss_newton_step(const skcms_Curve* curve,
2107
                                    skcms_TransferFunction* tf,
2108
0
                              float x0, float dx, int N) {
2109
    // We'll sample x from the range [x0,x1] (both inclusive) N times with even spacing.
2110
    //
2111
    // Let P = [ tf->g, tf->a, tf->b ] (the three terms that we're adjusting).
2112
    //
2113
    // We want to do P' = P + (Jf^T Jf)^-1 Jf^T r(P),
2114
    //   where r(P) is the residual vector
2115
    //   and Jf is the Jacobian matrix of f(), ∂r/∂P.
2116
    //
2117
    // Let's review the shape of each of these expressions:
2118
    //   r(P)   is [N x 1], a column vector with one entry per value of x tested
2119
    //   Jf     is [N x 3], a matrix with an entry for each (x,P) pair
2120
    //   Jf^T   is [3 x N], the transpose of Jf
2121
    //
2122
    //   Jf^T Jf   is [3 x N] * [N x 3] == [3 x 3], a 3x3 matrix,
2123
    //                                              and so is its inverse (Jf^T Jf)^-1
2124
    //   Jf^T r(P) is [3 x N] * [N x 1] == [3 x 1], a column vector with the same shape as P
2125
    //
2126
    // Our implementation strategy to get to the final ∆P is
2127
    //   1) evaluate Jf^T Jf,   call that lhs
2128
    //   2) evaluate Jf^T r(P), call that rhs
2129
    //   3) invert lhs
2130
    //   4) multiply inverse lhs by rhs
2131
    //
2132
    // This is a friendly implementation strategy because we don't have to have any
2133
    // buffers that scale with N, and equally nice don't have to perform any matrix
2134
    // operations that are variable size.
2135
    //
2136
    // Other implementation strategies could trade this off, e.g. evaluating the
2137
    // pseudoinverse of Jf ( (Jf^T Jf)^-1 Jf^T ) directly, then multiplying that by
2138
    // the residuals.  That would probably require implementing singular value
2139
    // decomposition, and would create a [3 x N] matrix to be multiplied by the
2140
    // [N x 1] residual vector, but on the upside I think that'd eliminate the
2141
    // possibility of this gauss_newton_step() function ever failing.
2142
2143
    // 0) start off with lhs and rhs safely zeroed.
2144
0
    skcms_Matrix3x3 lhs = {{ {0,0,0}, {0,0,0}, {0,0,0} }};
2145
0
    skcms_Vector3   rhs = {  {0,0,0} };
2146
2147
    // 1,2) evaluate lhs and evaluate rhs
2148
    //   We want to evaluate Jf only once, but both lhs and rhs involve Jf^T,
2149
    //   so we'll have to update lhs and rhs at the same time.
2150
0
    for (int i = 0; i < N; i++) {
2151
0
        float x = x0 + static_cast<float>(i)*dx;
2152
2153
0
        float dfdP[3] = {0,0,0};
2154
0
        float resid = rg_nonlinear(x,curve,tf, dfdP);
2155
2156
0
        for (int r = 0; r < 3; r++) {
2157
0
            for (int c = 0; c < 3; c++) {
2158
0
                lhs.vals[r][c] += dfdP[r] * dfdP[c];
2159
0
            }
2160
0
            rhs.vals[r] += dfdP[r] * resid;
2161
0
        }
2162
0
    }
2163
2164
    // If any of the 3 P parameters are unused, this matrix will be singular.
2165
    // Detect those cases and fix them up to indentity instead, so we can invert.
2166
0
    for (int k = 0; k < 3; k++) {
2167
0
        if (lhs.vals[0][k]==0 && lhs.vals[1][k]==0 && lhs.vals[2][k]==0 &&
2168
0
            lhs.vals[k][0]==0 && lhs.vals[k][1]==0 && lhs.vals[k][2]==0) {
2169
0
            lhs.vals[k][k] = 1;
2170
0
        }
2171
0
    }
2172
2173
    // 3) invert lhs
2174
0
    skcms_Matrix3x3 lhs_inv;
2175
0
    if (!skcms_Matrix3x3_invert(&lhs, &lhs_inv)) {
2176
0
        return false;
2177
0
    }
2178
2179
    // 4) multiply inverse lhs by rhs
2180
0
    skcms_Vector3 dP = mv_mul(&lhs_inv, &rhs);
2181
0
    tf->g += dP.vals[0];
2182
0
    tf->a += dP.vals[1];
2183
0
    tf->b += dP.vals[2];
2184
0
    return isfinitef_(tf->g) && isfinitef_(tf->a) && isfinitef_(tf->b);
2185
0
}
2186
2187
static float max_roundtrip_error_checked(const skcms_Curve* curve,
2188
0
                                         const skcms_TransferFunction* tf_inv) {
2189
0
    skcms_TransferFunction tf;
2190
0
    if (!skcms_TransferFunction_invert(tf_inv, &tf) || skcms_TFType_sRGBish != classify(tf)) {
2191
0
        return INFINITY_;
2192
0
    }
2193
2194
0
    skcms_TransferFunction tf_inv_again;
2195
0
    if (!skcms_TransferFunction_invert(&tf, &tf_inv_again)) {
2196
0
        return INFINITY_;
2197
0
    }
2198
2199
0
    return skcms_MaxRoundtripError(curve, &tf_inv_again);
2200
0
}
2201
2202
// Fit the points in [L,N) to the non-linear piece of tf, or return false if we can't.
2203
0
static bool fit_nonlinear(const skcms_Curve* curve, int L, int N, skcms_TransferFunction* tf) {
2204
    // This enforces a few constraints that are not modeled in gauss_newton_step()'s optimization.
2205
0
    auto fixup_tf = [tf]() {
2206
        // a must be non-negative. That ensures the function is monotonically increasing.
2207
        // We don't really know how to fix up a if it goes negative.
2208
0
        if (tf->a < 0) {
2209
0
            return false;
2210
0
        }
2211
        // ad+b must be non-negative. That ensures we don't end up with complex numbers in powf.
2212
        // We feel just barely not uneasy enough to tweak b so ad+b is zero in this case.
2213
0
        if (tf->a * tf->d + tf->b < 0) {
2214
0
            tf->b = -tf->a * tf->d;
2215
0
        }
2216
0
        assert (tf->a >= 0 &&
2217
0
                tf->a * tf->d + tf->b >= 0);
2218
2219
        // cd+f must be ~= (ad+b)^g+e. That ensures the function is continuous. We keep e as a free
2220
        // parameter so we can guarantee this.
2221
0
        tf->e =   tf->c*tf->d + tf->f
2222
0
          - powf_(tf->a*tf->d + tf->b, tf->g);
2223
2224
0
        return isfinitef_(tf->e);
2225
0
    };
2226
2227
0
    if (!fixup_tf()) {
2228
0
        return false;
2229
0
    }
2230
2231
    // No matter where we start, dx should always represent N even steps from 0 to 1.
2232
0
    const float dx = 1.0f / static_cast<float>(N-1);
2233
2234
0
    skcms_TransferFunction best_tf = *tf;
2235
0
    float best_max_error = INFINITY_;
2236
2237
    // Need this or several curves get worse... *sigh*
2238
0
    float init_error = max_roundtrip_error_checked(curve, tf);
2239
0
    if (init_error < best_max_error) {
2240
0
        best_max_error = init_error;
2241
0
        best_tf = *tf;
2242
0
    }
2243
2244
    // As far as we can tell, 1 Gauss-Newton step won't converge, and 3 steps is no better than 2.
2245
0
    for (int j = 0; j < 8; j++) {
2246
0
        if (!gauss_newton_step(curve, tf, static_cast<float>(L)*dx, dx, N-L) || !fixup_tf()) {
2247
0
            *tf = best_tf;
2248
0
            return isfinitef_(best_max_error);
2249
0
        }
2250
2251
0
        float max_error = max_roundtrip_error_checked(curve, tf);
2252
0
        if (max_error < best_max_error) {
2253
0
            best_max_error = max_error;
2254
0
            best_tf = *tf;
2255
0
        }
2256
0
    }
2257
2258
0
    *tf = best_tf;
2259
0
    return isfinitef_(best_max_error);
2260
0
}
2261
2262
bool skcms_ApproximateCurve(const skcms_Curve* curve,
2263
                            skcms_TransferFunction* approx,
2264
0
                            float* max_error) {
2265
0
    if (!curve || !approx || !max_error) {
2266
0
        return false;
2267
0
    }
2268
2269
0
    if (curve->table_entries == 0) {
2270
        // No point approximating an skcms_TransferFunction with an skcms_TransferFunction!
2271
0
        return false;
2272
0
    }
2273
2274
0
    if (curve->table_entries == 1 || curve->table_entries > (uint32_t)INT_MAX) {
2275
        // We need at least two points, and must put some reasonable cap on the maximum number.
2276
0
        return false;
2277
0
    }
2278
2279
0
    int N = (int)curve->table_entries;
2280
0
    const float dx = 1.0f / static_cast<float>(N - 1);
2281
2282
0
    *max_error = INFINITY_;
2283
0
    const float kTolerances[] = { 1.5f / 65535.0f, 1.0f / 512.0f };
2284
0
    for (int t = 0; t < ARRAY_COUNT(kTolerances); t++) {
2285
0
        skcms_TransferFunction tf,
2286
0
                               tf_inv;
2287
2288
        // It's problematic to fit curves with non-zero f, so always force it to zero explicitly.
2289
0
        tf.f = 0.0f;
2290
0
        int L = fit_linear(curve, N, kTolerances[t], &tf.c, &tf.d);
2291
2292
0
        if (L == N) {
2293
            // If the entire data set was linear, move the coefficients to the nonlinear portion
2294
            // with G == 1.  This lets use a canonical representation with d == 0.
2295
0
            tf.g = 1;
2296
0
            tf.a = tf.c;
2297
0
            tf.b = tf.f;
2298
0
            tf.c = tf.d = tf.e = tf.f = 0;
2299
0
        } else if (L == N - 1) {
2300
            // Degenerate case with only two points in the nonlinear segment. Solve directly.
2301
0
            tf.g = 1;
2302
0
            tf.a = (eval_curve(curve, static_cast<float>(N-1)*dx) -
2303
0
                    eval_curve(curve, static_cast<float>(N-2)*dx))
2304
0
                 / dx;
2305
0
            tf.b = eval_curve(curve, static_cast<float>(N-2)*dx)
2306
0
                 - tf.a * static_cast<float>(N-2)*dx;
2307
0
            tf.e = 0;
2308
0
        } else {
2309
            // Start by guessing a gamma-only curve through the midpoint.
2310
0
            int mid = (L + N) / 2;
2311
0
            float mid_x = static_cast<float>(mid) / static_cast<float>(N - 1);
2312
0
            float mid_y = eval_curve(curve, mid_x);
2313
0
            tf.g = log2f_(mid_y) / log2f_(mid_x);
2314
0
            tf.a = 1;
2315
0
            tf.b = 0;
2316
0
            tf.e =    tf.c*tf.d + tf.f
2317
0
              - powf_(tf.a*tf.d + tf.b, tf.g);
2318
2319
2320
0
            if (!skcms_TransferFunction_invert(&tf, &tf_inv) ||
2321
0
                !fit_nonlinear(curve, L,N, &tf_inv)) {
2322
0
                continue;
2323
0
            }
2324
2325
            // We fit tf_inv, so calculate tf to keep in sync.
2326
            // fit_nonlinear() should guarantee invertibility.
2327
0
            if (!skcms_TransferFunction_invert(&tf_inv, &tf)) {
2328
0
                assert(false);
2329
0
                continue;
2330
0
            }
2331
0
        }
2332
2333
        // We'd better have a sane, sRGB-ish TF by now.
2334
        // Other non-Bad TFs would be fine, but we know we've only ever tried to fit sRGBish;
2335
        // anything else is just some accident of math and the way we pun tf.g as a type flag.
2336
        // fit_nonlinear() should guarantee this, but the special cases may fail this test.
2337
0
        if (skcms_TFType_sRGBish != classify(tf)) {
2338
0
            continue;
2339
0
        }
2340
2341
        // We find our error by roundtripping the table through tf_inv.
2342
        //
2343
        // (The most likely use case for this approximation is to be inverted and
2344
        // used as the transfer function for a destination color space.)
2345
        //
2346
        // We've kept tf and tf_inv in sync above, but we can't guarantee that tf is
2347
        // invertible, so re-verify that here (and use the new inverse for testing).
2348
        // fit_nonlinear() should guarantee this, but the special cases that don't use
2349
        // it may fail this test.
2350
0
        if (!skcms_TransferFunction_invert(&tf, &tf_inv)) {
2351
0
            continue;
2352
0
        }
2353
2354
0
        float err = skcms_MaxRoundtripError(curve, &tf_inv);
2355
0
        if (*max_error > err) {
2356
0
            *max_error = err;
2357
0
            *approx    = tf;
2358
0
        }
2359
0
    }
2360
0
    return isfinitef_(*max_error);
2361
0
}
2362
2363
enum class CpuType { Baseline, HSW, SKX };
2364
2365
929k
static CpuType cpu_type() {
2366
    #if defined(SKCMS_PORTABLE) || !defined(__x86_64__) || defined(SKCMS_FORCE_BASELINE)
2367
        return CpuType::Baseline;
2368
    #elif defined(SKCMS_FORCE_HSW)
2369
        return CpuType::HSW;
2370
    #elif defined(SKCMS_FORCE_SKX)
2371
        return CpuType::SKX;
2372
    #else
2373
929k
        static const CpuType type = []{
2374
6
            if (!sAllowRuntimeCPUDetection) {
2375
0
                return CpuType::Baseline;
2376
0
            }
2377
            // See http://www.sandpile.org/x86/cpuid.htm
2378
2379
            // First, a basic cpuid(1) lets us check prerequisites for HSW, SKX.
2380
6
            uint32_t eax, ebx, ecx, edx;
2381
6
            __asm__ __volatile__("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
2382
6
                                         : "0"(1), "2"(0));
2383
6
            if ((edx & (1u<<25)) &&  // SSE
2384
6
                (edx & (1u<<26)) &&  // SSE2
2385
6
                (ecx & (1u<< 0)) &&  // SSE3
2386
6
                (ecx & (1u<< 9)) &&  // SSSE3
2387
6
                (ecx & (1u<<12)) &&  // FMA (N.B. not used, avoided even)
2388
6
                (ecx & (1u<<19)) &&  // SSE4.1
2389
6
                (ecx & (1u<<20)) &&  // SSE4.2
2390
6
                (ecx & (1u<<26)) &&  // XSAVE
2391
6
                (ecx & (1u<<27)) &&  // OSXSAVE
2392
6
                (ecx & (1u<<28)) &&  // AVX
2393
6
                (ecx & (1u<<29))) {  // F16C
2394
2395
                // Call cpuid(7) to check for AVX2 and AVX-512 bits.
2396
6
                __asm__ __volatile__("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
2397
6
                                             : "0"(7), "2"(0));
2398
                // eax from xgetbv(0) will tell us whether XMM, YMM, and ZMM state is saved.
2399
6
                uint32_t xcr0, dont_need_edx;
2400
6
                __asm__ __volatile__("xgetbv" : "=a"(xcr0), "=d"(dont_need_edx) : "c"(0));
2401
2402
6
                if ((xcr0 & (1u<<1)) &&  // XMM register state saved?
2403
6
                    (xcr0 & (1u<<2)) &&  // YMM register state saved?
2404
6
                    (ebx  & (1u<<5))) {  // AVX2
2405
                    // At this point we're at least HSW.  Continue checking for SKX.
2406
6
                    if ((xcr0 & (1u<< 5)) && // Opmasks state saved?
2407
6
                        (xcr0 & (1u<< 6)) && // First 16 ZMM registers saved?
2408
6
                        (xcr0 & (1u<< 7)) && // High 16 ZMM registers saved?
2409
6
                        (ebx  & (1u<<16)) && // AVX512F
2410
6
                        (ebx  & (1u<<17)) && // AVX512DQ
2411
6
                        (ebx  & (1u<<28)) && // AVX512CD
2412
6
                        (ebx  & (1u<<30)) && // AVX512BW
2413
6
                        (ebx  & (1u<<31))) { // AVX512VL
2414
0
                        return CpuType::SKX;
2415
0
                    }
2416
6
                    return CpuType::HSW;
2417
6
                }
2418
6
            }
2419
0
            return CpuType::Baseline;
2420
6
        }();
2421
929k
        return type;
2422
929k
    #endif
2423
929k
}
2424
2425
1.12M
static bool tf_is_gamma(const skcms_TransferFunction& tf) {
2426
1.12M
    return tf.g > 0 && tf.a == 1 &&
2427
1.12M
           tf.b == 0 && tf.c == 0 && tf.d == 0 && tf.e == 0 && tf.f == 0;
2428
1.12M
}
2429
2430
struct OpAndArg {
2431
    Op          op;
2432
    const void* arg;
2433
};
2434
2435
1.83M
static OpAndArg select_curve_op(const skcms_Curve* curve, int channel) {
2436
1.83M
    struct OpType {
2437
1.83M
        Op sGamma, sRGBish, PQish, HLGish, HLGinvish, table;
2438
1.83M
    };
2439
1.83M
    static constexpr OpType kOps[] = {
2440
1.83M
        { Op::gamma_r, Op::tf_r, Op::pq_r, Op::hlg_r, Op::hlginv_r, Op::table_r },
2441
1.83M
        { Op::gamma_g, Op::tf_g, Op::pq_g, Op::hlg_g, Op::hlginv_g, Op::table_g },
2442
1.83M
        { Op::gamma_b, Op::tf_b, Op::pq_b, Op::hlg_b, Op::hlginv_b, Op::table_b },
2443
1.83M
        { Op::gamma_a, Op::tf_a, Op::pq_a, Op::hlg_a, Op::hlginv_a, Op::table_a },
2444
1.83M
    };
2445
1.83M
    const auto& op = kOps[channel];
2446
2447
1.83M
    if (curve->table_entries == 0) {
2448
1.12M
        const OpAndArg noop = { Op::load_a8/*doesn't matter*/, nullptr };
2449
2450
1.12M
        const skcms_TransferFunction& tf = curve->parametric;
2451
2452
1.12M
        if (tf_is_gamma(tf)) {
2453
324k
            return tf.g != 1 ? OpAndArg{op.sGamma, &tf}
2454
324k
                             : noop;
2455
324k
        }
2456
2457
797k
        switch (classify(tf)) {
2458
0
            case skcms_TFType_Invalid:    return noop;
2459
797k
            case skcms_TFType_sRGBish:    return OpAndArg{op.sRGBish,   &tf};
2460
0
            case skcms_TFType_PQish:      return OpAndArg{op.PQish,     &tf};
2461
0
            case skcms_TFType_HLGish:     return OpAndArg{op.HLGish,    &tf};
2462
0
            case skcms_TFType_HLGinvish:  return OpAndArg{op.HLGinvish, &tf};
2463
797k
        }
2464
797k
    }
2465
712k
    return OpAndArg{op.table, curve};
2466
1.83M
}
2467
2468
586k
static int select_curve_ops(const skcms_Curve* curves, int numChannels, OpAndArg* ops) {
2469
    // We process the channels in reverse order, yielding ops in ABGR order.
2470
    // (Working backwards allows us to fuse trailing B+G+R ops into a single RGB op.)
2471
586k
    int cursor = 0;
2472
2.42M
    for (int index = numChannels; index-- > 0; ) {
2473
1.83M
        ops[cursor] = select_curve_op(&curves[index], index);
2474
1.83M
        if (ops[cursor].arg) {
2475
1.72M
            ++cursor;
2476
1.72M
        }
2477
1.83M
    }
2478
2479
    // Identify separate B+G+R ops and fuse them into a single RGB op.
2480
586k
    if (cursor >= 3) {
2481
560k
        struct FusableOps {
2482
560k
            Op r, g, b, rgb;
2483
560k
        };
2484
560k
        static constexpr FusableOps kFusableOps[] = {
2485
560k
            {Op::gamma_r,  Op::gamma_g,  Op::gamma_b,  Op::gamma_rgb},
2486
560k
            {Op::tf_r,     Op::tf_g,     Op::tf_b,     Op::tf_rgb},
2487
560k
            {Op::pq_r,     Op::pq_g,     Op::pq_b,     Op::pq_rgb},
2488
560k
            {Op::hlg_r,    Op::hlg_g,    Op::hlg_b,    Op::hlg_rgb},
2489
560k
            {Op::hlginv_r, Op::hlginv_g, Op::hlginv_b, Op::hlginv_rgb},
2490
560k
        };
2491
2492
560k
        int posR = cursor - 1;
2493
560k
        int posG = cursor - 2;
2494
560k
        int posB = cursor - 3;
2495
2.10M
        for (const FusableOps& fusableOp : kFusableOps) {
2496
2.10M
            if (ops[posR].op == fusableOp.r &&
2497
2.10M
                ops[posG].op == fusableOp.g &&
2498
2.10M
                ops[posB].op == fusableOp.b &&
2499
2.10M
                (0 == memcmp(ops[posR].arg, ops[posG].arg, sizeof(skcms_TransferFunction))) &&
2500
2.10M
                (0 == memcmp(ops[posR].arg, ops[posB].arg, sizeof(skcms_TransferFunction)))) {
2501
                // Fuse the three matching ops into one.
2502
230k
                ops[posB].op = fusableOp.rgb;
2503
230k
                cursor -= 2;
2504
230k
                break;
2505
230k
            }
2506
2.10M
        }
2507
560k
    }
2508
2509
586k
    return cursor;
2510
586k
}
2511
2512
1.85M
static size_t bytes_per_pixel(skcms_PixelFormat fmt) {
2513
1.85M
    switch (fmt >> 1) {   // ignore rgb/bgr
2514
0
        case skcms_PixelFormat_A_8              >> 1: return  1;
2515
215k
        case skcms_PixelFormat_G_8              >> 1: return  1;
2516
0
        case skcms_PixelFormat_GA_88            >> 1: return  2;
2517
0
        case skcms_PixelFormat_ABGR_4444        >> 1: return  2;
2518
0
        case skcms_PixelFormat_RGB_565          >> 1: return  2;
2519
10.9k
        case skcms_PixelFormat_RGB_888          >> 1: return  3;
2520
1.62M
        case skcms_PixelFormat_RGBA_8888        >> 1: return  4;
2521
0
        case skcms_PixelFormat_RGBA_8888_sRGB   >> 1: return  4;
2522
0
        case skcms_PixelFormat_RGBA_1010102     >> 1: return  4;
2523
0
        case skcms_PixelFormat_RGB_101010x_XR   >> 1: return  4;
2524
0
        case skcms_PixelFormat_RGB_161616LE     >> 1: return  6;
2525
0
        case skcms_PixelFormat_RGBA_10101010_XR >> 1: return  8;
2526
0
        case skcms_PixelFormat_RGBA_16161616LE  >> 1: return  8;
2527
1.74k
        case skcms_PixelFormat_RGB_161616BE     >> 1: return  6;
2528
4.12k
        case skcms_PixelFormat_RGBA_16161616BE  >> 1: return  8;
2529
0
        case skcms_PixelFormat_RGB_hhh_Norm     >> 1: return  6;
2530
0
        case skcms_PixelFormat_RGBA_hhhh_Norm   >> 1: return  8;
2531
0
        case skcms_PixelFormat_RGB_hhh          >> 1: return  6;
2532
0
        case skcms_PixelFormat_RGBA_hhhh        >> 1: return  8;
2533
0
        case skcms_PixelFormat_RGB_fff          >> 1: return 12;
2534
0
        case skcms_PixelFormat_RGBA_ffff        >> 1: return 16;
2535
1.85M
    }
2536
0
    assert(false);
2537
0
    return 0;
2538
1.85M
}
2539
2540
static bool prep_for_destination(const skcms_ICCProfile* profile,
2541
                                 skcms_Matrix3x3* fromXYZD50,
2542
                                 skcms_TransferFunction* invR,
2543
                                 skcms_TransferFunction* invG,
2544
225k
                                 skcms_TransferFunction* invB) {
2545
    // skcms_Transform() supports B2A destinations...
2546
225k
    if (profile->has_B2A) { return true; }
2547
    // ...and destinations with parametric transfer functions and an XYZD50 gamut matrix.
2548
225k
    return profile->has_trc
2549
225k
        && profile->has_toXYZD50
2550
225k
        && profile->trc[0].table_entries == 0
2551
225k
        && profile->trc[1].table_entries == 0
2552
225k
        && profile->trc[2].table_entries == 0
2553
225k
        && skcms_TransferFunction_invert(&profile->trc[0].parametric, invR)
2554
225k
        && skcms_TransferFunction_invert(&profile->trc[1].parametric, invG)
2555
225k
        && skcms_TransferFunction_invert(&profile->trc[2].parametric, invB)
2556
225k
        && skcms_Matrix3x3_invert(&profile->toXYZD50, fromXYZD50);
2557
225k
}
2558
2559
bool skcms_Transform(const void*             src,
2560
                     skcms_PixelFormat       srcFmt,
2561
                     skcms_AlphaFormat       srcAlpha,
2562
                     const skcms_ICCProfile* srcProfile,
2563
                     void*                   dst,
2564
                     skcms_PixelFormat       dstFmt,
2565
                     skcms_AlphaFormat       dstAlpha,
2566
                     const skcms_ICCProfile* dstProfile,
2567
929k
                     size_t                  nz) {
2568
929k
    const size_t dst_bpp = bytes_per_pixel(dstFmt),
2569
929k
                 src_bpp = bytes_per_pixel(srcFmt);
2570
    // Let's just refuse if the request is absurdly big.
2571
929k
    if (nz * dst_bpp > INT_MAX || nz * src_bpp > INT_MAX) {
2572
0
        return false;
2573
0
    }
2574
929k
    int n = (int)nz;
2575
2576
    // Null profiles default to sRGB. Passing null for both is handy when doing format conversion.
2577
929k
    if (!srcProfile) {
2578
704k
        srcProfile = skcms_sRGB_profile();
2579
704k
    }
2580
929k
    if (!dstProfile) {
2581
704k
        dstProfile = skcms_sRGB_profile();
2582
704k
    }
2583
2584
    // We can't transform in place unless the PixelFormats are the same size.
2585
929k
    if (dst == src && dst_bpp != src_bpp) {
2586
0
        return false;
2587
0
    }
2588
    // TODO: more careful alias rejection (like, dst == src + 1)?
2589
2590
929k
    Op          program[32];
2591
929k
    const void* context[32];
2592
2593
929k
    Op*          ops      = program;
2594
929k
    const void** contexts = context;
2595
2596
4.32M
    auto add_op = [&](Op o) {
2597
4.32M
        *ops++ = o;
2598
4.32M
        *contexts++ = nullptr;
2599
4.32M
    };
2600
2601
1.62M
    auto add_op_ctx = [&](Op o, const void* c) {
2602
1.62M
        *ops++ = o;
2603
1.62M
        *contexts++ = c;
2604
1.62M
    };
2605
2606
929k
    auto add_curve_ops = [&](const skcms_Curve* curves, int numChannels) {
2607
361k
        OpAndArg oa[4];
2608
361k
        assert(numChannels <= ARRAY_COUNT(oa));
2609
2610
361k
        int numOps = select_curve_ops(curves, numChannels, oa);
2611
2612
1.41M
        for (int i = 0; i < numOps; ++i) {
2613
1.05M
            add_op_ctx(oa[i].op, oa[i].arg);
2614
1.05M
        }
2615
361k
    };
2616
2617
    // These are always parametric curves of some sort.
2618
929k
    skcms_Curve dst_curves[3];
2619
929k
    dst_curves[0].table_entries =
2620
929k
    dst_curves[1].table_entries =
2621
929k
    dst_curves[2].table_entries = 0;
2622
2623
929k
    skcms_Matrix3x3        from_xyz;
2624
2625
929k
    switch (srcFmt >> 1) {
2626
0
        default: return false;
2627
0
        case skcms_PixelFormat_A_8              >> 1: add_op(Op::load_a8);          break;
2628
11.9k
        case skcms_PixelFormat_G_8              >> 1: add_op(Op::load_g8);          break;
2629
0
        case skcms_PixelFormat_GA_88            >> 1: add_op(Op::load_ga88);        break;
2630
0
        case skcms_PixelFormat_ABGR_4444        >> 1: add_op(Op::load_4444);        break;
2631
0
        case skcms_PixelFormat_RGB_565          >> 1: add_op(Op::load_565);         break;
2632
5.45k
        case skcms_PixelFormat_RGB_888          >> 1: add_op(Op::load_888);         break;
2633
906k
        case skcms_PixelFormat_RGBA_8888        >> 1: add_op(Op::load_8888);        break;
2634
0
        case skcms_PixelFormat_RGBA_1010102     >> 1: add_op(Op::load_1010102);     break;
2635
0
        case skcms_PixelFormat_RGB_101010x_XR   >> 1: add_op(Op::load_101010x_XR);  break;
2636
0
        case skcms_PixelFormat_RGBA_10101010_XR >> 1: add_op(Op::load_10101010_XR); break;
2637
0
        case skcms_PixelFormat_RGB_161616LE     >> 1: add_op(Op::load_161616LE);    break;
2638
0
        case skcms_PixelFormat_RGBA_16161616LE  >> 1: add_op(Op::load_16161616LE);  break;
2639
1.74k
        case skcms_PixelFormat_RGB_161616BE     >> 1: add_op(Op::load_161616BE);    break;
2640
4.12k
        case skcms_PixelFormat_RGBA_16161616BE  >> 1: add_op(Op::load_16161616BE);  break;
2641
0
        case skcms_PixelFormat_RGB_hhh_Norm     >> 1: add_op(Op::load_hhh);         break;
2642
0
        case skcms_PixelFormat_RGBA_hhhh_Norm   >> 1: add_op(Op::load_hhhh);        break;
2643
0
        case skcms_PixelFormat_RGB_hhh          >> 1: add_op(Op::load_hhh);         break;
2644
0
        case skcms_PixelFormat_RGBA_hhhh        >> 1: add_op(Op::load_hhhh);        break;
2645
0
        case skcms_PixelFormat_RGB_fff          >> 1: add_op(Op::load_fff);         break;
2646
0
        case skcms_PixelFormat_RGBA_ffff        >> 1: add_op(Op::load_ffff);        break;
2647
2648
0
        case skcms_PixelFormat_RGBA_8888_sRGB >> 1:
2649
0
            add_op(Op::load_8888);
2650
0
            add_op_ctx(Op::tf_rgb, skcms_sRGB_TransferFunction());
2651
0
            break;
2652
929k
    }
2653
929k
    if (srcFmt == skcms_PixelFormat_RGB_hhh_Norm ||
2654
929k
        srcFmt == skcms_PixelFormat_RGBA_hhhh_Norm) {
2655
0
        add_op(Op::clamp);
2656
0
    }
2657
929k
    if (srcFmt & 1) {
2658
704k
        add_op(Op::swap_rb);
2659
704k
    }
2660
929k
    skcms_ICCProfile gray_dst_profile;
2661
929k
    switch (dstFmt >> 1) {
2662
0
        case skcms_PixelFormat_G_8:
2663
0
        case skcms_PixelFormat_GA_88:
2664
            // When transforming to gray, stop at XYZ (by setting toXYZ to identity), then transform
2665
            // luminance (Y) by the destination transfer function.
2666
0
            gray_dst_profile = *dstProfile;
2667
0
            skcms_SetXYZD50(&gray_dst_profile, &skcms_XYZD50_profile()->toXYZD50);
2668
0
            dstProfile = &gray_dst_profile;
2669
0
            break;
2670
929k
        default:
2671
929k
            break;
2672
929k
    }
2673
2674
929k
    if (srcProfile->data_color_space == skcms_Signature_CMYK) {
2675
        // Photoshop creates CMYK images as inverse CMYK.
2676
        // These happen to be the only ones we've _ever_ seen.
2677
0
        add_op(Op::invert);
2678
        // With CMYK, ignore the alpha type, to avoid changing K or conflating CMY with K.
2679
0
        srcAlpha = skcms_AlphaFormat_Unpremul;
2680
0
    }
2681
2682
929k
    if (srcAlpha == skcms_AlphaFormat_Opaque) {
2683
0
        add_op(Op::force_opaque);
2684
929k
    } else if (srcAlpha == skcms_AlphaFormat_PremulAsEncoded) {
2685
704k
        add_op(Op::unpremul);
2686
704k
    }
2687
2688
929k
    if (dstProfile != srcProfile) {
2689
2690
225k
        if (!prep_for_destination(dstProfile,
2691
225k
                                  &from_xyz,
2692
225k
                                  &dst_curves[0].parametric,
2693
225k
                                  &dst_curves[1].parametric,
2694
225k
                                  &dst_curves[2].parametric)) {
2695
0
            return false;
2696
0
        }
2697
2698
225k
        if (srcProfile->has_A2B) {
2699
133k
            if (srcProfile->A2B.input_channels) {
2700
108k
                add_curve_ops(srcProfile->A2B.input_curves,
2701
108k
                              (int)srcProfile->A2B.input_channels);
2702
108k
                add_op(Op::clamp);
2703
108k
                add_op_ctx(Op::clut_A2B, &srcProfile->A2B);
2704
108k
            }
2705
2706
133k
            if (srcProfile->A2B.matrix_channels == 3) {
2707
26.9k
                add_curve_ops(srcProfile->A2B.matrix_curves, /*numChannels=*/3);
2708
2709
26.9k
                static const skcms_Matrix3x4 I = {{
2710
26.9k
                    {1,0,0,0},
2711
26.9k
                    {0,1,0,0},
2712
26.9k
                    {0,0,1,0},
2713
26.9k
                }};
2714
26.9k
                if (0 != memcmp(&I, &srcProfile->A2B.matrix, sizeof(I))) {
2715
26.9k
                    add_op_ctx(Op::matrix_3x4, &srcProfile->A2B.matrix);
2716
26.9k
                }
2717
26.9k
            }
2718
2719
133k
            if (srcProfile->A2B.output_channels == 3) {
2720
133k
                add_curve_ops(srcProfile->A2B.output_curves, /*numChannels=*/3);
2721
133k
            }
2722
2723
133k
            if (srcProfile->pcs == skcms_Signature_Lab) {
2724
0
                add_op(Op::lab_to_xyz);
2725
0
            }
2726
2727
133k
        } else if (srcProfile->has_trc && srcProfile->has_toXYZD50) {
2728
91.8k
            add_curve_ops(srcProfile->trc, /*numChannels=*/3);
2729
91.8k
        } else {
2730
0
            return false;
2731
0
        }
2732
2733
        // A2B sources are in XYZD50 by now, but TRC sources are still in their original gamut.
2734
225k
        assert (srcProfile->has_A2B || srcProfile->has_toXYZD50);
2735
2736
225k
        if (dstProfile->has_B2A) {
2737
            // B2A needs its input in XYZD50, so transform TRC sources now.
2738
0
            if (!srcProfile->has_A2B) {
2739
0
                add_op_ctx(Op::matrix_3x3, &srcProfile->toXYZD50);
2740
0
            }
2741
2742
0
            if (dstProfile->pcs == skcms_Signature_Lab) {
2743
0
                add_op(Op::xyz_to_lab);
2744
0
            }
2745
2746
0
            if (dstProfile->B2A.input_channels == 3) {
2747
0
                add_curve_ops(dstProfile->B2A.input_curves, /*numChannels=*/3);
2748
0
            }
2749
2750
0
            if (dstProfile->B2A.matrix_channels == 3) {
2751
0
                static const skcms_Matrix3x4 I = {{
2752
0
                    {1,0,0,0},
2753
0
                    {0,1,0,0},
2754
0
                    {0,0,1,0},
2755
0
                }};
2756
0
                if (0 != memcmp(&I, &dstProfile->B2A.matrix, sizeof(I))) {
2757
0
                    add_op_ctx(Op::matrix_3x4, &dstProfile->B2A.matrix);
2758
0
                }
2759
2760
0
                add_curve_ops(dstProfile->B2A.matrix_curves, /*numChannels=*/3);
2761
0
            }
2762
2763
0
            if (dstProfile->B2A.output_channels) {
2764
0
                add_op(Op::clamp);
2765
0
                add_op_ctx(Op::clut_B2A, &dstProfile->B2A);
2766
2767
0
                add_curve_ops(dstProfile->B2A.output_curves,
2768
0
                              (int)dstProfile->B2A.output_channels);
2769
0
            }
2770
225k
        } else {
2771
            // This is a TRC destination.
2772
            // We'll concat any src->xyz matrix with our xyz->dst matrix into one src->dst matrix.
2773
            // (A2B sources are already in XYZD50, making that src->xyz matrix I.)
2774
225k
            static const skcms_Matrix3x3 I = {{
2775
225k
                { 1.0f, 0.0f, 0.0f },
2776
225k
                { 0.0f, 1.0f, 0.0f },
2777
225k
                { 0.0f, 0.0f, 1.0f },
2778
225k
            }};
2779
225k
            const skcms_Matrix3x3* to_xyz = srcProfile->has_A2B ? &I : &srcProfile->toXYZD50;
2780
2781
            // There's a chance the source and destination gamuts are identical,
2782
            // in which case we can skip the gamut transform.
2783
225k
            if (0 != memcmp(&dstProfile->toXYZD50, to_xyz, sizeof(skcms_Matrix3x3))) {
2784
                // Concat the entire gamut transform into from_xyz,
2785
                // now slightly misnamed but it's a handy spot to stash the result.
2786
225k
                from_xyz = skcms_Matrix3x3_concat(&from_xyz, to_xyz);
2787
225k
                add_op_ctx(Op::matrix_3x3, &from_xyz);
2788
225k
            }
2789
2790
            // Encode back to dst RGB using its parametric transfer functions.
2791
225k
            OpAndArg oa[3];
2792
225k
            int numOps = select_curve_ops(dst_curves, /*numChannels=*/3, oa);
2793
439k
            for (int index = 0; index < numOps; ++index) {
2794
214k
                assert(oa[index].op != Op::table_r &&
2795
214k
                       oa[index].op != Op::table_g &&
2796
214k
                       oa[index].op != Op::table_b &&
2797
214k
                       oa[index].op != Op::table_a);
2798
214k
                add_op_ctx(oa[index].op, oa[index].arg);
2799
214k
            }
2800
225k
        }
2801
225k
    }
2802
2803
    // Clamp here before premul to make sure we're clamping to normalized values _and_ gamut,
2804
    // not just to values that fit in [0,1].
2805
    //
2806
    // E.g. r = 1.1, a = 0.5 would fit fine in fixed point after premul (ra=0.55,a=0.5),
2807
    // but would be carrying r > 1, which is really unexpected for downstream consumers.
2808
929k
    if (dstFmt < skcms_PixelFormat_RGB_hhh) {
2809
929k
        add_op(Op::clamp);
2810
929k
    }
2811
2812
929k
    if (dstProfile->data_color_space == skcms_Signature_CMYK) {
2813
        // Photoshop creates CMYK images as inverse CMYK.
2814
        // These happen to be the only ones we've _ever_ seen.
2815
0
        add_op(Op::invert);
2816
2817
        // CMYK has no alpha channel, so make sure dstAlpha is a no-op.
2818
0
        dstAlpha = skcms_AlphaFormat_Unpremul;
2819
0
    }
2820
2821
929k
    if (dstAlpha == skcms_AlphaFormat_Opaque) {
2822
0
        add_op(Op::force_opaque);
2823
929k
    } else if (dstAlpha == skcms_AlphaFormat_PremulAsEncoded) {
2824
3.80k
        add_op(Op::premul);
2825
3.80k
    }
2826
929k
    if (dstFmt & 1) {
2827
15.9k
        add_op(Op::swap_rb);
2828
15.9k
    }
2829
929k
    switch (dstFmt >> 1) {
2830
0
        default: return false;
2831
0
        case skcms_PixelFormat_A_8             >> 1: add_op(Op::store_a8);         break;
2832
203k
        case skcms_PixelFormat_G_8             >> 1: add_op(Op::store_g8);         break;
2833
0
        case skcms_PixelFormat_GA_88           >> 1: add_op(Op::store_ga88);       break;
2834
0
        case skcms_PixelFormat_ABGR_4444       >> 1: add_op(Op::store_4444);       break;
2835
0
        case skcms_PixelFormat_RGB_565         >> 1: add_op(Op::store_565);        break;
2836
5.45k
        case skcms_PixelFormat_RGB_888         >> 1: add_op(Op::store_888);        break;
2837
720k
        case skcms_PixelFormat_RGBA_8888       >> 1: add_op(Op::store_8888);       break;
2838
0
        case skcms_PixelFormat_RGBA_1010102    >> 1: add_op(Op::store_1010102);    break;
2839
0
        case skcms_PixelFormat_RGB_161616LE    >> 1: add_op(Op::store_161616LE);   break;
2840
0
        case skcms_PixelFormat_RGBA_16161616LE >> 1: add_op(Op::store_16161616LE); break;
2841
0
        case skcms_PixelFormat_RGB_161616BE    >> 1: add_op(Op::store_161616BE);   break;
2842
0
        case skcms_PixelFormat_RGBA_16161616BE >> 1: add_op(Op::store_16161616BE); break;
2843
0
        case skcms_PixelFormat_RGB_hhh_Norm    >> 1: add_op(Op::store_hhh);        break;
2844
0
        case skcms_PixelFormat_RGBA_hhhh_Norm  >> 1: add_op(Op::store_hhhh);       break;
2845
0
        case skcms_PixelFormat_RGB_101010x_XR  >> 1: add_op(Op::store_101010x_XR); break;
2846
0
        case skcms_PixelFormat_RGB_hhh         >> 1: add_op(Op::store_hhh);        break;
2847
0
        case skcms_PixelFormat_RGBA_hhhh       >> 1: add_op(Op::store_hhhh);       break;
2848
0
        case skcms_PixelFormat_RGB_fff         >> 1: add_op(Op::store_fff);        break;
2849
0
        case skcms_PixelFormat_RGBA_ffff       >> 1: add_op(Op::store_ffff);       break;
2850
2851
0
        case skcms_PixelFormat_RGBA_8888_sRGB >> 1:
2852
0
            add_op_ctx(Op::tf_rgb, skcms_sRGB_Inverse_TransferFunction());
2853
0
            add_op(Op::store_8888);
2854
0
            break;
2855
929k
    }
2856
2857
929k
    assert(ops      <= program + ARRAY_COUNT(program));
2858
929k
    assert(contexts <= context + ARRAY_COUNT(context));
2859
2860
929k
    auto run = baseline::run_program;
2861
929k
    switch (cpu_type()) {
2862
0
        case CpuType::SKX:
2863
0
            #if !defined(SKCMS_DISABLE_SKX)
2864
0
                run = skx::run_program;
2865
0
                break;
2866
0
            #endif
2867
2868
929k
        case CpuType::HSW:
2869
929k
            #if !defined(SKCMS_DISABLE_HSW)
2870
929k
                run = hsw::run_program;
2871
929k
                break;
2872
0
            #endif
2873
2874
0
        case CpuType::Baseline:
2875
0
            break;
2876
929k
    }
2877
2878
929k
    run(program, context, ops - program, (const char*)src, (char*)dst, n, src_bpp,dst_bpp);
2879
929k
    return true;
2880
929k
}
2881
2882
0
static void assert_usable_as_destination(const skcms_ICCProfile* profile) {
2883
0
#if defined(NDEBUG)
2884
0
    (void)profile;
2885
#else
2886
    skcms_Matrix3x3 fromXYZD50;
2887
    skcms_TransferFunction invR, invG, invB;
2888
    assert(prep_for_destination(profile, &fromXYZD50, &invR, &invG, &invB));
2889
#endif
2890
0
}
2891
2892
0
bool skcms_MakeUsableAsDestination(skcms_ICCProfile* profile) {
2893
0
    if (!profile->has_B2A) {
2894
0
        skcms_Matrix3x3 fromXYZD50;
2895
0
        if (!profile->has_trc || !profile->has_toXYZD50
2896
0
            || !skcms_Matrix3x3_invert(&profile->toXYZD50, &fromXYZD50)) {
2897
0
            return false;
2898
0
        }
2899
2900
0
        skcms_TransferFunction tf[3];
2901
0
        for (int i = 0; i < 3; i++) {
2902
0
            skcms_TransferFunction inv;
2903
0
            if (profile->trc[i].table_entries == 0
2904
0
                && skcms_TransferFunction_invert(&profile->trc[i].parametric, &inv)) {
2905
0
                tf[i] = profile->trc[i].parametric;
2906
0
                continue;
2907
0
            }
2908
2909
0
            float max_error;
2910
            // Parametric curves from skcms_ApproximateCurve() are guaranteed to be invertible.
2911
0
            if (!skcms_ApproximateCurve(&profile->trc[i], &tf[i], &max_error)) {
2912
0
                return false;
2913
0
            }
2914
0
        }
2915
2916
0
        for (int i = 0; i < 3; ++i) {
2917
0
            profile->trc[i].table_entries = 0;
2918
0
            profile->trc[i].parametric = tf[i];
2919
0
        }
2920
0
    }
2921
0
    assert_usable_as_destination(profile);
2922
0
    return true;
2923
0
}
2924
2925
0
bool skcms_MakeUsableAsDestinationWithSingleCurve(skcms_ICCProfile* profile) {
2926
    // Call skcms_MakeUsableAsDestination() with B2A disabled;
2927
    // on success that'll return a TRC/XYZ profile with three skcms_TransferFunctions.
2928
0
    skcms_ICCProfile result = *profile;
2929
0
    result.has_B2A = false;
2930
0
    if (!skcms_MakeUsableAsDestination(&result)) {
2931
0
        return false;
2932
0
    }
2933
2934
    // Of the three, pick the transfer function that best fits the other two.
2935
0
    int best_tf = 0;
2936
0
    float min_max_error = INFINITY_;
2937
0
    for (int i = 0; i < 3; i++) {
2938
0
        skcms_TransferFunction inv;
2939
0
        if (!skcms_TransferFunction_invert(&result.trc[i].parametric, &inv)) {
2940
0
            return false;
2941
0
        }
2942
2943
0
        float err = 0;
2944
0
        for (int j = 0; j < 3; ++j) {
2945
0
            err = fmaxf_(err, skcms_MaxRoundtripError(&profile->trc[j], &inv));
2946
0
        }
2947
0
        if (min_max_error > err) {
2948
0
            min_max_error = err;
2949
0
            best_tf = i;
2950
0
        }
2951
0
    }
2952
2953
0
    for (int i = 0; i < 3; i++) {
2954
0
        result.trc[i].parametric = result.trc[best_tf].parametric;
2955
0
    }
2956
2957
0
    *profile = result;
2958
0
    assert_usable_as_destination(profile);
2959
0
    return true;
2960
0
}