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

Side by Side Diff: src/codec/SkPngCodec.cpp

Issue 1726823002: Set SkColorSpace object for PNGs and parse ICC profiles (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Refactored a bit, added guesses for "default" gAMA/cHRM Created 4 years, 10 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 2015 Google Inc. 2 * Copyright 2015 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 "SkCodecPriv.h" 8 #include "SkCodecPriv.h"
9 #include "SkColorPriv.h" 9 #include "SkColorPriv.h"
10 #include "SkColorTable.h" 10 #include "SkColorTable.h"
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after
158 } 158 }
159 159
160 /////////////////////////////////////////////////////////////////////////////// 160 ///////////////////////////////////////////////////////////////////////////////
161 // Creation 161 // Creation
162 /////////////////////////////////////////////////////////////////////////////// 162 ///////////////////////////////////////////////////////////////////////////////
163 163
164 bool SkPngCodec::IsPng(const char* buf, size_t bytesRead) { 164 bool SkPngCodec::IsPng(const char* buf, size_t bytesRead) {
165 return !png_sig_cmp((png_bytep) buf, (png_size_t)0, bytesRead); 165 return !png_sig_cmp((png_bytep) buf, (png_size_t)0, bytesRead);
166 } 166 }
167 167
168 // Returns a colorSpace object that represents any color space information in
169 // the encoded data. If the encoded data contains no color space, this will
170 // return NULL.
171 SkColorSpace* read_color_space(png_structp png_ptr, png_infop info_ptr) {
172
173 // First check for an ICC profile
174 png_charp name;
scroggo 2016/02/24 18:31:36 Will png_get_iCCP let you pass nullptr for name? I
msarett 2016/02/29 21:47:54 We need to pass these in or png_get_iCCP does noth
175 int compression;
scroggo 2016/02/24 18:31:36 Do we need this variable?
msarett 2016/02/29 21:47:55 We need to pass these in or png_get_iCCP does noth
176 png_bytep profile;
177 png_uint_32 length;
178 if (PNG_INFO_iCCP == png_get_iCCP(png_ptr, info_ptr, &name, &compression, &p rofile,
179 &length)) {
180
181 // Compression can only ever be "deflate", so this is uninteresting.
scroggo 2016/02/24 18:31:36 I didn't understand this comment the first time ar
msarett 2016/02/29 21:47:55 libpng has already decompressed the profile for us
182 // Could knowing the name of the profile ever be interesting? Maybe for debugging?
scroggo 2016/02/24 18:31:36 I could see it being useful in theory. No idea how
msarett 2016/02/29 21:47:55 Yes I think that'd be the place to put it.
183 return SkColorSpace::NewICC(profile, length);
184 }
185
186 // Second, check for sRGB.
187 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) {
188
189 // Here we are ignoring the sRGB "intent". Should we use the "intent"?
scroggo 2016/02/24 18:31:36 FIXME/TODO? What is the "intent"?
msarett 2016/02/29 21:47:54 Done.
190 return SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
191 }
192
193 // Next, check for chromaticities.
194 png_fixed_point XYZ[9];
195 SkFloat3x3 toXYZD50;
196 png_fixed_point gamma;
197 SkFloat3 gammas;
198 if (png_get_cHRM_XYZ_fixed(png_ptr, info_ptr, &XYZ[0], &XYZ[1], &XYZ[2], &XY Z[3], &XYZ[4],
199 &XYZ[5], &XYZ[6], &XYZ[7], &XYZ[8])) {
200
201 // This could be wrong. I don't see any guarantee from the PNG specific ation that
scroggo 2016/02/24 18:31:36 FIXME? Maybe specify how you came up with this com
msarett 2016/02/29 21:47:54 The computation isn't wrong. What might be wrong
202 // these XYZ values will be D50. In fact, libpng will set its XYZ value s to D65
203 // values if it sees an sRGB chunk.
scroggo 2016/02/24 18:31:36 It seems like we'll only reach here if we did not
msarett 2016/02/29 21:47:54 Yes you're right, the comment is unclear. I'll re
204 for (int i = 0; i < 9; i++) {
205 toXYZD50.fMat[i] = ((float) XYZ[i]) * 0.00001f;
206 }
207
208 if (!(PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma))) {
209 // If the image does not specify gamma, let's choose linear. Should we default
210 // to sRGB? Instead of choosing a default, should we just give up o n the color
211 // profile?
212 gamma = PNG_GAMMA_LINEAR;
213 }
214 gammas.fVec[0] = gammas.fVec[1] = gammas.fVec[2] = ((float) gamma) * 0.0 0001f;
215
216 return SkColorSpace::NewRGB(toXYZD50, gammas);
217 }
218
219 // Last, check for gamma.
220 if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) {
221
222 // Guess a default value for cHRM? Or should we just give up?
223 // Here we use the identity matrix as a default.
scroggo 2016/02/24 18:31:36 Might want a method on SkFloat3x3 to set to the id
msarett 2016/02/29 21:47:55 I'll add a FIXME. Don't want to do this yet since
scroggo 2016/02/29 22:18:49 sgtm. FWIW, even if it's not the default, we could
224 memset(toXYZD50.fMat, 0, 9 * sizeof(float));
225 toXYZD50.fMat[0] = toXYZD50.fMat[4] = toXYZD50.fMat[8] = 1.0f;
226
227 // Set the gammas.
228 gammas.fVec[0] = gammas.fVec[1] = gammas.fVec[2] = ((float) gamma) * 0.0 0001f;
229
230 return SkColorSpace::NewRGB(toXYZD50, gammas);
231 }
232
233 // Finally, what should we do if there is no color space information in the PNG?
234 // The specification says that this indicates "gamma is unknown" and that th e
235 // "color is device dependent". I'm assuming we can represent this with NUL L.
236 // But should we guess sRGB? Are most images intended to be sRGB?
237 return nullptr;
238 }
239
168 // Reads the header and initializes the output fields, if not NULL. 240 // Reads the header and initializes the output fields, if not NULL.
169 // 241 //
170 // @param stream Input data. Will be read to get enough information to properly 242 // @param stream Input data. Will be read to get enough information to properly
171 // setup the codec. 243 // setup the codec.
172 // @param chunkReader SkPngChunkReader, for reading unknown chunks. May be NULL. 244 // @param chunkReader SkPngChunkReader, for reading unknown chunks. May be NULL.
173 // If not NULL, png_ptr will hold an *unowned* pointer to it. The caller is 245 // If not NULL, png_ptr will hold an *unowned* pointer to it. The caller is
174 // expected to continue to own it for the lifetime of the png_ptr. 246 // expected to continue to own it for the lifetime of the png_ptr.
175 // @param png_ptrp Optional output variable. If non-NULL, will be set to a new 247 // @param png_ptrp Optional output variable. If non-NULL, will be set to a new
176 // png_structp on success. 248 // png_structp on success.
177 // @param info_ptrp Optional output variable. If non-NULL, will be set to a new 249 // @param info_ptrp Optional output variable. If non-NULL, will be set to a new
178 // png_infop on success; 250 // png_infop on success;
179 // @param imageInfo Optional output variable. If non-NULL, will be set to 251 // @param imageInfo Optional output variable. If non-NULL, will be set to
180 // reflect the properties of the encoded image on success. 252 // reflect the properties of the encoded image on success.
181 // @param bitDepthPtr Optional output variable. If non-NULL, will be set to the 253 // @param bitDepthPtr Optional output variable. If non-NULL, will be set to the
182 // bit depth of the encoded image on success. 254 // bit depth of the encoded image on success.
183 // @param numberPassesPtr Optional output variable. If non-NULL, will be set to 255 // @param numberPassesPtr Optional output variable. If non-NULL, will be set to
184 // the number_passes of the encoded image on success. 256 // the number_passes of the encoded image on success.
257 // @param colorSpace Optional output variable. If non-NULL, will be set to a
258 // colorSpace object that represents any color space information in the
259 // encoded data. If the encoded data contains no color space, information
260 // this will be set to NULL.
185 // @return true on success, in which case the caller is responsible for calling 261 // @return true on success, in which case the caller is responsible for calling
186 // png_destroy_read_struct(png_ptrp, info_ptrp). 262 // png_destroy_read_struct(png_ptrp, info_ptrp).
187 // If it returns false, the passed in fields (except stream) are unchanged. 263 // If it returns false, the passed in fields (except stream) are unchanged.
188 static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader, 264 static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader,
189 png_structp* png_ptrp, png_infop* info_ptrp, 265 png_structp* png_ptrp, png_infop* info_ptrp,
190 SkImageInfo* imageInfo, int* bitDepthPtr, int* numberPas sesPtr) { 266 SkImageInfo* imageInfo, int* bitDepthPtr, int* numberPas sesPtr,
267 SkColorSpace** colorSpace) {
191 // The image is known to be a PNG. Decode enough to know the SkImageInfo. 268 // The image is known to be a PNG. Decode enough to know the SkImageInfo.
192 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, 269 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr,
193 sk_error_fn, sk_warning_fn); 270 sk_error_fn, sk_warning_fn);
194 if (!png_ptr) { 271 if (!png_ptr) {
195 return false; 272 return false;
196 } 273 }
197 274
198 AutoCleanPng autoClean(png_ptr); 275 AutoCleanPng autoClean(png_ptr);
199 276
200 png_infop info_ptr = png_create_info_struct(png_ptr); 277 png_infop info_ptr = png_create_info_struct(png_ptr);
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after
303 default: 380 default:
304 // All the color types have been covered above. 381 // All the color types have been covered above.
305 SkASSERT(false); 382 SkASSERT(false);
306 } 383 }
307 384
308 int numberPasses = png_set_interlace_handling(png_ptr); 385 int numberPasses = png_set_interlace_handling(png_ptr);
309 if (numberPassesPtr) { 386 if (numberPassesPtr) {
310 *numberPassesPtr = numberPasses; 387 *numberPassesPtr = numberPasses;
311 } 388 }
312 389
390 if (colorSpace) {
391 *colorSpace = read_color_space(png_ptr, info_ptr);
392 }
393
313 SkColorProfileType profileType = kLinear_SkColorProfileType; 394 SkColorProfileType profileType = kLinear_SkColorProfileType;
314 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) { 395 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) {
315 profileType = kSRGB_SkColorProfileType; 396 profileType = kSRGB_SkColorProfileType;
316 } 397 }
317 398
318 if (imageInfo) { 399 if (imageInfo) {
319 *imageInfo = SkImageInfo::Make(origWidth, origHeight, colorType, alphaTy pe, profileType); 400 *imageInfo = SkImageInfo::Make(origWidth, origHeight, colorType, alphaTy pe, profileType);
320 } 401 }
321 autoClean.detach(); 402 autoClean.detach();
322 if (png_ptrp) { 403 if (png_ptrp) {
323 *png_ptrp = png_ptr; 404 *png_ptrp = png_ptr;
324 } 405 }
325 if (info_ptrp) { 406 if (info_ptrp) {
326 *info_ptrp = info_ptr; 407 *info_ptrp = info_ptr;
327 } 408 }
328 409
329 return true; 410 return true;
330 } 411 }
331 412
332 SkPngCodec::SkPngCodec(const SkImageInfo& info, SkStream* stream, SkPngChunkRead er* chunkReader, 413 SkPngCodec::SkPngCodec(const SkImageInfo& info, SkStream* stream, SkPngChunkRead er* chunkReader,
333 png_structp png_ptr, png_infop info_ptr, int bitDepth, in t numberPasses) 414 png_structp png_ptr, png_infop info_ptr, int bitDepth, in t numberPasses,
334 : INHERITED(info, stream) 415 SkColorSpace* colorSpace)
416 : INHERITED(info, stream, colorSpace)
335 , fPngChunkReader(SkSafeRef(chunkReader)) 417 , fPngChunkReader(SkSafeRef(chunkReader))
336 , fPng_ptr(png_ptr) 418 , fPng_ptr(png_ptr)
337 , fInfo_ptr(info_ptr) 419 , fInfo_ptr(info_ptr)
338 , fSrcConfig(SkSwizzler::kUnknown) 420 , fSrcConfig(SkSwizzler::kUnknown)
339 , fNumberPasses(numberPasses) 421 , fNumberPasses(numberPasses)
340 , fBitDepth(bitDepth) 422 , fBitDepth(bitDepth)
341 {} 423 {}
342 424
343 SkPngCodec::~SkPngCodec() { 425 SkPngCodec::~SkPngCodec() {
344 this->destroyReadStruct(); 426 this->destroyReadStruct();
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
427 // This sets fPng_ptr and fInfo_ptr to nullptr. If read_header 509 // This sets fPng_ptr and fInfo_ptr to nullptr. If read_header
428 // succeeds, they will be repopulated, and if it fails, they will 510 // succeeds, they will be repopulated, and if it fails, they will
429 // remain nullptr. Any future accesses to fPng_ptr and fInfo_ptr will 511 // remain nullptr. Any future accesses to fPng_ptr and fInfo_ptr will
430 // come through this function which will rewind and again attempt 512 // come through this function which will rewind and again attempt
431 // to reinitialize them. 513 // to reinitialize them.
432 this->destroyReadStruct(); 514 this->destroyReadStruct();
433 515
434 png_structp png_ptr; 516 png_structp png_ptr;
435 png_infop info_ptr; 517 png_infop info_ptr;
436 if (!read_header(this->stream(), fPngChunkReader.get(), &png_ptr, &info_ptr, 518 if (!read_header(this->stream(), fPngChunkReader.get(), &png_ptr, &info_ptr,
437 nullptr, nullptr, nullptr)) { 519 nullptr, nullptr, nullptr, nullptr)) {
scroggo 2016/02/24 18:31:36 You can leave off this last nullptr if you call re
msarett 2016/02/29 21:47:55 Done.
438 return false; 520 return false;
439 } 521 }
440 522
441 fPng_ptr = png_ptr; 523 fPng_ptr = png_ptr;
442 fInfo_ptr = info_ptr; 524 fInfo_ptr = info_ptr;
443 return true; 525 return true;
444 } 526 }
445 527
446 SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst, 528 SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst,
447 size_t dstRowBytes, const Options& optio ns, 529 size_t dstRowBytes, const Options& optio ns,
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
534 if (colorPtr) { 616 if (colorPtr) {
535 return get_color_table_fill_value(colorType, colorPtr, 0); 617 return get_color_table_fill_value(colorType, colorPtr, 0);
536 } 618 }
537 return INHERITED::onGetFillValue(colorType); 619 return INHERITED::onGetFillValue(colorType);
538 } 620 }
539 621
540 // Subclass of SkPngCodec which supports scanline decoding 622 // Subclass of SkPngCodec which supports scanline decoding
541 class SkPngScanlineDecoder : public SkPngCodec { 623 class SkPngScanlineDecoder : public SkPngCodec {
542 public: 624 public:
543 SkPngScanlineDecoder(const SkImageInfo& srcInfo, SkStream* stream, 625 SkPngScanlineDecoder(const SkImageInfo& srcInfo, SkStream* stream,
544 SkPngChunkReader* chunkReader, png_structp png_ptr, png_infop info_p tr, int bitDepth) 626 SkPngChunkReader* chunkReader, png_structp png_ptr, png_infop info_p tr, int bitDepth,
545 : INHERITED(srcInfo, stream, chunkReader, png_ptr, info_ptr, bitDepth, 1 ) 627 SkColorSpace* colorSpace)
628 : INHERITED(srcInfo, stream, chunkReader, png_ptr, info_ptr, bitDepth, 1 , colorSpace)
546 , fSrcRow(nullptr) 629 , fSrcRow(nullptr)
547 {} 630 {}
548 631
549 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti ons, 632 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti ons,
550 SkPMColor ctable[], int* ctableCount) override { 633 SkPMColor ctable[], int* ctableCount) override {
551 if (!conversion_possible(dstInfo, this->getInfo())) { 634 if (!conversion_possible(dstInfo, this->getInfo())) {
552 return kInvalidConversion; 635 return kInvalidConversion;
553 } 636 }
554 637
555 const Result result = this->initializeSwizzler(dstInfo, options, ctable, 638 const Result result = this->initializeSwizzler(dstInfo, options, ctable,
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
600 uint8_t* fSrcRow; 683 uint8_t* fSrcRow;
601 684
602 typedef SkPngCodec INHERITED; 685 typedef SkPngCodec INHERITED;
603 }; 686 };
604 687
605 688
606 class SkPngInterlacedScanlineDecoder : public SkPngCodec { 689 class SkPngInterlacedScanlineDecoder : public SkPngCodec {
607 public: 690 public:
608 SkPngInterlacedScanlineDecoder(const SkImageInfo& srcInfo, SkStream* stream, 691 SkPngInterlacedScanlineDecoder(const SkImageInfo& srcInfo, SkStream* stream,
609 SkPngChunkReader* chunkReader, png_structp png_ptr, png_infop info_p tr, 692 SkPngChunkReader* chunkReader, png_structp png_ptr, png_infop info_p tr,
610 int bitDepth, int numberPasses) 693 int bitDepth, int numberPasses, SkColorSpace* colorSpace)
611 : INHERITED(srcInfo, stream, chunkReader, png_ptr, info_ptr, bitDepth, n umberPasses) 694 : INHERITED(srcInfo, stream, chunkReader, png_ptr, info_ptr, bitDepth, n umberPasses,
695 colorSpace)
612 , fHeight(-1) 696 , fHeight(-1)
613 , fCanSkipRewind(false) 697 , fCanSkipRewind(false)
614 { 698 {
615 SkASSERT(numberPasses != 1); 699 SkASSERT(numberPasses != 1);
616 } 700 }
617 701
618 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti ons, 702 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti ons,
619 SkPMColor ctable[], int* ctableCount) override { 703 SkPMColor ctable[], int* ctableCount) override {
620 if (!conversion_possible(dstInfo, this->getInfo())) { 704 if (!conversion_possible(dstInfo, this->getInfo())) {
621 return kInvalidConversion; 705 return kInvalidConversion;
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after
726 typedef SkPngCodec INHERITED; 810 typedef SkPngCodec INHERITED;
727 }; 811 };
728 812
729 SkCodec* SkPngCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkRead er) { 813 SkCodec* SkPngCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkRead er) {
730 SkAutoTDelete<SkStream> streamDeleter(stream); 814 SkAutoTDelete<SkStream> streamDeleter(stream);
731 png_structp png_ptr; 815 png_structp png_ptr;
732 png_infop info_ptr; 816 png_infop info_ptr;
733 SkImageInfo imageInfo; 817 SkImageInfo imageInfo;
734 int bitDepth; 818 int bitDepth;
735 int numberPasses; 819 int numberPasses;
820 SkColorSpace* colorSpace = nullptr;
736 821
737 if (!read_header(stream, chunkReader, &png_ptr, &info_ptr, &imageInfo, &bitD epth, 822 if (!read_header(stream, chunkReader, &png_ptr, &info_ptr, &imageInfo, &bitD epth,
738 &numberPasses)) { 823 &numberPasses, &colorSpace)) {
scroggo 2016/02/24 18:31:36 I think read_color_space looks like a good standal
msarett 2016/02/29 21:47:54 Done.
739 return nullptr; 824 return nullptr;
740 } 825 }
741 826
742 if (1 == numberPasses) { 827 if (1 == numberPasses) {
743 return new SkPngScanlineDecoder(imageInfo, streamDeleter.detach(), chunk Reader, 828 return new SkPngScanlineDecoder(imageInfo, streamDeleter.detach(), chunk Reader,
744 png_ptr, info_ptr, bitDepth); 829 png_ptr, info_ptr, bitDepth, colorSpace) ;
745 } 830 }
746 831
747 return new SkPngInterlacedScanlineDecoder(imageInfo, streamDeleter.detach(), chunkReader, 832 return new SkPngInterlacedScanlineDecoder(imageInfo, streamDeleter.detach(), chunkReader,
748 png_ptr, info_ptr, bitDepth, numbe rPasses); 833 png_ptr, info_ptr, bitDepth, numbe rPasses,
834 colorSpace);
749 } 835 }
OLDNEW
« include/codec/SkCodec.h ('K') | « src/codec/SkPngCodec.h ('k') | src/core/SkColorSpace.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698