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 "SkColorSpacePriv.h"
10 #include "SkColorSpaceXformPriv.h"
11 #include "SkColorSpace_Base.h"
12 #include "SkColorSpace_XYZ.h"
13 #include "SkEndian.h"
14 #include "SkFixed.h"
15 #include "SkICC.h"
16 #include "SkICCPriv.h"
17 #include "SkMD5.h"
18 #include "SkString.h"
19 #include "SkUtils.h"
20
SkICC(sk_sp<SkColorSpace> colorSpace)21 SkICC::SkICC(sk_sp<SkColorSpace> colorSpace)
22 : fColorSpace(std::move(colorSpace))
23 {}
24
Make(const void * ptr,size_t len)25 sk_sp<SkICC> SkICC::Make(const void* ptr, size_t len) {
26 sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeICC(ptr, len);
27 if (!colorSpace) {
28 return nullptr;
29 }
30
31 return sk_sp<SkICC>(new SkICC(std::move(colorSpace)));
32 }
33
toXYZD50(SkMatrix44 * toXYZD50) const34 bool SkICC::toXYZD50(SkMatrix44* toXYZD50) const {
35 const SkMatrix44* m = as_CSB(fColorSpace)->toXYZD50();
36 if (!m) {
37 return false;
38 }
39
40 *toXYZD50 = *m;
41 return true;
42 }
43
isNumericalTransferFn(SkColorSpaceTransferFn * coeffs) const44 bool SkICC::isNumericalTransferFn(SkColorSpaceTransferFn* coeffs) const {
45 return as_CSB(fColorSpace)->onIsNumericalTransferFn(coeffs);
46 }
47
48 static const int kDefaultTableSize = 512; // Arbitrary
49
fn_to_table(float * tablePtr,const SkColorSpaceTransferFn & fn)50 void fn_to_table(float* tablePtr, const SkColorSpaceTransferFn& fn) {
51 // Y = (aX + b)^g + e for X >= d
52 // Y = cX + f otherwise
53 for (int i = 0; i < kDefaultTableSize; i++) {
54 float x = ((float) i) / ((float) (kDefaultTableSize - 1));
55 if (x >= fn.fD) {
56 tablePtr[i] = clamp_0_1(powf(fn.fA * x + fn.fB, fn.fG) + fn.fE);
57 } else {
58 tablePtr[i] = clamp_0_1(fn.fC * x + fn.fF);
59 }
60 }
61 }
62
copy_to_table(float * tablePtr,const SkGammas * gammas,int index)63 void copy_to_table(float* tablePtr, const SkGammas* gammas, int index) {
64 SkASSERT(gammas->isTable(index));
65 const float* ptr = gammas->table(index);
66 const size_t bytes = gammas->tableSize(index) * sizeof(float);
67 memcpy(tablePtr, ptr, bytes);
68 }
69
rawTransferFnData(Tables * tables) const70 bool SkICC::rawTransferFnData(Tables* tables) const {
71 if (SkColorSpace_Base::Type::kA2B == as_CSB(fColorSpace)->type()) {
72 return false;
73 }
74 SkColorSpace_XYZ* colorSpace = (SkColorSpace_XYZ*) fColorSpace.get();
75
76 SkColorSpaceTransferFn fn;
77 if (this->isNumericalTransferFn(&fn)) {
78 tables->fStorage = SkData::MakeUninitialized(kDefaultTableSize * sizeof(float));
79 fn_to_table((float*) tables->fStorage->writable_data(), fn);
80 tables->fRed.fOffset = tables->fGreen.fOffset = tables->fBlue.fOffset = 0;
81 tables->fRed.fCount = tables->fGreen.fCount = tables->fBlue.fCount = kDefaultTableSize;
82 return true;
83 }
84
85 const SkGammas* gammas = colorSpace->gammas();
86 SkASSERT(gammas);
87 if (gammas->data(0) == gammas->data(1) && gammas->data(0) == gammas->data(2)) {
88 SkASSERT(gammas->isTable(0));
89 tables->fStorage = SkData::MakeUninitialized(gammas->tableSize(0) * sizeof(float));
90 copy_to_table((float*) tables->fStorage->writable_data(), gammas, 0);
91 tables->fRed.fOffset = tables->fGreen.fOffset = tables->fBlue.fOffset = 0;
92 tables->fRed.fCount = tables->fGreen.fCount = tables->fBlue.fCount = gammas->tableSize(0);
93 return true;
94 }
95
96 // Determine the storage size.
97 size_t storageSize = 0;
98 for (int i = 0; i < 3; i++) {
99 if (gammas->isTable(i)) {
100 storageSize += gammas->tableSize(i) * sizeof(float);
101 } else {
102 storageSize += kDefaultTableSize * sizeof(float);
103 }
104 }
105
106 // Fill in the tables.
107 tables->fStorage = SkData::MakeUninitialized(storageSize);
108 float* ptr = (float*) tables->fStorage->writable_data();
109 size_t offset = 0;
110 Channel rgb[3];
111 for (int i = 0; i < 3; i++) {
112 if (gammas->isTable(i)) {
113 copy_to_table(ptr, gammas, i);
114 rgb[i].fOffset = offset;
115 rgb[i].fCount = gammas->tableSize(i);
116 offset += rgb[i].fCount * sizeof(float);
117 ptr += rgb[i].fCount;
118 continue;
119 }
120
121 if (gammas->isNamed(i)) {
122 SkAssertResult(named_to_parametric(&fn, gammas->data(i).fNamed));
123 } else if (gammas->isValue(i)) {
124 value_to_parametric(&fn, gammas->data(i).fValue);
125 } else {
126 SkASSERT(gammas->isParametric(i));
127 fn = gammas->params(i);
128 }
129
130 fn_to_table(ptr, fn);
131 rgb[i].fOffset = offset;
132 rgb[i].fCount = kDefaultTableSize;
133 offset += kDefaultTableSize * sizeof(float);
134 ptr += kDefaultTableSize;
135 }
136
137 tables->fRed = rgb[0];
138 tables->fGreen = rgb[1];
139 tables->fBlue = rgb[2];
140 return true;
141 }
142
143 ///////////////////////////////////////////////////////////////////////////////////////////////////
144
145 static constexpr char kDescriptionTagBodyPrefix[12] =
146 { 'G', 'o', 'o', 'g', 'l', 'e', '/', 'S', 'k', 'i', 'a' , '/'};
147
148 static constexpr size_t kICCDescriptionTagSize = 44;
149
150 static_assert(kICCDescriptionTagSize ==
151 sizeof(kDescriptionTagBodyPrefix) + 2 * sizeof(SkMD5::Digest), "");
152 static constexpr size_t kDescriptionTagBodySize = kICCDescriptionTagSize * 2; // ascii->utf16be
153
154 static_assert(SkIsAlign4(kDescriptionTagBodySize), "Description must be aligned to 4-bytes.");
155 static constexpr uint32_t kDescriptionTagHeader[7] {
156 SkEndian_SwapBE32(kTAG_TextType), // Type signature
157 0, // Reserved
158 SkEndian_SwapBE32(1), // Number of records
159 SkEndian_SwapBE32(12), // Record size (must be 12)
160 SkEndian_SwapBE32(SkSetFourByteTag('e', 'n', 'U', 'S')), // English USA
161 SkEndian_SwapBE32(kDescriptionTagBodySize), // Length of string
162 SkEndian_SwapBE32(28), // Offset of string
163 };
164
165 static constexpr uint32_t kWhitePointTag[5] {
166 SkEndian_SwapBE32(kXYZ_PCSSpace),
167 0,
168 SkEndian_SwapBE32(0x0000f6d6), // X = 0.96420 (D50)
169 SkEndian_SwapBE32(0x00010000), // Y = 1.00000 (D50)
170 SkEndian_SwapBE32(0x0000d32d), // Z = 0.82491 (D50)
171 };
172
173 // Google Inc. 2016 (UTF-16)
174 static constexpr uint8_t kCopyrightTagBody[] = {
175 0x00, 0x47, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x67, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x20, 0x00,
176 0x49, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x2e, 0x00, 0x20, 0x00, 0x32, 0x00, 0x30, 0x00, 0x31,
177 0x00, 0x36,
178 };
179 static_assert(SkIsAlign4(sizeof(kCopyrightTagBody)), "Copyright must be aligned to 4-bytes.");
180 static constexpr uint32_t kCopyrightTagHeader[7] {
181 SkEndian_SwapBE32(kTAG_TextType), // Type signature
182 0, // Reserved
183 SkEndian_SwapBE32(1), // Number of records
184 SkEndian_SwapBE32(12), // Record size (must be 12)
185 SkEndian_SwapBE32(SkSetFourByteTag('e', 'n', 'U', 'S')), // English USA
186 SkEndian_SwapBE32(sizeof(kCopyrightTagBody)), // Length of string
187 SkEndian_SwapBE32(28), // Offset of string
188 };
189
190 // We will write a profile with the minimum nine required tags.
191 static constexpr uint32_t kICCNumEntries = 9;
192
193 static constexpr uint32_t kTAG_desc = SkSetFourByteTag('d', 'e', 's', 'c');
194 static constexpr uint32_t kTAG_desc_Bytes = sizeof(kDescriptionTagHeader) +
195 kDescriptionTagBodySize;
196 static constexpr uint32_t kTAG_desc_Offset = kICCHeaderSize +
197 kICCNumEntries * kICCTagTableEntrySize;
198
199 static constexpr uint32_t kTAG_XYZ_Bytes = 20;
200 static constexpr uint32_t kTAG_rXYZ_Offset = kTAG_desc_Offset + kTAG_desc_Bytes;
201 static constexpr uint32_t kTAG_gXYZ_Offset = kTAG_rXYZ_Offset + kTAG_XYZ_Bytes;
202 static constexpr uint32_t kTAG_bXYZ_Offset = kTAG_gXYZ_Offset + kTAG_XYZ_Bytes;
203
204 static constexpr uint32_t kTAG_TRC_Bytes = 40;
205 static constexpr uint32_t kTAG_rTRC_Offset = kTAG_bXYZ_Offset + kTAG_XYZ_Bytes;
206 static constexpr uint32_t kTAG_gTRC_Offset = kTAG_rTRC_Offset;
207 static constexpr uint32_t kTAG_bTRC_Offset = kTAG_rTRC_Offset;
208
209 static constexpr uint32_t kTAG_wtpt = SkSetFourByteTag('w', 't', 'p', 't');
210 static constexpr uint32_t kTAG_wtpt_Offset = kTAG_bTRC_Offset + kTAG_TRC_Bytes;
211
212 static constexpr uint32_t kTAG_cprt = SkSetFourByteTag('c', 'p', 'r', 't');
213 static constexpr uint32_t kTAG_cprt_Bytes = sizeof(kCopyrightTagHeader) +
214 sizeof(kCopyrightTagBody);
215 static constexpr uint32_t kTAG_cprt_Offset = kTAG_wtpt_Offset + kTAG_XYZ_Bytes;
216
217 static constexpr uint32_t kICCProfileSize = kTAG_cprt_Offset + kTAG_cprt_Bytes;
218
219 static constexpr uint32_t kICCHeader[kICCHeaderSize / 4] {
220 SkEndian_SwapBE32(kICCProfileSize), // Size of the profile
221 0, // Preferred CMM type (ignored)
222 SkEndian_SwapBE32(0x02100000), // Version 2.1
223 SkEndian_SwapBE32(kDisplay_Profile), // Display device profile
224 SkEndian_SwapBE32(kRGB_ColorSpace), // RGB input color space
225 SkEndian_SwapBE32(kXYZ_PCSSpace), // XYZ profile connection space
226 0, 0, 0, // Date and time (ignored)
227 SkEndian_SwapBE32(kACSP_Signature), // Profile signature
228 0, // Platform target (ignored)
229 0x00000000, // Flags: not embedded, can be used independently
230 0, // Device manufacturer (ignored)
231 0, // Device model (ignored)
232 0, 0, // Device attributes (ignored)
233 SkEndian_SwapBE32(1), // Relative colorimetric rendering intent
234 SkEndian_SwapBE32(0x0000f6d6), // D50 standard illuminant (X)
235 SkEndian_SwapBE32(0x00010000), // D50 standard illuminant (Y)
236 SkEndian_SwapBE32(0x0000d32d), // D50 standard illuminant (Z)
237 0, // Profile creator (ignored)
238 0, 0, 0, 0, // Profile id checksum (ignored)
239 0, 0, 0, 0, 0, 0, 0, // Reserved (ignored)
240 SkEndian_SwapBE32(kICCNumEntries), // Number of tags
241 };
242
243 static constexpr uint32_t kICCTagTable[3 * kICCNumEntries] {
244 // Profile description
245 SkEndian_SwapBE32(kTAG_desc),
246 SkEndian_SwapBE32(kTAG_desc_Offset),
247 SkEndian_SwapBE32(kTAG_desc_Bytes),
248
249 // rXYZ
250 SkEndian_SwapBE32(kTAG_rXYZ),
251 SkEndian_SwapBE32(kTAG_rXYZ_Offset),
252 SkEndian_SwapBE32(kTAG_XYZ_Bytes),
253
254 // gXYZ
255 SkEndian_SwapBE32(kTAG_gXYZ),
256 SkEndian_SwapBE32(kTAG_gXYZ_Offset),
257 SkEndian_SwapBE32(kTAG_XYZ_Bytes),
258
259 // bXYZ
260 SkEndian_SwapBE32(kTAG_bXYZ),
261 SkEndian_SwapBE32(kTAG_bXYZ_Offset),
262 SkEndian_SwapBE32(kTAG_XYZ_Bytes),
263
264 // rTRC
265 SkEndian_SwapBE32(kTAG_rTRC),
266 SkEndian_SwapBE32(kTAG_rTRC_Offset),
267 SkEndian_SwapBE32(kTAG_TRC_Bytes),
268
269 // gTRC
270 SkEndian_SwapBE32(kTAG_gTRC),
271 SkEndian_SwapBE32(kTAG_gTRC_Offset),
272 SkEndian_SwapBE32(kTAG_TRC_Bytes),
273
274 // bTRC
275 SkEndian_SwapBE32(kTAG_bTRC),
276 SkEndian_SwapBE32(kTAG_bTRC_Offset),
277 SkEndian_SwapBE32(kTAG_TRC_Bytes),
278
279 // White point
280 SkEndian_SwapBE32(kTAG_wtpt),
281 SkEndian_SwapBE32(kTAG_wtpt_Offset),
282 SkEndian_SwapBE32(kTAG_XYZ_Bytes),
283
284 // Copyright
285 SkEndian_SwapBE32(kTAG_cprt),
286 SkEndian_SwapBE32(kTAG_cprt_Offset),
287 SkEndian_SwapBE32(kTAG_cprt_Bytes),
288 };
289
write_xyz_tag(uint32_t * ptr,const SkMatrix44 & toXYZ,int col)290 static void write_xyz_tag(uint32_t* ptr, const SkMatrix44& toXYZ, int col) {
291 ptr[0] = SkEndian_SwapBE32(kXYZ_PCSSpace);
292 ptr[1] = 0;
293 ptr[2] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(0, col)));
294 ptr[3] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(1, col)));
295 ptr[4] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(2, col)));
296 }
297
write_trc_tag(uint32_t * ptr,const SkColorSpaceTransferFn & fn)298 static void write_trc_tag(uint32_t* ptr, const SkColorSpaceTransferFn& fn) {
299 ptr[0] = SkEndian_SwapBE32(kTAG_ParaCurveType);
300 ptr[1] = 0;
301 ptr[2] = (uint32_t) (SkEndian_SwapBE16(kGABCDEF_ParaCurveType));
302 ptr[3] = SkEndian_SwapBE32(SkFloatToFixed(fn.fG));
303 ptr[4] = SkEndian_SwapBE32(SkFloatToFixed(fn.fA));
304 ptr[5] = SkEndian_SwapBE32(SkFloatToFixed(fn.fB));
305 ptr[6] = SkEndian_SwapBE32(SkFloatToFixed(fn.fC));
306 ptr[7] = SkEndian_SwapBE32(SkFloatToFixed(fn.fD));
307 ptr[8] = SkEndian_SwapBE32(SkFloatToFixed(fn.fE));
308 ptr[9] = SkEndian_SwapBE32(SkFloatToFixed(fn.fF));
309 }
310
is_3x3(const SkMatrix44 & toXYZD50)311 static bool is_3x3(const SkMatrix44& toXYZD50) {
312 return 0.0f == toXYZD50.get(3, 0) && 0.0f == toXYZD50.get(3, 1) && 0.0f == toXYZD50.get(3, 2) &&
313 0.0f == toXYZD50.get(0, 3) && 0.0f == toXYZD50.get(1, 3) && 0.0f == toXYZD50.get(2, 3) &&
314 1.0f == toXYZD50.get(3, 3);
315 }
316
nearly_equal(float x,float y)317 static bool nearly_equal(float x, float y) {
318 // A note on why I chose this tolerance: transfer_fn_almost_equal() uses a
319 // tolerance of 0.001f, which doesn't seem to be enough to distinguish
320 // between similar transfer functions, for example: gamma2.2 and sRGB.
321 //
322 // If the tolerance is 0.0f, then this we can't distinguish between two
323 // different encodings of what is clearly the same colorspace. Some
324 // experimentation with example files lead to this number:
325 static constexpr float kTolerance = 1.0f / (1 << 11);
326 return ::fabsf(x - y) <= kTolerance;
327 }
328
nearly_equal(const SkColorSpaceTransferFn & u,const SkColorSpaceTransferFn & v)329 static bool nearly_equal(const SkColorSpaceTransferFn& u,
330 const SkColorSpaceTransferFn& v) {
331 return nearly_equal(u.fG, v.fG)
332 && nearly_equal(u.fA, v.fA)
333 && nearly_equal(u.fB, v.fB)
334 && nearly_equal(u.fC, v.fC)
335 && nearly_equal(u.fD, v.fD)
336 && nearly_equal(u.fE, v.fE)
337 && nearly_equal(u.fF, v.fF);
338 }
339
nearly_equal(const SkMatrix44 & toXYZD50,const float standard[9])340 static bool nearly_equal(const SkMatrix44& toXYZD50, const float standard[9]) {
341 return nearly_equal(toXYZD50.getFloat(0, 0), standard[0])
342 && nearly_equal(toXYZD50.getFloat(0, 1), standard[1])
343 && nearly_equal(toXYZD50.getFloat(0, 2), standard[2])
344 && nearly_equal(toXYZD50.getFloat(1, 0), standard[3])
345 && nearly_equal(toXYZD50.getFloat(1, 1), standard[4])
346 && nearly_equal(toXYZD50.getFloat(1, 2), standard[5])
347 && nearly_equal(toXYZD50.getFloat(2, 0), standard[6])
348 && nearly_equal(toXYZD50.getFloat(2, 1), standard[7])
349 && nearly_equal(toXYZD50.getFloat(2, 2), standard[8])
350 && nearly_equal(toXYZD50.getFloat(0, 3), 0.0f)
351 && nearly_equal(toXYZD50.getFloat(1, 3), 0.0f)
352 && nearly_equal(toXYZD50.getFloat(2, 3), 0.0f)
353 && nearly_equal(toXYZD50.getFloat(3, 0), 0.0f)
354 && nearly_equal(toXYZD50.getFloat(3, 1), 0.0f)
355 && nearly_equal(toXYZD50.getFloat(3, 2), 0.0f)
356 && nearly_equal(toXYZD50.getFloat(3, 3), 1.0f);
357 }
358
359 // Return nullptr if the color profile doen't have a special name.
get_color_profile_description(const SkColorSpaceTransferFn & fn,const SkMatrix44 & toXYZD50)360 const char* get_color_profile_description(const SkColorSpaceTransferFn& fn,
361 const SkMatrix44& toXYZD50) {
362 bool srgb_xfer = nearly_equal(fn, gSRGB_TransferFn);
363 bool srgb_gamut = nearly_equal(toXYZD50, gSRGB_toXYZD50);
364 if (srgb_xfer && srgb_gamut) {
365 return "sRGB";
366 }
367 bool line_xfer = nearly_equal(fn, gLinear_TransferFn);
368 if (line_xfer && srgb_gamut) {
369 return "Linear Transfer with sRGB Gamut";
370 }
371 bool twoDotTwo = nearly_equal(fn, g2Dot2_TransferFn);
372 if (twoDotTwo && srgb_gamut) {
373 return "2.2 Transfer with sRGB Gamut";
374 }
375 if (twoDotTwo && nearly_equal(toXYZD50, gAdobeRGB_toXYZD50)) {
376 return "AdobeRGB";
377 }
378 bool dcip3_gamut = nearly_equal(toXYZD50, gDCIP3_toXYZD50);
379 if (srgb_xfer || line_xfer) {
380 if (srgb_xfer && dcip3_gamut) {
381 return "sRGB Transfer with DCI-P3 Gamut";
382 }
383 if (line_xfer && dcip3_gamut) {
384 return "Linear Transfer with DCI-P3 Gamut";
385 }
386 bool rec2020 = nearly_equal(toXYZD50, gRec2020_toXYZD50);
387 if (srgb_xfer && rec2020) {
388 return "sRGB Transfer with Rec-BT-2020 Gamut";
389 }
390 if (line_xfer && rec2020) {
391 return "Linear Transfer with Rec-BT-2020 Gamut";
392 }
393 }
394 if (dcip3_gamut && nearly_equal(fn, gDCIP3_TransferFn)) {
395 return "DCI-P3";
396 }
397 return nullptr;
398 }
399
get_color_profile_tag(char dst[kICCDescriptionTagSize],const SkColorSpaceTransferFn & fn,const SkMatrix44 & toXYZD50)400 static void get_color_profile_tag(char dst[kICCDescriptionTagSize],
401 const SkColorSpaceTransferFn& fn,
402 const SkMatrix44& toXYZD50) {
403 SkASSERT(dst);
404 if (const char* description = get_color_profile_description(fn, toXYZD50)) {
405 SkASSERT(strlen(description) < kICCDescriptionTagSize);
406 strncpy(dst, description, kICCDescriptionTagSize);
407 // "If the length of src is less than n, strncpy() writes additional
408 // null bytes to dest to ensure that a total of n bytes are written."
409 } else {
410 strncpy(dst, kDescriptionTagBodyPrefix, sizeof(kDescriptionTagBodyPrefix));
411 SkMD5 md5;
412 for (int i = 0; i < 3; ++i) {
413 for (int j = 0; j < 3; ++j) {
414 float value = toXYZD50.getFloat(i,j);
415 md5.write(&value, sizeof(value));
416 }
417 }
418 static_assert(sizeof(fn) == sizeof(float) * 7, "packed");
419 md5.write(&fn, sizeof(fn));
420 SkMD5::Digest digest;
421 md5.finish(digest);
422 char* ptr = dst + sizeof(kDescriptionTagBodyPrefix);
423 for (unsigned i = 0; i < sizeof(SkMD5::Digest); ++i) {
424 uint8_t byte = digest.data[i];
425 *ptr++ = SkHexadecimalDigits::gUpper[byte >> 4];
426 *ptr++ = SkHexadecimalDigits::gUpper[byte & 0xF];
427 }
428 SkASSERT(ptr == dst + kICCDescriptionTagSize);
429 }
430 }
431
SkICCGetColorProfileTag(const SkColorSpaceTransferFn & fn,const SkMatrix44 & toXYZD50)432 SkString SkICCGetColorProfileTag(const SkColorSpaceTransferFn& fn,
433 const SkMatrix44& toXYZD50) {
434 char tag[kICCDescriptionTagSize];
435 get_color_profile_tag(tag, fn, toXYZD50);
436 size_t len = kICCDescriptionTagSize;
437 while (len > 0 && tag[len - 1] == '\0') {
438 --len; // tag is padded out with zeros
439 }
440 SkASSERT(len != 0);
441 return SkString(tag, len);
442 }
443
444 // returns pointer just beyond where we just wrote.
string_copy_ascii_to_utf16be(uint8_t * dst,const char * src,size_t count)445 static uint8_t* string_copy_ascii_to_utf16be(uint8_t* dst, const char* src, size_t count) {
446 while (count-- > 0) {
447 *dst++ = 0;
448 *dst++ = (uint8_t)(*src++);
449 }
450 return dst;
451 }
452
WriteToICC(const SkColorSpaceTransferFn & fn,const SkMatrix44 & toXYZD50)453 sk_sp<SkData> SkICC::WriteToICC(const SkColorSpaceTransferFn& fn, const SkMatrix44& toXYZD50) {
454 if (!is_3x3(toXYZD50) || !is_valid_transfer_fn(fn)) {
455 return nullptr;
456 }
457
458 SkAutoMalloc profile(kICCProfileSize);
459 uint8_t* ptr = (uint8_t*) profile.get();
460
461 // Write profile header
462 memcpy(ptr, kICCHeader, sizeof(kICCHeader));
463 ptr += sizeof(kICCHeader);
464
465 // Write tag table
466 memcpy(ptr, kICCTagTable, sizeof(kICCTagTable));
467 ptr += sizeof(kICCTagTable);
468
469 // Write profile description tag
470 memcpy(ptr, kDescriptionTagHeader, sizeof(kDescriptionTagHeader));
471 ptr += sizeof(kDescriptionTagHeader);
472 {
473 char colorProfileTag[kICCDescriptionTagSize];
474 get_color_profile_tag(colorProfileTag, fn, toXYZD50);
475 ptr = string_copy_ascii_to_utf16be(ptr, colorProfileTag, kICCDescriptionTagSize);
476 }
477
478 // Write XYZ tags
479 write_xyz_tag((uint32_t*) ptr, toXYZD50, 0);
480 ptr += kTAG_XYZ_Bytes;
481 write_xyz_tag((uint32_t*) ptr, toXYZD50, 1);
482 ptr += kTAG_XYZ_Bytes;
483 write_xyz_tag((uint32_t*) ptr, toXYZD50, 2);
484 ptr += kTAG_XYZ_Bytes;
485
486 // Write TRC tag
487 write_trc_tag((uint32_t*) ptr, fn);
488 ptr += kTAG_TRC_Bytes;
489
490 // Write white point tag (must be D50)
491 memcpy(ptr, kWhitePointTag, sizeof(kWhitePointTag));
492 ptr += sizeof(kWhitePointTag);
493
494 // Write copyright tag
495 memcpy(ptr, kCopyrightTagHeader, sizeof(kCopyrightTagHeader));
496 ptr += sizeof(kCopyrightTagHeader);
497 memcpy(ptr, kCopyrightTagBody, sizeof(kCopyrightTagBody));
498 ptr += sizeof(kCopyrightTagBody);
499
500 SkASSERT(kICCProfileSize == ptr - (uint8_t*) profile.get());
501 return SkData::MakeFromMalloc(profile.release(), kICCProfileSize);
502 }
503