1/*
2 * Copyright 2016 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 "SkAutoMalloc.h"
9#include "SkColorSpace_Base.h"
10#include "SkColorSpace_XYZ.h"
11#include "SkColorSpacePriv.h"
12#include "SkColorSpaceXformPriv.h"
13#include "SkEndian.h"
14#include "SkFixed.h"
15#include "SkICC.h"
16#include "SkICCPriv.h"
17
18SkICC::SkICC(sk_sp<SkColorSpace> colorSpace)
19    : fColorSpace(std::move(colorSpace))
20{}
21
22sk_sp<SkICC> SkICC::Make(const void* ptr, size_t len) {
23    sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeICC(ptr, len);
24    if (!colorSpace) {
25        return nullptr;
26    }
27
28    return sk_sp<SkICC>(new SkICC(std::move(colorSpace)));
29}
30
31bool SkICC::toXYZD50(SkMatrix44* toXYZD50) const {
32    const SkMatrix44* m = as_CSB(fColorSpace)->toXYZD50();
33    if (!m) {
34        return false;
35    }
36
37    *toXYZD50 = *m;
38    return true;
39}
40
41bool SkICC::isNumericalTransferFn(SkColorSpaceTransferFn* coeffs) const {
42    return as_CSB(fColorSpace)->onIsNumericalTransferFn(coeffs);
43}
44
45static const int kDefaultTableSize = 512; // Arbitrary
46
47void fn_to_table(float* tablePtr, const SkColorSpaceTransferFn& fn) {
48    // Y = (aX + b)^g + e  for X >= d
49    // Y = cX + f          otherwise
50    for (int i = 0; i < kDefaultTableSize; i++) {
51        float x = ((float) i) / ((float) (kDefaultTableSize - 1));
52        if (x >= fn.fD) {
53            tablePtr[i] = clamp_0_1(powf(fn.fA * x + fn.fB, fn.fG) + fn.fE);
54        } else {
55            tablePtr[i] = clamp_0_1(fn.fC * x + fn.fF);
56        }
57    }
58}
59
60void copy_to_table(float* tablePtr, const SkGammas* gammas, int index) {
61    SkASSERT(gammas->isTable(index));
62    const float* ptr = gammas->table(index);
63    const size_t bytes = gammas->tableSize(index) * sizeof(float);
64    memcpy(tablePtr, ptr, bytes);
65}
66
67bool SkICC::rawTransferFnData(Tables* tables) const {
68    if (SkColorSpace_Base::Type::kA2B == as_CSB(fColorSpace)->type()) {
69        return false;
70    }
71    SkColorSpace_XYZ* colorSpace = (SkColorSpace_XYZ*) fColorSpace.get();
72
73    SkColorSpaceTransferFn fn;
74    if (this->isNumericalTransferFn(&fn)) {
75        tables->fStorage = SkData::MakeUninitialized(kDefaultTableSize * sizeof(float));
76        fn_to_table((float*) tables->fStorage->writable_data(), fn);
77        tables->fRed.fOffset = tables->fGreen.fOffset = tables->fBlue.fOffset = 0;
78        tables->fRed.fCount = tables->fGreen.fCount = tables->fBlue.fCount = kDefaultTableSize;
79        return true;
80    }
81
82    const SkGammas* gammas = colorSpace->gammas();
83    SkASSERT(gammas);
84    if (gammas->data(0) == gammas->data(1) && gammas->data(0) == gammas->data(2)) {
85        SkASSERT(gammas->isTable(0));
86        tables->fStorage = SkData::MakeUninitialized(gammas->tableSize(0) * sizeof(float));
87        copy_to_table((float*) tables->fStorage->writable_data(), gammas, 0);
88        tables->fRed.fOffset = tables->fGreen.fOffset = tables->fBlue.fOffset = 0;
89        tables->fRed.fCount = tables->fGreen.fCount = tables->fBlue.fCount = gammas->tableSize(0);
90        return true;
91    }
92
93    // Determine the storage size.
94    size_t storageSize = 0;
95    for (int i = 0; i < 3; i++) {
96        if (gammas->isTable(i)) {
97            storageSize += gammas->tableSize(i) * sizeof(float);
98        } else {
99            storageSize += kDefaultTableSize * sizeof(float);
100        }
101    }
102
103    // Fill in the tables.
104    tables->fStorage = SkData::MakeUninitialized(storageSize);
105    float* ptr = (float*) tables->fStorage->writable_data();
106    size_t offset = 0;
107    Channel rgb[3];
108    for (int i = 0; i < 3; i++) {
109        if (gammas->isTable(i)) {
110            copy_to_table(ptr, gammas, i);
111            rgb[i].fOffset = offset;
112            rgb[i].fCount = gammas->tableSize(i);
113            offset += rgb[i].fCount * sizeof(float);
114            ptr += rgb[i].fCount;
115            continue;
116        }
117
118        if (gammas->isNamed(i)) {
119            SkAssertResult(named_to_parametric(&fn, gammas->data(i).fNamed));
120        } else if (gammas->isValue(i)) {
121            value_to_parametric(&fn, gammas->data(i).fValue);
122        } else {
123            SkASSERT(gammas->isParametric(i));
124            fn = gammas->params(i);
125        }
126
127        fn_to_table(ptr, fn);
128        rgb[i].fOffset = offset;
129        rgb[i].fCount = kDefaultTableSize;
130        offset += kDefaultTableSize * sizeof(float);
131        ptr += kDefaultTableSize;
132    }
133
134    tables->fRed = rgb[0];
135    tables->fGreen = rgb[1];
136    tables->fBlue = rgb[2];
137    return true;
138}
139
140///////////////////////////////////////////////////////////////////////////////////////////////////
141
142// Google Skia (UTF-16)
143static constexpr uint8_t kDescriptionTagBody[] = {
144        0x00, 0x47, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x67, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x20, 0x00,
145        0x53, 0x00, 0x6b, 0x00, 0x69, 0x00, 0x61, 0x00, 0x20,
146    };
147static_assert(SkIsAlign4(sizeof(kDescriptionTagBody)), "Description must be aligned to 4-bytes.");
148static constexpr uint32_t kDescriptionTagHeader[7] {
149    SkEndian_SwapBE32(kTAG_TextType),                        // Type signature
150    0,                                                       // Reserved
151    SkEndian_SwapBE32(1),                                    // Number of records
152    SkEndian_SwapBE32(12),                                   // Record size (must be 12)
153    SkEndian_SwapBE32(SkSetFourByteTag('e', 'n', 'U', 'S')), // English USA
154    SkEndian_SwapBE32(sizeof(kDescriptionTagBody)),          // Length of string
155    SkEndian_SwapBE32(28),                                   // Offset of string
156};
157
158static constexpr uint32_t kWhitePointTag[5] {
159    SkEndian_SwapBE32(kXYZ_PCSSpace),
160    0,
161    SkEndian_SwapBE32(0x0000f6d6), // X = 0.96420 (D50)
162    SkEndian_SwapBE32(0x00010000), // Y = 1.00000 (D50)
163    SkEndian_SwapBE32(0x0000d32d), // Z = 0.82491 (D50)
164};
165
166// Google Inc. 2016 (UTF-16)
167static constexpr uint8_t kCopyrightTagBody[] = {
168        0x00, 0x47, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x67, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x20, 0x00,
169        0x49, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x2e, 0x00, 0x20, 0x00, 0x32, 0x00, 0x30, 0x00, 0x31,
170        0x00, 0x36,
171};
172static_assert(SkIsAlign4(sizeof(kCopyrightTagBody)), "Copyright must be aligned to 4-bytes.");
173static constexpr uint32_t kCopyrightTagHeader[7] {
174    SkEndian_SwapBE32(kTAG_TextType),                        // Type signature
175    0,                                                       // Reserved
176    SkEndian_SwapBE32(1),                                    // Number of records
177    SkEndian_SwapBE32(12),                                   // Record size (must be 12)
178    SkEndian_SwapBE32(SkSetFourByteTag('e', 'n', 'U', 'S')), // English USA
179    SkEndian_SwapBE32(sizeof(kCopyrightTagBody)),            // Length of string
180    SkEndian_SwapBE32(28),                                   // Offset of string
181};
182
183// We will write a profile with the minimum nine required tags.
184static constexpr uint32_t kICCNumEntries = 9;
185
186static constexpr uint32_t kTAG_desc = SkSetFourByteTag('d', 'e', 's', 'c');
187static constexpr uint32_t kTAG_desc_Bytes = sizeof(kDescriptionTagHeader) +
188                                            sizeof(kDescriptionTagBody);
189static constexpr uint32_t kTAG_desc_Offset = kICCHeaderSize +
190                                             kICCNumEntries * kICCTagTableEntrySize;
191
192static constexpr uint32_t kTAG_XYZ_Bytes = 20;
193static constexpr uint32_t kTAG_rXYZ_Offset = kTAG_desc_Offset + kTAG_desc_Bytes;
194static constexpr uint32_t kTAG_gXYZ_Offset = kTAG_rXYZ_Offset + kTAG_XYZ_Bytes;
195static constexpr uint32_t kTAG_bXYZ_Offset = kTAG_gXYZ_Offset + kTAG_XYZ_Bytes;
196
197static constexpr uint32_t kTAG_TRC_Bytes = 40;
198static constexpr uint32_t kTAG_rTRC_Offset = kTAG_bXYZ_Offset + kTAG_XYZ_Bytes;
199static constexpr uint32_t kTAG_gTRC_Offset = kTAG_rTRC_Offset;
200static constexpr uint32_t kTAG_bTRC_Offset = kTAG_rTRC_Offset;
201
202static constexpr uint32_t kTAG_wtpt = SkSetFourByteTag('w', 't', 'p', 't');
203static constexpr uint32_t kTAG_wtpt_Offset = kTAG_bTRC_Offset + kTAG_TRC_Bytes;
204
205static constexpr uint32_t kTAG_cprt = SkSetFourByteTag('c', 'p', 'r', 't');
206static constexpr uint32_t kTAG_cprt_Bytes = sizeof(kCopyrightTagHeader) +
207                                            sizeof(kCopyrightTagBody);
208static constexpr uint32_t kTAG_cprt_Offset = kTAG_wtpt_Offset + kTAG_XYZ_Bytes;
209
210static constexpr uint32_t kICCProfileSize = kTAG_cprt_Offset + kTAG_cprt_Bytes;
211
212static constexpr uint32_t kICCHeader[kICCHeaderSize / 4] {
213    SkEndian_SwapBE32(kICCProfileSize),  // Size of the profile
214    0,                                   // Preferred CMM type (ignored)
215    SkEndian_SwapBE32(0x02100000),       // Version 2.1
216    SkEndian_SwapBE32(kDisplay_Profile), // Display device profile
217    SkEndian_SwapBE32(kRGB_ColorSpace),  // RGB input color space
218    SkEndian_SwapBE32(kXYZ_PCSSpace),    // XYZ profile connection space
219    0, 0, 0,                             // Date and time (ignored)
220    SkEndian_SwapBE32(kACSP_Signature),  // Profile signature
221    0,                                   // Platform target (ignored)
222    0x00000000,                          // Flags: not embedded, can be used independently
223    0,                                   // Device manufacturer (ignored)
224    0,                                   // Device model (ignored)
225    0, 0,                                // Device attributes (ignored)
226    SkEndian_SwapBE32(1),                // Relative colorimetric rendering intent
227    SkEndian_SwapBE32(0x0000f6d6),       // D50 standard illuminant (X)
228    SkEndian_SwapBE32(0x00010000),       // D50 standard illuminant (Y)
229    SkEndian_SwapBE32(0x0000d32d),       // D50 standard illuminant (Z)
230    0,                                   // Profile creator (ignored)
231    0, 0, 0, 0,                          // Profile id checksum (ignored)
232    0, 0, 0, 0, 0, 0, 0,                 // Reserved (ignored)
233    SkEndian_SwapBE32(kICCNumEntries),   // Number of tags
234};
235
236static constexpr uint32_t kICCTagTable[3 * kICCNumEntries] {
237    // Profile description
238    SkEndian_SwapBE32(kTAG_desc),
239    SkEndian_SwapBE32(kTAG_desc_Offset),
240    SkEndian_SwapBE32(kTAG_desc_Bytes),
241
242    // rXYZ
243    SkEndian_SwapBE32(kTAG_rXYZ),
244    SkEndian_SwapBE32(kTAG_rXYZ_Offset),
245    SkEndian_SwapBE32(kTAG_XYZ_Bytes),
246
247    // gXYZ
248    SkEndian_SwapBE32(kTAG_gXYZ),
249    SkEndian_SwapBE32(kTAG_gXYZ_Offset),
250    SkEndian_SwapBE32(kTAG_XYZ_Bytes),
251
252    // bXYZ
253    SkEndian_SwapBE32(kTAG_bXYZ),
254    SkEndian_SwapBE32(kTAG_bXYZ_Offset),
255    SkEndian_SwapBE32(kTAG_XYZ_Bytes),
256
257    // rTRC
258    SkEndian_SwapBE32(kTAG_rTRC),
259    SkEndian_SwapBE32(kTAG_rTRC_Offset),
260    SkEndian_SwapBE32(kTAG_TRC_Bytes),
261
262    // gTRC
263    SkEndian_SwapBE32(kTAG_gTRC),
264    SkEndian_SwapBE32(kTAG_gTRC_Offset),
265    SkEndian_SwapBE32(kTAG_TRC_Bytes),
266
267    // bTRC
268    SkEndian_SwapBE32(kTAG_bTRC),
269    SkEndian_SwapBE32(kTAG_bTRC_Offset),
270    SkEndian_SwapBE32(kTAG_TRC_Bytes),
271
272    // White point
273    SkEndian_SwapBE32(kTAG_wtpt),
274    SkEndian_SwapBE32(kTAG_wtpt_Offset),
275    SkEndian_SwapBE32(kTAG_XYZ_Bytes),
276
277    // Copyright
278    SkEndian_SwapBE32(kTAG_cprt),
279    SkEndian_SwapBE32(kTAG_cprt_Offset),
280    SkEndian_SwapBE32(kTAG_cprt_Bytes),
281};
282
283static void write_xyz_tag(uint32_t* ptr, const SkMatrix44& toXYZ, int col) {
284    ptr[0] = SkEndian_SwapBE32(kXYZ_PCSSpace);
285    ptr[1] = 0;
286    ptr[2] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(0, col)));
287    ptr[3] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(1, col)));
288    ptr[4] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(2, col)));
289}
290
291static void write_trc_tag(uint32_t* ptr, const SkColorSpaceTransferFn& fn) {
292    ptr[0] = SkEndian_SwapBE32(kTAG_ParaCurveType);
293    ptr[1] = 0;
294    ptr[2] = (uint32_t) (SkEndian_SwapBE16(kGABCDEF_ParaCurveType));
295    ptr[3] = SkEndian_SwapBE32(SkFloatToFixed(fn.fG));
296    ptr[4] = SkEndian_SwapBE32(SkFloatToFixed(fn.fA));
297    ptr[5] = SkEndian_SwapBE32(SkFloatToFixed(fn.fB));
298    ptr[6] = SkEndian_SwapBE32(SkFloatToFixed(fn.fC));
299    ptr[7] = SkEndian_SwapBE32(SkFloatToFixed(fn.fD));
300    ptr[8] = SkEndian_SwapBE32(SkFloatToFixed(fn.fE));
301    ptr[9] = SkEndian_SwapBE32(SkFloatToFixed(fn.fF));
302}
303
304static bool is_3x3(const SkMatrix44& toXYZD50) {
305    return 0.0f == toXYZD50.get(3, 0) && 0.0f == toXYZD50.get(3, 1) && 0.0f == toXYZD50.get(3, 2) &&
306           0.0f == toXYZD50.get(0, 3) && 0.0f == toXYZD50.get(1, 3) && 0.0f == toXYZD50.get(2, 3) &&
307           1.0f == toXYZD50.get(3, 3);
308}
309
310sk_sp<SkData> SkICC::WriteToICC(const SkColorSpaceTransferFn& fn, const SkMatrix44& toXYZD50) {
311    if (!is_3x3(toXYZD50) || !is_valid_transfer_fn(fn)) {
312        return nullptr;
313    }
314
315    SkAutoMalloc profile(kICCProfileSize);
316    uint8_t* ptr = (uint8_t*) profile.get();
317
318    // Write profile header
319    memcpy(ptr, kICCHeader, sizeof(kICCHeader));
320    ptr += sizeof(kICCHeader);
321
322    // Write tag table
323    memcpy(ptr, kICCTagTable, sizeof(kICCTagTable));
324    ptr += sizeof(kICCTagTable);
325
326    // Write profile description tag
327    memcpy(ptr, kDescriptionTagHeader, sizeof(kDescriptionTagHeader));
328    ptr += sizeof(kDescriptionTagHeader);
329    memcpy(ptr, kDescriptionTagBody, sizeof(kDescriptionTagBody));
330    ptr += sizeof(kDescriptionTagBody);
331
332    // Write XYZ tags
333    write_xyz_tag((uint32_t*) ptr, toXYZD50, 0);
334    ptr += kTAG_XYZ_Bytes;
335    write_xyz_tag((uint32_t*) ptr, toXYZD50, 1);
336    ptr += kTAG_XYZ_Bytes;
337    write_xyz_tag((uint32_t*) ptr, toXYZD50, 2);
338    ptr += kTAG_XYZ_Bytes;
339
340    // Write TRC tag
341    write_trc_tag((uint32_t*) ptr, fn);
342    ptr += kTAG_TRC_Bytes;
343
344    // Write white point tag (must be D50)
345    memcpy(ptr, kWhitePointTag, sizeof(kWhitePointTag));
346    ptr += sizeof(kWhitePointTag);
347
348    // Write copyright tag
349    memcpy(ptr, kCopyrightTagHeader, sizeof(kCopyrightTagHeader));
350    ptr += sizeof(kCopyrightTagHeader);
351    memcpy(ptr, kCopyrightTagBody, sizeof(kCopyrightTagBody));
352    ptr += sizeof(kCopyrightTagBody);
353
354    SkASSERT(kICCProfileSize == ptr - (uint8_t*) profile.get());
355    return SkData::MakeFromMalloc(profile.release(), kICCProfileSize);
356}
357