Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2)

Side by Side Diff: src/core/SkColorSpace.cpp

Issue 1922073003: Add comments and sanity checks to parsing of ICC headers (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2016 Google Inc. 2 * Copyright 2016 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "SkAtomics.h" 8 #include "SkAtomics.h"
9 #include "SkColorSpace.h" 9 #include "SkColorSpace.h"
10 10
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after
175 #define return_null(msg) \ 175 #define return_null(msg) \
176 do { \ 176 do { \
177 SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \ 177 SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \
178 return nullptr; \ 178 return nullptr; \
179 } while (0) 179 } while (0)
180 180
181 static uint16_t read_big_endian_short(const uint8_t* ptr) { 181 static uint16_t read_big_endian_short(const uint8_t* ptr) {
182 return ptr[0] << 8 | ptr[1]; 182 return ptr[0] << 8 | ptr[1];
183 } 183 }
184 184
185 static uint32_t read_big_endian_int(const uint8_t* ptr) { 185 static uint32_t read_big_endian_uint(const uint8_t* ptr) {
186 return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3]; 186 return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
187 } 187 }
188 188
189 static int32_t read_big_endian_int(const uint8_t* ptr) {
190 return (int32_t) read_big_endian_uint(ptr);
191 }
192
193 static bool color_space_almost_equal(float a, float b) {
194 return SkTAbs(a - b) < 0.01f;
195 }
196
189 // This is equal to the header size according to the ICC specification (128) 197 // This is equal to the header size according to the ICC specification (128)
190 // plus the size of the tag count (4). We include the tag count since we 198 // plus the size of the tag count (4). We include the tag count since we
191 // always require it to be present anyway. 199 // always require it to be present anyway.
192 static const size_t kICCHeaderSize = 132; 200 static const size_t kICCHeaderSize = 132;
193 201
194 // Contains a signature (4), offset (4), and size (4). 202 // Contains a signature (4), offset (4), and size (4).
195 static const size_t kICCTagTableEntrySize = 12; 203 static const size_t kICCTagTableEntrySize = 12;
196 204
197 static const uint32_t kRGB_ColorSpace = SkSetFourByteTag('R', 'G', 'B', ' '); 205 static const uint32_t kRGB_ColorSpace = SkSetFourByteTag('R', 'G', 'B', ' ');
198 static const uint32_t kGray_ColorSpace = SkSetFourByteTag('G', 'R', 'A', 'Y');
199 206
200 struct ICCProfileHeader { 207 struct ICCProfileHeader {
201 // TODO (msarett):
202 // Can we ignore less of these fields?
203 uint32_t fSize; 208 uint32_t fSize;
209
210 // No reason to care about the preferred color management module (ex: Adobe, Apple, etc.).
211 // We're always going to use this one.
204 uint32_t fCMMType_ignored; 212 uint32_t fCMMType_ignored;
213
205 uint32_t fVersion; 214 uint32_t fVersion;
206 uint32_t fClassProfile; 215 uint32_t fProfileClass;
207 uint32_t fColorSpace; 216 uint32_t fInputColorSpace;
208 uint32_t fPCS; 217 uint32_t fPCS;
209 uint32_t fDateTime_ignored[3]; 218 uint32_t fDateTime_ignored[3];
210 uint32_t fSignature; 219 uint32_t fSignature;
220
221 // Indicates the platform that this profile was created for (ex: Apple, Micr osoft). This
222 // doesn't really matter to us.
211 uint32_t fPlatformTarget_ignored; 223 uint32_t fPlatformTarget_ignored;
224
225 // Flags can indicate:
226 // (1) Whether this profile was embedded in a file. This flag is consistent ly wrong.
227 // Ex: The profile came from a file but indicates that it did not.
228 // (2) Whether we are allowed to use the profile independently of the color data. If set,
229 // this may allow us to use the embedded profile for testing separate fr om the original
230 // image.
212 uint32_t fFlags_ignored; 231 uint32_t fFlags_ignored;
213 uint32_t fManufacturer_ignored; 232
233 // We support many output devices. It doesn't make sense to think about the attributes of
234 // the device in the context of the image profile.
235 uint32_t fDeviceManufacturer_ignored;
214 uint32_t fDeviceModel_ignored; 236 uint32_t fDeviceModel_ignored;
215 uint32_t fDeviceAttributes_ignored[2]; 237 uint32_t fDeviceAttributes_ignored[2];
238
216 uint32_t fRenderingIntent; 239 uint32_t fRenderingIntent;
217 uint32_t fIlluminantXYZ_ignored[3]; 240 int32_t fIlluminantXYZ[3];
241
242 // We don't care who created the profile.
218 uint32_t fCreator_ignored; 243 uint32_t fCreator_ignored;
244
245 // This is an MD5 checksum. Could be useful for checking if profiles are eq ual.
219 uint32_t fProfileId_ignored[4]; 246 uint32_t fProfileId_ignored[4];
247
248 // Reserved for future use.
220 uint32_t fReserved_ignored[7]; 249 uint32_t fReserved_ignored[7];
250
221 uint32_t fTagCount; 251 uint32_t fTagCount;
222 252
223 void init(const uint8_t* src, size_t len) { 253 void init(const uint8_t* src, size_t len) {
224 SkASSERT(kICCHeaderSize == sizeof(*this)); 254 SkASSERT(kICCHeaderSize == sizeof(*this));
225 255
226 uint32_t* dst = (uint32_t*) this; 256 uint32_t* dst = (uint32_t*) this;
227 for (uint32_t i = 0; i < kICCHeaderSize / 4; i++, src+=4) { 257 for (uint32_t i = 0; i < kICCHeaderSize / 4; i++, src+=4) {
228 dst[i] = read_big_endian_int(src); 258 dst[i] = read_big_endian_uint(src);
229 } 259 }
230 } 260 }
231 261
232 bool valid() const { 262 bool valid() const {
233 // TODO (msarett):
234 // For now it's nice to fail loudly on invalid inputs. But, can we
235 // recover from some of these errors?
236
237 return_if_false(fSize >= kICCHeaderSize, "Size is too small"); 263 return_if_false(fSize >= kICCHeaderSize, "Size is too small");
238 264
239 uint8_t majorVersion = fVersion >> 24; 265 uint8_t majorVersion = fVersion >> 24;
240 return_if_false(majorVersion <= 4, "Unsupported version"); 266 return_if_false(majorVersion <= 4, "Unsupported version");
241 267
268 // These are the three basic classes of profiles that we might expect to see embedded
269 // in images. Four additional classes exist, but they generally are use d as a convenient
270 // way for CMMs to store calculated transforms.
242 const uint32_t kDisplay_Profile = SkSetFourByteTag('m', 'n', 't', 'r'); 271 const uint32_t kDisplay_Profile = SkSetFourByteTag('m', 'n', 't', 'r');
243 const uint32_t kInput_Profile = SkSetFourByteTag('s', 'c', 'n', 'r'); 272 const uint32_t kInput_Profile = SkSetFourByteTag('s', 'c', 'n', 'r');
244 const uint32_t kOutput_Profile = SkSetFourByteTag('p', 'r', 't', 'r'); 273 const uint32_t kOutput_Profile = SkSetFourByteTag('p', 'r', 't', 'r');
245 // TODO (msarett): 274 return_if_false(fProfileClass == kDisplay_Profile ||
246 // Should we also support DeviceLink, ColorSpace, Abstract, or NamedColo r? 275 fProfileClass == kInput_Profile ||
247 return_if_false(fClassProfile == kDisplay_Profile || 276 fProfileClass == kOutput_Profile,
248 fClassProfile == kInput_Profile || 277 "Unsupported profile");
249 fClassProfile == kOutput_Profile,
250 "Unsupported class profile");
251 278
252 // TODO (msarett): 279 // TODO (msarett):
253 // There are many more color spaces that we could try to support. 280 // All the profiles we've tested so far use RGB as the input color space .
254 return_if_false(fColorSpace == kRGB_ColorSpace || fColorSpace == kGray_C olorSpace, 281 return_if_false(fInputColorSpace == kRGB_ColorSpace, "Unsupported color space");
255 "Unsupported color space");
256 282
283 // TODO (msarett):
284 // All the profiles we've tested so far use XYZ as the profile connectio n space.
257 const uint32_t kXYZ_PCSSpace = SkSetFourByteTag('X', 'Y', 'Z', ' '); 285 const uint32_t kXYZ_PCSSpace = SkSetFourByteTag('X', 'Y', 'Z', ' ');
258 // TODO (msarett):
259 // Can we support PCS LAB as well?
260 return_if_false(fPCS == kXYZ_PCSSpace, "Unsupported PCS space"); 286 return_if_false(fPCS == kXYZ_PCSSpace, "Unsupported PCS space");
261 287
262 return_if_false(fSignature == SkSetFourByteTag('a', 'c', 's', 'p'), "Bad signature"); 288 return_if_false(fSignature == SkSetFourByteTag('a', 'c', 's', 'p'), "Bad signature");
263 289
264 // TODO (msarett): 290 // TODO (msarett):
265 // Should we treat different rendering intents differently? 291 // Should we treat different rendering intents differently?
266 // Valid rendering intents include kPerceptual (0), kRelative (1), 292 // Valid rendering intents include kPerceptual (0), kRelative (1),
267 // kSaturation (2), and kAbsolute (3). 293 // kSaturation (2), and kAbsolute (3).
268 return_if_false(fRenderingIntent <= 3, "Bad rendering intent"); 294 return_if_false(fRenderingIntent <= 3, "Bad rendering intent");
269 295
296 return_if_false(color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[0 ]), 0.96420f) &&
297 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[1 ]), 1.00000f) &&
298 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[2 ]), 0.82491f),
299 "Illuminant must be D50");
scroggo 2016/04/27 11:55:44 Was this intended to be more flexible in future ve
msarett 2016/04/27 13:11:19 I don't know... It doesn't look like they are hea
300
270 return_if_false(fTagCount <= 100, "Too many tags"); 301 return_if_false(fTagCount <= 100, "Too many tags");
271 302
272 return true; 303 return true;
273 } 304 }
274 }; 305 };
275 306
276 struct ICCTag { 307 struct ICCTag {
277 uint32_t fSignature; 308 uint32_t fSignature;
278 uint32_t fOffset; 309 uint32_t fOffset;
279 uint32_t fLength; 310 uint32_t fLength;
280 311
281 const uint8_t* init(const uint8_t* src) { 312 const uint8_t* init(const uint8_t* src) {
282 fSignature = read_big_endian_int(src); 313 fSignature = read_big_endian_uint(src);
283 fOffset = read_big_endian_int(src + 4); 314 fOffset = read_big_endian_uint(src + 4);
284 fLength = read_big_endian_int(src + 8); 315 fLength = read_big_endian_uint(src + 8);
285 return src + 12; 316 return src + 12;
286 } 317 }
287 318
288 bool valid(size_t len) { 319 bool valid(size_t len) {
289 return_if_false(fOffset + fLength <= len, "Tag too large for ICC profile "); 320 return_if_false(fOffset + fLength <= len, "Tag too large for ICC profile ");
290 return true; 321 return true;
291 } 322 }
292 323
293 const uint8_t* addr(const uint8_t* src) const { 324 const uint8_t* addr(const uint8_t* src) const {
294 return src + fOffset; 325 return src + fOffset;
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
328 359
329 static const uint32_t kTAG_CurveType = SkSetFourByteTag('c', 'u', 'r', 'v'); 360 static const uint32_t kTAG_CurveType = SkSetFourByteTag('c', 'u', 'r', 'v');
330 static const uint32_t kTAG_ParaCurveType = SkSetFourByteTag('p', 'a', 'r', 'a'); 361 static const uint32_t kTAG_ParaCurveType = SkSetFourByteTag('p', 'a', 'r', 'a');
331 362
332 static bool load_gamma(float* gamma, const uint8_t* src, size_t len) { 363 static bool load_gamma(float* gamma, const uint8_t* src, size_t len) {
333 if (len < 14) { 364 if (len < 14) {
334 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len); 365 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
335 return false; 366 return false;
336 } 367 }
337 368
338 uint32_t type = read_big_endian_int(src); 369 uint32_t type = read_big_endian_uint(src);
339 switch (type) { 370 switch (type) {
340 case kTAG_CurveType: { 371 case kTAG_CurveType: {
341 uint32_t count = read_big_endian_int(src + 8); 372 uint32_t count = read_big_endian_int(src + 8);
342 if (0 == count) { 373 if (0 == count) {
343 return false; 374 return false;
344 } 375 }
345 376
346 const uint16_t* table = (const uint16_t*) (src + 12); 377 const uint16_t* table = (const uint16_t*) (src + 12);
347 if (1 == count) { 378 if (1 == count) {
348 // Table entry is the exponent (bias 256). 379 // Table entry is the exponent (bias 256).
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
417 (tags[i].fSignature >> 0) & 0xFF, tags[i].fOffset, tags[i].fLen gth); 448 (tags[i].fSignature >> 0) & 0xFF, tags[i].fOffset, tags[i].fLen gth);
418 449
419 if (!tags[i].valid(kICCHeaderSize + len)) { 450 if (!tags[i].valid(kICCHeaderSize + len)) {
420 return_null("Tag is too large to fit in ICC profile"); 451 return_null("Tag is too large to fit in ICC profile");
421 } 452 }
422 } 453 }
423 454
424 // Load our XYZ and gamma matrices. 455 // Load our XYZ and gamma matrices.
425 SkFloat3x3 toXYZ; 456 SkFloat3x3 toXYZ;
426 SkFloat3 gamma {{ 1.0f, 1.0f, 1.0f }}; 457 SkFloat3 gamma {{ 1.0f, 1.0f, 1.0f }};
427 switch (header.fColorSpace) { 458 switch (header.fInputColorSpace) {
428 case kRGB_ColorSpace: { 459 case kRGB_ColorSpace: {
429 const ICCTag* r = ICCTag::Find(tags.get(), tagCount, kTAG_rXYZ); 460 const ICCTag* r = ICCTag::Find(tags.get(), tagCount, kTAG_rXYZ);
430 const ICCTag* g = ICCTag::Find(tags.get(), tagCount, kTAG_gXYZ); 461 const ICCTag* g = ICCTag::Find(tags.get(), tagCount, kTAG_gXYZ);
431 const ICCTag* b = ICCTag::Find(tags.get(), tagCount, kTAG_bXYZ); 462 const ICCTag* b = ICCTag::Find(tags.get(), tagCount, kTAG_bXYZ);
432 if (!r || !g || !b) { 463 if (!r || !g || !b) {
433 return_null("Need rgb tags for XYZ space"); 464 return_null("Need rgb tags for XYZ space");
434 } 465 }
435 466
436 if (!load_xyz(&toXYZ.fMat[0], r->addr((const uint8_t*) base), r->fLe ngth) || 467 if (!load_xyz(&toXYZ.fMat[0], r->addr((const uint8_t*) base), r->fLe ngth) ||
437 !load_xyz(&toXYZ.fMat[3], g->addr((const uint8_t*) base), g->fLe ngth) || 468 !load_xyz(&toXYZ.fMat[3], g->addr((const uint8_t*) base), g->fLe ngth) ||
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
541 } 572 }
542 573
543 // D65 white point of Rec. 709 [8] are: 574 // D65 white point of Rec. 709 [8] are:
544 // 575 //
545 // D65 white-point in unit luminance XYZ = 0.9505, 1.0000, 1.0890 576 // D65 white-point in unit luminance XYZ = 0.9505, 1.0000, 1.0890
546 // 577 //
547 // R G B white 578 // R G B white
548 // x 0.640 0.300 0.150 0.3127 579 // x 0.640 0.300 0.150 0.3127
549 // y 0.330 0.600 0.060 0.3290 580 // y 0.330 0.600 0.060 0.3290
550 // z 0.030 0.100 0.790 0.3582 581 // z 0.030 0.100 0.790 0.3582
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698