blob: e4be8f4e4e6c92623cd31ef076d9f210dc70e048 [file] [log] [blame]
msarett085cad42016-06-28 07:16:40 -07001/*
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 "SkColorSpace.h"
9#include "SkColorSpace_Base.h"
10#include "SkColorSpacePriv.h"
11#include "SkEndian.h"
12#include "SkFixed.h"
13#include "SkTemplates.h"
14
15#define return_if_false(pred, msg) \
16 do { \
17 if (!(pred)) { \
18 SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \
19 return false; \
20 } \
21 } while (0)
22
23#define return_null(msg) \
24 do { \
25 SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \
26 return nullptr; \
27 } while (0)
28
29static uint16_t read_big_endian_short(const uint8_t* ptr) {
30 return ptr[0] << 8 | ptr[1];
31}
32
33static uint32_t read_big_endian_uint(const uint8_t* ptr) {
34 return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
35}
36
37static int32_t read_big_endian_int(const uint8_t* ptr) {
38 return (int32_t) read_big_endian_uint(ptr);
39}
40
41// This is equal to the header size according to the ICC specification (128)
42// plus the size of the tag count (4). We include the tag count since we
43// always require it to be present anyway.
44static constexpr size_t kICCHeaderSize = 132;
45
46// Contains a signature (4), offset (4), and size (4).
47static constexpr size_t kICCTagTableEntrySize = 12;
48
49static constexpr uint32_t kRGB_ColorSpace = SkSetFourByteTag('R', 'G', 'B', ' ');
50static constexpr uint32_t kDisplay_Profile = SkSetFourByteTag('m', 'n', 't', 'r');
51static constexpr uint32_t kInput_Profile = SkSetFourByteTag('s', 'c', 'n', 'r');
52static constexpr uint32_t kOutput_Profile = SkSetFourByteTag('p', 'r', 't', 'r');
53static constexpr uint32_t kXYZ_PCSSpace = SkSetFourByteTag('X', 'Y', 'Z', ' ');
54static constexpr uint32_t kACSP_Signature = SkSetFourByteTag('a', 'c', 's', 'p');
55
56struct ICCProfileHeader {
57 uint32_t fSize;
58
59 // No reason to care about the preferred color management module (ex: Adobe, Apple, etc.).
60 // We're always going to use this one.
61 uint32_t fCMMType_ignored;
62
63 uint32_t fVersion;
64 uint32_t fProfileClass;
65 uint32_t fInputColorSpace;
66 uint32_t fPCS;
67 uint32_t fDateTime_ignored[3];
68 uint32_t fSignature;
69
70 // Indicates the platform that this profile was created for (ex: Apple, Microsoft). This
71 // doesn't really matter to us.
72 uint32_t fPlatformTarget_ignored;
73
74 // Flags can indicate:
75 // (1) Whether this profile was embedded in a file. This flag is consistently wrong.
76 // Ex: The profile came from a file but indicates that it did not.
77 // (2) Whether we are allowed to use the profile independently of the color data. If set,
78 // this may allow us to use the embedded profile for testing separate from the original
79 // image.
80 uint32_t fFlags_ignored;
81
82 // We support many output devices. It doesn't make sense to think about the attributes of
83 // the device in the context of the image profile.
84 uint32_t fDeviceManufacturer_ignored;
85 uint32_t fDeviceModel_ignored;
86 uint32_t fDeviceAttributes_ignored[2];
87
88 uint32_t fRenderingIntent;
89 int32_t fIlluminantXYZ[3];
90
91 // We don't care who created the profile.
92 uint32_t fCreator_ignored;
93
94 // This is an MD5 checksum. Could be useful for checking if profiles are equal.
95 uint32_t fProfileId_ignored[4];
96
97 // Reserved for future use.
98 uint32_t fReserved_ignored[7];
99
100 uint32_t fTagCount;
101
102 void init(const uint8_t* src, size_t len) {
103 SkASSERT(kICCHeaderSize == sizeof(*this));
104
105 uint32_t* dst = (uint32_t*) this;
106 for (uint32_t i = 0; i < kICCHeaderSize / 4; i++, src+=4) {
107 dst[i] = read_big_endian_uint(src);
108 }
109 }
110
111 bool valid() const {
112 return_if_false(fSize >= kICCHeaderSize, "Size is too small");
113
114 uint8_t majorVersion = fVersion >> 24;
115 return_if_false(majorVersion <= 4, "Unsupported version");
116
117 // These are the three basic classes of profiles that we might expect to see embedded
118 // in images. Four additional classes exist, but they generally are used as a convenient
119 // way for CMMs to store calculated transforms.
120 return_if_false(fProfileClass == kDisplay_Profile ||
121 fProfileClass == kInput_Profile ||
122 fProfileClass == kOutput_Profile,
123 "Unsupported profile");
124
125 // TODO (msarett):
126 // All the profiles we've tested so far use RGB as the input color space.
127 return_if_false(fInputColorSpace == kRGB_ColorSpace, "Unsupported color space");
128
129 // TODO (msarett):
130 // All the profiles we've tested so far use XYZ as the profile connection space.
131 return_if_false(fPCS == kXYZ_PCSSpace, "Unsupported PCS space");
132
133 return_if_false(fSignature == kACSP_Signature, "Bad signature");
134
135 // TODO (msarett):
136 // Should we treat different rendering intents differently?
137 // Valid rendering intents include kPerceptual (0), kRelative (1),
138 // kSaturation (2), and kAbsolute (3).
139 return_if_false(fRenderingIntent <= 3, "Bad rendering intent");
140
141 return_if_false(color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[0]), 0.96420f) &&
142 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[1]), 1.00000f) &&
143 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[2]), 0.82491f),
144 "Illuminant must be D50");
145
146 return_if_false(fTagCount <= 100, "Too many tags");
147
148 return true;
149 }
150};
151
152template <class T>
153static bool safe_add(T arg1, T arg2, size_t* result) {
154 SkASSERT(arg1 >= 0);
155 SkASSERT(arg2 >= 0);
156 if (arg1 >= 0 && arg2 <= std::numeric_limits<T>::max() - arg1) {
157 T sum = arg1 + arg2;
158 if (sum <= std::numeric_limits<size_t>::max()) {
159 *result = static_cast<size_t>(sum);
160 return true;
161 }
162 }
163 return false;
164}
165
166static bool safe_mul(uint32_t arg1, uint32_t arg2, uint32_t* result) {
167 uint64_t product64 = (uint64_t) arg1 * (uint64_t) arg2;
168 uint32_t product32 = (uint32_t) product64;
169 if (product32 != product64) {
170 return false;
171 }
172
173 *result = product32;
174 return true;
175}
176
177struct ICCTag {
178 uint32_t fSignature;
179 uint32_t fOffset;
180 uint32_t fLength;
181
182 const uint8_t* init(const uint8_t* src) {
183 fSignature = read_big_endian_uint(src);
184 fOffset = read_big_endian_uint(src + 4);
185 fLength = read_big_endian_uint(src + 8);
186 return src + 12;
187 }
188
189 bool valid(size_t len) {
190 size_t tagEnd;
191 return_if_false(safe_add(fOffset, fLength, &tagEnd),
192 "Tag too large, overflows integer addition");
193 return_if_false(tagEnd <= len, "Tag too large for ICC profile");
194 return true;
195 }
196
197 const uint8_t* addr(const uint8_t* src) const {
198 return src + fOffset;
199 }
200
201 static const ICCTag* Find(const ICCTag tags[], int count, uint32_t signature) {
202 for (int i = 0; i < count; ++i) {
203 if (tags[i].fSignature == signature) {
204 return &tags[i];
205 }
206 }
207 return nullptr;
208 }
209};
210
211static constexpr uint32_t kTAG_rXYZ = SkSetFourByteTag('r', 'X', 'Y', 'Z');
212static constexpr uint32_t kTAG_gXYZ = SkSetFourByteTag('g', 'X', 'Y', 'Z');
213static constexpr uint32_t kTAG_bXYZ = SkSetFourByteTag('b', 'X', 'Y', 'Z');
214static constexpr uint32_t kTAG_rTRC = SkSetFourByteTag('r', 'T', 'R', 'C');
215static constexpr uint32_t kTAG_gTRC = SkSetFourByteTag('g', 'T', 'R', 'C');
216static constexpr uint32_t kTAG_bTRC = SkSetFourByteTag('b', 'T', 'R', 'C');
217static constexpr uint32_t kTAG_A2B0 = SkSetFourByteTag('A', '2', 'B', '0');
218
219static bool load_xyz(float dst[3], const uint8_t* src, size_t len) {
220 if (len < 20) {
221 SkColorSpacePrintf("XYZ tag is too small (%d bytes)", len);
222 return false;
223 }
224
225 dst[0] = SkFixedToFloat(read_big_endian_int(src + 8));
226 dst[1] = SkFixedToFloat(read_big_endian_int(src + 12));
227 dst[2] = SkFixedToFloat(read_big_endian_int(src + 16));
228 SkColorSpacePrintf("XYZ %g %g %g\n", dst[0], dst[1], dst[2]);
229 return true;
230}
231
232static constexpr uint32_t kTAG_CurveType = SkSetFourByteTag('c', 'u', 'r', 'v');
233static constexpr uint32_t kTAG_ParaCurveType = SkSetFourByteTag('p', 'a', 'r', 'a');
234
235static bool load_gammas(SkGammaCurve* gammas, uint32_t numGammas, const uint8_t* src, size_t len) {
236 for (uint32_t i = 0; i < numGammas; i++) {
237 if (len < 12) {
238 // FIXME (msarett):
239 // We could potentially return false here after correctly parsing *some* of the
240 // gammas correctly. Should we somehow try to indicate a partial success?
241 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
242 return false;
243 }
244
245 // We need to count the number of bytes in the tag, so we are able to move to the
246 // next tag on the next loop iteration.
247 size_t tagBytes;
248
249 uint32_t type = read_big_endian_uint(src);
250 switch (type) {
251 case kTAG_CurveType: {
252 uint32_t count = read_big_endian_uint(src + 8);
253
254 // tagBytes = 12 + 2 * count
255 // We need to do safe addition here to avoid integer overflow.
256 if (!safe_add(count, count, &tagBytes) ||
257 !safe_add((size_t) 12, tagBytes, &tagBytes))
258 {
259 SkColorSpacePrintf("Invalid gamma count");
260 return false;
261 }
262
263 if (0 == count) {
264 // Some tags require a gamma curve, but the author doesn't actually want
265 // to transform the data. In this case, it is common to see a curve with
266 // a count of 0.
267 gammas[i].fNamed = SkColorSpace::kLinear_GammaNamed;
268 break;
269 } else if (len < tagBytes) {
270 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
271 return false;
272 }
273
274 const uint16_t* table = (const uint16_t*) (src + 12);
275 if (1 == count) {
276 // The table entry is the gamma (with a bias of 256).
277 float value = (read_big_endian_short((const uint8_t*) table)) / 256.0f;
278 set_gamma_value(&gammas[i], value);
279 SkColorSpacePrintf("gamma %g\n", value);
280 break;
281 }
282
283 // Check for frequently occurring sRGB curves.
284 // We do this by sampling a few values and see if they match our expectation.
285 // A more robust solution would be to compare each value in this curve against
286 // an sRGB curve to see if we remain below an error threshold. At this time,
287 // we haven't seen any images in the wild that make this kind of
288 // calculation necessary. We encounter identical gamma curves over and
289 // over again, but relatively few variations.
290 if (1024 == count) {
291 // The magic values were chosen because they match a very common sRGB
292 // gamma table and the less common Canon sRGB gamma table (which use
293 // different rounding rules).
294 if (0 == read_big_endian_short((const uint8_t*) &table[0]) &&
295 3366 == read_big_endian_short((const uint8_t*) &table[257]) &&
296 14116 == read_big_endian_short((const uint8_t*) &table[513]) &&
297 34318 == read_big_endian_short((const uint8_t*) &table[768]) &&
298 65535 == read_big_endian_short((const uint8_t*) &table[1023])) {
299 gammas[i].fNamed = SkColorSpace::kSRGB_GammaNamed;
300 break;
301 }
302 } else if (26 == count) {
303 // The magic values were chosen because they match a very common sRGB
304 // gamma table.
305 if (0 == read_big_endian_short((const uint8_t*) &table[0]) &&
306 3062 == read_big_endian_short((const uint8_t*) &table[6]) &&
307 12824 == read_big_endian_short((const uint8_t*) &table[12]) &&
308 31237 == read_big_endian_short((const uint8_t*) &table[18]) &&
309 65535 == read_big_endian_short((const uint8_t*) &table[25])) {
310 gammas[i].fNamed = SkColorSpace::kSRGB_GammaNamed;
311 break;
312 }
313 } else if (4096 == count) {
314 // The magic values were chosen because they match Nikon, Epson, and
315 // LCMS sRGB gamma tables (all of which use different rounding rules).
316 if (0 == read_big_endian_short((const uint8_t*) &table[0]) &&
317 950 == read_big_endian_short((const uint8_t*) &table[515]) &&
318 3342 == read_big_endian_short((const uint8_t*) &table[1025]) &&
319 14079 == read_big_endian_short((const uint8_t*) &table[2051]) &&
320 65535 == read_big_endian_short((const uint8_t*) &table[4095])) {
321 gammas[i].fNamed = SkColorSpace::kSRGB_GammaNamed;
322 break;
323 }
324 }
325
326 // Otherwise, fill in the interpolation table.
327 gammas[i].fTableSize = count;
328 gammas[i].fTable = std::unique_ptr<float[]>(new float[count]);
329 for (uint32_t j = 0; j < count; j++) {
330 gammas[i].fTable[j] =
331 (read_big_endian_short((const uint8_t*) &table[j])) / 65535.0f;
332 }
333 break;
334 }
335 case kTAG_ParaCurveType: {
336 enum ParaCurveType {
337 kExponential_ParaCurveType = 0,
338 kGAB_ParaCurveType = 1,
339 kGABC_ParaCurveType = 2,
340 kGABDE_ParaCurveType = 3,
341 kGABCDEF_ParaCurveType = 4,
342 };
343
344 // Determine the format of the parametric curve tag.
345 uint16_t format = read_big_endian_short(src + 8);
346 if (kExponential_ParaCurveType == format) {
347 tagBytes = 12 + 4;
348 if (len < tagBytes) {
349 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
350 return false;
351 }
352
353 // Y = X^g
354 int32_t g = read_big_endian_int(src + 12);
355 set_gamma_value(&gammas[i], SkFixedToFloat(g));
356 } else {
357 // Here's where the real parametric gammas start. There are many
358 // permutations of the same equations.
359 //
360 // Y = (aX + b)^g + c for X >= d
361 // Y = eX + f otherwise
362 //
363 // We will fill in with zeros as necessary to always match the above form.
364 float g = 0.0f, a = 0.0f, b = 0.0f, c = 0.0f, d = 0.0f, e = 0.0f, f = 0.0f;
365 switch(format) {
366 case kGAB_ParaCurveType: {
367 tagBytes = 12 + 12;
368 if (len < tagBytes) {
369 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
370 return false;
371 }
372
373 // Y = (aX + b)^g for X >= -b/a
374 // Y = 0 otherwise
375 g = SkFixedToFloat(read_big_endian_int(src + 12));
376 a = SkFixedToFloat(read_big_endian_int(src + 16));
377 if (0.0f == a) {
378 return false;
379 }
380
381 b = SkFixedToFloat(read_big_endian_int(src + 20));
382 d = -b / a;
383 break;
384 }
385 case kGABC_ParaCurveType:
386 tagBytes = 12 + 16;
387 if (len < tagBytes) {
388 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
389 return false;
390 }
391
392 // Y = (aX + b)^g + c for X >= -b/a
393 // Y = c otherwise
394 g = SkFixedToFloat(read_big_endian_int(src + 12));
395 a = SkFixedToFloat(read_big_endian_int(src + 16));
396 if (0.0f == a) {
397 return false;
398 }
399
400 b = SkFixedToFloat(read_big_endian_int(src + 20));
401 c = SkFixedToFloat(read_big_endian_int(src + 24));
402 d = -b / a;
403 f = c;
404 break;
405 case kGABDE_ParaCurveType:
406 tagBytes = 12 + 20;
407 if (len < tagBytes) {
408 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
409 return false;
410 }
411
412 // Y = (aX + b)^g for X >= d
413 // Y = cX otherwise
414 g = SkFixedToFloat(read_big_endian_int(src + 12));
415 a = SkFixedToFloat(read_big_endian_int(src + 16));
416 b = SkFixedToFloat(read_big_endian_int(src + 20));
417 d = SkFixedToFloat(read_big_endian_int(src + 28));
418 e = SkFixedToFloat(read_big_endian_int(src + 24));
419 break;
420 case kGABCDEF_ParaCurveType:
421 tagBytes = 12 + 28;
422 if (len < tagBytes) {
423 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
424 return false;
425 }
426
427 // Y = (aX + b)^g + c for X >= d
428 // Y = eX + f otherwise
429 // NOTE: The ICC spec writes "cX" in place of "eX" but I think
430 // it's a typo.
431 g = SkFixedToFloat(read_big_endian_int(src + 12));
432 a = SkFixedToFloat(read_big_endian_int(src + 16));
433 b = SkFixedToFloat(read_big_endian_int(src + 20));
434 c = SkFixedToFloat(read_big_endian_int(src + 24));
435 d = SkFixedToFloat(read_big_endian_int(src + 28));
436 e = SkFixedToFloat(read_big_endian_int(src + 32));
437 f = SkFixedToFloat(read_big_endian_int(src + 36));
438 break;
439 default:
440 SkColorSpacePrintf("Invalid parametric curve type\n");
441 return false;
442 }
443
444 // Recognize and simplify a very common parametric representation of sRGB gamma.
445 if (color_space_almost_equal(0.9479f, a) &&
446 color_space_almost_equal(0.0521f, b) &&
447 color_space_almost_equal(0.0000f, c) &&
448 color_space_almost_equal(0.0405f, d) &&
449 color_space_almost_equal(0.0774f, e) &&
450 color_space_almost_equal(0.0000f, f) &&
451 color_space_almost_equal(2.4000f, g)) {
452 gammas[i].fNamed = SkColorSpace::kSRGB_GammaNamed;
453 } else {
454 // Fail on invalid gammas.
455 if (d <= 0.0f) {
456 // Y = (aX + b)^g + c for always
457 if (0.0f == a || 0.0f == g) {
458 SkColorSpacePrintf("A or G is zero, constant gamma function "
459 "is nonsense");
460 return false;
461 }
462 } else if (d >= 1.0f) {
463 // Y = eX + f for always
464 if (0.0f == e) {
465 SkColorSpacePrintf("E is zero, constant gamma function is "
466 "nonsense");
467 return false;
468 }
469 } else if ((0.0f == a || 0.0f == g) && 0.0f == e) {
470 SkColorSpacePrintf("A or G, and E are zero, constant gamma function "
471 "is nonsense");
472 return false;
473 }
474
475 gammas[i].fG = g;
476 gammas[i].fA = a;
477 gammas[i].fB = b;
478 gammas[i].fC = c;
479 gammas[i].fD = d;
480 gammas[i].fE = e;
481 gammas[i].fF = f;
482 }
483 }
484
485 break;
486 }
487 default:
488 SkColorSpacePrintf("Unsupported gamma tag type %d\n", type);
489 return false;
490 }
491
492 // Ensure that we have successfully read a gamma representation.
493 SkASSERT(gammas[i].isNamed() || gammas[i].isValue() || gammas[i].isTable() ||
494 gammas[i].isParametric());
495
496 // Adjust src and len if there is another gamma curve to load.
497 if (i != numGammas - 1) {
498 // Each curve is padded to 4-byte alignment.
499 tagBytes = SkAlign4(tagBytes);
500 if (len < tagBytes) {
501 return false;
502 }
503
504 src += tagBytes;
505 len -= tagBytes;
506 }
507 }
508
509 return true;
510}
511
512static constexpr uint32_t kTAG_AtoBType = SkSetFourByteTag('m', 'A', 'B', ' ');
513
514bool load_color_lut(SkColorLookUpTable* colorLUT, uint32_t inputChannels, uint32_t outputChannels,
515 const uint8_t* src, size_t len) {
516 // 16 bytes reserved for grid points, 2 for precision, 2 for padding.
517 // The color LUT data follows after this header.
518 static constexpr uint32_t kColorLUTHeaderSize = 20;
519 if (len < kColorLUTHeaderSize) {
520 SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", len);
521 return false;
522 }
523 size_t dataLen = len - kColorLUTHeaderSize;
524
525 SkASSERT(3 == inputChannels && 3 == outputChannels);
526 colorLUT->fInputChannels = inputChannels;
527 colorLUT->fOutputChannels = outputChannels;
528 uint32_t numEntries = 1;
529 for (uint32_t i = 0; i < inputChannels; i++) {
530 colorLUT->fGridPoints[i] = src[i];
531 if (0 == src[i]) {
532 SkColorSpacePrintf("Each input channel must have at least one grid point.");
533 return false;
534 }
535
536 if (!safe_mul(numEntries, src[i], &numEntries)) {
537 SkColorSpacePrintf("Too many entries in Color LUT.");
538 return false;
539 }
540 }
541
542 if (!safe_mul(numEntries, outputChannels, &numEntries)) {
543 SkColorSpacePrintf("Too many entries in Color LUT.");
544 return false;
545 }
546
547 // Space is provided for a maximum of the 16 input channels. Now we determine the precision
548 // of the table values.
549 uint8_t precision = src[16];
550 switch (precision) {
551 case 1: // 8-bit data
552 case 2: // 16-bit data
553 break;
554 default:
555 SkColorSpacePrintf("Color LUT precision must be 8-bit or 16-bit.\n");
556 return false;
557 }
558
559 uint32_t clutBytes;
560 if (!safe_mul(numEntries, precision, &clutBytes)) {
561 SkColorSpacePrintf("Too many entries in Color LUT.");
562 return false;
563 }
564
565 if (dataLen < clutBytes) {
566 SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", len);
567 return false;
568 }
569
570 // Movable struct colorLUT has ownership of fTable.
571 colorLUT->fTable = std::unique_ptr<float[]>(new float[numEntries]);
572 const uint8_t* ptr = src + kColorLUTHeaderSize;
573 for (uint32_t i = 0; i < numEntries; i++, ptr += precision) {
574 if (1 == precision) {
575 colorLUT->fTable[i] = ((float) ptr[i]) / 255.0f;
576 } else {
577 colorLUT->fTable[i] = ((float) read_big_endian_short(ptr)) / 65535.0f;
578 }
579 }
580
581 return true;
582}
583
584bool load_matrix(SkMatrix44* toXYZ, const uint8_t* src, size_t len) {
585 if (len < 48) {
586 SkColorSpacePrintf("Matrix tag is too small (%d bytes).", len);
587 return false;
588 }
589
590 // For this matrix to behave like our "to XYZ D50" matrices, it needs to be scaled.
591 constexpr float scale = 65535.0 / 32768.0;
592 float array[16];
593 array[ 0] = scale * SkFixedToFloat(read_big_endian_int(src));
594 array[ 1] = scale * SkFixedToFloat(read_big_endian_int(src + 4));
595 array[ 2] = scale * SkFixedToFloat(read_big_endian_int(src + 8));
596 array[ 3] = scale * SkFixedToFloat(read_big_endian_int(src + 36)); // translate R
597 array[ 4] = scale * SkFixedToFloat(read_big_endian_int(src + 12));
598 array[ 5] = scale * SkFixedToFloat(read_big_endian_int(src + 16));
599 array[ 6] = scale * SkFixedToFloat(read_big_endian_int(src + 20));
600 array[ 7] = scale * SkFixedToFloat(read_big_endian_int(src + 40)); // translate G
601 array[ 8] = scale * SkFixedToFloat(read_big_endian_int(src + 24));
602 array[ 9] = scale * SkFixedToFloat(read_big_endian_int(src + 28));
603 array[10] = scale * SkFixedToFloat(read_big_endian_int(src + 32));
604 array[11] = scale * SkFixedToFloat(read_big_endian_int(src + 44)); // translate B
605 array[12] = 0.0f;
606 array[13] = 0.0f;
607 array[14] = 0.0f;
608 array[15] = 1.0f;
609 toXYZ->setColMajorf(array);
610 return true;
611}
612
613bool load_a2b0(SkColorLookUpTable* colorLUT, SkGammaCurve* gammas, SkMatrix44* toXYZ,
614 const uint8_t* src, size_t len) {
615 if (len < 32) {
616 SkColorSpacePrintf("A to B tag is too small (%d bytes).", len);
617 return false;
618 }
619
620 uint32_t type = read_big_endian_uint(src);
621 if (kTAG_AtoBType != type) {
622 // FIXME (msarett): Need to support lut8Type and lut16Type.
623 SkColorSpacePrintf("Unsupported A to B tag type.\n");
624 return false;
625 }
626
627 // Read the number of channels. The four bytes that we skipped are reserved and
628 // must be zero.
629 uint8_t inputChannels = src[8];
630 uint8_t outputChannels = src[9];
631 if (3 != inputChannels || 3 != outputChannels) {
632 // We only handle (supposedly) RGB inputs and RGB outputs. The numbers of input
633 // channels and output channels both must be 3.
634 SkColorSpacePrintf("Input and output channels must equal 3 in A to B tag.\n");
635 return false;
636 }
637
638 // Read the offsets of each element in the A to B tag. With the exception of A curves and
639 // B curves (which we do not yet support), we will handle these elements in the order in
640 // which they should be applied (rather than the order in which they occur in the tag).
641 // If the offset is non-zero it indicates that the element is present.
642 uint32_t offsetToACurves = read_big_endian_int(src + 28);
643 uint32_t offsetToBCurves = read_big_endian_int(src + 12);
644 if ((0 != offsetToACurves) || (0 != offsetToBCurves)) {
645 // FIXME (msarett): Handle A and B curves.
646 // Note that the A curve is technically required in order to have a color LUT.
647 // However, all the A curves I have seen so far have are just placeholders that
648 // don't actually transform the data.
649 SkColorSpacePrintf("Ignoring A and/or B curve. Output may be wrong.\n");
650 }
651
652 uint32_t offsetToColorLUT = read_big_endian_int(src + 24);
653 if (0 != offsetToColorLUT && offsetToColorLUT < len) {
654 if (!load_color_lut(colorLUT, inputChannels, outputChannels, src + offsetToColorLUT,
655 len - offsetToColorLUT)) {
656 SkColorSpacePrintf("Failed to read color LUT from A to B tag.\n");
657 }
658 }
659
660 uint32_t offsetToMCurves = read_big_endian_int(src + 20);
661 if (0 != offsetToMCurves && offsetToMCurves < len) {
662 if (!load_gammas(gammas, outputChannels, src + offsetToMCurves, len - offsetToMCurves)) {
663 SkColorSpacePrintf("Failed to read M curves from A to B tag. Using linear gamma.\n");
664 gammas[0].fNamed = SkColorSpace::kLinear_GammaNamed;
665 gammas[1].fNamed = SkColorSpace::kLinear_GammaNamed;
666 gammas[2].fNamed = SkColorSpace::kLinear_GammaNamed;
667 }
668 }
669
670 uint32_t offsetToMatrix = read_big_endian_int(src + 16);
671 if (0 != offsetToMatrix && offsetToMatrix < len) {
672 if (!load_matrix(toXYZ, src + offsetToMatrix, len - offsetToMatrix)) {
673 SkColorSpacePrintf("Failed to read matrix from A to B tag.\n");
674 toXYZ->setIdentity();
675 }
676 }
677
678 return true;
679}
680
681sk_sp<SkColorSpace> SkColorSpace::NewICC(const void* input, size_t len) {
682 if (!input || len < kICCHeaderSize) {
683 return_null("Data is null or not large enough to contain an ICC profile");
684 }
685
686 // Create our own copy of the input.
687 void* memory = sk_malloc_throw(len);
688 memcpy(memory, input, len);
689 sk_sp<SkData> data = SkData::MakeFromMalloc(memory, len);
690 const void* base = data->data();
691 const uint8_t* ptr = (const uint8_t*) base;
692
693 // Read the ICC profile header and check to make sure that it is valid.
694 ICCProfileHeader header;
695 header.init(ptr, len);
696 if (!header.valid()) {
697 return nullptr;
698 }
699
700 // Adjust ptr and len before reading the tags.
701 if (len < header.fSize) {
702 SkColorSpacePrintf("ICC profile might be truncated.\n");
703 } else if (len > header.fSize) {
704 SkColorSpacePrintf("Caller provided extra data beyond the end of the ICC profile.\n");
705 len = header.fSize;
706 }
707 ptr += kICCHeaderSize;
708 len -= kICCHeaderSize;
709
710 // Parse tag headers.
711 uint32_t tagCount = header.fTagCount;
712 SkColorSpacePrintf("ICC profile contains %d tags.\n", tagCount);
713 if (len < kICCTagTableEntrySize * tagCount) {
714 return_null("Not enough input data to read tag table entries");
715 }
716
717 SkAutoTArray<ICCTag> tags(tagCount);
718 for (uint32_t i = 0; i < tagCount; i++) {
719 ptr = tags[i].init(ptr);
720 SkColorSpacePrintf("[%d] %c%c%c%c %d %d\n", i, (tags[i].fSignature >> 24) & 0xFF,
721 (tags[i].fSignature >> 16) & 0xFF, (tags[i].fSignature >> 8) & 0xFF,
722 (tags[i].fSignature >> 0) & 0xFF, tags[i].fOffset, tags[i].fLength);
723
724 if (!tags[i].valid(kICCHeaderSize + len)) {
725 return_null("Tag is too large to fit in ICC profile");
726 }
727 }
728
729 switch (header.fInputColorSpace) {
730 case kRGB_ColorSpace: {
731 // Recognize the rXYZ, gXYZ, and bXYZ tags.
732 const ICCTag* r = ICCTag::Find(tags.get(), tagCount, kTAG_rXYZ);
733 const ICCTag* g = ICCTag::Find(tags.get(), tagCount, kTAG_gXYZ);
734 const ICCTag* b = ICCTag::Find(tags.get(), tagCount, kTAG_bXYZ);
735 if (r && g && b) {
736 float toXYZ[9];
737 if (!load_xyz(&toXYZ[0], r->addr((const uint8_t*) base), r->fLength) ||
738 !load_xyz(&toXYZ[3], g->addr((const uint8_t*) base), g->fLength) ||
739 !load_xyz(&toXYZ[6], b->addr((const uint8_t*) base), b->fLength))
740 {
741 return_null("Need valid rgb tags for XYZ space");
742 }
743 SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor);
744 mat.set3x3RowMajorf(toXYZ);
745
746 // It is not uncommon to see missing or empty gamma tags. This indicates
747 // that we should use unit gamma.
748 SkGammaCurve curves[3];
749 r = ICCTag::Find(tags.get(), tagCount, kTAG_rTRC);
750 g = ICCTag::Find(tags.get(), tagCount, kTAG_gTRC);
751 b = ICCTag::Find(tags.get(), tagCount, kTAG_bTRC);
752 if (!r || !load_gammas(&curves[0], 1, r->addr((const uint8_t*) base), r->fLength))
753 {
754 SkColorSpacePrintf("Failed to read R gamma tag.\n");
755 curves[0].fNamed = SkColorSpace::kLinear_GammaNamed;
756 }
757 if (!g || !load_gammas(&curves[1], 1, g->addr((const uint8_t*) base), g->fLength))
758 {
759 SkColorSpacePrintf("Failed to read G gamma tag.\n");
760 curves[1].fNamed = SkColorSpace::kLinear_GammaNamed;
761 }
762 if (!b || !load_gammas(&curves[2], 1, b->addr((const uint8_t*) base), b->fLength))
763 {
764 SkColorSpacePrintf("Failed to read B gamma tag.\n");
765 curves[2].fNamed = SkColorSpace::kLinear_GammaNamed;
766 }
767
768 GammaNamed gammaNamed = SkGammas::Named(curves);
769 if (kNonStandard_GammaNamed == gammaNamed) {
770 sk_sp<SkGammas> gammas = sk_make_sp<SkGammas>(std::move(curves[0]),
771 std::move(curves[1]),
772 std::move(curves[2]));
773 return sk_sp<SkColorSpace>(new SkColorSpace_Base(nullptr, std::move(gammas),
774 mat, std::move(data)));
775 } else {
776 return SkColorSpace_Base::NewRGB(gammaNamed, mat);
777 }
778 }
779
780 // Recognize color profile specified by A2B0 tag.
781 const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0);
782 if (a2b0) {
783 sk_sp<SkColorLookUpTable> colorLUT = sk_make_sp<SkColorLookUpTable>();
784 SkGammaCurve curves[3];
785 SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor);
786 if (!load_a2b0(colorLUT.get(), curves, &toXYZ, a2b0->addr((const uint8_t*) base),
787 a2b0->fLength)) {
788 return_null("Failed to parse A2B0 tag");
789 }
790
791 GammaNamed gammaNamed = SkGammas::Named(curves);
792 colorLUT = colorLUT->fTable ? colorLUT : nullptr;
793 if (colorLUT || kNonStandard_GammaNamed == gammaNamed) {
794 sk_sp<SkGammas> gammas = sk_make_sp<SkGammas>(std::move(curves[0]),
795 std::move(curves[1]),
796 std::move(curves[2]));
797
798 return sk_sp<SkColorSpace>(new SkColorSpace_Base(std::move(colorLUT),
799 std::move(gammas), toXYZ,
800 std::move(data)));
801 } else {
802 return SkColorSpace_Base::NewRGB(gammaNamed, toXYZ);
803 }
804 }
805 }
806 default:
807 break;
808 }
809
810 return_null("ICC profile contains unsupported colorspace");
811}
812
813///////////////////////////////////////////////////////////////////////////////////////////////////
814
815// We will write a profile with the minimum nine required tags.
816static constexpr uint32_t kICCNumEntries = 9;
817
818static constexpr uint32_t kTAG_desc = SkSetFourByteTag('d', 'e', 's', 'c');
819static constexpr uint32_t kTAG_desc_Bytes = 12;
820static constexpr uint32_t kTAG_desc_Offset = kICCHeaderSize + kICCNumEntries*kICCTagTableEntrySize;
821
822static constexpr uint32_t kTAG_XYZ_Bytes = 20;
823static constexpr uint32_t kTAG_rXYZ_Offset = kTAG_desc_Offset + kTAG_desc_Bytes;
824static constexpr uint32_t kTAG_gXYZ_Offset = kTAG_rXYZ_Offset + kTAG_XYZ_Bytes;
825static constexpr uint32_t kTAG_bXYZ_Offset = kTAG_gXYZ_Offset + kTAG_XYZ_Bytes;
826
827static constexpr uint32_t kTAG_TRC_Bytes = 14;
828static constexpr uint32_t kTAG_rTRC_Offset = kTAG_bXYZ_Offset + kTAG_XYZ_Bytes;
829static constexpr uint32_t kTAG_gTRC_Offset = kTAG_rTRC_Offset + SkAlign4(kTAG_TRC_Bytes);
830static constexpr uint32_t kTAG_bTRC_Offset = kTAG_gTRC_Offset + SkAlign4(kTAG_TRC_Bytes);
831
832static constexpr uint32_t kTAG_wtpt = SkSetFourByteTag('w', 't', 'p', 't');
833static constexpr uint32_t kTAG_wtpt_Offset = kTAG_bTRC_Offset + SkAlign4(kTAG_TRC_Bytes);
834
835static constexpr uint32_t kTAG_cprt = SkSetFourByteTag('c', 'p', 'r', 't');
836static constexpr uint32_t kTAG_cprt_Bytes = 12;
837static constexpr uint32_t kTAG_cprt_Offset = kTAG_wtpt_Offset + kTAG_XYZ_Bytes;
838
839static constexpr uint32_t kICCProfileSize = kTAG_cprt_Offset + kTAG_cprt_Bytes;
840
841static constexpr uint32_t gICCHeader[kICCHeaderSize / 4] {
842 SkEndian_SwapBE32(kICCProfileSize), // Size of the profile
843 0, // Preferred CMM type (ignored)
844 SkEndian_SwapBE32(0x02100000), // Version 2.1
845 SkEndian_SwapBE32(kDisplay_Profile), // Display device profile
846 SkEndian_SwapBE32(kRGB_ColorSpace), // RGB input color space
847 SkEndian_SwapBE32(kXYZ_PCSSpace), // XYZ profile connection space
848 0, 0, 0, // Date and time (ignored)
849 SkEndian_SwapBE32(kACSP_Signature), // Profile signature
850 0, // Platform target (ignored)
851 0x00000000, // Flags: not embedded, can be used independently
852 0, // Device manufacturer (ignored)
853 0, // Device model (ignored)
854 0, 0, // Device attributes (ignored)
855 SkEndian_SwapBE32(1), // Relative colorimetric rendering intent
856 SkEndian_SwapBE32(0x0000f6d6), // D50 standard illuminant (X)
857 SkEndian_SwapBE32(0x00010000), // D50 standard illuminant (Y)
858 SkEndian_SwapBE32(0x0000d32d), // D50 standard illuminant (Z)
859 0, // Profile creator (ignored)
860 0, 0, 0, 0, // Profile id checksum (ignored)
861 0, 0, 0, 0, 0, 0, 0, // Reserved (ignored)
862 SkEndian_SwapBE32(kICCNumEntries), // Number of tags
863};
864
865static constexpr uint32_t gICCTagTable[3 * kICCNumEntries] {
866 // Profile description
867 SkEndian_SwapBE32(kTAG_desc),
868 SkEndian_SwapBE32(kTAG_desc_Offset),
869 SkEndian_SwapBE32(kTAG_desc_Bytes),
870
871 // rXYZ
872 SkEndian_SwapBE32(kTAG_rXYZ),
873 SkEndian_SwapBE32(kTAG_rXYZ_Offset),
874 SkEndian_SwapBE32(kTAG_XYZ_Bytes),
875
876 // gXYZ
877 SkEndian_SwapBE32(kTAG_gXYZ),
878 SkEndian_SwapBE32(kTAG_gXYZ_Offset),
879 SkEndian_SwapBE32(kTAG_XYZ_Bytes),
880
881 // bXYZ
882 SkEndian_SwapBE32(kTAG_bXYZ),
883 SkEndian_SwapBE32(kTAG_bXYZ_Offset),
884 SkEndian_SwapBE32(kTAG_XYZ_Bytes),
885
886 // rTRC
887 SkEndian_SwapBE32(kTAG_rTRC),
888 SkEndian_SwapBE32(kTAG_rTRC_Offset),
889 SkEndian_SwapBE32(kTAG_TRC_Bytes),
890
891 // gTRC
892 SkEndian_SwapBE32(kTAG_gTRC),
893 SkEndian_SwapBE32(kTAG_gTRC_Offset),
894 SkEndian_SwapBE32(kTAG_TRC_Bytes),
895
896 // bTRC
897 SkEndian_SwapBE32(kTAG_bTRC),
898 SkEndian_SwapBE32(kTAG_bTRC_Offset),
899 SkEndian_SwapBE32(kTAG_TRC_Bytes),
900
901 // White point
902 SkEndian_SwapBE32(kTAG_wtpt),
903 SkEndian_SwapBE32(kTAG_wtpt_Offset),
904 SkEndian_SwapBE32(kTAG_XYZ_Bytes),
905
906 // Copyright
907 SkEndian_SwapBE32(kTAG_cprt),
908 SkEndian_SwapBE32(kTAG_cprt_Offset),
909 SkEndian_SwapBE32(kTAG_cprt_Bytes),
910};
911
912static constexpr uint32_t kTAG_TextType = SkSetFourByteTag('m', 'l', 'u', 'c');
913static constexpr uint32_t gEmptyTextTag[3] {
914 SkEndian_SwapBE32(kTAG_TextType), // Type signature
915 0, // Reserved
916 0, // Zero records
917};
918
919static void write_xyz_tag(uint32_t* ptr, const SkMatrix44& toXYZ, int row) {
920 ptr[0] = SkEndian_SwapBE32(kXYZ_PCSSpace);
921 ptr[1] = 0;
922 ptr[2] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(row, 0)));
923 ptr[3] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(row, 1)));
924 ptr[4] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(row, 2)));
925}
926
927static void write_trc_tag(uint32_t* ptr, float value) {
928 ptr[0] = SkEndian_SwapBE32(kTAG_CurveType);
929 ptr[1] = 0;
930
931 // Gamma will be specified with a single value.
932 ptr[2] = SkEndian_SwapBE32(1);
933
934 // Convert gamma to 16-bit fixed point.
935 uint16_t* ptr16 = (uint16_t*) (ptr + 3);
936 ptr16[0] = SkEndian_SwapBE16((uint16_t) (value * 256.0f));
937
938 // Pad tag with zero.
939 ptr16[1] = 0;
940}
941
942static float get_gamma_value(const SkGammaCurve* curve) {
943 switch (curve->fNamed) {
944 case SkColorSpace::kSRGB_GammaNamed:
945 // FIXME (msarett):
946 // kSRGB cannot be represented by a value. Here we fall through to 2.2f,
947 // which is a close guess. To be more accurate, we need to represent sRGB
948 // gamma with a parametric curve.
949 case SkColorSpace::k2Dot2Curve_GammaNamed:
950 return 2.2f;
951 case SkColorSpace::kLinear_GammaNamed:
952 return 1.0f;
953 default:
954 SkASSERT(curve->isValue());
955 return curve->fValue;
956 }
957}
958
959sk_sp<SkData> SkColorSpace_Base::writeToICC() const {
960 // Return if this object was created from a profile, or if we have already serialized
961 // the profile.
962 if (fProfileData) {
963 return fProfileData;
964 }
965
966 // The client may create an SkColorSpace using an SkMatrix44, but currently we only
967 // support writing profiles with 3x3 matrices.
968 // TODO (msarett): Fix this!
969 if (0.0f != fToXYZD50.getFloat(3, 0) || 0.0f != fToXYZD50.getFloat(3, 1) ||
970 0.0f != fToXYZD50.getFloat(3, 2) || 0.0f != fToXYZD50.getFloat(0, 3) ||
971 0.0f != fToXYZD50.getFloat(1, 3) || 0.0f != fToXYZD50.getFloat(2, 3))
972 {
973 return nullptr;
974 }
975
976 SkAutoMalloc profile(kICCProfileSize);
977 uint8_t* ptr = (uint8_t*) profile.get();
978
979 // Write profile header
980 memcpy(ptr, gICCHeader, sizeof(gICCHeader));
981 ptr += sizeof(gICCHeader);
982
983 // Write tag table
984 memcpy(ptr, gICCTagTable, sizeof(gICCTagTable));
985 ptr += sizeof(gICCTagTable);
986
987 // Write profile description tag
988 memcpy(ptr, gEmptyTextTag, sizeof(gEmptyTextTag));
989 ptr += sizeof(gEmptyTextTag);
990
991 // Write XYZ tags
992 write_xyz_tag((uint32_t*) ptr, fToXYZD50, 0);
993 ptr += kTAG_XYZ_Bytes;
994 write_xyz_tag((uint32_t*) ptr, fToXYZD50, 1);
995 ptr += kTAG_XYZ_Bytes;
996 write_xyz_tag((uint32_t*) ptr, fToXYZD50, 2);
997 ptr += kTAG_XYZ_Bytes;
998
999 // Write TRC tags
1000 GammaNamed gammaNamed = this->gammaNamed();
1001 if (kNonStandard_GammaNamed == gammaNamed) {
1002 write_trc_tag((uint32_t*) ptr, get_gamma_value(&as_CSB(this)->fGammas->fRed));
1003 ptr += SkAlign4(kTAG_TRC_Bytes);
1004 write_trc_tag((uint32_t*) ptr, get_gamma_value(&as_CSB(this)->fGammas->fGreen));
1005 ptr += SkAlign4(kTAG_TRC_Bytes);
1006 write_trc_tag((uint32_t*) ptr, get_gamma_value(&as_CSB(this)->fGammas->fBlue));
1007 ptr += SkAlign4(kTAG_TRC_Bytes);
1008 } else {
1009 switch (gammaNamed) {
1010 case SkColorSpace::kSRGB_GammaNamed:
1011 // FIXME (msarett):
1012 // kSRGB cannot be represented by a value. Here we fall through to 2.2f,
1013 // which is a close guess. To be more accurate, we need to represent sRGB
1014 // gamma with a parametric curve.
1015 case SkColorSpace::k2Dot2Curve_GammaNamed:
1016 write_trc_tag((uint32_t*) ptr, 2.2f);
1017 ptr += SkAlign4(kTAG_TRC_Bytes);
1018 write_trc_tag((uint32_t*) ptr, 2.2f);
1019 ptr += SkAlign4(kTAG_TRC_Bytes);
1020 write_trc_tag((uint32_t*) ptr, 2.2f);
1021 ptr += SkAlign4(kTAG_TRC_Bytes);
1022 break;
1023 case SkColorSpace::kLinear_GammaNamed:
1024 write_trc_tag((uint32_t*) ptr, 1.0f);
1025 ptr += SkAlign4(kTAG_TRC_Bytes);
1026 write_trc_tag((uint32_t*) ptr, 1.0f);
1027 ptr += SkAlign4(kTAG_TRC_Bytes);
1028 write_trc_tag((uint32_t*) ptr, 1.0f);
1029 ptr += SkAlign4(kTAG_TRC_Bytes);
1030 break;
1031 default:
1032 SkASSERT(false);
1033 break;
1034 }
1035 }
1036
1037 // Write white point tag
1038 uint32_t* ptr32 = (uint32_t*) ptr;
1039 ptr32[0] = SkEndian_SwapBE32(kXYZ_PCSSpace);
1040 ptr32[1] = 0;
1041 // TODO (msarett): These values correspond to the D65 white point. This may not always be
1042 // correct.
1043 ptr32[2] = SkEndian_SwapBE32(0x0000f351);
1044 ptr32[3] = SkEndian_SwapBE32(0x00010000);
1045 ptr32[4] = SkEndian_SwapBE32(0x000116cc);
1046 ptr += kTAG_XYZ_Bytes;
1047
1048 // Write copyright tag
1049 memcpy(ptr, gEmptyTextTag, sizeof(gEmptyTextTag));
1050
1051 // TODO (msarett): Should we try to hold onto the data so we can return immediately if
1052 // the client calls again?
1053 return SkData::MakeFromMalloc(profile.release(), kICCProfileSize);
1054}