blob: 5a510903c681d4cc9afb3fbf2b9de9e18df30569 [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
Hal Canary95e3c052017-01-11 12:44:43 -05008#include "SkAutoMalloc.h"
msarett085cad42016-06-28 07:16:40 -07009#include "SkColorSpace.h"
Hal Canary95e3c052017-01-11 12:44:43 -050010#include "SkColorSpacePriv.h"
raftias94888332016-10-18 10:02:51 -070011#include "SkColorSpace_A2B.h"
msarett085cad42016-06-28 07:16:40 -070012#include "SkColorSpace_Base.h"
raftias94888332016-10-18 10:02:51 -070013#include "SkColorSpace_XYZ.h"
msarett085cad42016-06-28 07:16:40 -070014#include "SkEndian.h"
15#include "SkFixed.h"
Matt Sarett8e042862016-12-19 09:43:30 -050016#include "SkICCPriv.h"
msarett085cad42016-06-28 07:16:40 -070017#include "SkTemplates.h"
18
19#define return_if_false(pred, msg) \
20 do { \
21 if (!(pred)) { \
22 SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \
23 return false; \
24 } \
25 } while (0)
26
27#define return_null(msg) \
28 do { \
29 SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \
30 return nullptr; \
31 } while (0)
32
msarett959d45b2016-07-21 13:19:03 -070033static uint16_t read_big_endian_u16(const uint8_t* ptr) {
msarett085cad42016-06-28 07:16:40 -070034 return ptr[0] << 8 | ptr[1];
35}
36
msarett959d45b2016-07-21 13:19:03 -070037static uint32_t read_big_endian_u32(const uint8_t* ptr) {
msarett085cad42016-06-28 07:16:40 -070038 return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
39}
40
msarett959d45b2016-07-21 13:19:03 -070041static int32_t read_big_endian_i32(const uint8_t* ptr) {
42 return (int32_t) read_big_endian_u32(ptr);
msarett085cad42016-06-28 07:16:40 -070043}
44
Matt Sarett523116d2017-01-12 18:36:38 -050045static constexpr float kWhitePointD50[] = { 0.96420f, 1.00000f, 0.82491f, };
46
msarett085cad42016-06-28 07:16:40 -070047struct ICCProfileHeader {
48 uint32_t fSize;
49
50 // No reason to care about the preferred color management module (ex: Adobe, Apple, etc.).
51 // We're always going to use this one.
52 uint32_t fCMMType_ignored;
53
54 uint32_t fVersion;
55 uint32_t fProfileClass;
56 uint32_t fInputColorSpace;
57 uint32_t fPCS;
58 uint32_t fDateTime_ignored[3];
59 uint32_t fSignature;
60
61 // Indicates the platform that this profile was created for (ex: Apple, Microsoft). This
62 // doesn't really matter to us.
63 uint32_t fPlatformTarget_ignored;
64
65 // Flags can indicate:
66 // (1) Whether this profile was embedded in a file. This flag is consistently wrong.
67 // Ex: The profile came from a file but indicates that it did not.
68 // (2) Whether we are allowed to use the profile independently of the color data. If set,
69 // this may allow us to use the embedded profile for testing separate from the original
70 // image.
71 uint32_t fFlags_ignored;
72
73 // We support many output devices. It doesn't make sense to think about the attributes of
74 // the device in the context of the image profile.
75 uint32_t fDeviceManufacturer_ignored;
76 uint32_t fDeviceModel_ignored;
77 uint32_t fDeviceAttributes_ignored[2];
78
79 uint32_t fRenderingIntent;
80 int32_t fIlluminantXYZ[3];
81
82 // We don't care who created the profile.
83 uint32_t fCreator_ignored;
84
85 // This is an MD5 checksum. Could be useful for checking if profiles are equal.
86 uint32_t fProfileId_ignored[4];
87
88 // Reserved for future use.
89 uint32_t fReserved_ignored[7];
90
91 uint32_t fTagCount;
92
93 void init(const uint8_t* src, size_t len) {
94 SkASSERT(kICCHeaderSize == sizeof(*this));
95
96 uint32_t* dst = (uint32_t*) this;
97 for (uint32_t i = 0; i < kICCHeaderSize / 4; i++, src+=4) {
msarett959d45b2016-07-21 13:19:03 -070098 dst[i] = read_big_endian_u32(src);
msarett085cad42016-06-28 07:16:40 -070099 }
100 }
101
102 bool valid() const {
103 return_if_false(fSize >= kICCHeaderSize, "Size is too small");
104
105 uint8_t majorVersion = fVersion >> 24;
106 return_if_false(majorVersion <= 4, "Unsupported version");
107
msarett18895db2016-07-15 10:47:19 -0700108 // These are the four basic classes of profiles that we might expect to see embedded
109 // in images. Additional classes exist, but they generally are used as a convenient
msarett085cad42016-06-28 07:16:40 -0700110 // way for CMMs to store calculated transforms.
111 return_if_false(fProfileClass == kDisplay_Profile ||
112 fProfileClass == kInput_Profile ||
msarett18895db2016-07-15 10:47:19 -0700113 fProfileClass == kOutput_Profile ||
114 fProfileClass == kColorSpace_Profile,
msarett085cad42016-06-28 07:16:40 -0700115 "Unsupported profile");
116
raftias54761282016-12-01 13:44:07 -0500117 switch (fInputColorSpace) {
118 case kRGB_ColorSpace:
119 SkColorSpacePrintf("RGB Input Color Space");
120 break;
121 case kCMYK_ColorSpace:
122 SkColorSpacePrintf("CMYK Input Color Space\n");
123 break;
raftias91db12d2016-12-02 11:56:59 -0500124 case kGray_ColorSpace:
125 SkColorSpacePrintf("Gray Input Color Space\n");
126 break;
raftias54761282016-12-01 13:44:07 -0500127 default:
128 SkColorSpacePrintf("Unsupported Input Color Space: %c%c%c%c\n",
129 (fInputColorSpace>>24)&0xFF, (fInputColorSpace>>16)&0xFF,
130 (fInputColorSpace>> 8)&0xFF, (fInputColorSpace>> 0)&0xFF);
131 return false;
132 }
msarett085cad42016-06-28 07:16:40 -0700133
raftias026f2232016-10-24 09:52:26 -0700134 switch (fPCS) {
135 case kXYZ_PCSSpace:
136 SkColorSpacePrintf("XYZ PCS\n");
137 break;
138 case kLAB_PCSSpace:
139 SkColorSpacePrintf("Lab PCS\n");
140 break;
141 default:
142 // ICC currently (V4.3) only specifices XYZ and Lab PCS spaces
raftias54761282016-12-01 13:44:07 -0500143 SkColorSpacePrintf("Unsupported PCS space: %c%c%c%c\n",
144 (fPCS>>24)&0xFF, (fPCS>>16)&0xFF,
145 (fPCS>> 8)&0xFF, (fPCS>> 0)&0xFF);
raftias026f2232016-10-24 09:52:26 -0700146 return false;
147 }
msarett085cad42016-06-28 07:16:40 -0700148
149 return_if_false(fSignature == kACSP_Signature, "Bad signature");
150
151 // TODO (msarett):
152 // Should we treat different rendering intents differently?
153 // Valid rendering intents include kPerceptual (0), kRelative (1),
154 // kSaturation (2), and kAbsolute (3).
msarett3bf75092016-08-24 06:22:41 -0700155 if (fRenderingIntent > 3) {
msarett18895db2016-07-15 10:47:19 -0700156 // Warn rather than fail here. Occasionally, we see perfectly
157 // normal profiles with wacky rendering intents.
158 SkColorSpacePrintf("Warning, bad rendering intent.\n");
159 }
msarett085cad42016-06-28 07:16:40 -0700160
Matt Sarett523116d2017-01-12 18:36:38 -0500161 return_if_false(
162 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[0]), kWhitePointD50[0]) &&
163 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[1]), kWhitePointD50[1]) &&
164 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[2]), kWhitePointD50[2]),
165 "Illuminant must be D50");
msarett085cad42016-06-28 07:16:40 -0700166
167 return_if_false(fTagCount <= 100, "Too many tags");
168
169 return true;
170 }
171};
172
173template <class T>
174static bool safe_add(T arg1, T arg2, size_t* result) {
175 SkASSERT(arg1 >= 0);
176 SkASSERT(arg2 >= 0);
177 if (arg1 >= 0 && arg2 <= std::numeric_limits<T>::max() - arg1) {
178 T sum = arg1 + arg2;
179 if (sum <= std::numeric_limits<size_t>::max()) {
180 *result = static_cast<size_t>(sum);
181 return true;
182 }
183 }
184 return false;
185}
186
187static bool safe_mul(uint32_t arg1, uint32_t arg2, uint32_t* result) {
188 uint64_t product64 = (uint64_t) arg1 * (uint64_t) arg2;
189 uint32_t product32 = (uint32_t) product64;
190 if (product32 != product64) {
191 return false;
192 }
193
194 *result = product32;
195 return true;
196}
197
198struct ICCTag {
199 uint32_t fSignature;
200 uint32_t fOffset;
201 uint32_t fLength;
202
203 const uint8_t* init(const uint8_t* src) {
msarett959d45b2016-07-21 13:19:03 -0700204 fSignature = read_big_endian_u32(src);
205 fOffset = read_big_endian_u32(src + 4);
206 fLength = read_big_endian_u32(src + 8);
msarett085cad42016-06-28 07:16:40 -0700207 return src + 12;
208 }
209
210 bool valid(size_t len) {
211 size_t tagEnd;
212 return_if_false(safe_add(fOffset, fLength, &tagEnd),
213 "Tag too large, overflows integer addition");
214 return_if_false(tagEnd <= len, "Tag too large for ICC profile");
215 return true;
216 }
217
218 const uint8_t* addr(const uint8_t* src) const {
219 return src + fOffset;
220 }
221
222 static const ICCTag* Find(const ICCTag tags[], int count, uint32_t signature) {
223 for (int i = 0; i < count; ++i) {
224 if (tags[i].fSignature == signature) {
225 return &tags[i];
226 }
227 }
228 return nullptr;
229 }
230};
231
msarett085cad42016-06-28 07:16:40 -0700232static bool load_xyz(float dst[3], const uint8_t* src, size_t len) {
233 if (len < 20) {
234 SkColorSpacePrintf("XYZ tag is too small (%d bytes)", len);
235 return false;
236 }
237
msarett959d45b2016-07-21 13:19:03 -0700238 dst[0] = SkFixedToFloat(read_big_endian_i32(src + 8));
239 dst[1] = SkFixedToFloat(read_big_endian_i32(src + 12));
240 dst[2] = SkFixedToFloat(read_big_endian_i32(src + 16));
msarett085cad42016-06-28 07:16:40 -0700241 SkColorSpacePrintf("XYZ %g %g %g\n", dst[0], dst[1], dst[2]);
242 return true;
243}
244
msarett1b93bd12016-07-21 07:11:26 -0700245static SkGammas::Type set_gamma_value(SkGammas::Data* data, float value) {
246 if (color_space_almost_equal(2.2f, value)) {
msarett600c7372016-09-07 12:03:53 -0700247 data->fNamed = k2Dot2Curve_SkGammaNamed;
msarett1b93bd12016-07-21 07:11:26 -0700248 return SkGammas::Type::kNamed_Type;
msarett959ccc12016-07-20 15:10:02 -0700249 }
msarett456bf302016-07-20 16:14:16 -0700250
msarett1b93bd12016-07-21 07:11:26 -0700251 if (color_space_almost_equal(1.0f, value)) {
msarett600c7372016-09-07 12:03:53 -0700252 data->fNamed = kLinear_SkGammaNamed;
msarett1b93bd12016-07-21 07:11:26 -0700253 return SkGammas::Type::kNamed_Type;
254 }
255
256 if (color_space_almost_equal(0.0f, value)) {
257 return SkGammas::Type::kNone_Type;
258 }
259
260 data->fValue = value;
261 return SkGammas::Type::kValue_Type;
262}
263
264static float read_big_endian_16_dot_16(const uint8_t buf[4]) {
265 // It just so happens that SkFixed is also 16.16!
msarett959d45b2016-07-21 13:19:03 -0700266 return SkFixedToFloat(read_big_endian_i32(buf));
msarett1b93bd12016-07-21 07:11:26 -0700267}
268
269/**
270 * @param outData Set to the appropriate value on success. If we have table or
271 * parametric gamma, it is the responsibility of the caller to set
272 * fOffset.
273 * @param outParams If this is a parametric gamma, this is set to the appropriate
274 * parameters on success.
275 * @param outTagBytes Will be set to the length of the tag on success.
276 * @src Pointer to tag data.
277 * @len Length of tag data in bytes.
278 *
279 * @return kNone_Type on failure, otherwise the type of the gamma tag.
280 */
Matt Sarettdf44fc52016-10-11 16:57:50 -0400281static SkGammas::Type parse_gamma(SkGammas::Data* outData, SkColorSpaceTransferFn* outParams,
282 size_t* outTagBytes, const uint8_t* src, size_t len) {
msarett1b93bd12016-07-21 07:11:26 -0700283 if (len < 12) {
284 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
285 return SkGammas::Type::kNone_Type;
286 }
287
288 // In the case of consecutive gamma tags, we need to count the number of bytes in the
289 // tag, so that we can move on to the next tag.
290 size_t tagBytes;
291
msarett959d45b2016-07-21 13:19:03 -0700292 uint32_t type = read_big_endian_u32(src);
msarett1b93bd12016-07-21 07:11:26 -0700293 // Bytes 4-7 are reserved and should be set to zero.
294 switch (type) {
295 case kTAG_CurveType: {
msarett959d45b2016-07-21 13:19:03 -0700296 uint32_t count = read_big_endian_u32(src + 8);
msarett1b93bd12016-07-21 07:11:26 -0700297
298 // tagBytes = 12 + 2 * count
299 // We need to do safe addition here to avoid integer overflow.
300 if (!safe_add(count, count, &tagBytes) ||
301 !safe_add((size_t) 12, tagBytes, &tagBytes))
302 {
303 SkColorSpacePrintf("Invalid gamma count");
304 return SkGammas::Type::kNone_Type;
305 }
306
307 if (len < tagBytes) {
308 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
309 return SkGammas::Type::kNone_Type;
310 }
311 *outTagBytes = tagBytes;
312
313 if (0 == count) {
314 // Some tags require a gamma curve, but the author doesn't actually want
315 // to transform the data. In this case, it is common to see a curve with
316 // a count of 0.
msarett600c7372016-09-07 12:03:53 -0700317 outData->fNamed = kLinear_SkGammaNamed;
msarett1b93bd12016-07-21 07:11:26 -0700318 return SkGammas::Type::kNamed_Type;
319 }
320
321 const uint16_t* table = (const uint16_t*) (src + 12);
322 if (1 == count) {
323 // The table entry is the gamma (with a bias of 256).
msarett959d45b2016-07-21 13:19:03 -0700324 float value = (read_big_endian_u16((const uint8_t*) table)) / 256.0f;
msarett1b93bd12016-07-21 07:11:26 -0700325 SkColorSpacePrintf("gamma %g\n", value);
326
327 return set_gamma_value(outData, value);
328 }
329
Matt Sarette31d8a12017-05-17 15:21:59 -0400330 // This optimization is especially important for A2B profiles, where we do
331 // not resize tables or interpolate lookups.
332 if (2 == count) {
333 if (0 == read_big_endian_u16((const uint8_t*) &table[0]) &&
334 65535 == read_big_endian_u16((const uint8_t*) &table[1])) {
335 outData->fNamed = kLinear_SkGammaNamed;
336 return SkGammas::Type::kNamed_Type;
337 }
338 }
339
msarett1b93bd12016-07-21 07:11:26 -0700340 // Check for frequently occurring sRGB curves.
341 // We do this by sampling a few values and see if they match our expectation.
342 // A more robust solution would be to compare each value in this curve against
343 // an sRGB curve to see if we remain below an error threshold. At this time,
344 // we haven't seen any images in the wild that make this kind of
345 // calculation necessary. We encounter identical gamma curves over and
346 // over again, but relatively few variations.
347 if (1024 == count) {
348 // The magic values were chosen because they match both the very common
349 // HP sRGB gamma table and the less common Canon sRGB gamma table (which use
350 // different rounding rules).
msarett959d45b2016-07-21 13:19:03 -0700351 if (0 == read_big_endian_u16((const uint8_t*) &table[0]) &&
352 3366 == read_big_endian_u16((const uint8_t*) &table[257]) &&
353 14116 == read_big_endian_u16((const uint8_t*) &table[513]) &&
354 34318 == read_big_endian_u16((const uint8_t*) &table[768]) &&
355 65535 == read_big_endian_u16((const uint8_t*) &table[1023])) {
msarett600c7372016-09-07 12:03:53 -0700356 outData->fNamed = kSRGB_SkGammaNamed;
msarett1b93bd12016-07-21 07:11:26 -0700357 return SkGammas::Type::kNamed_Type;
358 }
359 }
360
361 if (26 == count) {
Matt Sarettb3d1c2e2016-10-27 12:44:18 -0400362 // The magic values match a clever "minimum size" approach to representing sRGB.
363 // code.facebook.com/posts/411525055626587/under-the-hood-improving-facebook-photos
msarett959d45b2016-07-21 13:19:03 -0700364 if (0 == read_big_endian_u16((const uint8_t*) &table[0]) &&
365 3062 == read_big_endian_u16((const uint8_t*) &table[6]) &&
366 12824 == read_big_endian_u16((const uint8_t*) &table[12]) &&
367 31237 == read_big_endian_u16((const uint8_t*) &table[18]) &&
368 65535 == read_big_endian_u16((const uint8_t*) &table[25])) {
msarett600c7372016-09-07 12:03:53 -0700369 outData->fNamed = kSRGB_SkGammaNamed;
msarett1b93bd12016-07-21 07:11:26 -0700370 return SkGammas::Type::kNamed_Type;
371 }
372 }
373
374 if (4096 == count) {
375 // The magic values were chosen because they match Nikon, Epson, and
Matt Sarettb3d1c2e2016-10-27 12:44:18 -0400376 // lcms2 sRGB gamma tables (all of which use different rounding rules).
msarett959d45b2016-07-21 13:19:03 -0700377 if (0 == read_big_endian_u16((const uint8_t*) &table[0]) &&
378 950 == read_big_endian_u16((const uint8_t*) &table[515]) &&
379 3342 == read_big_endian_u16((const uint8_t*) &table[1025]) &&
380 14079 == read_big_endian_u16((const uint8_t*) &table[2051]) &&
381 65535 == read_big_endian_u16((const uint8_t*) &table[4095])) {
msarett600c7372016-09-07 12:03:53 -0700382 outData->fNamed = kSRGB_SkGammaNamed;
msarett1b93bd12016-07-21 07:11:26 -0700383 return SkGammas::Type::kNamed_Type;
384 }
385 }
386
msarette54f4b72016-09-06 14:41:42 -0700387 // Otherwise, we will represent gamma with a table.
388 outData->fTable.fSize = count;
389 return SkGammas::Type::kTable_Type;
msarett1b93bd12016-07-21 07:11:26 -0700390 }
391 case kTAG_ParaCurveType: {
msarett1b93bd12016-07-21 07:11:26 -0700392 // Determine the format of the parametric curve tag.
msarett959d45b2016-07-21 13:19:03 -0700393 uint16_t format = read_big_endian_u16(src + 8);
msarett1b93bd12016-07-21 07:11:26 -0700394 if (format > kGABCDEF_ParaCurveType) {
395 SkColorSpacePrintf("Unsupported gamma tag type %d\n", type);
396 return SkGammas::Type::kNone_Type;
397 }
398
399 if (kExponential_ParaCurveType == format) {
400 tagBytes = 12 + 4;
401 if (len < tagBytes) {
402 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
403 return SkGammas::Type::kNone_Type;
404 }
405
406 // Y = X^g
407 float g = read_big_endian_16_dot_16(src + 12);
408
409 *outTagBytes = tagBytes;
410 return set_gamma_value(outData, g);
411 }
412
413 // Here's where the real parametric gammas start. There are many
414 // permutations of the same equations.
415 //
Matt Sarett24107172016-12-19 14:33:35 -0500416 // Y = (aX + b)^g + e for X >= d
417 // Y = cX + f otherwise
msarett1b93bd12016-07-21 07:11:26 -0700418 //
419 // We will fill in with zeros as necessary to always match the above form.
420 if (len < 24) {
421 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
422 return SkGammas::Type::kNone_Type;
423 }
424 float g = read_big_endian_16_dot_16(src + 12);
425 float a = read_big_endian_16_dot_16(src + 16);
426 float b = read_big_endian_16_dot_16(src + 20);
427 float c = 0.0f, d = 0.0f, e = 0.0f, f = 0.0f;
428 switch(format) {
429 case kGAB_ParaCurveType:
430 tagBytes = 12 + 12;
431
432 // Y = (aX + b)^g for X >= -b/a
433 // Y = 0 otherwise
434 d = -b / a;
435 break;
436 case kGABC_ParaCurveType:
437 tagBytes = 12 + 16;
438 if (len < tagBytes) {
439 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
440 return SkGammas::Type::kNone_Type;
441 }
442
Matt Sarett24107172016-12-19 14:33:35 -0500443 // Y = (aX + b)^g + e for X >= -b/a
444 // Y = e otherwise
445 e = read_big_endian_16_dot_16(src + 24);
msarett1b93bd12016-07-21 07:11:26 -0700446 d = -b / a;
Matt Sarett24107172016-12-19 14:33:35 -0500447 f = e;
msarett1b93bd12016-07-21 07:11:26 -0700448 break;
449 case kGABDE_ParaCurveType:
450 tagBytes = 12 + 20;
451 if (len < tagBytes) {
452 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
453 return SkGammas::Type::kNone_Type;
454 }
455
456 // Y = (aX + b)^g for X >= d
Matt Sarett24107172016-12-19 14:33:35 -0500457 // Y = cX otherwise
458 c = read_big_endian_16_dot_16(src + 24);
msarett1b93bd12016-07-21 07:11:26 -0700459 d = read_big_endian_16_dot_16(src + 28);
msarett1b93bd12016-07-21 07:11:26 -0700460 break;
461 case kGABCDEF_ParaCurveType:
462 tagBytes = 12 + 28;
463 if (len < tagBytes) {
464 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
465 return SkGammas::Type::kNone_Type;
466 }
467
Matt Sarett24107172016-12-19 14:33:35 -0500468 // Y = (aX + b)^g + e for X >= d
469 // Y = cX + f otherwise
msarett1b93bd12016-07-21 07:11:26 -0700470 c = read_big_endian_16_dot_16(src + 24);
471 d = read_big_endian_16_dot_16(src + 28);
472 e = read_big_endian_16_dot_16(src + 32);
473 f = read_big_endian_16_dot_16(src + 36);
474 break;
475 default:
476 SkASSERT(false);
477 return SkGammas::Type::kNone_Type;
478 }
479
msarett1b93bd12016-07-21 07:11:26 -0700480 outParams->fG = g;
481 outParams->fA = a;
482 outParams->fB = b;
483 outParams->fC = c;
484 outParams->fD = d;
485 outParams->fE = e;
486 outParams->fF = f;
Matt Sarettdf44fc52016-10-11 16:57:50 -0400487
488 if (!is_valid_transfer_fn(*outParams)) {
489 return SkGammas::Type::kNone_Type;
490 }
491
492 if (is_almost_srgb(*outParams)) {
493 outData->fNamed = kSRGB_SkGammaNamed;
494 return SkGammas::Type::kNamed_Type;
495 }
496
497 if (is_almost_2dot2(*outParams)) {
498 outData->fNamed = k2Dot2Curve_SkGammaNamed;
499 return SkGammas::Type::kNamed_Type;
500 }
501
502 *outTagBytes = tagBytes;
msarett1b93bd12016-07-21 07:11:26 -0700503 return SkGammas::Type::kParam_Type;
504 }
505 default:
506 SkColorSpacePrintf("Unsupported gamma tag type %d\n", type);
507 return SkGammas::Type::kNone_Type;
508 }
509}
510
511/**
512 * Returns the additional size in bytes needed to store the gamma tag.
513 */
514static size_t gamma_alloc_size(SkGammas::Type type, const SkGammas::Data& data) {
515 switch (type) {
516 case SkGammas::Type::kNamed_Type:
517 case SkGammas::Type::kValue_Type:
518 return 0;
519 case SkGammas::Type::kTable_Type:
520 return sizeof(float) * data.fTable.fSize;
521 case SkGammas::Type::kParam_Type:
Matt Sarettdf44fc52016-10-11 16:57:50 -0400522 return sizeof(SkColorSpaceTransferFn);
msarett1b93bd12016-07-21 07:11:26 -0700523 default:
524 SkASSERT(false);
525 return 0;
526 }
527}
528
529/**
530 * Sets invalid gamma to the default value.
531 */
532static void handle_invalid_gamma(SkGammas::Type* type, SkGammas::Data* data) {
533 if (SkGammas::Type::kNone_Type == *type) {
534 *type = SkGammas::Type::kNamed_Type;
msarett712bb1e2016-09-05 10:33:12 -0700535
536 // Guess sRGB in the case of a malformed transfer function.
msarett600c7372016-09-07 12:03:53 -0700537 data->fNamed = kSRGB_SkGammaNamed;
msarett1b93bd12016-07-21 07:11:26 -0700538 }
539}
540
541/**
542 * Finish loading the gammas, now that we have allocated memory for the SkGammas struct.
543 *
544 * There's nothing to do for the simple cases, but for table gammas we need to actually
545 * read the table into heap memory. And for parametric gammas, we need to copy over the
546 * parameter values.
547 *
548 * @param memory Pointer to start of the SkGammas memory block
549 * @param offset Bytes of memory (after the SkGammas struct) that are already in use.
550 * @param data In-out variable. Will fill in the offset to the table or parameters
551 * if necessary.
552 * @param params Parameters for gamma curve. Only initialized/used when we have a
553 * parametric gamma.
554 * @param src Pointer to start of the gamma tag.
555 *
556 * @return Additional bytes of memory that are being used by this gamma curve.
557 */
558static size_t load_gammas(void* memory, size_t offset, SkGammas::Type type,
Matt Sarettdf44fc52016-10-11 16:57:50 -0400559 SkGammas::Data* data, const SkColorSpaceTransferFn& params,
msarett1b93bd12016-07-21 07:11:26 -0700560 const uint8_t* src) {
561 void* storage = SkTAddOffset<void>(memory, offset + sizeof(SkGammas));
562
563 switch (type) {
564 case SkGammas::Type::kNamed_Type:
565 case SkGammas::Type::kValue_Type:
566 // Nothing to do here.
567 return 0;
568 case SkGammas::Type::kTable_Type: {
569 data->fTable.fOffset = offset;
570
571 float* outTable = (float*) storage;
572 const uint16_t* inTable = (const uint16_t*) (src + 12);
573 for (int i = 0; i < data->fTable.fSize; i++) {
msarett959d45b2016-07-21 13:19:03 -0700574 outTable[i] = (read_big_endian_u16((const uint8_t*) &inTable[i])) / 65535.0f;
msarett1b93bd12016-07-21 07:11:26 -0700575 }
576
577 return sizeof(float) * data->fTable.fSize;
578 }
579 case SkGammas::Type::kParam_Type:
580 data->fTable.fOffset = offset;
Matt Sarettdf44fc52016-10-11 16:57:50 -0400581 memcpy(storage, &params, sizeof(SkColorSpaceTransferFn));
582 return sizeof(SkColorSpaceTransferFn);
msarett1b93bd12016-07-21 07:11:26 -0700583 default:
584 SkASSERT(false);
585 return 0;
586 }
msarett085cad42016-06-28 07:16:40 -0700587}
588
raftias71de0722016-11-21 14:22:53 -0500589static constexpr uint32_t kTAG_AtoBType = SkSetFourByteTag('m', 'A', 'B', ' ');
590static constexpr uint32_t kTAG_lut8Type = SkSetFourByteTag('m', 'f', 't', '1');
591static constexpr uint32_t kTAG_lut16Type = SkSetFourByteTag('m', 'f', 't', '2');
msarett085cad42016-06-28 07:16:40 -0700592
msarett959d45b2016-07-21 13:19:03 -0700593static bool load_color_lut(sk_sp<SkColorLookUpTable>* colorLUT, uint32_t inputChannels,
raftias71de0722016-11-21 14:22:53 -0500594 size_t precision, const uint8_t gridPoints[3], const uint8_t* src,
595 size_t len) {
596 switch (precision) {
597 case 1: // 8-bit data
598 case 2: // 16-bit data
599 break;
600 default:
601 SkColorSpacePrintf("Color LUT precision must be 8-bit or 16-bit. Found: %d-bit\n",
602 8*precision);
603 return false;
msarett085cad42016-06-28 07:16:40 -0700604 }
msarett085cad42016-06-28 07:16:40 -0700605
raftias71de0722016-11-21 14:22:53 -0500606 uint32_t numEntries = SkColorLookUpTable::kOutputChannels;
msarett085cad42016-06-28 07:16:40 -0700607 for (uint32_t i = 0; i < inputChannels; i++) {
Matt Sarett9d687df2017-05-05 10:38:01 -0400608 if (1 >= gridPoints[i]) {
609 SkColorSpacePrintf("Each input channel must have at least two grid points.");
msarett085cad42016-06-28 07:16:40 -0700610 return false;
611 }
612
raftias71de0722016-11-21 14:22:53 -0500613 if (!safe_mul(numEntries, gridPoints[i], &numEntries)) {
msarett085cad42016-06-28 07:16:40 -0700614 SkColorSpacePrintf("Too many entries in Color LUT.");
615 return false;
616 }
617 }
618
msarett085cad42016-06-28 07:16:40 -0700619 uint32_t clutBytes;
620 if (!safe_mul(numEntries, precision, &clutBytes)) {
raftias71de0722016-11-21 14:22:53 -0500621 SkColorSpacePrintf("Too many entries in Color LUT.\n");
msarett085cad42016-06-28 07:16:40 -0700622 return false;
623 }
624
raftias71de0722016-11-21 14:22:53 -0500625 if (len < clutBytes) {
626 SkColorSpacePrintf("Color LUT tag is too small (%d / %d bytes).\n", len, clutBytes);
msarett085cad42016-06-28 07:16:40 -0700627 return false;
628 }
629
630 // Movable struct colorLUT has ownership of fTable.
msarett959d45b2016-07-21 13:19:03 -0700631 void* memory = sk_malloc_throw(sizeof(SkColorLookUpTable) + sizeof(float) * numEntries);
632 *colorLUT = sk_sp<SkColorLookUpTable>(new (memory) SkColorLookUpTable(inputChannels,
633 gridPoints));
634
635 float* table = SkTAddOffset<float>(memory, sizeof(SkColorLookUpTable));
raftias71de0722016-11-21 14:22:53 -0500636 const uint8_t* ptr = src;
msarett085cad42016-06-28 07:16:40 -0700637 for (uint32_t i = 0; i < numEntries; i++, ptr += precision) {
638 if (1 == precision) {
raftias80739b82016-10-20 10:38:58 -0700639 table[i] = ((float) *ptr) / 255.0f;
msarett085cad42016-06-28 07:16:40 -0700640 } else {
msarett959d45b2016-07-21 13:19:03 -0700641 table[i] = ((float) read_big_endian_u16(ptr)) / 65535.0f;
msarett085cad42016-06-28 07:16:40 -0700642 }
643 }
644
645 return true;
646}
647
raftias14d28992016-11-21 12:59:36 -0500648/**
649 * Reads a matrix out of an A2B tag of an ICC profile.
650 * If |translate| is true, it will load a 3x4 matrix out that corresponds to a XYZ
651 * transform as well as a translation, and if |translate| is false it only loads a
652 * 3x3 matrix with no translation
653 *
654 * @param matrix The matrix to store the result in
655 * @param src Data to load the matrix out of.
656 * @param len The length of |src|.
657 * Must have 48 bytes if |translate| is set and 36 bytes otherwise.
658 * @param translate Whether to read the translation column or not
659 * @param pcs The profile connection space of the profile this matrix is for
660 *
661 * @return false on failure, true on success
662 */
663static bool load_matrix(SkMatrix44* matrix, const uint8_t* src, size_t len, bool translate,
664 SkColorSpace_A2B::PCS pcs) {
665 const size_t minLen = translate ? 48 : 36;
666 if (len < minLen) {
msarett085cad42016-06-28 07:16:40 -0700667 SkColorSpacePrintf("Matrix tag is too small (%d bytes).", len);
668 return false;
669 }
670
raftias14d28992016-11-21 12:59:36 -0500671 float encodingFactor;
672 switch (pcs) {
673 case SkColorSpace_A2B::PCS::kLAB:
674 encodingFactor = 1.f;
675 break;
676 case SkColorSpace_A2B::PCS::kXYZ:
677 encodingFactor = 65535 / 32768.f;
678 break;
679 default:
680 encodingFactor = 1.f;
681 SkASSERT(false);
682 break;
683 }
msarett085cad42016-06-28 07:16:40 -0700684 float array[16];
raftias14d28992016-11-21 12:59:36 -0500685 array[ 0] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src));
686 array[ 1] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 4));
687 array[ 2] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 8));
688
689 array[ 4] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 12));
690 array[ 5] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 16));
691 array[ 6] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 20));
692
693 array[ 8] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 24));
694 array[ 9] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 28));
695 array[10] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 32));
696
697 if (translate) {
698 array[ 3] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 36)); // translate R
699 array[ 7] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 40)); // translate G
700 array[11] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 44)); // translate B
701 } else {
702 array[ 3] = 0.0f;
703 array[ 7] = 0.0f;
704 array[11] = 0.0f;
705 }
706
msarett085cad42016-06-28 07:16:40 -0700707 array[12] = 0.0f;
708 array[13] = 0.0f;
709 array[14] = 0.0f;
710 array[15] = 1.0f;
raftias14d28992016-11-21 12:59:36 -0500711 matrix->setRowMajorf(array);
712 SkColorSpacePrintf("A2B0 matrix loaded:\n");
713 for (int r = 0; r < 4; ++r) {
714 SkColorSpacePrintf("|");
715 for (int c = 0; c < 4; ++c) {
716 SkColorSpacePrintf(" %f ", matrix->get(r, c));
717 }
718 SkColorSpacePrintf("|\n");
719 }
msarett085cad42016-06-28 07:16:40 -0700720 return true;
721}
722
msarett600c7372016-09-07 12:03:53 -0700723static inline SkGammaNamed is_named(const sk_sp<SkGammas>& gammas) {
raftias54761282016-12-01 13:44:07 -0500724 for (uint8_t i = 0; i < gammas->channels(); ++i) {
725 if (!gammas->isNamed(i) || gammas->data(i).fNamed != gammas->data(0).fNamed) {
726 return kNonStandard_SkGammaNamed;
727 }
msarett0f0c4f02016-08-22 09:44:35 -0700728 }
raftias54761282016-12-01 13:44:07 -0500729 return gammas->data(0).fNamed;
msarett0f0c4f02016-08-22 09:44:35 -0700730}
731
raftias94888332016-10-18 10:02:51 -0700732/**
733 * Parse and load an entire stored curve. Handles invalid gammas as well.
734 *
735 * There's nothing to do for the simple cases, but for table gammas we need to actually
736 * read the table into heap memory. And for parametric gammas, we need to copy over the
737 * parameter values.
738 *
raftias54761282016-12-01 13:44:07 -0500739 * @param gammaNamed Out-variable. The named gamma curve.
740 * @param gammas Out-variable. The stored gamma curve information. Can be null if
741 * gammaNamed is a named curve
742 * @param inputChannels The number of gamma input channels
743 * @param rTagPtr Pointer to start of the gamma tag.
744 * @param taglen The size in bytes of the tag
raftias94888332016-10-18 10:02:51 -0700745 *
raftias54761282016-12-01 13:44:07 -0500746 * @return false on failure, true on success
raftias94888332016-10-18 10:02:51 -0700747 */
748static bool parse_and_load_gamma(SkGammaNamed* gammaNamed, sk_sp<SkGammas>* gammas,
raftias54761282016-12-01 13:44:07 -0500749 uint8_t inputChannels, const uint8_t* tagSrc, size_t tagLen) {
750 SkGammas::Data data[kMaxColorChannels];
751 SkColorSpaceTransferFn params[kMaxColorChannels];
752 SkGammas::Type type[kMaxColorChannels];
753 const uint8_t* tagPtr[kMaxColorChannels];
754
755 tagPtr[0] = tagSrc;
msarett0f0c4f02016-08-22 09:44:35 -0700756
raftias026f2232016-10-24 09:52:26 -0700757 *gammaNamed = kNonStandard_SkGammaNamed;
758
raftias94888332016-10-18 10:02:51 -0700759 // On an invalid first gamma, tagBytes remains set as zero. This causes the two
760 // subsequent to be treated as identical (which is what we want).
761 size_t tagBytes = 0;
raftias54761282016-12-01 13:44:07 -0500762 type[0] = parse_gamma(&data[0], &params[0], &tagBytes, tagPtr[0], tagLen);
763 handle_invalid_gamma(&type[0], &data[0]);
raftias94888332016-10-18 10:02:51 -0700764 size_t alignedTagBytes = SkAlign4(tagBytes);
765
raftias54761282016-12-01 13:44:07 -0500766 bool allChannelsSame = false;
767 if (inputChannels * alignedTagBytes <= tagLen) {
768 allChannelsSame = true;
769 for (uint8_t i = 1; i < inputChannels; ++i) {
770 if (0 != memcmp(tagSrc, tagSrc + i * alignedTagBytes, tagBytes)) {
771 allChannelsSame = false;
772 break;
773 }
774 }
775 }
776 if (allChannelsSame) {
777 if (SkGammas::Type::kNamed_Type == type[0]) {
778 *gammaNamed = data[0].fNamed;
raftias94888332016-10-18 10:02:51 -0700779 } else {
780 size_t allocSize = sizeof(SkGammas);
raftias54761282016-12-01 13:44:07 -0500781 return_if_false(safe_add(allocSize, gamma_alloc_size(type[0], data[0]), &allocSize),
raftias94888332016-10-18 10:02:51 -0700782 "SkGammas struct is too large to allocate");
783 void* memory = sk_malloc_throw(allocSize);
raftias54761282016-12-01 13:44:07 -0500784 *gammas = sk_sp<SkGammas>(new (memory) SkGammas(inputChannels));
785 load_gammas(memory, 0, type[0], &data[0], params[0], tagPtr[0]);
raftias94888332016-10-18 10:02:51 -0700786
raftias54761282016-12-01 13:44:07 -0500787 for (uint8_t channel = 0; channel < inputChannels; ++channel) {
788 (*gammas)->fType[channel] = type[0];
789 (*gammas)->fData[channel] = data[0];
790 }
raftias94888332016-10-18 10:02:51 -0700791 }
792 } else {
raftias54761282016-12-01 13:44:07 -0500793 for (uint8_t channel = 1; channel < inputChannels; ++channel) {
794 tagPtr[channel] = tagPtr[channel - 1] + alignedTagBytes;
795 tagLen = tagLen > alignedTagBytes ? tagLen - alignedTagBytes : 0;
796 tagBytes = 0;
797 type[channel] = parse_gamma(&data[channel], &params[channel], &tagBytes,
798 tagPtr[channel], tagLen);
799 handle_invalid_gamma(&type[channel], &data[channel]);
800 alignedTagBytes = SkAlign4(tagBytes);
801 }
raftias94888332016-10-18 10:02:51 -0700802
803 size_t allocSize = sizeof(SkGammas);
raftias54761282016-12-01 13:44:07 -0500804 for (uint8_t channel = 0; channel < inputChannels; ++channel) {
805 return_if_false(safe_add(allocSize, gamma_alloc_size(type[channel], data[channel]),
806 &allocSize),
807 "SkGammas struct is too large to allocate");
808 }
raftias94888332016-10-18 10:02:51 -0700809 void* memory = sk_malloc_throw(allocSize);
raftias54761282016-12-01 13:44:07 -0500810 *gammas = sk_sp<SkGammas>(new (memory) SkGammas(inputChannels));
raftias94888332016-10-18 10:02:51 -0700811
812 uint32_t offset = 0;
raftias54761282016-12-01 13:44:07 -0500813 for (uint8_t channel = 0; channel < inputChannels; ++channel) {
814 (*gammas)->fType[channel] = type[channel];
815 offset += load_gammas(memory,offset, type[channel], &data[channel], params[channel],
816 tagPtr[channel]);
817 (*gammas)->fData[channel] = data[channel];
raftias94888332016-10-18 10:02:51 -0700818
raftias54761282016-12-01 13:44:07 -0500819 }
raftias94888332016-10-18 10:02:51 -0700820 }
raftias71de0722016-11-21 14:22:53 -0500821
raftias94888332016-10-18 10:02:51 -0700822 if (kNonStandard_SkGammaNamed == *gammaNamed) {
823 *gammaNamed = is_named(*gammas);
824 if (kNonStandard_SkGammaNamed != *gammaNamed) {
825 // No need to keep the gammas struct, the enum is enough.
826 *gammas = nullptr;
827 }
828 }
829 return true;
830}
831
raftiase8ea07a2016-12-05 14:21:45 -0500832static bool is_lut_gamma_linear(const uint8_t* src, size_t count, size_t precision) {
833 // check for linear gamma (this is very common in lut gammas, as they aren't optional)
834 const float normalizeX = 1.f / (count - 1);
835 for (uint32_t x = 0; x < count; ++x) {
836 const float y = precision == 1 ? (src[x] / 255.f)
837 : (read_big_endian_u16(src + 2*x) / 65535.f);
838 if (!color_space_almost_equal(x * normalizeX, y)) {
839 return false;
840 }
841 }
842 return true;
843}
844
845static bool load_lut_gammas(sk_sp<SkGammas>* gammas, SkGammaNamed* gammaNamed, size_t numTables,
846 size_t entriesPerTable, size_t precision, const uint8_t* src,
847 size_t len) {
raftias71de0722016-11-21 14:22:53 -0500848 if (precision != 1 && precision != 2) {
849 SkColorSpacePrintf("Invalid gamma table precision %d\n", precision);
850 return false;
851 }
852 uint32_t totalEntries;
853 return_if_false(safe_mul(entriesPerTable, numTables, &totalEntries),
854 "Too many entries in gamma table.");
855 uint32_t readBytes;
856 return_if_false(safe_mul(precision, totalEntries, &readBytes),
857 "SkGammas struct is too large to read");
858 if (len < readBytes) {
859 SkColorSpacePrintf("Gamma table is too small. Provided: %d. Required: %d\n",
860 len, readBytes);
861 return false;
862 }
863
864 uint32_t writeBytesPerChannel;
865 return_if_false(safe_mul(sizeof(float), entriesPerTable, &writeBytesPerChannel),
866 "SkGammas struct is too large to allocate");
867 const size_t readBytesPerChannel = precision * entriesPerTable;
868 size_t numTablesToUse = 1;
869 for (size_t tableIndex = 1; tableIndex < numTables; ++tableIndex) {
870 if (0 != memcmp(src, src + readBytesPerChannel * tableIndex, readBytesPerChannel)) {
871 numTablesToUse = numTables;
872 break;
873 }
874 }
875
raftiase8ea07a2016-12-05 14:21:45 -0500876 if (1 == numTablesToUse) {
877 if (is_lut_gamma_linear(src, entriesPerTable, precision)) {
878 *gammaNamed = kLinear_SkGammaNamed;
879 return true;
880 }
881 }
882 *gammaNamed = kNonStandard_SkGammaNamed;
883
raftias71de0722016-11-21 14:22:53 -0500884 uint32_t writetableBytes;
885 return_if_false(safe_mul(numTablesToUse, writeBytesPerChannel, &writetableBytes),
886 "SkGammas struct is too large to allocate");
887 size_t allocSize = sizeof(SkGammas);
888 return_if_false(safe_add(allocSize, (size_t)writetableBytes, &allocSize),
889 "SkGammas struct is too large to allocate");
890
891 void* memory = sk_malloc_throw(allocSize);
raftias54761282016-12-01 13:44:07 -0500892 *gammas = sk_sp<SkGammas>(new (memory) SkGammas(numTables));
raftias71de0722016-11-21 14:22:53 -0500893
894 for (size_t tableIndex = 0; tableIndex < numTablesToUse; ++tableIndex) {
895 const uint8_t* ptr = src + readBytesPerChannel * tableIndex;
896 const size_t offset = sizeof(SkGammas) + tableIndex * writeBytesPerChannel;
897 float* table = SkTAddOffset<float>(memory, offset);
898 if (1 == precision) {
899 for (uint32_t i = 0; i < entriesPerTable; ++i, ptr += 1) {
900 table[i] = ((float) *ptr) / 255.0f;
901 }
902 } else if (2 == precision) {
903 for (uint32_t i = 0; i < entriesPerTable; ++i, ptr += 2) {
904 table[i] = ((float) read_big_endian_u16(ptr)) / 65535.0f;
905 }
906 }
907 }
908
raftias54761282016-12-01 13:44:07 -0500909 SkASSERT(1 == numTablesToUse|| numTables == numTablesToUse);
raftias71de0722016-11-21 14:22:53 -0500910
raftias54761282016-12-01 13:44:07 -0500911 size_t tableOffset = 0;
912 for (size_t tableIndex = 0; tableIndex < numTables; ++tableIndex) {
913 (*gammas)->fType[tableIndex] = SkGammas::Type::kTable_Type;
914 (*gammas)->fData[tableIndex].fTable.fOffset = tableOffset;
915 (*gammas)->fData[tableIndex].fTable.fSize = entriesPerTable;
916 if (numTablesToUse > 1) {
917 tableOffset += writeBytesPerChannel;
918 }
raftias71de0722016-11-21 14:22:53 -0500919 }
920
raftias71de0722016-11-21 14:22:53 -0500921 return true;
922}
923
raftias026f2232016-10-24 09:52:26 -0700924bool load_a2b0_a_to_b_type(std::vector<SkColorSpace_A2B::Element>* elements, const uint8_t* src,
raftias14d28992016-11-21 12:59:36 -0500925 size_t len, SkColorSpace_A2B::PCS pcs) {
raftias71de0722016-11-21 14:22:53 -0500926 SkASSERT(len >= 32);
raftias026f2232016-10-24 09:52:26 -0700927 // Read the number of channels. The four bytes (4-7) that we skipped are reserved and
msarett085cad42016-06-28 07:16:40 -0700928 // must be zero.
raftias026f2232016-10-24 09:52:26 -0700929 const uint8_t inputChannels = src[8];
930 const uint8_t outputChannels = src[9];
raftias54761282016-12-01 13:44:07 -0500931 if (SkColorLookUpTable::kOutputChannels != outputChannels) {
932 // We only handle RGB outputs. The number of output channels must be 3.
933 SkColorSpacePrintf("Output channels (%d) must equal 3 in A to B tag.\n", outputChannels);
msarett085cad42016-06-28 07:16:40 -0700934 return false;
935 }
raftias54761282016-12-01 13:44:07 -0500936 if (inputChannels == 0 || inputChannels > 4) {
937 // And we only support 4 input channels.
938 // ICC says up to 16 but our decode can only handle 4.
939 // It could easily be extended to support up to 8, but we only allow CMYK/RGB
940 // input color spaces which are 3 and 4 so let's restrict it to 4 instead of 8.
941 // We can always change this check when we support bigger input spaces.
942 SkColorSpacePrintf("Input channels (%d) must be between 1 and 4 in A to B tag.\n",
943 inputChannels);
944 return false;
945 }
946
raftias14d28992016-11-21 12:59:36 -0500947
raftias026f2232016-10-24 09:52:26 -0700948 // It is important that these are loaded in the order of application, as the
949 // order you construct an A2B color space's elements is the order it is applied
msarett085cad42016-06-28 07:16:40 -0700950
msarett085cad42016-06-28 07:16:40 -0700951 // If the offset is non-zero it indicates that the element is present.
raftias026f2232016-10-24 09:52:26 -0700952 const uint32_t offsetToACurves = read_big_endian_i32(src + 28);
raftias94888332016-10-18 10:02:51 -0700953 if (0 != offsetToACurves && offsetToACurves < len) {
954 const size_t tagLen = len - offsetToACurves;
raftias026f2232016-10-24 09:52:26 -0700955 SkGammaNamed gammaNamed;
956 sk_sp<SkGammas> gammas;
raftias54761282016-12-01 13:44:07 -0500957 if (!parse_and_load_gamma(&gammaNamed, &gammas, inputChannels, src + offsetToACurves,
958 tagLen)) {
raftias94888332016-10-18 10:02:51 -0700959 return false;
960 }
raftias026f2232016-10-24 09:52:26 -0700961 if (gammas) {
962 elements->push_back(SkColorSpace_A2B::Element(std::move(gammas)));
raftias54761282016-12-01 13:44:07 -0500963 } else if (kLinear_SkGammaNamed != gammaNamed) {
964 elements->push_back(SkColorSpace_A2B::Element(gammaNamed, inputChannels));
raftias026f2232016-10-24 09:52:26 -0700965 }
msarett085cad42016-06-28 07:16:40 -0700966 }
967
raftias026f2232016-10-24 09:52:26 -0700968 const uint32_t offsetToColorLUT = read_big_endian_i32(src + 24);
msarett085cad42016-06-28 07:16:40 -0700969 if (0 != offsetToColorLUT && offsetToColorLUT < len) {
raftias026f2232016-10-24 09:52:26 -0700970 sk_sp<SkColorLookUpTable> colorLUT;
raftias71de0722016-11-21 14:22:53 -0500971 const uint8_t* clutSrc = src + offsetToColorLUT;
972 const size_t clutLen = len - offsetToColorLUT;
973 // 16 bytes reserved for grid points, 1 for precision, 3 for padding.
974 // The color LUT data follows after this header.
975 static constexpr uint32_t kColorLUTHeaderSize = 20;
976 if (clutLen < kColorLUTHeaderSize) {
977 SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", clutLen);
978 return false;
979 }
980
raftias54761282016-12-01 13:44:07 -0500981 SkASSERT(inputChannels <= kMaxColorChannels);
982 uint8_t gridPoints[kMaxColorChannels];
raftias71de0722016-11-21 14:22:53 -0500983 for (uint32_t i = 0; i < inputChannels; ++i) {
984 gridPoints[i] = clutSrc[i];
985 }
986 // Space is provided for a maximum of 16 input channels.
987 // Now we determine the precision of the table values.
988 const uint8_t precision = clutSrc[16];
989 if (!load_color_lut(&colorLUT, inputChannels, precision, gridPoints,
990 clutSrc + kColorLUTHeaderSize, clutLen - kColorLUTHeaderSize)) {
msarett085cad42016-06-28 07:16:40 -0700991 SkColorSpacePrintf("Failed to read color LUT from A to B tag.\n");
raftias94888332016-10-18 10:02:51 -0700992 return false;
msarett085cad42016-06-28 07:16:40 -0700993 }
raftias026f2232016-10-24 09:52:26 -0700994 elements->push_back(SkColorSpace_A2B::Element(std::move(colorLUT)));
msarett085cad42016-06-28 07:16:40 -0700995 }
996
raftias026f2232016-10-24 09:52:26 -0700997 const uint32_t offsetToMCurves = read_big_endian_i32(src + 20);
msarett085cad42016-06-28 07:16:40 -0700998 if (0 != offsetToMCurves && offsetToMCurves < len) {
raftias94888332016-10-18 10:02:51 -0700999 const size_t tagLen = len - offsetToMCurves;
raftias026f2232016-10-24 09:52:26 -07001000 SkGammaNamed gammaNamed;
1001 sk_sp<SkGammas> gammas;
raftias54761282016-12-01 13:44:07 -05001002 if (!parse_and_load_gamma(&gammaNamed, &gammas, outputChannels, src + offsetToMCurves,
1003 tagLen)) {
raftias94888332016-10-18 10:02:51 -07001004 return false;
msarett0f0c4f02016-08-22 09:44:35 -07001005 }
raftias026f2232016-10-24 09:52:26 -07001006 if (gammas) {
1007 elements->push_back(SkColorSpace_A2B::Element(std::move(gammas)));
raftias54761282016-12-01 13:44:07 -05001008 } else if (kLinear_SkGammaNamed != gammaNamed) {
1009 elements->push_back(SkColorSpace_A2B::Element(gammaNamed, outputChannels));
raftias026f2232016-10-24 09:52:26 -07001010 }
msarett0f0c4f02016-08-22 09:44:35 -07001011 }
1012
raftias026f2232016-10-24 09:52:26 -07001013 const uint32_t offsetToMatrix = read_big_endian_i32(src + 16);
msarett085cad42016-06-28 07:16:40 -07001014 if (0 != offsetToMatrix && offsetToMatrix < len) {
raftias026f2232016-10-24 09:52:26 -07001015 SkMatrix44 matrix(SkMatrix44::kUninitialized_Constructor);
raftias14d28992016-11-21 12:59:36 -05001016 if (!load_matrix(&matrix, src + offsetToMatrix, len - offsetToMatrix, true, pcs)) {
msarett085cad42016-06-28 07:16:40 -07001017 SkColorSpacePrintf("Failed to read matrix from A to B tag.\n");
raftias54761282016-12-01 13:44:07 -05001018 } else if (!matrix.isIdentity()) {
raftias026f2232016-10-24 09:52:26 -07001019 elements->push_back(SkColorSpace_A2B::Element(matrix));
raftias94888332016-10-18 10:02:51 -07001020 }
1021 }
raftias14d28992016-11-21 12:59:36 -05001022
raftias026f2232016-10-24 09:52:26 -07001023 const uint32_t offsetToBCurves = read_big_endian_i32(src + 12);
raftias94888332016-10-18 10:02:51 -07001024 if (0 != offsetToBCurves && offsetToBCurves < len) {
1025 const size_t tagLen = len - offsetToBCurves;
raftias026f2232016-10-24 09:52:26 -07001026 SkGammaNamed gammaNamed;
1027 sk_sp<SkGammas> gammas;
raftias54761282016-12-01 13:44:07 -05001028 if (!parse_and_load_gamma(&gammaNamed, &gammas, outputChannels, src + offsetToBCurves,
1029 tagLen)) {
raftias94888332016-10-18 10:02:51 -07001030 return false;
msarett085cad42016-06-28 07:16:40 -07001031 }
raftias026f2232016-10-24 09:52:26 -07001032 if (gammas) {
1033 elements->push_back(SkColorSpace_A2B::Element(std::move(gammas)));
raftias54761282016-12-01 13:44:07 -05001034 } else if (kLinear_SkGammaNamed != gammaNamed) {
1035 elements->push_back(SkColorSpace_A2B::Element(gammaNamed, outputChannels));
raftias026f2232016-10-24 09:52:26 -07001036 }
msarett085cad42016-06-28 07:16:40 -07001037 }
1038
1039 return true;
1040}
1041
raftias71de0722016-11-21 14:22:53 -05001042bool load_a2b0_lutn_type(std::vector<SkColorSpace_A2B::Element>* elements, const uint8_t* src,
1043 size_t len, SkColorSpace_A2B::PCS pcs) {
1044 const uint32_t type = read_big_endian_u32(src);
1045 switch (type) {
1046 case kTAG_lut8Type:
1047 SkASSERT(len >= 48);
1048 break;
1049 case kTAG_lut16Type:
1050 SkASSERT(len >= 52);
1051 break;
1052 default:
1053 SkASSERT(false);
1054 return false;
1055 }
1056 // Read the number of channels.
1057 // The four bytes (4-7) that we skipped are reserved and must be zero.
1058 const uint8_t inputChannels = src[8];
1059 const uint8_t outputChannels = src[9];
raftias54761282016-12-01 13:44:07 -05001060 if (SkColorLookUpTable::kOutputChannels != outputChannels) {
1061 // We only handle RGB outputs. The number of output channels must be 3.
1062 SkColorSpacePrintf("Output channels (%d) must equal 3 in A to B tag.\n", outputChannels);
1063 return false;
1064 }
1065 if (inputChannels == 0 || inputChannels > 4) {
1066 // And we only support 4 input channels.
1067 // ICC says up to 16 but our decode can only handle 4.
1068 // It could easily be extended to support up to 8, but we only allow CMYK/RGB
1069 // input color spaces which are 3 and 4 so let's restrict it to 4 instead of 8.
1070 // We can always change this check when we support bigger input spaces.
1071 SkColorSpacePrintf("Input channels (%d) must be between 1 and 4 in A to B tag.\n",
1072 inputChannels);
raftias71de0722016-11-21 14:22:53 -05001073 return false;
1074 }
1075
1076 const uint8_t clutGridPoints = src[10];
1077 // 11th byte reserved for padding (required to be zero)
1078
1079 SkMatrix44 matrix(SkMatrix44::kUninitialized_Constructor);
1080 load_matrix(&matrix, &src[12], len - 12, false, pcs);
raftias54761282016-12-01 13:44:07 -05001081 if (!matrix.isIdentity()) {
1082 // ICC specs (10.8/10.9) say lut8/16Type profiles must have identity matrices
1083 // if the input color space is not PCSXYZ, and we do not support PCSXYZ input color spaces
1084 // so we should never encounter a non-identity matrix here.
raftiasfc6d9e92016-12-08 11:56:34 -05001085 // However, 2 test images from the ICC website have RGB input spaces and non-identity
1086 // matrices so we're not going to fail here, despite being against the spec.
1087 SkColorSpacePrintf("Warning: non-Identity matrix found in non-XYZ input color space"
1088 "lut profile");
1089 elements->push_back(SkColorSpace_A2B::Element(matrix));
raftias54761282016-12-01 13:44:07 -05001090 }
raftias71de0722016-11-21 14:22:53 -05001091
1092 size_t dataOffset = 48;
1093 // # of input table entries
1094 size_t inTableEntries = 256;
1095 // # of output table entries
1096 size_t outTableEntries = 256;
1097 size_t precision = 1;
1098 if (kTAG_lut16Type == type) {
1099 dataOffset = 52;
1100 inTableEntries = read_big_endian_u16(src + 48);
1101 outTableEntries = read_big_endian_u16(src + 50);
1102 precision = 2;
raftias8daef3e2016-11-22 13:21:22 -05001103
1104 constexpr size_t kMaxLut16GammaEntries = 4096;
1105 if (inTableEntries < 2) {
1106 SkColorSpacePrintf("Too few (%d) input gamma table entries. Must have at least 2.\n",
1107 inTableEntries);
1108 return false;
1109 } else if (inTableEntries > kMaxLut16GammaEntries) {
1110 SkColorSpacePrintf("Too many (%d) input gamma table entries. Must have at most %d.\n",
1111 inTableEntries, kMaxLut16GammaEntries);
1112 return false;
1113 }
1114
1115 if (outTableEntries < 2) {
1116 SkColorSpacePrintf("Too few (%d) output gamma table entries. Must have at least 2.\n",
1117 outTableEntries);
1118 return false;
1119 } else if (outTableEntries > kMaxLut16GammaEntries) {
1120 SkColorSpacePrintf("Too many (%d) output gamma table entries. Must have at most %d.\n",
1121 outTableEntries, kMaxLut16GammaEntries);
1122 return false;
1123 }
raftias71de0722016-11-21 14:22:53 -05001124 }
1125
1126 const size_t inputOffset = dataOffset;
1127 return_if_false(len >= inputOffset, "A2B0 lutnType tag too small for input gamma table");
1128 sk_sp<SkGammas> inputGammas;
raftiase8ea07a2016-12-05 14:21:45 -05001129 SkGammaNamed inputGammaNamed;
1130 if (!load_lut_gammas(&inputGammas, &inputGammaNamed, inputChannels, inTableEntries, precision,
raftias71de0722016-11-21 14:22:53 -05001131 src + inputOffset, len - inputOffset)) {
1132 SkColorSpacePrintf("Failed to read input gammas from lutnType tag.\n");
1133 return false;
1134 }
raftiase8ea07a2016-12-05 14:21:45 -05001135 SkASSERT(inputGammas || inputGammaNamed != kNonStandard_SkGammaNamed);
raftias54761282016-12-01 13:44:07 -05001136 if (kLinear_SkGammaNamed != inputGammaNamed) {
1137 if (kNonStandard_SkGammaNamed != inputGammaNamed) {
1138 elements->push_back(SkColorSpace_A2B::Element(inputGammaNamed, inputChannels));
1139 } else {
1140 elements->push_back(SkColorSpace_A2B::Element(std::move(inputGammas)));
1141 }
1142 }
raftias71de0722016-11-21 14:22:53 -05001143
1144 const size_t clutOffset = inputOffset + precision*inTableEntries*inputChannels;
1145 return_if_false(len >= clutOffset, "A2B0 lutnType tag too small for CLUT");
1146 sk_sp<SkColorLookUpTable> colorLUT;
raftias54761282016-12-01 13:44:07 -05001147 const uint8_t gridPoints[kMaxColorChannels] = {
1148 clutGridPoints, clutGridPoints, clutGridPoints, clutGridPoints
1149 };
raftias71de0722016-11-21 14:22:53 -05001150 if (!load_color_lut(&colorLUT, inputChannels, precision, gridPoints, src + clutOffset,
1151 len - clutOffset)) {
1152 SkColorSpacePrintf("Failed to read color LUT from lutnType tag.\n");
1153 return false;
1154 }
1155 SkASSERT(colorLUT);
1156 elements->push_back(SkColorSpace_A2B::Element(std::move(colorLUT)));
1157
1158 size_t clutSize = precision * outputChannels;
1159 for (int i = 0; i < inputChannels; ++i) {
1160 clutSize *= clutGridPoints;
1161 }
1162 const size_t outputOffset = clutOffset + clutSize;
1163 return_if_false(len >= outputOffset, "A2B0 lutnType tag too small for output gamma table");
1164 sk_sp<SkGammas> outputGammas;
raftiase8ea07a2016-12-05 14:21:45 -05001165 SkGammaNamed outputGammaNamed;
1166 if (!load_lut_gammas(&outputGammas, &outputGammaNamed, outputChannels, outTableEntries,
1167 precision, src + outputOffset, len - outputOffset)) {
raftias71de0722016-11-21 14:22:53 -05001168 SkColorSpacePrintf("Failed to read output gammas from lutnType tag.\n");
1169 return false;
1170 }
raftiase8ea07a2016-12-05 14:21:45 -05001171 SkASSERT(outputGammas || outputGammaNamed != kNonStandard_SkGammaNamed);
raftias54761282016-12-01 13:44:07 -05001172 if (kLinear_SkGammaNamed != outputGammaNamed) {
1173 if (kNonStandard_SkGammaNamed != outputGammaNamed) {
1174 elements->push_back(SkColorSpace_A2B::Element(outputGammaNamed, outputChannels));
1175 } else {
1176 elements->push_back(SkColorSpace_A2B::Element(std::move(outputGammas)));
1177 }
1178 }
raftias71de0722016-11-21 14:22:53 -05001179
1180 return true;
1181}
1182
Matt Sarett523116d2017-01-12 18:36:38 -05001183static inline int icf_channels(SkColorSpace_Base::ICCTypeFlag iccType) {
1184 switch (iccType) {
1185 case SkColorSpace_Base::kRGB_ICCTypeFlag:
raftias54761282016-12-01 13:44:07 -05001186 return 3;
Matt Sarett523116d2017-01-12 18:36:38 -05001187 case SkColorSpace_Base::kCMYK_ICCTypeFlag:
raftias54761282016-12-01 13:44:07 -05001188 return 4;
1189 default:
1190 SkASSERT(false);
Matt Sarett523116d2017-01-12 18:36:38 -05001191 return 0;
raftias54761282016-12-01 13:44:07 -05001192 }
1193}
1194
raftias026f2232016-10-24 09:52:26 -07001195static bool load_a2b0(std::vector<SkColorSpace_A2B::Element>* elements, const uint8_t* src,
raftias54761282016-12-01 13:44:07 -05001196 size_t len, SkColorSpace_A2B::PCS pcs,
Matt Sarett523116d2017-01-12 18:36:38 -05001197 SkColorSpace_Base::ICCTypeFlag iccType) {
Matt Sarett6e834792017-04-18 11:08:29 -04001198 if (len < 4) {
1199 return false;
1200 }
raftias026f2232016-10-24 09:52:26 -07001201 const uint32_t type = read_big_endian_u32(src);
Matt Sarett6e834792017-04-18 11:08:29 -04001202
raftias026f2232016-10-24 09:52:26 -07001203 switch (type) {
1204 case kTAG_AtoBType:
1205 if (len < 32) {
1206 SkColorSpacePrintf("A to B tag is too small (%d bytes).", len);
1207 return false;
1208 }
1209 SkColorSpacePrintf("A2B0 tag is of type lutAtoBType\n");
raftias54761282016-12-01 13:44:07 -05001210 if (!load_a2b0_a_to_b_type(elements, src, len, pcs)) {
1211 return false;
1212 }
1213 break;
raftias71de0722016-11-21 14:22:53 -05001214 case kTAG_lut8Type:
1215 if (len < 48) {
1216 SkColorSpacePrintf("lut8 tag is too small (%d bytes).", len);
1217 return false;
1218 }
1219 SkColorSpacePrintf("A2B0 tag of type lut8Type\n");
raftias54761282016-12-01 13:44:07 -05001220 if (!load_a2b0_lutn_type(elements, src, len, pcs)) {
1221 return false;
1222 }
1223 break;
raftias71de0722016-11-21 14:22:53 -05001224 case kTAG_lut16Type:
1225 if (len < 52) {
1226 SkColorSpacePrintf("lut16 tag is too small (%d bytes).", len);
1227 return false;
1228 }
1229 SkColorSpacePrintf("A2B0 tag of type lut16Type\n");
raftias54761282016-12-01 13:44:07 -05001230 if (!load_a2b0_lutn_type(elements, src, len, pcs)) {
1231 return false;
1232 }
1233 break;
raftias026f2232016-10-24 09:52:26 -07001234 default:
1235 SkColorSpacePrintf("Unsupported A to B tag type: %c%c%c%c\n", (type>>24)&0xFF,
1236 (type>>16)&0xFF, (type>>8)&0xFF, type&0xFF);
raftias54761282016-12-01 13:44:07 -05001237 return false;
raftias026f2232016-10-24 09:52:26 -07001238 }
raftias2bb35922016-12-01 16:40:51 -05001239 SkASSERT(SkColorSpace_A2B::PCS::kLAB == pcs || SkColorSpace_A2B::PCS::kXYZ == pcs);
1240 static constexpr int kPCSChannels = 3; // must be PCSLAB or PCSXYZ
1241 if (elements->empty()) {
Matt Sarett523116d2017-01-12 18:36:38 -05001242 return kPCSChannels == icf_channels(iccType);
raftias2bb35922016-12-01 16:40:51 -05001243 }
raftias54761282016-12-01 13:44:07 -05001244 // now let's verify that the input/output channels of each A2B element actually match up
Matt Sarett523116d2017-01-12 18:36:38 -05001245 if (icf_channels(iccType) != elements->front().inputChannels()) {
raftias54761282016-12-01 13:44:07 -05001246 SkColorSpacePrintf("Input channel count does not match first A2B element's input count");
1247 return false;
1248 }
1249 for (size_t i = 1; i < elements->size(); ++i) {
1250 if ((*elements)[i - 1].outputChannels() != (*elements)[i].inputChannels()) {
1251 SkColorSpacePrintf("A2B elements don't agree in input/output channel counts");
1252 return false;
1253 }
1254 }
raftias54761282016-12-01 13:44:07 -05001255 if (kPCSChannels != elements->back().outputChannels()) {
1256 SkColorSpacePrintf("PCS channel count doesn't match last A2B element's output count");
1257 return false;
1258 }
1259 return true;
raftias026f2232016-10-24 09:52:26 -07001260}
1261
msarett1b93bd12016-07-21 07:11:26 -07001262static bool tag_equals(const ICCTag* a, const ICCTag* b, const uint8_t* base) {
1263 if (!a || !b) {
1264 return a == b;
1265 }
1266
1267 if (a->fLength != b->fLength) {
1268 return false;
1269 }
1270
1271 if (a->fOffset == b->fOffset) {
1272 return true;
1273 }
1274
1275 return !memcmp(a->addr(base), b->addr(base), a->fLength);
1276}
1277
Matt Sarett01866612016-10-31 13:41:57 -04001278static inline bool is_close_to_d50(const SkMatrix44& matrix) {
1279 // rX + gX + bX
1280 float X = matrix.getFloat(0, 0) + matrix.getFloat(0, 1) + matrix.getFloat(0, 2);
1281
1282 // rY + gY + bY
1283 float Y = matrix.getFloat(1, 0) + matrix.getFloat(1, 1) + matrix.getFloat(1, 2);
1284
1285 // rZ + gZ + bZ
1286 float Z = matrix.getFloat(2, 0) + matrix.getFloat(2, 1) + matrix.getFloat(2, 2);
1287
1288 static const float kD50_WhitePoint[3] = { 0.96420f, 1.00000f, 0.82491f };
1289
1290 // This is a bit more lenient than QCMS and Adobe. Is there a reason to be stricter here?
1291 return (SkTAbs(X - kD50_WhitePoint[0]) <= 0.04f) &&
1292 (SkTAbs(Y - kD50_WhitePoint[1]) <= 0.04f) &&
1293 (SkTAbs(Z - kD50_WhitePoint[2]) <= 0.04f);
1294}
1295
Matt Sarett595599f2016-12-15 13:05:53 -05001296static sk_sp<SkColorSpace> make_xyz(const ICCProfileHeader& header, ICCTag* tags, int tagCount,
1297 const uint8_t* base, sk_sp<SkData> profileData) {
1298 if (kLAB_PCSSpace == header.fPCS) {
1299 return nullptr;
1300 }
1301
1302 // Recognize the rXYZ, gXYZ, and bXYZ tags.
1303 const ICCTag* r = ICCTag::Find(tags, tagCount, kTAG_rXYZ);
1304 const ICCTag* g = ICCTag::Find(tags, tagCount, kTAG_gXYZ);
1305 const ICCTag* b = ICCTag::Find(tags, tagCount, kTAG_bXYZ);
1306 if (!r || !g || !b) {
1307 return nullptr;
1308 }
1309
1310 float toXYZ[9];
1311 if (!load_xyz(&toXYZ[0], r->addr(base), r->fLength) ||
1312 !load_xyz(&toXYZ[3], g->addr(base), g->fLength) ||
1313 !load_xyz(&toXYZ[6], b->addr(base), b->fLength))
1314 {
1315 return_null("Need valid rgb tags for XYZ space");
1316 }
1317 SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor);
1318 mat.set3x3(toXYZ[0], toXYZ[1], toXYZ[2],
1319 toXYZ[3], toXYZ[4], toXYZ[5],
1320 toXYZ[6], toXYZ[7], toXYZ[8]);
1321 if (!is_close_to_d50(mat)) {
1322 return_null("XYZ matrix is not D50");
1323 }
1324
1325 // If some, but not all, of the gamma tags are missing, assume that all
1326 // gammas are meant to be the same.
1327 r = ICCTag::Find(tags, tagCount, kTAG_rTRC);
1328 g = ICCTag::Find(tags, tagCount, kTAG_gTRC);
1329 b = ICCTag::Find(tags, tagCount, kTAG_bTRC);
1330 if ((!r || !g || !b)) {
1331 if (!r) {
1332 r = g ? g : b;
1333 }
1334 if (!g) {
1335 g = r ? r : b;
1336 }
1337 if (!b) {
1338 b = r ? r : g;
1339 }
1340 }
1341
1342 SkGammaNamed gammaNamed = kNonStandard_SkGammaNamed;
1343 sk_sp<SkGammas> gammas = nullptr;
1344 size_t tagBytes;
1345 if (r && g && b) {
1346 if (tag_equals(r, g, base) && tag_equals(g, b, base)) {
1347 SkGammas::Data data;
1348 SkColorSpaceTransferFn params;
1349 SkGammas::Type type =
1350 parse_gamma(&data, &params, &tagBytes, r->addr(base), r->fLength);
1351 handle_invalid_gamma(&type, &data);
1352
1353 if (SkGammas::Type::kNamed_Type == type) {
1354 gammaNamed = data.fNamed;
1355 } else {
1356 size_t allocSize = sizeof(SkGammas);
1357 if (!safe_add(allocSize, gamma_alloc_size(type, data), &allocSize)) {
1358 return_null("SkGammas struct is too large to allocate");
1359 }
1360 void* memory = sk_malloc_throw(allocSize);
1361 gammas = sk_sp<SkGammas>(new (memory) SkGammas(3));
1362 load_gammas(memory, 0, type, &data, params, r->addr(base));
1363
1364 for (int i = 0; i < 3; ++i) {
1365 gammas->fType[i] = type;
1366 gammas->fData[i] = data;
1367 }
1368 }
1369 } else {
1370 SkGammas::Data rData;
1371 SkColorSpaceTransferFn rParams;
1372 SkGammas::Type rType =
1373 parse_gamma(&rData, &rParams, &tagBytes, r->addr(base), r->fLength);
1374 handle_invalid_gamma(&rType, &rData);
1375
1376 SkGammas::Data gData;
1377 SkColorSpaceTransferFn gParams;
1378 SkGammas::Type gType =
1379 parse_gamma(&gData, &gParams, &tagBytes, g->addr(base), g->fLength);
1380 handle_invalid_gamma(&gType, &gData);
1381
1382 SkGammas::Data bData;
1383 SkColorSpaceTransferFn bParams;
1384 SkGammas::Type bType =
1385 parse_gamma(&bData, &bParams, &tagBytes, b->addr(base), b->fLength);
1386 handle_invalid_gamma(&bType, &bData);
1387
1388 size_t allocSize = sizeof(SkGammas);
1389 if (!safe_add(allocSize, gamma_alloc_size(rType, rData), &allocSize) ||
1390 !safe_add(allocSize, gamma_alloc_size(gType, gData), &allocSize) ||
1391 !safe_add(allocSize, gamma_alloc_size(bType, bData), &allocSize)) {
1392 return_null("SkGammas struct is too large to allocate");
1393 }
1394 void* memory = sk_malloc_throw(allocSize);
1395 gammas = sk_sp<SkGammas>(new (memory) SkGammas(3));
1396
1397 uint32_t offset = 0;
1398 gammas->fType[0] = rType;
1399 offset += load_gammas(memory, offset, rType, &rData, rParams,
1400 r->addr(base));
1401
1402 gammas->fType[1] = gType;
1403 offset += load_gammas(memory, offset, gType, &gData, gParams,
1404 g->addr(base));
1405
1406 gammas->fType[2] = bType;
1407 load_gammas(memory, offset, bType, &bData, bParams, b->addr(base));
1408
1409 gammas->fData[0] = rData;
1410 gammas->fData[1] = gData;
1411 gammas->fData[2] = bData;
1412 }
1413 } else {
1414 // Guess sRGB if the profile is missing transfer functions.
1415 gammaNamed = kSRGB_SkGammaNamed;
1416 }
1417
1418 if (kNonStandard_SkGammaNamed == gammaNamed) {
1419 // It's possible that we'll initially detect non-matching gammas, only for
1420 // them to evaluate to the same named gamma curve.
1421 gammaNamed = is_named(gammas);
1422 }
1423
1424 if (kNonStandard_SkGammaNamed == gammaNamed) {
1425 return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(gammaNamed,
1426 std::move(gammas),
Matt Sarettf3880932017-03-24 10:06:03 -04001427 mat, std::move(profileData)));
Matt Sarett595599f2016-12-15 13:05:53 -05001428 }
1429
Matt Sarettf3880932017-03-24 10:06:03 -04001430 return SkColorSpace_Base::MakeRGB(gammaNamed, mat);
Matt Sarett595599f2016-12-15 13:05:53 -05001431}
1432
1433static sk_sp<SkColorSpace> make_gray(const ICCProfileHeader& header, ICCTag* tags, int tagCount,
1434 const uint8_t* base, sk_sp<SkData> profileData) {
1435 if (kLAB_PCSSpace == header.fPCS) {
1436 return nullptr;
1437 }
1438
1439 const ICCTag* grayTRC = ICCTag::Find(tags, tagCount, kTAG_kTRC);
1440 if (!grayTRC) {
1441 return_null("grayTRC tag required for monochrome profiles.");
1442 }
1443 SkGammas::Data data;
1444 SkColorSpaceTransferFn params;
1445 size_t tagBytes;
1446 SkGammas::Type type =
1447 parse_gamma(&data, &params, &tagBytes, grayTRC->addr(base), grayTRC->fLength);
1448 handle_invalid_gamma(&type, &data);
1449
Matt Sarett523116d2017-01-12 18:36:38 -05001450 SkMatrix44 toXYZD50(SkMatrix44::kIdentity_Constructor);
1451 toXYZD50.setFloat(0, 0, kWhitePointD50[0]);
1452 toXYZD50.setFloat(1, 1, kWhitePointD50[1]);
1453 toXYZD50.setFloat(2, 2, kWhitePointD50[2]);
Matt Sarett595599f2016-12-15 13:05:53 -05001454 if (SkGammas::Type::kNamed_Type == type) {
Matt Sarettf3880932017-03-24 10:06:03 -04001455 return SkColorSpace_Base::MakeRGB(data.fNamed, toXYZD50);
Matt Sarett595599f2016-12-15 13:05:53 -05001456 }
Matt Sarett523116d2017-01-12 18:36:38 -05001457
1458 size_t allocSize = sizeof(SkGammas);
1459 if (!safe_add(allocSize, gamma_alloc_size(type, data), &allocSize)) {
1460 return_null("SkGammas struct is too large to allocate");
1461 }
1462 void* memory = sk_malloc_throw(allocSize);
1463 sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas(3));
1464 load_gammas(memory, 0, type, &data, params, grayTRC->addr(base));
1465 for (int i = 0; i < 3; ++i) {
1466 gammas->fType[i] = type;
1467 gammas->fData[i] = data;
1468 }
1469
1470 return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(kNonStandard_SkGammaNamed,
1471 std::move(gammas),
Matt Sarettf3880932017-03-24 10:06:03 -04001472 toXYZD50, std::move(profileData)));
Matt Sarett595599f2016-12-15 13:05:53 -05001473}
1474
Matt Sarett523116d2017-01-12 18:36:38 -05001475static sk_sp<SkColorSpace> make_a2b(SkColorSpace_Base::ICCTypeFlag iccType,
Matt Sarett8e042862016-12-19 09:43:30 -05001476 const ICCProfileHeader& header, ICCTag* tags, int tagCount,
1477 const uint8_t* base, sk_sp<SkData> profileData) {
Matt Sarett595599f2016-12-15 13:05:53 -05001478 const ICCTag* a2b0 = ICCTag::Find(tags, tagCount, kTAG_A2B0);
1479 if (a2b0) {
1480 const SkColorSpace_A2B::PCS pcs = kXYZ_PCSSpace == header.fPCS
1481 ? SkColorSpace_A2B::PCS::kXYZ
1482 : SkColorSpace_A2B::PCS::kLAB;
1483 std::vector<SkColorSpace_A2B::Element> elements;
Matt Sarett523116d2017-01-12 18:36:38 -05001484 if (load_a2b0(&elements, a2b0->addr(base), a2b0->fLength, pcs, iccType)) {
1485 return sk_sp<SkColorSpace>(new SkColorSpace_A2B(iccType, std::move(elements),
Matt Sarett595599f2016-12-15 13:05:53 -05001486 pcs, std::move(profileData)));
1487 }
1488 }
1489
1490 return nullptr;
1491}
1492
Brian Osman526972e2016-10-24 09:24:02 -04001493sk_sp<SkColorSpace> SkColorSpace::MakeICC(const void* input, size_t len) {
Matt Sarett523116d2017-01-12 18:36:38 -05001494 return SkColorSpace_Base::MakeICC(input, len, SkColorSpace_Base::kRGB_ICCTypeFlag);
raftias54761282016-12-01 13:44:07 -05001495}
1496
1497sk_sp<SkColorSpace> SkColorSpace_Base::MakeICC(const void* input, size_t len,
Matt Sarett523116d2017-01-12 18:36:38 -05001498 ICCTypeFlag desiredType) {
msarett085cad42016-06-28 07:16:40 -07001499 if (!input || len < kICCHeaderSize) {
1500 return_null("Data is null or not large enough to contain an ICC profile");
1501 }
1502
1503 // Create our own copy of the input.
1504 void* memory = sk_malloc_throw(len);
1505 memcpy(memory, input, len);
raftias91db12d2016-12-02 11:56:59 -05001506 sk_sp<SkData> profileData = SkData::MakeFromMalloc(memory, len);
1507 const uint8_t* base = profileData->bytes();
msarett1b93bd12016-07-21 07:11:26 -07001508 const uint8_t* ptr = base;
msarett085cad42016-06-28 07:16:40 -07001509
1510 // Read the ICC profile header and check to make sure that it is valid.
1511 ICCProfileHeader header;
1512 header.init(ptr, len);
1513 if (!header.valid()) {
1514 return nullptr;
1515 }
1516
1517 // Adjust ptr and len before reading the tags.
1518 if (len < header.fSize) {
1519 SkColorSpacePrintf("ICC profile might be truncated.\n");
1520 } else if (len > header.fSize) {
1521 SkColorSpacePrintf("Caller provided extra data beyond the end of the ICC profile.\n");
1522 len = header.fSize;
1523 }
1524 ptr += kICCHeaderSize;
1525 len -= kICCHeaderSize;
1526
1527 // Parse tag headers.
1528 uint32_t tagCount = header.fTagCount;
1529 SkColorSpacePrintf("ICC profile contains %d tags.\n", tagCount);
1530 if (len < kICCTagTableEntrySize * tagCount) {
1531 return_null("Not enough input data to read tag table entries");
1532 }
1533
1534 SkAutoTArray<ICCTag> tags(tagCount);
1535 for (uint32_t i = 0; i < tagCount; i++) {
1536 ptr = tags[i].init(ptr);
1537 SkColorSpacePrintf("[%d] %c%c%c%c %d %d\n", i, (tags[i].fSignature >> 24) & 0xFF,
1538 (tags[i].fSignature >> 16) & 0xFF, (tags[i].fSignature >> 8) & 0xFF,
1539 (tags[i].fSignature >> 0) & 0xFF, tags[i].fOffset, tags[i].fLength);
1540
1541 if (!tags[i].valid(kICCHeaderSize + len)) {
1542 return_null("Tag is too large to fit in ICC profile");
1543 }
1544 }
1545
Matt Sarett595599f2016-12-15 13:05:53 -05001546 switch (header.fInputColorSpace) {
1547 case kRGB_ColorSpace: {
Matt Sarett523116d2017-01-12 18:36:38 -05001548 if (!(kRGB_ICCTypeFlag & desiredType)) {
Matt Sarett595599f2016-12-15 13:05:53 -05001549 return_null("Provided input color format (RGB) does not match profile.");
1550 }
1551
1552 sk_sp<SkColorSpace> colorSpace =
1553 make_xyz(header, tags.get(), tagCount, base, profileData);
1554 if (colorSpace) {
1555 return colorSpace;
1556 }
1557
Matt Sarett523116d2017-01-12 18:36:38 -05001558 desiredType = kRGB_ICCTypeFlag;
Matt Sarett595599f2016-12-15 13:05:53 -05001559 break;
raftias54761282016-12-01 13:44:07 -05001560 }
Matt Sarett595599f2016-12-15 13:05:53 -05001561 case kGray_ColorSpace: {
Matt Sarett523116d2017-01-12 18:36:38 -05001562 if (!(kGray_ICCTypeFlag & desiredType)) {
Matt Sarett595599f2016-12-15 13:05:53 -05001563 return_null("Provided input color format (Gray) does not match profile.");
1564 }
1565
Matt Sarett523116d2017-01-12 18:36:38 -05001566 return make_gray(header, tags.get(), tagCount, base, profileData);
Matt Sarett595599f2016-12-15 13:05:53 -05001567 }
1568 case kCMYK_ColorSpace:
Matt Sarett523116d2017-01-12 18:36:38 -05001569 if (!(kCMYK_ICCTypeFlag & desiredType)) {
Matt Sarett595599f2016-12-15 13:05:53 -05001570 return_null("Provided input color format (CMYK) does not match profile.");
1571 }
1572
Matt Sarett523116d2017-01-12 18:36:38 -05001573 desiredType = kCMYK_ICCTypeFlag;
Matt Sarett595599f2016-12-15 13:05:53 -05001574 break;
1575 default:
1576 return_null("ICC profile contains unsupported colorspace");
raftias54761282016-12-01 13:44:07 -05001577 }
1578
Matt Sarett523116d2017-01-12 18:36:38 -05001579 return make_a2b(desiredType, header, tags.get(), tagCount, base, profileData);
msarett085cad42016-06-28 07:16:40 -07001580}