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 1726823002: Set SkColorSpace object for PNGs and parse ICC profiles (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Remove constexpr Created 4 years, 9 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
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 140 matching lines...) Expand 10 before | Expand all | Expand 10 after
151 case kSRGB_Named: 151 case kSRGB_Named:
152 return new SkColorSpace(gSRGB_toXYZD50, gSRGB_gamma, kSRGB_Named); 152 return new SkColorSpace(gSRGB_toXYZD50, gSRGB_gamma, kSRGB_Named);
153 default: 153 default:
154 break; 154 break;
155 } 155 }
156 return nullptr; 156 return nullptr;
157 } 157 }
158 158
159 //////////////////////////////////////////////////////////////////////////////// /////////////////// 159 //////////////////////////////////////////////////////////////////////////////// ///////////////////
160 160
161 #include "SkFixed.h"
162 #include "SkTemplates.h"
163
164 #define SkColorSpacePrintf(...)
165
166 #define return_if_false(pred, msg) \
167 do { \
168 if (!(pred)) { \
169 SkColorSpacePrintf("Invalid ICC Profile: %s.\n", msg); \
170 return false; \
171 } \
172 } while (0)
173
174 #define return_null(msg) \
175 do { \
176 SkDebugf("ICC Profile: %s\n", (msg)); \
177 return nullptr; \
178 } while (0)
179
180 static uint16_t read_big_endian_short(const uint8_t* ptr) {
181 return ptr[0] << 8 | ptr[1];
182 }
183
184 static uint32_t read_big_endian_int(const uint8_t* ptr) {
185 return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
186 }
187
188 // This is equal to the header size according to the ICC specification (128)
189 // plus the size of the tag count (4). We include the tag count since we
190 // always require it to be present anyway.
191 static const size_t kICCHeaderSize = 132;
192
193 // Contains a signature (4), offset (4), and size (4).
194 static const size_t kICCTagTableEntrySize = 12;
195
196 static const uint32_t kRGB_ColorSpace = SkSetFourByteTag('R', 'G', 'B', ' ');
197 static const uint32_t kGray_ColorSpace = SkSetFourByteTag('G', 'R', 'A', 'Y');
198
199 struct ICCProfileHeader {
200 // TODO (msarett):
201 // Can we ignore less of these fields?
202 uint32_t fSize;
203 uint32_t fCMMType_ignored;
204 uint32_t fVersion;
205 uint32_t fClassProfile;
206 uint32_t fColorSpace;
207 uint32_t fPCS;
208 uint32_t fDateTime_ignored[3];
209 uint32_t fSignature;
210 uint32_t fPlatformTarget_ignored;
211 uint32_t fFlags_ignored;
212 uint32_t fManufacturer_ignored;
213 uint32_t fDeviceModel_ignored;
214 uint32_t fDeviceAttributes_ignored[2];
215 uint32_t fRenderingIntent;
216 uint32_t fIlluminantXYZ_ignored[3];
217 uint32_t fCreator_ignored;
218 uint32_t fProfileId_ignored[4];
219 uint32_t fReserved_ignored[7];
220 uint32_t fTagCount;
221
222 void init(const uint8_t* src, size_t len) {
223 SkASSERT(kICCHeaderSize == sizeof(*this));
224
225 uint32_t* dst = (uint32_t*) this;
226 for (uint32_t i = 0; i < kICCHeaderSize / 4; i++, src+=4) {
227 dst[i] = read_big_endian_int(src);
228 }
229 }
230
231 bool valid() const {
232 // TODO (msarett):
233 // For now it's nice to fail loudly on invalid inputs. But, can we
234 // recover from some of these errors?
235
236 return_if_false(fSize >= kICCHeaderSize, "Size is too small");
237
238 uint8_t majorVersion = fVersion >> 24;
239 return_if_false(majorVersion <= 4, "Unsupported version");
240
241 const uint32_t kDisplay_Profile = SkSetFourByteTag('m', 'n', 't', 'r');
242 const uint32_t kInput_Profile = SkSetFourByteTag('s', 'c', 'n', 'r');
243 const uint32_t kOutput_Profile = SkSetFourByteTag('p', 'r', 't', 'r');
244 // TODO (msarett):
245 // Should we also support DeviceLink, ColorSpace, Abstract, or NamedColo r?
246 return_if_false(fClassProfile == kDisplay_Profile ||
247 fClassProfile == kInput_Profile ||
248 fClassProfile == kOutput_Profile,
249 "Unsupported class profile");
250
251 // TODO (msarett):
252 // There are many more color spaces that we could try to support.
253 return_if_false(fColorSpace == kRGB_ColorSpace || fColorSpace == kGray_C olorSpace,
254 "Unsupported color space");
255
256 const uint32_t kXYZ_PCSSpace = SkSetFourByteTag('X', 'Y', 'Z', ' ');
257 // TODO (msarett):
258 // Can we support PCS LAB as well?
259 return_if_false(fPCS == kXYZ_PCSSpace, "Unsupported PCS space");
260
261 return_if_false(fSignature == SkSetFourByteTag('a', 'c', 's', 'p'), "Bad signature");
262
263 // TODO (msarett):
264 // Share this with sRGB?
265 enum ICCIntents {
scroggo 2016/03/01 21:29:57 Looks like this is never used?
msarett 2016/03/02 00:22:54 Yes we don't use this... I'll delete it and add a
266 kPerceptual = 0,
267 kRelativeColorimetric = 1,
268 kSaturation = 2,
269 kAbsoluteColorimetric = 3,
270 };
271 return_if_false(fRenderingIntent <= 3, "Bad rendering intent");
272
273 return_if_false(fTagCount <= 100, "Too many tags");
274
275 return true;
276 }
277 };
278
279 struct ICCTag {
280 uint32_t fSignature;
281 uint32_t fOffset;
282 uint32_t fLength;
283
284 const uint8_t* init(const uint8_t* src) {
285 fSignature = read_big_endian_int(src);
286 fOffset = read_big_endian_int(src + 4);
287 fLength = read_big_endian_int(src + 8);
288 return src + 12;
289 }
290
291 bool valid(size_t len) {
292 return_if_false(fOffset + fLength <= len, "Tag too large for ICC profile .\n");
293 return true;
294 }
295
296 const uint8_t* addr(const uint8_t* src) const {
297 return src + fOffset;
298 }
299
300 static const ICCTag* Find(const ICCTag tags[], int count, uint32_t signature ) {
301 for (int i = 0; i < count; ++i) {
302 if (tags[i].fSignature == signature) {
303 return &tags[i];
304 }
305 }
306 return nullptr;
307 }
308 };
309
310 // TODO (msarett):
311 // Should we recognize more tags?
312 static const uint32_t kTAG_rXYZ = SkSetFourByteTag('r', 'X', 'Y', 'Z');
313 static const uint32_t kTAG_gXYZ = SkSetFourByteTag('g', 'X', 'Y', 'Z');
314 static const uint32_t kTAG_bXYZ = SkSetFourByteTag('b', 'X', 'Y', 'Z');
315 static const uint32_t kTAG_rTRC = SkSetFourByteTag('r', 'T', 'R', 'C');
316 static const uint32_t kTAG_gTRC = SkSetFourByteTag('g', 'T', 'R', 'C');
317 static const uint32_t kTAG_bTRC = SkSetFourByteTag('b', 'T', 'R', 'C');
318
319 bool load_xyz(float dst[3], const uint8_t* src, size_t len) {
320 if (len < 20) {
321 SkColorSpacePrintf("XYZ tag is too small (%d bytes)", len);
322 return false;
323 }
324
325 dst[0] = SkFixedToFloat(read_big_endian_int(src + 8));
326 dst[1] = SkFixedToFloat(read_big_endian_int(src + 12));
327 dst[2] = SkFixedToFloat(read_big_endian_int(src + 16));
328 SkColorSpacePrintf("XYZ %g %g %g\n", dst[0], dst[1], dst[2]);
329 return true;
330 }
331
332 static const uint32_t kTAG_CurveType = SkSetFourByteTag('c', 'u', 'r', 'v');
333 static const uint32_t kTAG_ParaCurveType = SkSetFourByteTag('p', 'a', 'r', 'a');
334
335 static bool load_gamma(float* gamma, const uint8_t* src, size_t len) {
scroggo 2016/03/01 21:29:56 This returns a bool, but the result is never used.
msarett 2016/03/02 00:22:54 I think you could argue this should be void, since
336 if (len < 14) {
337 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
338 return false;
339 }
340
341 uint32_t type = read_big_endian_int(src);
342 switch (type) {
343 case kTAG_CurveType: {
344 uint32_t count = read_big_endian_int(src + 8);
345 if (0 == count) {
346 return false;
347 }
348
349 const uint16_t* table = (const uint16_t*) (src + 12);
350 if (1 == count) {
351 // Table entry is the exponent (bias 256).
352 uint16_t value = read_big_endian_short((const uint8_t*) table);
353 *gamma = value / 256.0f;
354 SkColorSpacePrintf("gamma %d %g\n", value, *gamma);
355 return true;
356 }
357
358 // Check length again if we have a table.
359 if (len < 12 + 2 * count) {
360 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
361 return false;
362 }
363
364 // Print the interpolation table. For now, we ignore this and guess 2.2f.
365 for (uint32_t i = 0; i < count; i++) {
366 SkColorSpacePrintf("curve[%d] %d\n", i,
367 read_big_endian_short((const uint8_t*) &table[i]));
368 }
369
370 *gamma = 2.2f;
371 return true;
372 } break;
373 case kTAG_ParaCurveType: {
374 // Guess 2.2f.
375 SkColorSpacePrintf("parametric curve\n");
376 *gamma = 2.2f;
377 return true;
378 } break;
379 default:
380 SkColorSpacePrintf("Unsupported gamma tag type %d\n", type);
381 return false;
382 }
383 }
384
385 SkColorSpace* SkColorSpace::NewICC(const void* base, size_t len) {
386 const uint8_t* ptr = (const uint8_t*) base;
387
388 if (len < kICCHeaderSize) {
389 SkColorSpacePrintf("Data is not large enough to contain an ICC profile.\ n");
390 return nullptr;
391 }
392
393 // Read the ICC profile header and check to make sure that it is valid.
394 ICCProfileHeader header;
395 header.init(ptr, len);
396 if (!header.valid()) {
397 return nullptr;
398 }
399
400 // Adjust ptr and len before reading the tags.
401 if (len < header.fSize) {
402 SkColorSpacePrintf("ICC profile might be truncated.\n");
403 } else if (len > header.fSize) {
404 SkColorSpacePrintf("Caller provided extra data beyonf the end of the ICC profile.\n");
405 len = header.fSize;
406 }
407 ptr += kICCHeaderSize;
408 len -= kICCHeaderSize;
409
410 // Parse tag headers.
411 uint32_t tagCount = header.fTagCount;
412 SkColorSpacePrintf("ICC profile contains %d tags.\n", tagCount);
413 if (len < kICCTagTableEntrySize * tagCount) {
414 SkColorSpacePrintf("Not enough input data to read tag table entries.\n") ;
scroggo 2016/03/01 21:29:56 It seems like this should return null? (Otherwise
msarett 2016/03/02 00:22:54 Yes thanks.
415 }
416
417 SkAutoTArray<ICCTag> tags(tagCount);
418 for (uint32_t i = 0; i < tagCount; i++) {
419 ptr = tags[i].init(ptr);
420 SkColorSpacePrintf("[%d] %c%c%c%c %d %d\n", i, (tags[i].fSignature >> 24 ) & 0xFF,
421 (tags[i].fSignature >> 16) & 0xFF, (tags[i].fSignature >> 8) & 0xFF,
422 (tags[i].fSignature >> 0) & 0xFF, tags[i].fOffset, tags[i].fLen gth);
423
424 if (!tags[i].valid(kICCHeaderSize + len)) {
425 return nullptr;
scroggo 2016/03/01 21:29:57 failure message?
msarett 2016/03/02 00:22:54 Done.
426 }
427 }
428
429 // Load our XYZ and gamma matrices.
430 SkFloat3x3 toXYZ;
431 SkFloat3 gamma {{ 1.0f, 1.0f, 1.0f }};
432 switch (header.fColorSpace) {
433 case kRGB_ColorSpace: {
434 const ICCTag* r = ICCTag::Find(tags.get(), tagCount, kTAG_rXYZ);
435 const ICCTag* g = ICCTag::Find(tags.get(), tagCount, kTAG_gXYZ);
436 const ICCTag* b = ICCTag::Find(tags.get(), tagCount, kTAG_bXYZ);
437 if (!r || !g || !b) {
438 return_null("Need rgb tags for XYZ space");
439 }
440
441 if (!load_xyz(&toXYZ.fMat[0], r->addr((const uint8_t*) base), r->fLe ngth) ||
442 !load_xyz(&toXYZ.fMat[3], g->addr((const uint8_t*) base), g->fLe ngth) ||
443 !load_xyz(&toXYZ.fMat[6], b->addr((const uint8_t*) base), b->fLe ngth)) {
scroggo 2016/03/01 21:29:57 nit: hard to read this line as is. I find putting
msarett 2016/03/02 00:22:54 Done.
444 return_null("Need valid rgb tags for XYZ space");
445 }
446
447 r = ICCTag::Find(tags.get(), tagCount, kTAG_rTRC);
448 g = ICCTag::Find(tags.get(), tagCount, kTAG_gTRC);
449 b = ICCTag::Find(tags.get(), tagCount, kTAG_bTRC);
450 if (r) {
451 load_gamma(&gamma.fVec[0], r->addr((const uint8_t*) base), r->fL ength);
452 }
453 if (g) {
454 load_gamma(&gamma.fVec[1], g->addr((const uint8_t*) base), g->fL ength);
455 }
456 if (b) {
457 load_gamma(&gamma.fVec[2], b->addr((const uint8_t*) base), b->fL ength);
458 }
459 return SkColorSpace::NewRGB(toXYZ, gamma);
460 }
461 default:
462 break;
463 }
464
465 return_null("ICC profile contains unsupported colorspace.\n");
466 }
467
468 //////////////////////////////////////////////////////////////////////////////// ///////////////////
469
161 SkColorSpace::Result SkColorSpace::Concat(const SkColorSpace* src, const SkColor Space* dst, 470 SkColorSpace::Result SkColorSpace::Concat(const SkColorSpace* src, const SkColor Space* dst,
162 SkFloat3x3* result) { 471 SkFloat3x3* result) {
163 if (!src || !dst || (src->named() == kDevice_Named) || (src->named() == dst- >named())) { 472 if (!src || !dst || (src->named() == kDevice_Named) || (src->named() == dst- >named())) {
164 if (result) { 473 if (result) {
165 *result = {{ 1, 0, 0, 0, 1, 0, 0, 0, 1 }}; 474 *result = {{ 1, 0, 0, 0, 1, 0, 0, 0, 1 }};
166 } 475 }
167 return kIdentity_Result; 476 return kIdentity_Result;
168 } 477 }
169 if (result) { 478 if (result) {
170 *result = concat(src->fToXYZD50, invert(dst->fToXYZD50)); 479 *result = concat(src->fToXYZD50, invert(dst->fToXYZD50));
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
236 } 545 }
237 546
238 // D65 white point of Rec. 709 [8] are: 547 // D65 white point of Rec. 709 [8] are:
239 // 548 //
240 // D65 white-point in unit luminance XYZ = 0.9505, 1.0000, 1.0890 549 // D65 white-point in unit luminance XYZ = 0.9505, 1.0000, 1.0890
241 // 550 //
242 // R G B white 551 // R G B white
243 // x 0.640 0.300 0.150 0.3127 552 // x 0.640 0.300 0.150 0.3127
244 // y 0.330 0.600 0.060 0.3290 553 // y 0.330 0.600 0.060 0.3290
245 // z 0.030 0.100 0.790 0.3582 554 // z 0.030 0.100 0.790 0.3582
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698