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
SkICC(sk_sp<SkColorSpace> colorSpace)18 SkICC::SkICC(sk_sp<SkColorSpace> colorSpace)
19 : fColorSpace(std::move(colorSpace))
20 {}
21
Make(const void * ptr,size_t len)22 sk_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
toXYZD50(SkMatrix44 * toXYZD50) const31 bool 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
isNumericalTransferFn(SkColorSpaceTransferFn * coeffs) const41 bool SkICC::isNumericalTransferFn(SkColorSpaceTransferFn* coeffs) const {
42 return as_CSB(fColorSpace)->onIsNumericalTransferFn(coeffs);
43 }
44
45 static const int kDefaultTableSize = 512; // Arbitrary
46
fn_to_table(float * tablePtr,const SkColorSpaceTransferFn & fn)47 void 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
copy_to_table(float * tablePtr,const SkGammas * gammas,int index)60 void 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
rawTransferFnData(Tables * tables) const67 bool 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)
143 static 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 };
147 static_assert(SkIsAlign4(sizeof(kDescriptionTagBody)), "Description must be aligned to 4-bytes.");
148 static 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
158 static 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)
167 static 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 };
172 static_assert(SkIsAlign4(sizeof(kCopyrightTagBody)), "Copyright must be aligned to 4-bytes.");
173 static 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.
184 static constexpr uint32_t kICCNumEntries = 9;
185
186 static constexpr uint32_t kTAG_desc = SkSetFourByteTag('d', 'e', 's', 'c');
187 static constexpr uint32_t kTAG_desc_Bytes = sizeof(kDescriptionTagHeader) +
188 sizeof(kDescriptionTagBody);
189 static constexpr uint32_t kTAG_desc_Offset = kICCHeaderSize +
190 kICCNumEntries * kICCTagTableEntrySize;
191
192 static constexpr uint32_t kTAG_XYZ_Bytes = 20;
193 static constexpr uint32_t kTAG_rXYZ_Offset = kTAG_desc_Offset + kTAG_desc_Bytes;
194 static constexpr uint32_t kTAG_gXYZ_Offset = kTAG_rXYZ_Offset + kTAG_XYZ_Bytes;
195 static constexpr uint32_t kTAG_bXYZ_Offset = kTAG_gXYZ_Offset + kTAG_XYZ_Bytes;
196
197 static constexpr uint32_t kTAG_TRC_Bytes = 40;
198 static constexpr uint32_t kTAG_rTRC_Offset = kTAG_bXYZ_Offset + kTAG_XYZ_Bytes;
199 static constexpr uint32_t kTAG_gTRC_Offset = kTAG_rTRC_Offset;
200 static constexpr uint32_t kTAG_bTRC_Offset = kTAG_rTRC_Offset;
201
202 static constexpr uint32_t kTAG_wtpt = SkSetFourByteTag('w', 't', 'p', 't');
203 static constexpr uint32_t kTAG_wtpt_Offset = kTAG_bTRC_Offset + kTAG_TRC_Bytes;
204
205 static constexpr uint32_t kTAG_cprt = SkSetFourByteTag('c', 'p', 'r', 't');
206 static constexpr uint32_t kTAG_cprt_Bytes = sizeof(kCopyrightTagHeader) +
207 sizeof(kCopyrightTagBody);
208 static constexpr uint32_t kTAG_cprt_Offset = kTAG_wtpt_Offset + kTAG_XYZ_Bytes;
209
210 static constexpr uint32_t kICCProfileSize = kTAG_cprt_Offset + kTAG_cprt_Bytes;
211
212 static 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
236 static 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
write_xyz_tag(uint32_t * ptr,const SkMatrix44 & toXYZ,int col)283 static 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
write_trc_tag(uint32_t * ptr,const SkColorSpaceTransferFn & fn)291 static 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
is_3x3(const SkMatrix44 & toXYZD50)304 static 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
WriteToICC(const SkColorSpaceTransferFn & fn,const SkMatrix44 & toXYZD50)310 sk_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