Coverage Report

Created: 2026-08-13 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libavif/apps/shared/iccmaker.c
Line
Count
Source
1
// Copyright 2023 Yuan Tong. All rights reserved.
2
// SPDX-License-Identifier: BSD-2-Clause
3
4
#include "iccmaker.h"
5
6
#include <math.h>
7
#include <string.h>
8
9
// ICCv2 profile specification: https://www.color.org/icc32.pdf
10
11
/**
12
 * Color Profile Structure
13
 *
14
 * Header:
15
 *  size         = 376 bytes (*1)
16
 *  CMM          = 'lcms' (*2)
17
 *  Version      = 2.2.0
18
 *  Device Class = Display
19
 *  Color Space  = RGB
20
 *  Conn. Space  = XYZ
21
 *  Date, Time   = 1 Jan 2000, 0:00:00
22
 *  Platform     = Microsoft
23
 *  Flags        = Not Embedded Profile, Use anywhere
24
 *  Dev. Mnfctr. = 0x0
25
 *  Dev. Model   = 0x0
26
 *  Dev. Attrbts = Reflective, Glossy, Positive, Color
27
 *  Rndrng Intnt = Perceptual
28
 *  Illuminant   = 0.96420288, 1.00000000, 0.82490540    [Lab 100.000000, 0.000000, 0.000000]
29
 *  Creator      = 'avif'
30
 *
31
 * Profile Tags:
32
 *                    Tag    ID      Offset         Size                 Value
33
 *                   ----  ------    ------         ----                 -----
34
 *  profileDescriptionTag  'desc'       240           95                  avif
35
 *     mediaWhitePointTag  'wtpt'       268 (*3)      20        (to be filled)
36
 *         redColorantTag  'rXYZ'       288           20        (to be filled)
37
 *       greenColorantTag  'gXYZ'       308           20        (to be filled)
38
 *        blueColorantTag  'bXYZ'       328           20        (to be filled)
39
 *              redTRCTag  'rTRC'       348 (*4)      16        (to be filled)
40
 *            greenTRCTag  'gTRC'       348           16        (to be filled)
41
 *             blueTRCTag  'bTRC'       348           16        (to be filled)
42
 *           copyrightTag  'cprt'       364           12                   CC0
43
 *
44
 * (*1): The template data is padded to 448 bytes according to MD5 specification, so that computation can be applied
45
 *       directly on it. The actual ICC profile data is the first 376 bytes.
46
 * (*2): 6.1.2 CMM Type: The signatures must be registered in order to avoid conflicts.
47
 *       The registry can be found at https://www.color.org/signatures2.xalter (Private and ICC tag and CMM registry)
48
 *       Therefore we are using the signature of Little CMS.
49
 * (*3): The profileDescriptionTag requires 95 bytes of data, but with some trick, the content of the last 67 bytes
50
 *       can be anything. Therefore we are placing the following tags in this region to reduce profile size.
51
 * (*4): The transfer characteristic (gamma) of the 3 channels are the same, so the data can be shared.
52
 */
53
54
static const uint8_t iccColorTemplate[448] = {
55
    0x00, 0x00, 0x01, 0x78, 0x6c, 0x63, 0x6d, 0x73, 0x02, 0x20, 0x00, 0x00, 0x6d, 0x6e, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58,
56
    0x59, 0x5a, 0x20, 0x07, 0xd0, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x63, 0x73, 0x70, 0x4d, 0x53,
57
    0x46, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
58
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xd6, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d, 0x61, 0x76, 0x69, 0x66,
59
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
60
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
61
    0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x5f, 0x77, 0x74, 0x70,
62
    0x74, 0x00, 0x00, 0x01, 0x0c, 0x00, 0x00, 0x00, 0x14, 0x72, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x14,
63
    0x67, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0x34, 0x00, 0x00, 0x00, 0x14, 0x62, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0x48, 0x00,
64
    0x00, 0x00, 0x14, 0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0x5c, 0x00, 0x00, 0x00, 0x10, 0x67, 0x54, 0x52, 0x43, 0x00, 0x00,
65
    0x01, 0x5c, 0x00, 0x00, 0x00, 0x10, 0x62, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0x5c, 0x00, 0x00, 0x00, 0x10, 0x63, 0x70, 0x72,
66
    0x74, 0x00, 0x00, 0x01, 0x6c, 0x00, 0x00, 0x00, 0x0c, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
67
    0x61, 0x76, 0x69, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x59, 0x5a, 0x20, 0x00,
68
    0x00, 0x00, 0x00, 0x00, 0x00, 0xf3, 0x54, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x16, 0xc9, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00,
69
    0x00, 0x00, 0x00, 0x00, 0x6f, 0xa0, 0x00, 0x00, 0x38, 0xf2, 0x00, 0x00, 0x03, 0x8f, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00,
70
    0x00, 0x00, 0x00, 0x62, 0x96, 0x00, 0x00, 0xb7, 0x89, 0x00, 0x00, 0x18, 0xda, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00,
71
    0x00, 0x00, 0x24, 0xa0, 0x00, 0x00, 0x0f, 0x85, 0x00, 0x00, 0xb6, 0xc4, 0x63, 0x75, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00,
72
    0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x43, 0x43, 0x30, 0x00, 0x80, 0x00,
73
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
74
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
75
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0,
76
    0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
77
};
78
79
static const size_t iccColorLength = 376;
80
81
static const ptrdiff_t colorWhiteOffset = 0x114;
82
static const ptrdiff_t colorRedOffset = 0x128;
83
static const ptrdiff_t colorGreenOffset = 0x13c;
84
static const ptrdiff_t colorBlueOffset = 0x150;
85
static const ptrdiff_t colorGammaOffset = 0x168;
86
87
/**
88
 * Gray Profile Structure
89
 *
90
 * Header:
91
 *  size         = 275 bytes
92
 *  CMM          = 'lcms'
93
 *  Version      = 2.2.0
94
 *  Device Class = Display
95
 *  Color Space  = Gray
96
 *  Conn. Space  = XYZ
97
 *  Date, Time   = 1 Jan 2000, 0:00:00
98
 *  Platform     = Microsoft
99
 *  Flags        = Not Embedded Profile, Use anywhere
100
 *  Dev. Mnfctr. = 0x0
101
 *  Dev. Model   = 0x0
102
 *  Dev. Attrbts = Reflective, Glossy, Positive, Color
103
 *  Rndrng Intnt = Perceptual
104
 *  Illuminant   = 0.96420288, 1.00000000, 0.82490540    [Lab 100.000000, 0.000000, 0.000000]
105
 *  Creator      = 'avif'
106
 *
107
 * Profile Tags:
108
 *                    Tag    ID      Offset         Size                 Value
109
 *                   ----  ------    ------         ----                 -----
110
 *  profileDescriptionTag  'desc'       180           95                  avif
111
 *     mediaWhitePointTag  'wtpt'       208           20        (to be filled)
112
 *             grayTRCTag  'kTRC'       228           16        (to be filled)
113
 *           copyrightTag  'cprt'       244           12                   CC0
114
 */
115
116
static const uint8_t iccGrayTemplate[320] = {
117
    0x00, 0x00, 0x01, 0x13, 0x6c, 0x63, 0x6d, 0x73, 0x02, 0x20, 0x00, 0x00, 0x6d, 0x6e, 0x74, 0x72, 0x47, 0x52, 0x41, 0x59,
118
    0x58, 0x59, 0x5a, 0x20, 0x07, 0xd0, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x63, 0x73, 0x70,
119
    0x4d, 0x53, 0x46, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
120
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xd6, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d,
121
    0x61, 0x76, 0x69, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
122
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
123
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0xb4,
124
    0x00, 0x00, 0x00, 0x5f, 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x14, 0x6b, 0x54, 0x52, 0x43,
125
    0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0x10, 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0x0c,
126
    0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x61, 0x76, 0x69, 0x66, 0x00, 0x00, 0x00, 0x00,
127
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf3, 0x54,
128
    0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x16, 0xc9, 0x63, 0x75, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
129
    0x01, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x43, 0x43, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
130
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
131
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
132
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
133
};
134
135
static const size_t iccGrayLength = 275;
136
137
static const ptrdiff_t grayWhiteOffset = 0xd8;
138
static const ptrdiff_t grayGammaOffset = 0xf0;
139
140
static const ptrdiff_t checksumOffset = 0x54;
141
142
static const double small = 1e-12;
143
144
static uint32_t readLittleEndianU32(const uint8_t * data)
145
0
{
146
0
    return ((uint32_t)data[0] << 0) | ((uint32_t)data[1] << 8) | ((uint32_t)data[2] << 16) | ((uint32_t)data[3] << 24);
147
0
}
148
149
static void writeLittleEndianU32(uint8_t * data, uint32_t value)
150
0
{
151
0
    data[0] = (value >> 0) & 0xff;
152
0
    data[1] = (value >> 8) & 0xff;
153
0
    data[2] = (value >> 16) & 0xff;
154
0
    data[3] = (value >> 24) & 0xff;
155
0
}
156
157
static void writeBigEndianU16(uint8_t * data, uint16_t value)
158
0
{
159
0
    data[0] = (value >> 8) & 0xff;
160
0
    data[1] = (value >> 0) & 0xff;
161
0
}
162
163
static void writeBigEndianU32(uint8_t * data, uint32_t value)
164
0
{
165
0
    data[0] = (value >> 24) & 0xff;
166
0
    data[1] = (value >> 16) & 0xff;
167
0
    data[2] = (value >> 8) & 0xff;
168
0
    data[3] = (value >> 0) & 0xff;
169
0
}
170
171
static avifBool putS15Fixed16(uint8_t * data, double value)
172
0
{
173
0
    value = round(value * 65536);
174
0
    if (value > INT32_MAX || value < INT32_MIN) {
175
0
        return AVIF_FALSE;
176
0
    }
177
178
0
    int32_t fixed = (int32_t)value;
179
    // reinterpret into uint32_t to ensure the exact bits are written.
180
0
    writeBigEndianU32(data, *(uint32_t *)&fixed);
181
182
0
    return AVIF_TRUE;
183
0
}
184
185
static avifBool putU8Fixed8(uint8_t * data, float value)
186
0
{
187
0
    value = roundf(value * 256);
188
0
    if (value > UINT16_MAX || value < 1) {
189
0
        return AVIF_FALSE;
190
0
    }
191
192
0
    uint16_t fixed = (uint16_t)value;
193
0
    writeBigEndianU16(data, fixed);
194
0
    return AVIF_TRUE;
195
0
}
196
197
static avifBool putColorant(uint8_t * data, const double XYZ[3])
198
0
{
199
0
    if (!putS15Fixed16(data, XYZ[0])) {
200
0
        return AVIF_FALSE;
201
0
    }
202
203
0
    if (!putS15Fixed16(data + 4, XYZ[1])) {
204
0
        return AVIF_FALSE;
205
0
    }
206
207
0
    if (!putS15Fixed16(data + 8, XYZ[2])) {
208
0
        return AVIF_FALSE;
209
0
    }
210
211
0
    return AVIF_TRUE;
212
0
}
213
214
static avifBool xyToXYZ(const float xy[2], double XYZ[3])
215
0
{
216
0
    if (fabsf(xy[1]) < small) {
217
0
        return AVIF_FALSE;
218
0
    }
219
220
0
    const double factor = 1.0 / xy[1];
221
0
    XYZ[0] = xy[0] * factor;
222
0
    XYZ[1] = 1;
223
0
    XYZ[2] = (1 - xy[0] - xy[1]) * factor;
224
225
0
    return AVIF_TRUE;
226
0
}
227
228
// Computes I = M^-1. Returns false if M seems to be singular.
229
static avifBool matInv(const double M[3][3], double I[3][3])
230
0
{
231
0
    double det = M[0][0] * (M[1][1] * M[2][2] - M[2][1] * M[1][2]) - M[0][1] * (M[1][0] * M[2][2] - M[1][2] * M[2][0]) +
232
0
                 M[0][2] * (M[1][0] * M[2][1] - M[1][1] * M[2][0]);
233
0
    if (fabs(det) < small) {
234
0
        return AVIF_FALSE;
235
0
    }
236
0
    det = 1 / det;
237
238
0
    I[0][0] = (M[1][1] * M[2][2] - M[2][1] * M[1][2]) * det;
239
0
    I[0][1] = (M[0][2] * M[2][1] - M[0][1] * M[2][2]) * det;
240
0
    I[0][2] = (M[0][1] * M[1][2] - M[0][2] * M[1][1]) * det;
241
0
    I[1][0] = (M[1][2] * M[2][0] - M[1][0] * M[2][2]) * det;
242
0
    I[1][1] = (M[0][0] * M[2][2] - M[0][2] * M[2][0]) * det;
243
0
    I[1][2] = (M[1][0] * M[0][2] - M[0][0] * M[1][2]) * det;
244
0
    I[2][0] = (M[1][0] * M[2][1] - M[2][0] * M[1][1]) * det;
245
0
    I[2][1] = (M[2][0] * M[0][1] - M[0][0] * M[2][1]) * det;
246
0
    I[2][2] = (M[0][0] * M[1][1] - M[1][0] * M[0][1]) * det;
247
248
0
    return AVIF_TRUE;
249
0
}
250
251
// Computes C = A*B
252
static void matMul(const double A[3][3], const double B[3][3], double C[3][3])
253
0
{
254
0
    C[0][0] = A[0][0] * B[0][0] + A[0][1] * B[1][0] + A[0][2] * B[2][0];
255
0
    C[0][1] = A[0][0] * B[0][1] + A[0][1] * B[1][1] + A[0][2] * B[2][1];
256
0
    C[0][2] = A[0][0] * B[0][2] + A[0][1] * B[1][2] + A[0][2] * B[2][2];
257
0
    C[1][0] = A[1][0] * B[0][0] + A[1][1] * B[1][0] + A[1][2] * B[2][0];
258
0
    C[1][1] = A[1][0] * B[0][1] + A[1][1] * B[1][1] + A[1][2] * B[2][1];
259
0
    C[1][2] = A[1][0] * B[0][2] + A[1][1] * B[1][2] + A[1][2] * B[2][2];
260
0
    C[2][0] = A[2][0] * B[0][0] + A[2][1] * B[1][0] + A[2][2] * B[2][0];
261
0
    C[2][1] = A[2][0] * B[0][1] + A[2][1] * B[1][1] + A[2][2] * B[2][1];
262
0
    C[2][2] = A[2][0] * B[0][2] + A[2][1] * B[1][2] + A[2][2] * B[2][2];
263
0
}
264
265
// Set M to have values of d on the leading diagonal, and zero elsewhere.
266
static void matDiag(const double d[3], double M[3][3])
267
0
{
268
0
    M[0][0] = d[0];
269
0
    M[0][1] = 0;
270
0
    M[0][2] = 0;
271
0
    M[1][0] = 0;
272
0
    M[1][1] = d[1];
273
0
    M[1][2] = 0;
274
0
    M[2][0] = 0;
275
0
    M[2][1] = 0;
276
0
    M[2][2] = d[2];
277
0
}
278
279
static void swap(double * a, double * b)
280
0
{
281
0
    double tmp = *a;
282
0
    *a = *b;
283
0
    *b = tmp;
284
0
}
285
286
// Transpose M
287
static void matTrans(double M[3][3])
288
0
{
289
0
    swap(&M[0][1], &M[1][0]);
290
0
    swap(&M[0][2], &M[2][0]);
291
0
    swap(&M[1][2], &M[2][1]);
292
0
}
293
294
// Computes y = M.x
295
static void vecMul(const double M[3][3], const double x[3], double y[3])
296
0
{
297
0
    y[0] = M[0][0] * x[0] + M[0][1] * x[1] + M[0][2] * x[2];
298
0
    y[1] = M[1][0] * x[0] + M[1][1] * x[1] + M[1][2] * x[2];
299
0
    y[2] = M[2][0] * x[0] + M[2][1] * x[1] + M[2][2] * x[2];
300
0
}
301
302
// MD5 algorithm. See https://www.ietf.org/rfc/rfc1321.html#appendix-A.3
303
// This function writes the MD5 checksum in place at offset `checksumOffset` of `data`.
304
// This function shall only be called with a copy of iccColorTemplate or iccGrayTemplate, and sizeof(icc*Template).
305
static void computeMD5(uint8_t * data, size_t length)
306
0
{
307
0
    static const uint32_t sineparts[64] = {
308
0
        0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af,
309
0
        0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
310
0
        0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8,
311
0
        0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
312
0
        0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244, 0x432aff97,
313
0
        0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
314
0
        0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
315
0
    };
316
0
    static const uint8_t shift[64] = {
317
0
        7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9,  14, 20, 5, 9,  14, 20, 5, 9,  14, 20, 5, 9,  14, 20,
318
0
        4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21,
319
0
    };
320
321
0
    uint32_t a0 = 0x67452301, b0 = 0xefcdab89, c0 = 0x98badcfe, d0 = 0x10325476;
322
323
0
    for (uint32_t i = 0; i < length; i += 64) {
324
0
        uint32_t a = a0, b = b0, c = c0, d = d0, f, g;
325
0
        for (uint32_t j = 0; j < 64; j++) {
326
0
            if (j < 16) {
327
0
                f = (b & c) | ((~b) & d);
328
0
                g = j;
329
0
            } else if (j < 32) {
330
0
                f = (d & b) | ((~d) & c);
331
0
                g = (5 * j + 1) & 0xf;
332
0
            } else if (j < 48) {
333
0
                f = b ^ c ^ d;
334
0
                g = (3 * j + 5) & 0xf;
335
0
            } else {
336
0
                f = c ^ (b | (~d));
337
0
                g = (7 * j) & 0xf;
338
0
            }
339
0
            uint32_t u = readLittleEndianU32(data + i + g * 4);
340
0
            f += a + sineparts[j] + u;
341
0
            a = d;
342
0
            d = c;
343
0
            c = b;
344
0
            b += (f << shift[j]) | (f >> (32u - shift[j]));
345
0
        }
346
0
        a0 += a;
347
0
        b0 += b;
348
0
        c0 += c;
349
0
        d0 += d;
350
0
    }
351
352
0
    uint8_t * output = data + checksumOffset;
353
0
    writeLittleEndianU32(output, a0);
354
0
    writeLittleEndianU32(output + 4, b0);
355
0
    writeLittleEndianU32(output + 8, c0);
356
0
    writeLittleEndianU32(output + 12, d0);
357
0
}
358
359
// Bradford chromatic adaptation matrix
360
// from https://www.researchgate.net/publication/253799640_A_uniform_colour_space_based_upon_CIECAM97s
361
static const double bradford[3][3] = {
362
    { 0.8951, 0.2664, -0.1614 },
363
    { -0.7502, 1.7135, 0.0367 },
364
    { 0.0389, -0.0685, 1.0296 },
365
};
366
367
// LMS values for D50 whitepoint
368
static const double lmsD50[3] = { 0.996284, 1.02043, 0.818644 };
369
370
avifBool avifGenerateRGBICC(avifRWData * icc, float gamma, const float primaries[8])
371
0
{
372
0
    uint8_t buffer[sizeof(iccColorTemplate)];
373
0
    memcpy(buffer, iccColorTemplate, sizeof(iccColorTemplate));
374
375
0
    double whitePointXYZ[3];
376
0
    if (!xyToXYZ(&primaries[6], whitePointXYZ)) {
377
0
        return AVIF_FALSE;
378
0
    }
379
380
0
    if (!putColorant(buffer + colorWhiteOffset, whitePointXYZ)) {
381
0
        return AVIF_FALSE;
382
0
    }
383
384
0
    const double rgbPrimaries[3][3] = {
385
0
        { primaries[0], primaries[2], primaries[4] },
386
0
        { primaries[1], primaries[3], primaries[5] },
387
0
        { 1.0 - primaries[0] - primaries[1], 1.0 - primaries[2] - primaries[3], 1.0 - primaries[4] - primaries[5] }
388
0
    };
389
390
0
    double rgbPrimariesInv[3][3];
391
0
    if (!matInv(rgbPrimaries, rgbPrimariesInv)) {
392
0
        return AVIF_FALSE;
393
0
    }
394
395
0
    double rgbCoefficients[3];
396
0
    vecMul(rgbPrimariesInv, whitePointXYZ, rgbCoefficients);
397
398
0
    double rgbCoefficientsMat[3][3];
399
0
    matDiag(rgbCoefficients, rgbCoefficientsMat);
400
401
0
    double rgbXYZ[3][3];
402
0
    matMul(rgbPrimaries, rgbCoefficientsMat, rgbXYZ);
403
404
    // ICC stores primaries XYZ under PCS.
405
    // Adapt using linear bradford transform
406
    // from https://onlinelibrary.wiley.com/doi/pdf/10.1002/9781119021780.app3
407
0
    double lms[3];
408
0
    vecMul(bradford, whitePointXYZ, lms);
409
0
    for (int i = 0; i < 3; ++i) {
410
0
        if (fabs(lms[i]) < small) {
411
0
            return AVIF_FALSE;
412
0
        }
413
0
        lms[i] = lmsD50[i] / lms[i];
414
0
    }
415
416
0
    double adaptation[3][3];
417
0
    matDiag(lms, adaptation);
418
419
0
    double tmp[3][3];
420
0
    matMul(adaptation, bradford, tmp);
421
422
0
    double bradfordInv[3][3];
423
0
    if (!matInv(bradford, bradfordInv)) {
424
0
        return AVIF_FALSE;
425
0
    }
426
0
    matMul(bradfordInv, tmp, adaptation);
427
428
0
    double rgbXYZD50[3][3];
429
0
    matMul(adaptation, rgbXYZ, rgbXYZD50);
430
0
    matTrans(rgbXYZD50);
431
432
0
    if (!putColorant(buffer + colorRedOffset, rgbXYZD50[0])) {
433
0
        return AVIF_FALSE;
434
0
    }
435
436
0
    if (!putColorant(buffer + colorGreenOffset, rgbXYZD50[1])) {
437
0
        return AVIF_FALSE;
438
0
    }
439
440
0
    if (!putColorant(buffer + colorBlueOffset, rgbXYZD50[2])) {
441
0
        return AVIF_FALSE;
442
0
    }
443
444
0
    if (!putU8Fixed8(buffer + colorGammaOffset, gamma)) {
445
0
        return AVIF_FALSE;
446
0
    }
447
448
0
    computeMD5(buffer, sizeof(iccColorTemplate));
449
0
    if (avifRWDataSet(icc, buffer, iccColorLength) != AVIF_RESULT_OK) {
450
0
        return AVIF_FALSE;
451
0
    }
452
453
0
    return AVIF_TRUE;
454
0
}
455
456
avifBool avifGenerateGrayICC(avifRWData * icc, float gamma, const float white[2])
457
0
{
458
0
    uint8_t buffer[sizeof(iccGrayTemplate)];
459
0
    memcpy(buffer, iccGrayTemplate, sizeof(iccGrayTemplate));
460
461
0
    double whitePointXYZ[3];
462
0
    if (!xyToXYZ(white, whitePointXYZ)) {
463
0
        return AVIF_FALSE;
464
0
    }
465
466
0
    if (!putColorant(buffer + grayWhiteOffset, whitePointXYZ)) {
467
0
        return AVIF_FALSE;
468
0
    }
469
470
0
    if (!putU8Fixed8(buffer + grayGammaOffset, gamma)) {
471
0
        return AVIF_FALSE;
472
0
    }
473
474
0
    computeMD5(buffer, sizeof(iccGrayTemplate));
475
0
    if (avifRWDataSet(icc, buffer, iccGrayLength) != AVIF_RESULT_OK) {
476
0
        return AVIF_FALSE;
477
0
    }
478
479
0
    return AVIF_TRUE;
480
0
}