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 "SkBitmap.h" | 8 #include "SkBitmap.h" |
9 #include "SkCodecPriv.h" | 9 #include "SkCodecPriv.h" |
10 #include "SkColorPriv.h" | 10 #include "SkColorPriv.h" |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
79 fPng_ptr = nullptr; | 79 fPng_ptr = nullptr; |
80 fInfo_ptr = nullptr; | 80 fInfo_ptr = nullptr; |
81 } | 81 } |
82 | 82 |
83 private: | 83 private: |
84 png_structp fPng_ptr; | 84 png_structp fPng_ptr; |
85 png_infop fInfo_ptr; | 85 png_infop fInfo_ptr; |
86 }; | 86 }; |
87 #define AutoCleanPng(...) SK_REQUIRE_LOCAL_VAR(AutoCleanPng) | 87 #define AutoCleanPng(...) SK_REQUIRE_LOCAL_VAR(AutoCleanPng) |
88 | 88 |
89 // Method for coverting to either an SkPMColor or a similarly packed | |
90 // unpremultiplied color. | |
91 typedef uint32_t (*PackColorProc)(U8CPU a, U8CPU r, U8CPU g, U8CPU b); | |
92 | |
93 // Note: SkColorTable claims to store SkPMColors, which is not necessarily | 89 // Note: SkColorTable claims to store SkPMColors, which is not necessarily |
94 // the case here. | 90 // the case here. |
95 // TODO: If we add support for non-native swizzles, we'll need to handle that he
re. | 91 // TODO: If we add support for non-native swizzles, we'll need to handle that he
re. |
96 bool SkPngCodec::decodePalette(bool premultiply, int* ctableCount) { | 92 bool SkPngCodec::decodePalette(bool premultiply, SkColorType dstColorType, int*
ctableCount) { |
97 | 93 |
98 int numColors; | 94 int numColors; |
99 png_color* palette; | 95 png_color* palette; |
100 if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numColors)) { | 96 if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numColors)) { |
101 return false; | 97 return false; |
102 } | 98 } |
103 | 99 |
104 // Note: These are not necessarily SkPMColors. | 100 // Note: These are not necessarily SkPMColors. |
105 SkPMColor colorPtr[256]; | 101 SkPMColor colorPtr[256]; |
106 | 102 |
107 png_bytep alphas; | 103 png_bytep alphas; |
108 int numColorsWithAlpha = 0; | 104 int numColorsWithAlpha = 0; |
109 if (png_get_tRNS(fPng_ptr, fInfo_ptr, &alphas, &numColorsWithAlpha, nullptr)
) { | 105 if (png_get_tRNS(fPng_ptr, fInfo_ptr, &alphas, &numColorsWithAlpha, nullptr)
) { |
110 // Choose which function to use to create the color table. If the final
destination's | 106 // Choose which function to use to create the color table. If the final
destination's |
111 // colortype is unpremultiplied, the color table will store unpremultipl
ied colors. | 107 // colortype is unpremultiplied, the color table will store unpremultipl
ied colors. |
112 PackColorProc proc; | 108 PackColorProc proc = choose_pack_color_proc(premultiply, dstColorType); |
113 if (premultiply) { | |
114 proc = &SkPremultiplyARGBInline; | |
115 } else { | |
116 proc = &SkPackARGB32NoCheck; | |
117 } | |
118 | 109 |
119 for (int i = 0; i < numColorsWithAlpha; i++) { | 110 for (int i = 0; i < numColorsWithAlpha; i++) { |
120 // We don't have a function in SkOpts that combines a set of alphas
with a set | 111 // We don't have a function in SkOpts that combines a set of alphas
with a set |
121 // of RGBs. We could write one, but it's hardly worth it, given tha
t this | 112 // of RGBs. We could write one, but it's hardly worth it, given tha
t this |
122 // is such a small fraction of the total decode time. | 113 // is such a small fraction of the total decode time. |
123 colorPtr[i] = proc(alphas[i], palette->red, palette->green, palette-
>blue); | 114 colorPtr[i] = proc(alphas[i], palette->red, palette->green, palette-
>blue); |
124 palette++; | 115 palette++; |
125 } | 116 } |
126 } | 117 } |
127 | 118 |
128 if (numColorsWithAlpha < numColors) { | 119 if (numColorsWithAlpha < numColors) { |
129 // The optimized code depends on a 3-byte png_color struct with the colo
rs | 120 // The optimized code depends on a 3-byte png_color struct with the colo
rs |
130 // in RGB order. These checks make sure it is safe to use. | 121 // in RGB order. These checks make sure it is safe to use. |
131 static_assert(3 == sizeof(png_color), "png_color struct has changed. Op
ts are broken."); | 122 static_assert(3 == sizeof(png_color), "png_color struct has changed. Op
ts are broken."); |
132 #ifdef SK_DEBUG | 123 #ifdef SK_DEBUG |
133 SkASSERT(&palette->red < &palette->green); | 124 SkASSERT(&palette->red < &palette->green); |
134 SkASSERT(&palette->green < &palette->blue); | 125 SkASSERT(&palette->green < &palette->blue); |
135 #endif | 126 #endif |
136 | 127 |
137 #ifdef SK_PMCOLOR_IS_RGBA | 128 if (is_rgba(dstColorType)) { |
138 SkOpts::RGB_to_RGB1(colorPtr + numColorsWithAlpha, palette, numColors -
numColorsWithAlpha); | 129 SkOpts::RGB_to_RGB1(colorPtr + numColorsWithAlpha, palette, |
139 #else | 130 numColors - numColorsWithAlpha); |
140 SkOpts::RGB_to_BGR1(colorPtr + numColorsWithAlpha, palette, numColors -
numColorsWithAlpha); | 131 } else { |
141 #endif | 132 SkOpts::RGB_to_BGR1(colorPtr + numColorsWithAlpha, palette, |
| 133 numColors - numColorsWithAlpha); |
| 134 } |
142 } | 135 } |
143 | 136 |
144 // Pad the color table with the last color in the table (or black) in the ca
se that | 137 // Pad the color table with the last color in the table (or black) in the ca
se that |
145 // invalid pixel indices exceed the number of colors in the table. | 138 // invalid pixel indices exceed the number of colors in the table. |
146 const int maxColors = 1 << fBitDepth; | 139 const int maxColors = 1 << fBitDepth; |
147 if (numColors < maxColors) { | 140 if (numColors < maxColors) { |
148 SkPMColor lastColor = numColors > 0 ? colorPtr[numColors - 1] : SK_Color
BLACK; | 141 SkPMColor lastColor = numColors > 0 ? colorPtr[numColors - 1] : SK_Color
BLACK; |
149 sk_memset32(colorPtr + numColors, lastColor, maxColors - numColors); | 142 sk_memset32(colorPtr + numColors, lastColor, maxColors - numColors); |
150 } | 143 } |
151 | 144 |
(...skipping 266 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
418 return true; | 411 return true; |
419 } | 412 } |
420 | 413 |
421 SkPngCodec::SkPngCodec(const SkEncodedInfo& info, SkStream* stream, SkPngChunkRe
ader* chunkReader, | 414 SkPngCodec::SkPngCodec(const SkEncodedInfo& info, SkStream* stream, SkPngChunkRe
ader* chunkReader, |
422 png_structp png_ptr, png_infop info_ptr, int bitDepth, in
t numberPasses, | 415 png_structp png_ptr, png_infop info_ptr, int bitDepth, in
t numberPasses, |
423 sk_sp<SkColorSpace> colorSpace) | 416 sk_sp<SkColorSpace> colorSpace) |
424 : INHERITED(info, stream, colorSpace) | 417 : INHERITED(info, stream, colorSpace) |
425 , fPngChunkReader(SkSafeRef(chunkReader)) | 418 , fPngChunkReader(SkSafeRef(chunkReader)) |
426 , fPng_ptr(png_ptr) | 419 , fPng_ptr(png_ptr) |
427 , fInfo_ptr(info_ptr) | 420 , fInfo_ptr(info_ptr) |
428 , fSrcConfig(SkSwizzler::kUnknown) | |
429 , fNumberPasses(numberPasses) | 421 , fNumberPasses(numberPasses) |
430 , fBitDepth(bitDepth) | 422 , fBitDepth(bitDepth) |
431 {} | 423 {} |
432 | 424 |
433 SkPngCodec::~SkPngCodec() { | 425 SkPngCodec::~SkPngCodec() { |
434 this->destroyReadStruct(); | 426 this->destroyReadStruct(); |
435 } | 427 } |
436 | 428 |
437 void SkPngCodec::destroyReadStruct() { | 429 void SkPngCodec::destroyReadStruct() { |
438 if (fPng_ptr) { | 430 if (fPng_ptr) { |
(...skipping 14 matching lines...) Expand all Loading... |
453 SkPMColor ctable[], | 445 SkPMColor ctable[], |
454 int* ctableCount) { | 446 int* ctableCount) { |
455 // FIXME: Could we use the return value of setjmp to specify the type of | 447 // FIXME: Could we use the return value of setjmp to specify the type of |
456 // error? | 448 // error? |
457 if (setjmp(png_jmpbuf(fPng_ptr))) { | 449 if (setjmp(png_jmpbuf(fPng_ptr))) { |
458 SkCodecPrintf("setjmp long jump!\n"); | 450 SkCodecPrintf("setjmp long jump!\n"); |
459 return kInvalidInput; | 451 return kInvalidInput; |
460 } | 452 } |
461 png_read_update_info(fPng_ptr, fInfo_ptr); | 453 png_read_update_info(fPng_ptr, fInfo_ptr); |
462 | 454 |
463 // suggestedColorType was determined in read_header() based on the encodedCo
lorType | 455 if (SkEncodedInfo::kPalette_Color == this->getEncodedInfo().color()) { |
464 const SkColorType suggestedColorType = this->getInfo().colorType(); | 456 if (!this->decodePalette(kPremul_SkAlphaType == requestedInfo.alphaType(
), |
465 | 457 requestedInfo.colorType(), ctableCount)) { |
466 switch (suggestedColorType) { | 458 return kInvalidInput; |
467 case kIndex_8_SkColorType: | |
468 //decode palette to Skia format | |
469 fSrcConfig = SkSwizzler::kIndex; | |
470 if (!this->decodePalette(kPremul_SkAlphaType == requestedInfo.alphaT
ype(), | |
471 ctableCount)) { | |
472 return kInvalidInput; | |
473 } | |
474 break; | |
475 case kGray_8_SkColorType: | |
476 fSrcConfig = SkSwizzler::kGray; | |
477 break; | |
478 case kN32_SkColorType: { | |
479 const uint8_t encodedColorType = png_get_color_type(fPng_ptr, fInfo_
ptr); | |
480 if (PNG_COLOR_TYPE_GRAY_ALPHA == encodedColorType || | |
481 PNG_COLOR_TYPE_GRAY == encodedColorType) { | |
482 // If encodedColorType is GRAY, there must be a transparent chun
k. | |
483 // Otherwise, suggestedColorType would be kGray. We have alread
y | |
484 // instructed libpng to convert the transparent chunk to alpha, | |
485 // so we can treat both GRAY and GRAY_ALPHA as kGrayAlpha. | |
486 SkASSERT(encodedColorType == PNG_COLOR_TYPE_GRAY_ALPHA || | |
487 png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)); | |
488 | |
489 fSrcConfig = SkSwizzler::kGrayAlpha; | |
490 } else { | |
491 if (this->getInfo().alphaType() == kOpaque_SkAlphaType) { | |
492 fSrcConfig = SkSwizzler::kRGB; | |
493 } else { | |
494 fSrcConfig = SkSwizzler::kRGBA; | |
495 } | |
496 } | |
497 break; | |
498 } | 459 } |
499 default: | |
500 // We will always recommend one of the above colorTypes. | |
501 SkASSERT(false); | |
502 } | 460 } |
503 | 461 |
504 // Copy the color table to the client if they request kIndex8 mode | 462 // Copy the color table to the client if they request kIndex8 mode |
505 copy_color_table(requestedInfo, fColorTable, ctable, ctableCount); | 463 copy_color_table(requestedInfo, fColorTable, ctable, ctableCount); |
506 | 464 |
507 // Create the swizzler. SkPngCodec retains ownership of the color table. | 465 // Create the swizzler. SkPngCodec retains ownership of the color table. |
508 const SkPMColor* colors = get_color_ptr(fColorTable.get()); | 466 const SkPMColor* colors = get_color_ptr(fColorTable.get()); |
509 fSwizzler.reset(SkSwizzler::CreateSwizzler(fSrcConfig, colors, requestedInfo
, options)); | 467 fSwizzler.reset(SkSwizzler::CreateSwizzler(this->getEncodedInfo(), colors, r
equestedInfo, |
| 468 options)); |
510 SkASSERT(fSwizzler); | 469 SkASSERT(fSwizzler); |
511 | 470 |
512 return kSuccess; | 471 return kSuccess; |
513 } | 472 } |
514 | 473 |
515 | 474 |
516 bool SkPngCodec::onRewind() { | 475 bool SkPngCodec::onRewind() { |
517 // This sets fPng_ptr and fInfo_ptr to nullptr. If read_header | 476 // This sets fPng_ptr and fInfo_ptr to nullptr. If read_header |
518 // succeeds, they will be repopulated, and if it fails, they will | 477 // succeeds, they will be repopulated, and if it fails, they will |
519 // remain nullptr. Any future accesses to fPng_ptr and fInfo_ptr will | 478 // remain nullptr. Any future accesses to fPng_ptr and fInfo_ptr will |
(...skipping 26 matching lines...) Expand all Loading... |
546 } | 505 } |
547 | 506 |
548 // Note that ctable and ctableCount may be modified if there is a color tabl
e | 507 // Note that ctable and ctableCount may be modified if there is a color tabl
e |
549 const Result result = this->initializeSwizzler(requestedInfo, options, ctabl
e, ctableCount); | 508 const Result result = this->initializeSwizzler(requestedInfo, options, ctabl
e, ctableCount); |
550 if (result != kSuccess) { | 509 if (result != kSuccess) { |
551 return result; | 510 return result; |
552 } | 511 } |
553 | 512 |
554 const int width = requestedInfo.width(); | 513 const int width = requestedInfo.width(); |
555 const int height = requestedInfo.height(); | 514 const int height = requestedInfo.height(); |
556 const int bpp = SkSwizzler::BytesPerPixel(fSrcConfig); | 515 const int bpp = this->getEncodedInfo().bytesPerPixel(); |
557 const size_t srcRowBytes = width * bpp; | 516 const size_t srcRowBytes = width * bpp; |
558 | 517 |
559 // FIXME: Could we use the return value of setjmp to specify the type of | 518 // FIXME: Could we use the return value of setjmp to specify the type of |
560 // error? | 519 // error? |
561 int row = 0; | 520 int row = 0; |
562 // This must be declared above the call to setjmp to avoid memory leaks on i
ncomplete images. | 521 // This must be declared above the call to setjmp to avoid memory leaks on i
ncomplete images. |
563 SkAutoTMalloc<uint8_t> storage; | 522 SkAutoTMalloc<uint8_t> storage; |
564 if (setjmp(png_jmpbuf(fPng_ptr))) { | 523 if (setjmp(png_jmpbuf(fPng_ptr))) { |
565 // Assume that any error that occurs while reading rows is caused by an
incomplete input. | 524 // Assume that any error that occurs while reading rows is caused by an
incomplete input. |
566 if (fNumberPasses > 1) { | 525 if (fNumberPasses > 1) { |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
642 if (!conversion_possible(dstInfo, this->getInfo())) { | 601 if (!conversion_possible(dstInfo, this->getInfo())) { |
643 return kInvalidConversion; | 602 return kInvalidConversion; |
644 } | 603 } |
645 | 604 |
646 const Result result = this->initializeSwizzler(dstInfo, options, ctable, | 605 const Result result = this->initializeSwizzler(dstInfo, options, ctable, |
647 ctableCount); | 606 ctableCount); |
648 if (result != kSuccess) { | 607 if (result != kSuccess) { |
649 return result; | 608 return result; |
650 } | 609 } |
651 | 610 |
652 fStorage.reset(this->getInfo().width() * SkSwizzler::BytesPerPixel(this-
>srcConfig())); | 611 fStorage.reset(this->getInfo().width() * this->getEncodedInfo().bytesPer
Pixel()); |
653 fSrcRow = fStorage.get(); | 612 fSrcRow = fStorage.get(); |
654 | 613 |
655 return kSuccess; | 614 return kSuccess; |
656 } | 615 } |
657 | 616 |
658 int onGetScanlines(void* dst, int count, size_t rowBytes) override { | 617 int onGetScanlines(void* dst, int count, size_t rowBytes) override { |
659 // Assume that an error in libpng indicates an incomplete input. | 618 // Assume that an error in libpng indicates an incomplete input. |
660 int row = 0; | 619 int row = 0; |
661 if (setjmp(png_jmpbuf(this->png_ptr()))) { | 620 if (setjmp(png_jmpbuf(this->png_ptr()))) { |
662 SkCodecPrintf("setjmp long jump!\n"); | 621 SkCodecPrintf("setjmp long jump!\n"); |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
714 } | 673 } |
715 | 674 |
716 const Result result = this->initializeSwizzler(dstInfo, options, ctable, | 675 const Result result = this->initializeSwizzler(dstInfo, options, ctable, |
717 ctableCount); | 676 ctableCount); |
718 if (result != kSuccess) { | 677 if (result != kSuccess) { |
719 return result; | 678 return result; |
720 } | 679 } |
721 | 680 |
722 fHeight = dstInfo.height(); | 681 fHeight = dstInfo.height(); |
723 // FIXME: This need not be called on a second call to onStartScanlineDec
ode. | 682 // FIXME: This need not be called on a second call to onStartScanlineDec
ode. |
724 fSrcRowBytes = this->getInfo().width() * SkSwizzler::BytesPerPixel(this-
>srcConfig()); | 683 fSrcRowBytes = this->getInfo().width() * this->getEncodedInfo().bytesPer
Pixel(); |
725 fGarbageRow.reset(fSrcRowBytes); | 684 fGarbageRow.reset(fSrcRowBytes); |
726 fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get()); | 685 fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get()); |
727 fCanSkipRewind = true; | 686 fCanSkipRewind = true; |
728 | 687 |
729 return SkCodec::kSuccess; | 688 return SkCodec::kSuccess; |
730 } | 689 } |
731 | 690 |
732 int onGetScanlines(void* dst, int count, size_t dstRowBytes) override { | 691 int onGetScanlines(void* dst, int count, size_t dstRowBytes) override { |
733 // rewind stream if have previously called onGetScanlines, | 692 // rewind stream if have previously called onGetScanlines, |
734 // since we need entire progressive image to get scanlines | 693 // since we need entire progressive image to get scanlines |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
835 | 794 |
836 if (1 == numberPasses) { | 795 if (1 == numberPasses) { |
837 return new SkPngScanlineDecoder(imageInfo, streamDeleter.release(), chun
kReader, | 796 return new SkPngScanlineDecoder(imageInfo, streamDeleter.release(), chun
kReader, |
838 png_ptr, info_ptr, bitDepth, colorSpace)
; | 797 png_ptr, info_ptr, bitDepth, colorSpace)
; |
839 } | 798 } |
840 | 799 |
841 return new SkPngInterlacedScanlineDecoder(imageInfo, streamDeleter.release()
, chunkReader, | 800 return new SkPngInterlacedScanlineDecoder(imageInfo, streamDeleter.release()
, chunkReader, |
842 png_ptr, info_ptr, bitDepth, numbe
rPasses, | 801 png_ptr, info_ptr, bitDepth, numbe
rPasses, |
843 colorSpace); | 802 colorSpace); |
844 } | 803 } |
OLD | NEW |