OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 } |
OLD | NEW |