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

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

Issue 2184543003: Perform color correction on png decodes (Closed) Base URL: https://skia.googlesource.com/skia.git@colorjpegs
Patch Set: Created 4 years, 4 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 "SkBitmap.h" 8 #include "SkBitmap.h"
9 #include "SkCodecPriv.h" 9 #include "SkCodecPriv.h"
10 #include "SkColorPriv.h" 10 #include "SkColorPriv.h"
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
80 fPng_ptr = nullptr; 80 fPng_ptr = nullptr;
81 fInfo_ptr = nullptr; 81 fInfo_ptr = nullptr;
82 } 82 }
83 83
84 private: 84 private:
85 png_structp fPng_ptr; 85 png_structp fPng_ptr;
86 png_infop fInfo_ptr; 86 png_infop fInfo_ptr;
87 }; 87 };
88 #define AutoCleanPng(...) SK_REQUIRE_LOCAL_VAR(AutoCleanPng) 88 #define AutoCleanPng(...) SK_REQUIRE_LOCAL_VAR(AutoCleanPng)
89 89
90 // Note: SkColorTable claims to store SkPMColors, which is not necessarily 90 // Note: SkColorTable claims to store SkPMColors, which is not necessarily the c ase here.
91 // the case here.
92 // TODO: If we add support for non-native swizzles, we'll need to handle that he re.
93 bool SkPngCodec::createColorTable(SkColorType dstColorType, bool premultiply, in t* ctableCount) { 91 bool SkPngCodec::createColorTable(SkColorType dstColorType, bool premultiply, in t* ctableCount) {
94 92
95 int numColors; 93 int numColors;
96 png_color* palette; 94 png_color* palette;
97 if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numColors)) { 95 if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numColors)) {
98 return false; 96 return false;
99 } 97 }
100 98
101 // Note: These are not necessarily SkPMColors. 99 // Note: These are not necessarily SkPMColors.
102 SkPMColor colorPtr[256]; 100 SkPMColor colorPtr[256];
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after
329 // implemented to guess sRGB in this case. 327 // implemented to guess sRGB in this case.
330 return nullptr; 328 return nullptr;
331 } 329 }
332 330
333 static int bytes_per_pixel(int bitsPerPixel) { 331 static int bytes_per_pixel(int bitsPerPixel) {
334 // Note that we will have to change this implementation if we start 332 // Note that we will have to change this implementation if we start
335 // supporting outputs from libpng that are less than 8-bits per component. 333 // supporting outputs from libpng that are less than 8-bits per component.
336 return bitsPerPixel / 8; 334 return bitsPerPixel / 8;
337 } 335 }
338 336
339 // Subclass of SkPngCodec which supports scanline decoding 337 static bool png_conversion_possible(const SkImageInfo& dst, const SkImageInfo& s rc) {
340 class SkPngScanlineDecoder : public SkPngCodec { 338 // Ensure the alpha type is valid
339 if (!valid_alpha(dst.alphaType(), src.alphaType())) {
340 return false;
341 }
342
343 // Check for supported color types
344 switch (dst.colorType()) {
345 case kRGBA_8888_SkColorType:
346 case kBGRA_8888_SkColorType:
347 case kRGBA_F16_SkColorType:
348 return true;
349 case kRGB_565_SkColorType:
350 return kOpaque_SkAlphaType == src.alphaType();
351 default:
352 return dst.colorType() == src.colorType();
353 }
354 }
355
356 void SkPngCodec::allocateStorage(const SkImageInfo& dstInfo) {
357 const int width = this->getInfo().width();
358 size_t colorXformBytes = fColorXform ?
359 width * sizeof(SkColorTypeBytesPerPixel(dstInfo.colorType())) : 0;
360
361 fStorage.reset(fSrcRowBytes + colorXformBytes);
362 fSwizzlerSrcRow = fStorage.get();
363 fColorXformSrcRow = fColorXform ? SkTAddOffset<uint32_t>(fSwizzlerSrcRow, fS rcRowBytes) : 0;
364 }
365
366 class SkPngNormalCodec : public SkPngCodec {
341 public: 367 public:
342 SkPngScanlineDecoder(int width, int height, const SkEncodedInfo& info, SkStr eam* stream, 368 SkPngNormalCodec(int width, int height, const SkEncodedInfo& info, SkStream* stream,
343 SkPngChunkReader* chunkReader, png_structp png_ptr, png_infop info_p tr, int bitDepth, 369 SkPngChunkReader* chunkReader, png_structp png_ptr, png_infop info_p tr, int bitDepth,
344 sk_sp<SkColorSpace> colorSpace) 370 sk_sp<SkColorSpace> colorSpace)
345 : INHERITED(width, height, info, stream, chunkReader, png_ptr, info_ptr, bitDepth, 1, 371 : INHERITED(width, height, info, stream, chunkReader, png_ptr, info_ptr, bitDepth, 1,
346 colorSpace) 372 colorSpace)
347 , fSrcRow(nullptr)
348 {} 373 {}
349 374
350 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti ons, 375 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti ons,
351 SkPMColor ctable[], int* ctableCount) override { 376 SkPMColor ctable[], int* ctableCount) override {
352 if (!conversion_possible(dstInfo, this->getInfo())) { 377 if (!png_conversion_possible(dstInfo, this->getInfo()) ||
378 !this->initializeXforms(dstInfo, options, ctable, ctableCount))
379 {
353 return kInvalidConversion; 380 return kInvalidConversion;
354 } 381 }
355 382
356 const Result result = this->initializeSwizzler(dstInfo, options, ctable, 383 this->allocateStorage(dstInfo);
357 ctableCount);
358 if (result != kSuccess) {
359 return result;
360 }
361
362 fStorage.reset(this->getInfo().width() *
363 (bytes_per_pixel(this->getEncodedInfo().bitsPerPixel())));
364 fSrcRow = fStorage.get();
365
366 return kSuccess; 384 return kSuccess;
367 } 385 }
368 386
369 int onGetScanlines(void* dst, int count, size_t rowBytes) override { 387 int readRows(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, int cou nt, int startRow)
388 override {
389 SkASSERT(0 == startRow);
390
370 // Assume that an error in libpng indicates an incomplete input. 391 // Assume that an error in libpng indicates an incomplete input.
371 int row = 0; 392 int y = 0;
372 if (setjmp(png_jmpbuf(this->png_ptr()))) { 393 if (setjmp(png_jmpbuf(fPng_ptr))) {
373 SkCodecPrintf("setjmp long jump!\n"); 394 SkCodecPrintf("Failed to read row.\n");
374 return row; 395 return y;
375 } 396 }
376 397
377 void* dstRow = dst; 398 void* swizzlerDstRow = dst;
378 for (; row < count; row++) { 399 size_t swizzlerDstRowBytes = rowBytes;
379 png_read_row(this->png_ptr(), fSrcRow, nullptr); 400 if (fColorXform) {
380 this->swizzler()->swizzle(dstRow, fSrcRow); 401 swizzlerDstRow = fColorXformSrcRow;
381 dstRow = SkTAddOffset<void>(dstRow, rowBytes); 402 swizzlerDstRowBytes = 0;
382 } 403 }
383 404
384 return row; 405 bool needsPremul = kUnpremul_SkAlphaType == this->getInfo().alphaType() &&
406 kPremul_SkAlphaType == dstInfo.alphaType();
407 for (; y < count; y++) {
408 png_read_row(fPng_ptr, fSwizzlerSrcRow, nullptr);
409 fSwizzler->swizzle(swizzlerDstRow, fSwizzlerSrcRow);
410
411 if (fColorXform) {
412 fColorXform->apply(dst, (const uint32_t*) swizzlerDstRow, dstInf o.width(),
413 dstInfo.colorType(), needsPremul);
414 dst = SkTAddOffset<void>(dst, rowBytes);
415 }
416
417 swizzlerDstRow = SkTAddOffset<void>(swizzlerDstRow, swizzlerDstRowBy tes);
418 }
419
420 return y;
421 }
422
423 int onGetScanlines(void* dst, int count, size_t rowBytes) override {
424 return this->readRows(this->dstInfo(), dst, rowBytes, count, 0);
385 } 425 }
386 426
387 bool onSkipScanlines(int count) override { 427 bool onSkipScanlines(int count) override {
388 // Assume that an error in libpng indicates an incomplete input. 428 if (setjmp(png_jmpbuf(fPng_ptr))) {
389 if (setjmp(png_jmpbuf(this->png_ptr()))) { 429 SkCodecPrintf("Failed to skip row.\n");
390 SkCodecPrintf("setjmp long jump!\n");
391 return false; 430 return false;
392 } 431 }
393 432
394 for (int row = 0; row < count; row++) { 433 for (int row = 0; row < count; row++) {
395 png_read_row(this->png_ptr(), fSrcRow, nullptr); 434 png_read_row(fPng_ptr, fSwizzlerSrcRow, nullptr);
396 } 435 }
397 return true; 436 return true;
398 } 437 }
399 438
400 private:
401 SkAutoTMalloc<uint8_t> fStorage;
402 uint8_t* fSrcRow;
403
404 typedef SkPngCodec INHERITED; 439 typedef SkPngCodec INHERITED;
405 }; 440 };
406 441
407 442
408 class SkPngInterlacedScanlineDecoder : public SkPngCodec { 443 class SkPngInterlacedCodec : public SkPngCodec {
409 public: 444 public:
410 SkPngInterlacedScanlineDecoder(int width, int height, const SkEncodedInfo& i nfo, 445 SkPngInterlacedCodec(int width, int height, const SkEncodedInfo& info,
411 SkStream* stream, SkPngChunkReader* chunkReader, png_structp png_ptr , 446 SkStream* stream, SkPngChunkReader* chunkReader, png_structp png_ptr ,
412 png_infop info_ptr, int bitDepth, int numberPasses, sk_sp<SkColorSpa ce> colorSpace) 447 png_infop info_ptr, int bitDepth, int numberPasses, sk_sp<SkColorSpa ce> colorSpace)
413 : INHERITED(width, height, info, stream, chunkReader, png_ptr, info_ptr, bitDepth, 448 : INHERITED(width, height, info, stream, chunkReader, png_ptr, info_ptr, bitDepth,
414 numberPasses, colorSpace) 449 numberPasses, colorSpace)
415 , fHeight(-1)
416 , fCanSkipRewind(false) 450 , fCanSkipRewind(false)
417 { 451 {
418 SkASSERT(numberPasses != 1); 452 SkASSERT(numberPasses != 1);
419 } 453 }
420 454
421 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti ons, 455 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti ons,
422 SkPMColor ctable[], int* ctableCount) override { 456 SkPMColor ctable[], int* ctableCount) override {
423 if (!conversion_possible(dstInfo, this->getInfo())) { 457 if (!png_conversion_possible(dstInfo, this->getInfo()) ||
458 !this->initializeXforms(dstInfo, options, ctable, ctableCount))
459 {
424 return kInvalidConversion; 460 return kInvalidConversion;
425 } 461 }
426 462
427 const Result result = this->initializeSwizzler(dstInfo, options, ctable, 463 this->allocateStorage(dstInfo);
428 ctableCount);
429 if (result != kSuccess) {
430 return result;
431 }
432
433 fHeight = dstInfo.height();
434 // FIXME: This need not be called on a second call to onStartScanlineDec ode.
435 fSrcRowBytes = this->getInfo().width() *
436 (bytes_per_pixel(this->getEncodedInfo().bitsPerPixel()));
437 fGarbageRow.reset(fSrcRowBytes);
438 fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get());
439 fCanSkipRewind = true; 464 fCanSkipRewind = true;
440
441 return SkCodec::kSuccess; 465 return SkCodec::kSuccess;
442 } 466 }
443 467
444 int onGetScanlines(void* dst, int count, size_t dstRowBytes) override { 468 int readRows(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, int cou nt, int startRow)
469 override {
470 if (setjmp(png_jmpbuf(fPng_ptr))) {
471 SkCodecPrintf("Failed to get scanlines.\n");
472 // FIXME (msarett): Returning 0 is pessimistic. If we can complete a single pass,
473 // we may be able to report that all of the memory has been initiali zed. Even if we
474 // fail on the first pass, we can still report than some scanlines a re initialized.
475 return 0;
476 }
477
478 SkAutoTMalloc<uint8_t> storage(count * fSrcRowBytes);
479 uint8_t* srcRow;
480 for (int i = 0; i < fNumberPasses; i++) {
481 // Discard rows that we planned to skip.
482 for (int y = 0; y < startRow; y++){
483 png_read_row(fPng_ptr, fSwizzlerSrcRow, nullptr);
484 }
485 // Read rows we care about into storage.
486 srcRow = storage.get();
487 for (int y = 0; y < count; y++) {
488 png_read_row(fPng_ptr, srcRow, nullptr);
489 srcRow += fSrcRowBytes;
490 }
491 // Discard rows that we don't need.
492 for (int y = 0; y < this->getInfo().height() - startRow - count; y++ ) {
493 png_read_row(fPng_ptr, fSwizzlerSrcRow, nullptr);
494 }
495 }
496
497 // Swizzle and xform the rows we care about
498 void* swizzlerDstRow = dst;
499 size_t swizzlerDstRowBytes = rowBytes;
500 if (fColorXform) {
501 swizzlerDstRow = fColorXformSrcRow;
502 swizzlerDstRowBytes = 0;
503 }
504
505 bool needsPremul = kUnpremul_SkAlphaType == this->getInfo().alphaType() &&
506 kPremul_SkAlphaType == dstInfo.alphaType();
507 srcRow = storage.get();
508 for (int y = 0; y < count; y++) {
509 fSwizzler->swizzle(swizzlerDstRow, srcRow);
510 srcRow = SkTAddOffset<uint8_t>(srcRow, fSrcRowBytes);
511
512 if (fColorXform) {
513 if (fColorXform) {
514 fColorXform->apply(dst, (const uint32_t*) swizzlerDstRow, ds tInfo.width(),
515 dstInfo.colorType(), needsPremul);
516 dst = SkTAddOffset<void>(dst, rowBytes);
517 }
518 }
519
520 swizzlerDstRow = SkTAddOffset<void>(swizzlerDstRow, swizzlerDstRowBy tes);
521 }
522
523 return count;
524 }
525
526 int onGetScanlines(void* dst, int count, size_t rowBytes) override {
445 // rewind stream if have previously called onGetScanlines, 527 // rewind stream if have previously called onGetScanlines,
446 // since we need entire progressive image to get scanlines 528 // since we need entire progressive image to get scanlines
447 if (fCanSkipRewind) { 529 if (fCanSkipRewind) {
448 // We already rewound in onStartScanlineDecode, so there is no reaso n to rewind. 530 // We already rewound in onStartScanlineDecode, so there is no reaso n to rewind.
449 // Next time onGetScanlines is called, we will need to rewind. 531 // Next time onGetScanlines is called, we will need to rewind.
450 fCanSkipRewind = false; 532 fCanSkipRewind = false;
451 } else { 533 } else {
452 // rewindIfNeeded resets fCurrScanline, since it assumes that start 534 // rewindIfNeeded resets fCurrScanline, since it assumes that start
453 // needs to be called again before scanline decoding. PNG scanline 535 // needs to be called again before scanline decoding. PNG scanline
454 // decoding is the exception, since it needs to rewind between 536 // decoding is the exception, since it needs to rewind between
455 // calls to getScanlines. Keep track of fCurrScanline, to undo the 537 // calls to getScanlines. Keep track of fCurrScanline, to undo the
456 // reset. 538 // reset.
457 const int currScanline = this->nextScanline(); 539 const int currScanline = this->nextScanline();
458 // This method would never be called if currScanline is -1 540 // This method would never be called if currScanline is -1
459 SkASSERT(currScanline != -1); 541 SkASSERT(currScanline != -1);
460 542
461 if (!this->rewindIfNeeded()) { 543 if (!this->rewindIfNeeded()) {
462 return kCouldNotRewind; 544 return kCouldNotRewind;
463 } 545 }
464 this->updateCurrScanline(currScanline); 546 this->updateCurrScanline(currScanline);
465 } 547 }
466 548
467 if (setjmp(png_jmpbuf(this->png_ptr()))) { 549 return this->readRows(this->dstInfo(), dst, rowBytes, count, this->nextS canline());
468 SkCodecPrintf("setjmp long jump!\n");
469 // FIXME (msarett): Returning 0 is pessimistic. If we can complete a single pass,
470 // we may be able to report that all of the memory has been initiali zed. Even if we
471 // fail on the first pass, we can still report than some scanlines a re initialized.
472 return 0;
473 }
474 SkAutoTMalloc<uint8_t> storage(count * fSrcRowBytes);
475 uint8_t* storagePtr = storage.get();
476 uint8_t* srcRow;
477 const int startRow = this->nextScanline();
478 for (int i = 0; i < this->numberPasses(); i++) {
479 // read rows we planned to skip into garbage row
480 for (int y = 0; y < startRow; y++){
481 png_read_row(this->png_ptr(), fGarbageRowPtr, nullptr);
482 }
483 // read rows we care about into buffer
484 srcRow = storagePtr;
485 for (int y = 0; y < count; y++) {
486 png_read_row(this->png_ptr(), srcRow, nullptr);
487 srcRow += fSrcRowBytes;
488 }
489 // read rows we don't want into garbage buffer
490 for (int y = 0; y < fHeight - startRow - count; y++) {
491 png_read_row(this->png_ptr(), fGarbageRowPtr, nullptr);
492 }
493 }
494 //swizzle the rows we care about
495 srcRow = storagePtr;
496 void* dstRow = dst;
497 for (int y = 0; y < count; y++) {
498 this->swizzler()->swizzle(dstRow, srcRow);
499 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
500 srcRow += fSrcRowBytes;
501 }
502
503 return count;
504 } 550 }
505 551
506 bool onSkipScanlines(int count) override { 552 bool onSkipScanlines(int count) override {
507 // The non-virtual version will update fCurrScanline. 553 // The non-virtual version will update fCurrScanline.
508 return true; 554 return true;
509 } 555 }
510 556
511 SkScanlineOrder onGetScanlineOrder() const override { 557 SkScanlineOrder onGetScanlineOrder() const override {
512 return kNone_SkScanlineOrder; 558 return kNone_SkScanlineOrder;
513 } 559 }
514 560
515 private: 561 private:
516 int fHeight;
517 size_t fSrcRowBytes;
518 SkAutoMalloc fGarbageRow;
519 uint8_t* fGarbageRowPtr;
520 // FIXME: This imitates behavior in SkCodec::rewindIfNeeded. That function 562 // FIXME: This imitates behavior in SkCodec::rewindIfNeeded. That function
521 // is called whenever some action is taken that reads the stream and 563 // is called whenever some action is taken that reads the stream and
522 // therefore the next call will require a rewind. So it modifies a boolean 564 // therefore the next call will require a rewind. So it modifies a boolean
523 // to note that the *next* time it is called a rewind is needed. 565 // to note that the *next* time it is called a rewind is needed.
524 // SkPngInterlacedScanlineDecoder has an extra wrinkle - calling 566 // SkPngInterlacedCodec has an extra wrinkle - calling
525 // onStartScanlineDecode followed by onGetScanlines does *not* require a 567 // onStartScanlineDecode followed by onGetScanlines does *not* require a
526 // rewind. Since rewindIfNeeded does not have this flexibility, we need to 568 // rewind. Since rewindIfNeeded does not have this flexibility, we need to
527 // add another layer. 569 // add another layer.
528 bool fCanSkipRewind; 570 bool fCanSkipRewind;
529 571
530 typedef SkPngCodec INHERITED; 572 typedef SkPngCodec INHERITED;
531 }; 573 };
532 574
533 // Reads the header and initializes the output fields, if not NULL. 575 // Reads the header and initializes the output fields, if not NULL.
534 // 576 //
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after
672 if (outCodec) { 714 if (outCodec) {
673 sk_sp<SkColorSpace> colorSpace = read_color_space(png_ptr, info_ptr); 715 sk_sp<SkColorSpace> colorSpace = read_color_space(png_ptr, info_ptr);
674 if (!colorSpace) { 716 if (!colorSpace) {
675 // Treat unmarked pngs as sRGB. 717 // Treat unmarked pngs as sRGB.
676 colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); 718 colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
677 } 719 }
678 720
679 SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8); 721 SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8);
680 722
681 if (1 == numberPasses) { 723 if (1 == numberPasses) {
682 *outCodec = new SkPngScanlineDecoder(origWidth, origHeight, info, st ream, 724 *outCodec = new SkPngNormalCodec(origWidth, origHeight, info, stream ,
683 chunkReader, png_ptr, info_ptr, bitDepth, colorSpace); 725 chunkReader, png_ptr, info_ptr, bitDepth, colorSpace);
684 } else { 726 } else {
685 *outCodec = new SkPngInterlacedScanlineDecoder(origWidth, origHeight , info, stream, 727 *outCodec = new SkPngInterlacedCodec(origWidth, origHeight, info, st ream,
686 chunkReader, png_ptr, info_ptr, bitDepth, numberPasses, colo rSpace); 728 chunkReader, png_ptr, info_ptr, bitDepth, numberPasses, colo rSpace);
687 } 729 }
688 } 730 }
689 731
690 return true; 732 return true;
691 } 733 }
692 734
693 SkPngCodec::SkPngCodec(int width, int height, const SkEncodedInfo& info, SkStrea m* stream, 735 SkPngCodec::SkPngCodec(int width, int height, const SkEncodedInfo& info, SkStrea m* stream,
694 SkPngChunkReader* chunkReader, png_structp png_ptr, png_i nfop info_ptr, 736 SkPngChunkReader* chunkReader, png_structp png_ptr, png_i nfop info_ptr,
695 int bitDepth, int numberPasses, sk_sp<SkColorSpace> color Space) 737 int bitDepth, int numberPasses, sk_sp<SkColorSpace> color Space)
696 : INHERITED(width, height, info, stream, colorSpace) 738 : INHERITED(width, height, info, stream, colorSpace)
697 , fPngChunkReader(SkSafeRef(chunkReader)) 739 , fPngChunkReader(SkSafeRef(chunkReader))
698 , fPng_ptr(png_ptr) 740 , fPng_ptr(png_ptr)
699 , fInfo_ptr(info_ptr) 741 , fInfo_ptr(info_ptr)
742 , fSwizzlerSrcRow(nullptr)
743 , fColorXformSrcRow(nullptr)
744 , fSrcRowBytes(width * (bytes_per_pixel(this->getEncodedInfo().bitsPerPixel( ))))
700 , fNumberPasses(numberPasses) 745 , fNumberPasses(numberPasses)
701 , fBitDepth(bitDepth) 746 , fBitDepth(bitDepth)
702 {} 747 {}
703 748
704 SkPngCodec::~SkPngCodec() { 749 SkPngCodec::~SkPngCodec() {
705 this->destroyReadStruct(); 750 this->destroyReadStruct();
706 } 751 }
707 752
708 void SkPngCodec::destroyReadStruct() { 753 void SkPngCodec::destroyReadStruct() {
709 if (fPng_ptr) { 754 if (fPng_ptr) {
710 // We will never have a nullptr fInfo_ptr with a non-nullptr fPng_ptr 755 // We will never have a nullptr fInfo_ptr with a non-nullptr fPng_ptr
711 SkASSERT(fInfo_ptr); 756 SkASSERT(fInfo_ptr);
712 png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, nullptr); 757 png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, nullptr);
713 fPng_ptr = nullptr; 758 fPng_ptr = nullptr;
714 fInfo_ptr = nullptr; 759 fInfo_ptr = nullptr;
715 } 760 }
716 } 761 }
717 762
718 /////////////////////////////////////////////////////////////////////////////// 763 ///////////////////////////////////////////////////////////////////////////////
719 // Getting the pixels 764 // Getting the pixels
720 /////////////////////////////////////////////////////////////////////////////// 765 ///////////////////////////////////////////////////////////////////////////////
721 766
722 SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, 767 bool SkPngCodec::initializeXforms(const SkImageInfo& dstInfo, const Options& opt ions,
723 const Options& options, 768 SkPMColor ctable[], int* ctableCount) {
724 SkPMColor ctable[],
725 int* ctableCount) {
726 // FIXME: Could we use the return value of setjmp to specify the type of
727 // error?
728 if (setjmp(png_jmpbuf(fPng_ptr))) { 769 if (setjmp(png_jmpbuf(fPng_ptr))) {
729 SkCodecPrintf("setjmp long jump!\n"); 770 SkCodecPrintf("Failed on png_read_update_info.\n");
730 return kInvalidInput; 771 return false;
731 } 772 }
732 png_read_update_info(fPng_ptr, fInfo_ptr); 773 png_read_update_info(fPng_ptr, fInfo_ptr);
733 774
775 SkImageInfo swizzlerInfo = dstInfo;
776 bool needsColorXform = needs_color_xform(dstInfo, this->getInfo());
777 if (needsColorXform) {
778 switch (dstInfo.colorType()) {
779 case kRGBA_8888_SkColorType:
780 case kBGRA_8888_SkColorType:
781 case kRGBA_F16_SkColorType:
782 swizzlerInfo = swizzlerInfo.makeColorType(kRGBA_8888_SkColorType );
783 if (kPremul_SkAlphaType == dstInfo.alphaType()) {
784 swizzlerInfo = swizzlerInfo.makeAlphaType(kUnpremul_SkAlphaT ype);
785 }
786 break;
787 default:
788 return false;
789 }
790
791 fColorXform = SkColorSpaceXform::New(sk_ref_sp(this->getInfo().colorSpac e()),
792 sk_ref_sp(dstInfo.colorSpace()));
793
794 if (!fColorXform && kRGBA_F16_SkColorType == dstInfo.colorType()) {
795 return kInvalidParameters;
796 }
797 }
798
734 if (SkEncodedInfo::kPalette_Color == this->getEncodedInfo().color()) { 799 if (SkEncodedInfo::kPalette_Color == this->getEncodedInfo().color()) {
735 if (!this->createColorTable(requestedInfo.colorType(), 800 if (!this->createColorTable(swizzlerInfo.colorType(),
736 kPremul_SkAlphaType == requestedInfo.alphaType(), ctableCount)) { 801 kPremul_SkAlphaType == swizzlerInfo.alphaTyp e(), ctableCount)) {
737 return kInvalidInput; 802 return false;
738 } 803 }
739 } 804 }
740 805
741 // Copy the color table to the client if they request kIndex8 mode 806 // Copy the color table to the client if they request kIndex8 mode
742 copy_color_table(requestedInfo, fColorTable, ctable, ctableCount); 807 copy_color_table(swizzlerInfo, fColorTable, ctable, ctableCount);
743 808
744 // Create the swizzler. SkPngCodec retains ownership of the color table. 809 // Create the swizzler. SkPngCodec retains ownership of the color table.
745 const SkPMColor* colors = get_color_ptr(fColorTable.get()); 810 const SkPMColor* colors = get_color_ptr(fColorTable.get());
746 fSwizzler.reset(SkSwizzler::CreateSwizzler(this->getEncodedInfo(), colors, r equestedInfo, 811 fSwizzler.reset(SkSwizzler::CreateSwizzler(this->getEncodedInfo(), colors, s wizzlerInfo,
747 options)); 812 options));
748 SkASSERT(fSwizzler); 813 SkASSERT(fSwizzler);
749 814 return true;
750 return kSuccess;
751 } 815 }
752 816
753
754 bool SkPngCodec::onRewind() { 817 bool SkPngCodec::onRewind() {
755 // This sets fPng_ptr and fInfo_ptr to nullptr. If read_header 818 // This sets fPng_ptr and fInfo_ptr to nullptr. If read_header
756 // succeeds, they will be repopulated, and if it fails, they will 819 // succeeds, they will be repopulated, and if it fails, they will
757 // remain nullptr. Any future accesses to fPng_ptr and fInfo_ptr will 820 // remain nullptr. Any future accesses to fPng_ptr and fInfo_ptr will
758 // come through this function which will rewind and again attempt 821 // come through this function which will rewind and again attempt
759 // to reinitialize them. 822 // to reinitialize them.
760 this->destroyReadStruct(); 823 this->destroyReadStruct();
761 824
762 png_structp png_ptr; 825 png_structp png_ptr;
763 png_infop info_ptr; 826 png_infop info_ptr;
764 if (!read_header(this->stream(), fPngChunkReader.get(), nullptr, &png_ptr, & info_ptr)) { 827 if (!read_header(this->stream(), fPngChunkReader.get(), nullptr, &png_ptr, & info_ptr)) {
765 return false; 828 return false;
766 } 829 }
767 830
768 fPng_ptr = png_ptr; 831 fPng_ptr = png_ptr;
769 fInfo_ptr = info_ptr; 832 fInfo_ptr = info_ptr;
833
834 fSwizzler.reset(nullptr);
835 fColorXform.reset(nullptr);
770 return true; 836 return true;
771 } 837 }
772 838
773 SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst, 839 SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst,
774 size_t dstRowBytes, const Options& optio ns, 840 size_t rowBytes, const Options& options,
775 SkPMColor ctable[], int* ctableCount, 841 SkPMColor ctable[], int* ctableCount,
776 int* rowsDecoded) { 842 int* rowsDecoded) {
777 if (!conversion_possible(requestedInfo, this->getInfo())) { 843 if (!png_conversion_possible(dstInfo, this->getInfo()) ||
844 !this->initializeXforms(dstInfo, options, ctable, ctableCount))
845 {
778 return kInvalidConversion; 846 return kInvalidConversion;
779 } 847 }
848
780 if (options.fSubset) { 849 if (options.fSubset) {
781 // Subsets are not supported.
782 return kUnimplemented; 850 return kUnimplemented;
783 } 851 }
784 852
785 // Note that ctable and ctableCount may be modified if there is a color tabl e 853 this->allocateStorage(dstInfo);
786 const Result result = this->initializeSwizzler(requestedInfo, options, ctabl e, ctableCount); 854 int count = this->readRows(dstInfo, dst, rowBytes, dstInfo.height(), 0);
787 if (result != kSuccess) { 855 if (count > dstInfo.height()) {
788 return result; 856 *rowsDecoded = count;
857 return kIncompleteInput;
789 } 858 }
790 859
791 const int width = requestedInfo.width();
792 const int height = requestedInfo.height();
793 const int bpp = bytes_per_pixel(this->getEncodedInfo().bitsPerPixel());
794 const size_t srcRowBytes = width * bpp;
795
796 // FIXME: Could we use the return value of setjmp to specify the type of
797 // error?
798 int row = 0;
799 // This must be declared above the call to setjmp to avoid memory leaks on i ncomplete images.
800 SkAutoTMalloc<uint8_t> storage;
801 if (setjmp(png_jmpbuf(fPng_ptr))) {
802 // Assume that any error that occurs while reading rows is caused by an incomplete input.
803 if (fNumberPasses > 1) {
804 // FIXME (msarett): Handle incomplete interlaced pngs.
805 return (row == height) ? kSuccess : kInvalidInput;
806 }
807 // FIXME: We do a poor job on incomplete pngs compared to other decoders (ex: Chromium,
808 // Ubuntu Image Viewer). This is because we use the default buffer size in libpng (8192
809 // bytes), and if we can't fill the buffer, we immediately fail.
810 // For example, if we try to read 8192 bytes, and the image (incorrectly ) only contains
811 // half that, which may have been enough to contain a non-zero number of lines, we fail
812 // when we could have decoded a few more lines and then failed.
813 // The read function that we provide for libpng has no way of indicating that we have
814 // made a partial read.
815 // Making our buffer size smaller improves our incomplete decodes, but w hat impact does
816 // it have on regular decode performance? Should we investigate using a different API
817 // instead of png_read_row? Chromium uses png_process_data.
818 *rowsDecoded = row;
819 return (row == height) ? kSuccess : kIncompleteInput;
820 }
821
822 // FIXME: We could split these out based on subclass.
823 void* dstRow = dst;
824 if (fNumberPasses > 1) {
825 storage.reset(height * srcRowBytes);
826 uint8_t* const base = storage.get();
827
828 for (int i = 0; i < fNumberPasses; i++) {
829 uint8_t* srcRow = base;
830 for (int y = 0; y < height; y++) {
831 png_read_row(fPng_ptr, srcRow, nullptr);
832 srcRow += srcRowBytes;
833 }
834 }
835
836 // Now swizzle it.
837 uint8_t* srcRow = base;
838 for (; row < height; row++) {
839 fSwizzler->swizzle(dstRow, srcRow);
840 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
841 srcRow += srcRowBytes;
842 }
843 } else {
844 storage.reset(srcRowBytes);
845 uint8_t* srcRow = storage.get();
846 for (; row < height; row++) {
847 png_read_row(fPng_ptr, srcRow, nullptr);
848 fSwizzler->swizzle(dstRow, srcRow);
849 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
850 }
851 }
852
853 // read rest of file, and get additional comment and time chunks in info_ptr
854 png_read_end(fPng_ptr, fInfo_ptr);
855
856 return kSuccess; 860 return kSuccess;
857 } 861 }
858 862
859 uint32_t SkPngCodec::onGetFillValue(SkColorType colorType) const { 863 uint32_t SkPngCodec::onGetFillValue(SkColorType colorType) const {
860 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); 864 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
861 if (colorPtr) { 865 if (colorPtr) {
862 return get_color_table_fill_value(colorType, colorPtr, 0); 866 return get_color_table_fill_value(colorType, colorPtr, 0);
863 } 867 }
864 return INHERITED::onGetFillValue(colorType); 868 return INHERITED::onGetFillValue(colorType);
865 } 869 }
866 870
867 SkCodec* SkPngCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkRead er) { 871 SkCodec* SkPngCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkRead er) {
868 SkAutoTDelete<SkStream> streamDeleter(stream); 872 SkAutoTDelete<SkStream> streamDeleter(stream);
869 873
870 SkCodec* outCodec; 874 SkCodec* outCodec;
871 if (read_header(stream, chunkReader, &outCodec, nullptr, nullptr)) { 875 if (read_header(stream, chunkReader, &outCodec, nullptr, nullptr)) {
872 // Codec has taken ownership of the stream. 876 // Codec has taken ownership of the stream.
873 SkASSERT(outCodec); 877 SkASSERT(outCodec);
874 streamDeleter.release(); 878 streamDeleter.release();
875 return outCodec; 879 return outCodec;
876 } 880 }
877 881
878 return nullptr; 882 return nullptr;
879 } 883 }
OLDNEW
« no previous file with comments | « src/codec/SkPngCodec.h ('k') | src/core/SkColorSpaceXform.h » ('j') | src/opts/SkOpts_sse41.cpp » ('J')

Powered by Google App Engine
This is Rietveld 408576698