| Index: src/images/SkImageDecoder_libpng.cpp
|
| diff --git a/src/images/SkImageDecoder_libpng.cpp b/src/images/SkImageDecoder_libpng.cpp
|
| index cd8152a36bbf808387bca3c7399f29424de2dad4..c3df5d10a874e5243b94d84c16b2e6f375aeb910 100644
|
| --- a/src/images/SkImageDecoder_libpng.cpp
|
| +++ b/src/images/SkImageDecoder_libpng.cpp
|
| @@ -5,14 +5,12 @@
|
| * found in the LICENSE file.
|
| */
|
|
|
| -#include "SkImageDecoder.h"
|
| #include "SkImageEncoder.h"
|
| #include "SkColor.h"
|
| #include "SkColorPriv.h"
|
| #include "SkDither.h"
|
| #include "SkMath.h"
|
| #include "SkRTConf.h"
|
| -#include "SkScaledBitmapSampler.h"
|
| #include "SkStream.h"
|
| #include "SkTemplates.h"
|
| #include "SkUtils.h"
|
| @@ -44,88 +42,10 @@ SK_CONF_DECLARE(bool, c_suppressPNGImageDecoderWarnings,
|
| "Suppress most PNG warnings when calling image decode "
|
| "functions.");
|
|
|
| -class SkPNGImageIndex {
|
| -public:
|
| - // Takes ownership of stream.
|
| - SkPNGImageIndex(SkStreamRewindable* stream, png_structp png_ptr, png_infop info_ptr)
|
| - : fStream(stream)
|
| - , fPng_ptr(png_ptr)
|
| - , fInfo_ptr(info_ptr)
|
| - , fColorType(kUnknown_SkColorType) {
|
| - SkASSERT(stream != nullptr);
|
| - }
|
| - ~SkPNGImageIndex() {
|
| - if (fPng_ptr) {
|
| - png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL);
|
| - }
|
| - }
|
| -
|
| - SkAutoTDelete<SkStreamRewindable> fStream;
|
| - png_structp fPng_ptr;
|
| - png_infop fInfo_ptr;
|
| - SkColorType fColorType;
|
| -};
|
| -
|
| -class SkPNGImageDecoder : public SkImageDecoder {
|
| -public:
|
| - SkPNGImageDecoder() {
|
| - fImageIndex = nullptr;
|
| - }
|
| - Format getFormat() const override {
|
| - return kPNG_Format;
|
| - }
|
| -
|
| - virtual ~SkPNGImageDecoder() { delete fImageIndex; }
|
| -
|
| -protected:
|
| - Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override;
|
| -
|
| -private:
|
| - SkPNGImageIndex* fImageIndex;
|
| -
|
| - bool onDecodeInit(SkStream* stream, png_structp *png_ptrp, png_infop *info_ptrp);
|
| - bool decodePalette(png_structp png_ptr, png_infop info_ptr, int bitDepth,
|
| - bool * SK_RESTRICT hasAlphap, bool *reallyHasAlphap,
|
| - SkColorTable **colorTablep);
|
| - bool getBitmapColorType(png_structp, png_infop, SkColorType*, bool* hasAlpha,
|
| - SkPMColor* theTranspColor);
|
| -
|
| - typedef SkImageDecoder INHERITED;
|
| -};
|
| -
|
| -#ifndef png_jmpbuf
|
| -# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
|
| -#endif
|
| -
|
| -#define PNG_BYTES_TO_CHECK 4
|
| -
|
| -/* Automatically clean up after throwing an exception */
|
| -struct PNGAutoClean {
|
| - PNGAutoClean(png_structp p, png_infop i): png_ptr(p), info_ptr(i) {}
|
| - ~PNGAutoClean() {
|
| - png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
|
| - }
|
| -private:
|
| - png_structp png_ptr;
|
| - png_infop info_ptr;
|
| -};
|
| -
|
| -static void sk_read_fn(png_structp png_ptr, png_bytep data, png_size_t length) {
|
| - SkStream* sk_stream = (SkStream*) png_get_io_ptr(png_ptr);
|
| - size_t bytes = sk_stream->read(data, length);
|
| - if (bytes != length) {
|
| - png_error(png_ptr, "Read Error!");
|
| - }
|
| -}
|
| +///////////////////////////////////////////////////////////////////////////////
|
|
|
| -#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
|
| -static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) {
|
| - SkPngChunkReader* peeker = (SkPngChunkReader*)png_get_user_chunk_ptr(png_ptr);
|
| - // readChunk() returning true means continue decoding
|
| - return peeker->readChunk((const char*)chunk->name, chunk->data, chunk->size) ?
|
| - 1 : -1;
|
| -}
|
| -#endif
|
| +#include "SkColorPriv.h"
|
| +#include "SkUnPreMultiply.h"
|
|
|
| static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
|
| if (!c_suppressPNGImageDecoderWarnings) {
|
| @@ -134,577 +54,6 @@ static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
|
| longjmp(png_jmpbuf(png_ptr), 1);
|
| }
|
|
|
| -static void skip_src_rows(png_structp png_ptr, uint8_t storage[], int count) {
|
| - for (int i = 0; i < count; i++) {
|
| - uint8_t* tmp = storage;
|
| - png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
|
| - }
|
| -}
|
| -
|
| -static bool pos_le(int value, int max) {
|
| - return value > 0 && value <= max;
|
| -}
|
| -
|
| -static bool substituteTranspColor(SkBitmap* bm, SkPMColor match) {
|
| - SkASSERT(bm->colorType() == kN32_SkColorType);
|
| -
|
| - bool reallyHasAlpha = false;
|
| -
|
| - for (int y = bm->height() - 1; y >= 0; --y) {
|
| - SkPMColor* p = bm->getAddr32(0, y);
|
| - for (int x = bm->width() - 1; x >= 0; --x) {
|
| - if (match == *p) {
|
| - *p = 0;
|
| - reallyHasAlpha = true;
|
| - }
|
| - p += 1;
|
| - }
|
| - }
|
| - return reallyHasAlpha;
|
| -}
|
| -
|
| -static bool canUpscalePaletteToConfig(SkColorType dstColorType, bool srcHasAlpha) {
|
| - switch (dstColorType) {
|
| - case kN32_SkColorType:
|
| - case kARGB_4444_SkColorType:
|
| - return true;
|
| - case kRGB_565_SkColorType:
|
| - // only return true if the src is opaque (since 565 is opaque)
|
| - return !srcHasAlpha;
|
| - default:
|
| - return false;
|
| - }
|
| -}
|
| -
|
| -// call only if color_type is PALETTE. Returns true if the ctable has alpha
|
| -static bool hasTransparencyInPalette(png_structp png_ptr, png_infop info_ptr) {
|
| - png_bytep trans;
|
| - int num_trans;
|
| -
|
| - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
|
| - png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, nullptr);
|
| - return num_trans > 0;
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -void do_nothing_warning_fn(png_structp, png_const_charp) {
|
| - /* do nothing */
|
| -}
|
| -
|
| -bool SkPNGImageDecoder::onDecodeInit(SkStream* sk_stream, png_structp *png_ptrp,
|
| - png_infop *info_ptrp) {
|
| - /* Create and initialize the png_struct with the desired error handler
|
| - * functions. If you want to use the default stderr and longjump method,
|
| - * you can supply nullptr for the last three parameters. We also supply the
|
| - * the compiler header file version, so that we know if the application
|
| - * was compiled with a compatible version of the library. */
|
| -
|
| - png_error_ptr user_warning_fn =
|
| - (c_suppressPNGImageDecoderWarnings) ? (&do_nothing_warning_fn) : nullptr;
|
| - /* nullptr means to leave as default library behavior. */
|
| - /* c_suppressPNGImageDecoderWarnings default depends on SK_DEBUG. */
|
| - /* To suppress warnings with a SK_DEBUG binary, set the
|
| - * environment variable "skia_images_png_suppressDecoderWarnings"
|
| - * to "true". Inside a program that links to skia:
|
| - * SK_CONF_SET("images.png.suppressDecoderWarnings", true); */
|
| -
|
| - png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
|
| - nullptr, sk_error_fn, user_warning_fn);
|
| - // png_voidp user_error_ptr, user_error_fn, user_warning_fn);
|
| - if (png_ptr == nullptr) {
|
| - return false;
|
| - }
|
| -
|
| - *png_ptrp = png_ptr;
|
| -
|
| - /* Allocate/initialize the memory for image information. */
|
| - png_infop info_ptr = png_create_info_struct(png_ptr);
|
| - if (info_ptr == nullptr) {
|
| - png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
|
| - return false;
|
| - }
|
| - *info_ptrp = info_ptr;
|
| -
|
| - /* Set error handling if you are using the setjmp/longjmp method (this is
|
| - * the normal method of doing things with libpng). REQUIRED unless you
|
| - * set up your own error handlers in the png_create_read_struct() earlier.
|
| - */
|
| - if (setjmp(png_jmpbuf(png_ptr))) {
|
| - png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
|
| - return false;
|
| - }
|
| -
|
| - /* If you are using replacement read functions, instead of calling
|
| - * png_init_io() here you would call:
|
| - */
|
| - png_set_read_fn(png_ptr, (void *)sk_stream, sk_read_fn);
|
| - /* where user_io_ptr is a structure you want available to the callbacks */
|
| - /* If we have already read some of the signature */
|
| -// png_set_sig_bytes(png_ptr, 0 /* sig_read */ );
|
| -
|
| -#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
|
| - // hookup our peeker so we can see any user-chunks the caller may be interested in
|
| - png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0);
|
| - if (this->getPeeker()) {
|
| - png_set_read_user_chunk_fn(png_ptr, (png_voidp)this->getPeeker(), sk_read_user_chunk);
|
| - }
|
| -#endif
|
| - /* The call to png_read_info() gives us all of the information from the
|
| - * PNG file before the first IDAT (image data chunk). */
|
| - png_read_info(png_ptr, info_ptr);
|
| - png_uint_32 origWidth, origHeight;
|
| - int bitDepth, colorType;
|
| - png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
|
| - &colorType, int_p_NULL, int_p_NULL, int_p_NULL);
|
| -
|
| - /* tell libpng to strip 16 bit/color files down to 8 bits/color */
|
| - if (bitDepth == 16) {
|
| - png_set_strip_16(png_ptr);
|
| - }
|
| -#ifdef PNG_READ_PACK_SUPPORTED
|
| - /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
|
| - * byte into separate bytes (useful for paletted and grayscale images). */
|
| - if (bitDepth < 8) {
|
| - png_set_packing(png_ptr);
|
| - }
|
| -#endif
|
| - /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
|
| - if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
|
| - png_set_expand_gray_1_2_4_to_8(png_ptr);
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -SkImageDecoder::Result SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
|
| - Mode mode) {
|
| - png_structp png_ptr;
|
| - png_infop info_ptr;
|
| -
|
| - if (!onDecodeInit(sk_stream, &png_ptr, &info_ptr)) {
|
| - return kFailure;
|
| - }
|
| -
|
| - PNGAutoClean autoClean(png_ptr, info_ptr);
|
| -
|
| - if (setjmp(png_jmpbuf(png_ptr))) {
|
| - return kFailure;
|
| - }
|
| -
|
| - png_uint_32 origWidth, origHeight;
|
| - int bitDepth, pngColorType, interlaceType;
|
| - png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
|
| - &pngColorType, &interlaceType, int_p_NULL, int_p_NULL);
|
| -
|
| - SkColorType colorType;
|
| - bool hasAlpha = false;
|
| - SkPMColor theTranspColor = 0; // 0 tells us not to try to match
|
| -
|
| - if (!this->getBitmapColorType(png_ptr, info_ptr, &colorType, &hasAlpha, &theTranspColor)) {
|
| - return kFailure;
|
| - }
|
| -
|
| - SkAlphaType alphaType = this->getRequireUnpremultipliedColors() ?
|
| - kUnpremul_SkAlphaType : kPremul_SkAlphaType;
|
| - const int sampleSize = this->getSampleSize();
|
| - SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
|
| - decodedBitmap->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(),
|
| - colorType, alphaType));
|
| -
|
| - if (SkImageDecoder::kDecodeBounds_Mode == mode) {
|
| - return kSuccess;
|
| - }
|
| -
|
| - // from here down we are concerned with colortables and pixels
|
| -
|
| - // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype
|
| - // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we
|
| - // draw lots faster if we can flag the bitmap has being opaque
|
| - bool reallyHasAlpha = false;
|
| - SkColorTable* colorTable = nullptr;
|
| -
|
| - if (pngColorType == PNG_COLOR_TYPE_PALETTE) {
|
| - decodePalette(png_ptr, info_ptr, bitDepth, &hasAlpha, &reallyHasAlpha, &colorTable);
|
| - }
|
| -
|
| - SkAutoUnref aur(colorTable);
|
| -
|
| - if (!this->allocPixelRef(decodedBitmap,
|
| - kIndex_8_SkColorType == colorType ? colorTable : nullptr)) {
|
| - return kFailure;
|
| - }
|
| -
|
| - SkAutoLockPixels alp(*decodedBitmap);
|
| -
|
| - // Repeat setjmp, otherwise variables declared since the last call (e.g. alp
|
| - // and aur) won't get their destructors called in case of a failure.
|
| - if (setjmp(png_jmpbuf(png_ptr))) {
|
| - return kFailure;
|
| - }
|
| -
|
| - /* Turn on interlace handling. REQUIRED if you are not using
|
| - * png_read_image(). To see how to handle interlacing passes,
|
| - * see the png_read_row() method below:
|
| - */
|
| - const int number_passes = (interlaceType != PNG_INTERLACE_NONE) ?
|
| - png_set_interlace_handling(png_ptr) : 1;
|
| -
|
| - /* Optional call to gamma correct and add the background to the palette
|
| - * and update info structure. REQUIRED if you are expecting libpng to
|
| - * update the palette for you (ie you selected such a transform above).
|
| - */
|
| - png_read_update_info(png_ptr, info_ptr);
|
| -
|
| - if ((kAlpha_8_SkColorType == colorType || kIndex_8_SkColorType == colorType) &&
|
| - 1 == sampleSize) {
|
| - if (kAlpha_8_SkColorType == colorType) {
|
| - // For an A8 bitmap, we assume there is an alpha for speed. It is
|
| - // possible the bitmap is opaque, but that is an unlikely use case
|
| - // since it would not be very interesting.
|
| - reallyHasAlpha = true;
|
| - // A8 is only allowed if the original was GRAY.
|
| - SkASSERT(PNG_COLOR_TYPE_GRAY == pngColorType);
|
| - }
|
| - for (int i = 0; i < number_passes; i++) {
|
| - for (png_uint_32 y = 0; y < origHeight; y++) {
|
| - uint8_t* bmRow = decodedBitmap->getAddr8(0, y);
|
| - png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
|
| - }
|
| - }
|
| - } else {
|
| - SkScaledBitmapSampler::SrcConfig sc;
|
| - int srcBytesPerPixel = 4;
|
| -
|
| - if (colorTable != nullptr) {
|
| - sc = SkScaledBitmapSampler::kIndex;
|
| - srcBytesPerPixel = 1;
|
| - } else if (kAlpha_8_SkColorType == colorType) {
|
| - // A8 is only allowed if the original was GRAY.
|
| - SkASSERT(PNG_COLOR_TYPE_GRAY == pngColorType);
|
| - sc = SkScaledBitmapSampler::kGray;
|
| - srcBytesPerPixel = 1;
|
| - } else if (hasAlpha) {
|
| - sc = SkScaledBitmapSampler::kRGBA;
|
| - } else {
|
| - sc = SkScaledBitmapSampler::kRGBX;
|
| - }
|
| -
|
| - /* We have to pass the colortable explicitly, since we may have one
|
| - even if our decodedBitmap doesn't, due to the request that we
|
| - upscale png's palette to a direct model
|
| - */
|
| - const SkPMColor* colors = colorTable ? colorTable->readColors() : nullptr;
|
| - if (!sampler.begin(decodedBitmap, sc, *this, colors)) {
|
| - return kFailure;
|
| - }
|
| - const int height = decodedBitmap->height();
|
| -
|
| - if (number_passes > 1) {
|
| - SkAutoTMalloc<uint8_t> storage(origWidth * origHeight * srcBytesPerPixel);
|
| - uint8_t* base = storage.get();
|
| - size_t rowBytes = origWidth * srcBytesPerPixel;
|
| -
|
| - for (int i = 0; i < number_passes; i++) {
|
| - uint8_t* row = base;
|
| - for (png_uint_32 y = 0; y < origHeight; y++) {
|
| - uint8_t* bmRow = row;
|
| - png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
|
| - row += rowBytes;
|
| - }
|
| - }
|
| - // now sample it
|
| - base += sampler.srcY0() * rowBytes;
|
| - for (int y = 0; y < height; y++) {
|
| - reallyHasAlpha |= sampler.next(base);
|
| - base += sampler.srcDY() * rowBytes;
|
| - }
|
| - } else {
|
| - SkAutoTMalloc<uint8_t> storage(origWidth * srcBytesPerPixel);
|
| - uint8_t* srcRow = storage.get();
|
| - skip_src_rows(png_ptr, srcRow, sampler.srcY0());
|
| -
|
| - for (int y = 0; y < height; y++) {
|
| - uint8_t* tmp = srcRow;
|
| - png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
|
| - reallyHasAlpha |= sampler.next(srcRow);
|
| - if (y < height - 1) {
|
| - skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1);
|
| - }
|
| - }
|
| -
|
| - // skip the rest of the rows (if any)
|
| - png_uint_32 read = (height - 1) * sampler.srcDY() +
|
| - sampler.srcY0() + 1;
|
| - SkASSERT(read <= origHeight);
|
| - skip_src_rows(png_ptr, srcRow, origHeight - read);
|
| - }
|
| - }
|
| -
|
| - /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
|
| - png_read_end(png_ptr, info_ptr);
|
| -
|
| - if (0 != theTranspColor) {
|
| - reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
|
| - }
|
| - if (reallyHasAlpha && this->getRequireUnpremultipliedColors()) {
|
| - switch (decodedBitmap->colorType()) {
|
| - case kIndex_8_SkColorType:
|
| - // Fall through.
|
| - case kARGB_4444_SkColorType:
|
| - // We have chosen not to support unpremul for these colortypes.
|
| - return kFailure;
|
| - default: {
|
| - // Fall through to finish the decode. This colortype either
|
| - // supports unpremul or it is irrelevant because it has no
|
| - // alpha (or only alpha).
|
| - // These brackets prevent a warning.
|
| - }
|
| - }
|
| - }
|
| -
|
| - if (!reallyHasAlpha) {
|
| - decodedBitmap->setAlphaType(kOpaque_SkAlphaType);
|
| - }
|
| - return kSuccess;
|
| -}
|
| -
|
| -
|
| -
|
| -bool SkPNGImageDecoder::getBitmapColorType(png_structp png_ptr, png_infop info_ptr,
|
| - SkColorType* colorTypep,
|
| - bool* hasAlphap,
|
| - SkPMColor* SK_RESTRICT theTranspColorp) {
|
| - png_uint_32 origWidth, origHeight;
|
| - int bitDepth, colorType;
|
| - png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
|
| - &colorType, int_p_NULL, int_p_NULL, int_p_NULL);
|
| -
|
| -#ifdef PNG_sBIT_SUPPORTED
|
| - // check for sBIT chunk data, in case we should disable dithering because
|
| - // our data is not truely 8bits per component
|
| - png_color_8p sig_bit;
|
| - if (this->getDitherImage() && png_get_sBIT(png_ptr, info_ptr, &sig_bit)) {
|
| -#if 0
|
| - SkDebugf("----- sBIT %d %d %d %d\n", sig_bit->red, sig_bit->green,
|
| - sig_bit->blue, sig_bit->alpha);
|
| -#endif
|
| - // 0 seems to indicate no information available
|
| - if (pos_le(sig_bit->red, SK_R16_BITS) &&
|
| - pos_le(sig_bit->green, SK_G16_BITS) &&
|
| - pos_le(sig_bit->blue, SK_B16_BITS)) {
|
| - this->setDitherImage(false);
|
| - }
|
| - }
|
| -#endif
|
| -
|
| - if (colorType == PNG_COLOR_TYPE_PALETTE) {
|
| - bool paletteHasAlpha = hasTransparencyInPalette(png_ptr, info_ptr);
|
| - *colorTypep = this->getPrefColorType(kIndex_SrcDepth, paletteHasAlpha);
|
| - // now see if we can upscale to their requested colortype
|
| - if (!canUpscalePaletteToConfig(*colorTypep, paletteHasAlpha)) {
|
| - *colorTypep = kIndex_8_SkColorType;
|
| - }
|
| - } else {
|
| - png_color_16p transpColor = nullptr;
|
| - int numTransp = 0;
|
| -
|
| - png_get_tRNS(png_ptr, info_ptr, nullptr, &numTransp, &transpColor);
|
| -
|
| - bool valid = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS);
|
| -
|
| - if (valid && numTransp == 1 && transpColor != nullptr) {
|
| - /* Compute our transparent color, which we'll match against later.
|
| - We don't really handle 16bit components properly here, since we
|
| - do our compare *after* the values have been knocked down to 8bit
|
| - which means we will find more matches than we should. The real
|
| - fix seems to be to see the actual 16bit components, do the
|
| - compare, and then knock it down to 8bits ourselves.
|
| - */
|
| - if (colorType & PNG_COLOR_MASK_COLOR) {
|
| - if (16 == bitDepth) {
|
| - *theTranspColorp = SkPackARGB32(0xFF, transpColor->red >> 8,
|
| - transpColor->green >> 8,
|
| - transpColor->blue >> 8);
|
| - } else {
|
| - /* We apply the mask because in a very small
|
| - number of corrupt PNGs, (transpColor->red > 255)
|
| - and (bitDepth == 8), for certain versions of libpng. */
|
| - *theTranspColorp = SkPackARGB32(0xFF,
|
| - 0xFF & (transpColor->red),
|
| - 0xFF & (transpColor->green),
|
| - 0xFF & (transpColor->blue));
|
| - }
|
| - } else { // gray
|
| - if (16 == bitDepth) {
|
| - *theTranspColorp = SkPackARGB32(0xFF, transpColor->gray >> 8,
|
| - transpColor->gray >> 8,
|
| - transpColor->gray >> 8);
|
| - } else {
|
| - /* We apply the mask because in a very small
|
| - number of corrupt PNGs, (transpColor->red >
|
| - 255) and (bitDepth == 8), for certain versions
|
| - of libpng. For safety we assume the same could
|
| - happen with a grayscale PNG. */
|
| - *theTranspColorp = SkPackARGB32(0xFF,
|
| - 0xFF & (transpColor->gray),
|
| - 0xFF & (transpColor->gray),
|
| - 0xFF & (transpColor->gray));
|
| - }
|
| - }
|
| - }
|
| -
|
| - if (valid ||
|
| - PNG_COLOR_TYPE_RGB_ALPHA == colorType ||
|
| - PNG_COLOR_TYPE_GRAY_ALPHA == colorType) {
|
| - *hasAlphap = true;
|
| - }
|
| -
|
| - SrcDepth srcDepth = k32Bit_SrcDepth;
|
| - if (PNG_COLOR_TYPE_GRAY == colorType) {
|
| - srcDepth = k8BitGray_SrcDepth;
|
| - // Remove this assert, which fails on desk_pokemonwiki.skp
|
| - //SkASSERT(!*hasAlphap);
|
| - }
|
| -
|
| - *colorTypep = this->getPrefColorType(srcDepth, *hasAlphap);
|
| - // now match the request against our capabilities
|
| - if (*hasAlphap) {
|
| - if (*colorTypep != kARGB_4444_SkColorType) {
|
| - *colorTypep = kN32_SkColorType;
|
| - }
|
| - } else {
|
| - if (kAlpha_8_SkColorType == *colorTypep) {
|
| - if (k8BitGray_SrcDepth != srcDepth) {
|
| - // Converting a non grayscale image to A8 is not currently supported.
|
| - *colorTypep = kN32_SkColorType;
|
| - }
|
| - } else if (*colorTypep != kRGB_565_SkColorType &&
|
| - *colorTypep != kARGB_4444_SkColorType) {
|
| - *colorTypep = kN32_SkColorType;
|
| - }
|
| - }
|
| - }
|
| -
|
| - // sanity check for size
|
| - {
|
| - int64_t size = sk_64_mul(origWidth, origHeight);
|
| - // now check that if we are 4-bytes per pixel, we also don't overflow
|
| - if (size < 0 || size > (0x7FFFFFFF >> 2)) {
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - // If the image has alpha and the decoder wants unpremultiplied
|
| - // colors, the only supported colortype is 8888.
|
| - if (this->getRequireUnpremultipliedColors() && *hasAlphap) {
|
| - *colorTypep = kN32_SkColorType;
|
| - }
|
| -
|
| - if (fImageIndex != nullptr) {
|
| - if (kUnknown_SkColorType == fImageIndex->fColorType) {
|
| - // This is the first time for this subset decode. From now on,
|
| - // all decodes must be in the same colortype.
|
| - fImageIndex->fColorType = *colorTypep;
|
| - } else if (fImageIndex->fColorType != *colorTypep) {
|
| - // Requesting a different colortype for a subsequent decode is not
|
| - // supported. Report failure before we make changes to png_ptr.
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - bool convertGrayToRGB = PNG_COLOR_TYPE_GRAY == colorType && *colorTypep != kAlpha_8_SkColorType;
|
| -
|
| - // Unless the user is requesting A8, convert a grayscale image into RGB.
|
| - // GRAY_ALPHA will always be converted to RGB
|
| - if (convertGrayToRGB || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
|
| - png_set_gray_to_rgb(png_ptr);
|
| - }
|
| -
|
| - // Add filler (or alpha) byte (after each RGB triplet) if necessary.
|
| - if (colorType == PNG_COLOR_TYPE_RGB || convertGrayToRGB) {
|
| - png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -typedef uint32_t (*PackColorProc)(U8CPU a, U8CPU r, U8CPU g, U8CPU b);
|
| -
|
| -bool SkPNGImageDecoder::decodePalette(png_structp png_ptr, png_infop info_ptr,
|
| - int bitDepth, bool *hasAlphap,
|
| - bool *reallyHasAlphap,
|
| - SkColorTable **colorTablep) {
|
| - int numPalette;
|
| - png_colorp palette;
|
| - png_bytep trans;
|
| - int numTrans;
|
| -
|
| - png_get_PLTE(png_ptr, info_ptr, &palette, &numPalette);
|
| -
|
| - SkPMColor colorStorage[256]; // worst-case storage
|
| - SkPMColor* colorPtr = colorStorage;
|
| -
|
| - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
|
| - png_get_tRNS(png_ptr, info_ptr, &trans, &numTrans, nullptr);
|
| - *hasAlphap = (numTrans > 0);
|
| - } else {
|
| - numTrans = 0;
|
| - }
|
| -
|
| - // check for bad images that might make us crash
|
| - if (numTrans > numPalette) {
|
| - numTrans = numPalette;
|
| - }
|
| -
|
| - int index = 0;
|
| - int transLessThanFF = 0;
|
| -
|
| - // Choose which function to use to create the color table. If the final destination's
|
| - // colortype is unpremultiplied, the color table will store unpremultiplied colors.
|
| - PackColorProc proc;
|
| - if (this->getRequireUnpremultipliedColors()) {
|
| - proc = &SkPackARGB32NoCheck;
|
| - } else {
|
| - proc = &SkPreMultiplyARGB;
|
| - }
|
| - for (; index < numTrans; index++) {
|
| - transLessThanFF |= (int)*trans - 0xFF;
|
| - *colorPtr++ = proc(*trans++, palette->red, palette->green, palette->blue);
|
| - palette++;
|
| - }
|
| - bool reallyHasAlpha = (transLessThanFF < 0);
|
| -
|
| - for (; index < numPalette; index++) {
|
| - *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
|
| - palette++;
|
| - }
|
| -
|
| - /* BUGGY IMAGE WORKAROUND
|
| -
|
| - Invalid images could contain pixel values that are greater than the number of palette
|
| - entries. Since we use pixel values as indices into the palette this could result in reading
|
| - beyond the end of the palette which could leak the contents of uninitialized memory. To
|
| - ensure this doesn't happen, we grow the colortable to the maximum size that can be
|
| - addressed by the bitdepth of the image and fill it with the last palette color or black if
|
| - the palette is empty (really broken image).
|
| - */
|
| - int colorCount = SkTMax(numPalette, 1 << SkTMin(bitDepth, 8));
|
| - SkPMColor lastColor = index > 0 ? colorPtr[-1] : SkPackARGB32(0xFF, 0, 0, 0);
|
| - for (; index < colorCount; index++) {
|
| - *colorPtr++ = lastColor;
|
| - }
|
| -
|
| - *colorTablep = new SkColorTable(colorStorage, colorCount);
|
| - *reallyHasAlphap = reallyHasAlpha;
|
| - return true;
|
| -}
|
| -
|
| -///////////////////////////////////////////////////////////////////////////////
|
| -
|
| -#include "SkColorPriv.h"
|
| -#include "SkUnPreMultiply.h"
|
| -
|
| static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
|
| SkWStream* sk_stream = (SkWStream*)png_get_io_ptr(png_ptr);
|
| if (!sk_stream->write(data, len)) {
|
| @@ -985,37 +334,11 @@ bool SkPNGImageEncoder::doEncode(SkWStream* stream, const SkBitmap& bitmap,
|
| }
|
|
|
| ///////////////////////////////////////////////////////////////////////////////
|
| -DEFINE_DECODER_CREATOR(PNGImageDecoder);
|
| DEFINE_ENCODER_CREATOR(PNGImageEncoder);
|
| ///////////////////////////////////////////////////////////////////////////////
|
|
|
| -static bool is_png(SkStreamRewindable* stream) {
|
| - char buf[PNG_BYTES_TO_CHECK];
|
| - if (stream->read(buf, PNG_BYTES_TO_CHECK) == PNG_BYTES_TO_CHECK &&
|
| - !png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) {
|
| - return true;
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -SkImageDecoder* sk_libpng_dfactory(SkStreamRewindable* stream) {
|
| - if (is_png(stream)) {
|
| - return new SkPNGImageDecoder;
|
| - }
|
| - return nullptr;
|
| -}
|
| -
|
| -static SkImageDecoder::Format get_format_png(SkStreamRewindable* stream) {
|
| - if (is_png(stream)) {
|
| - return SkImageDecoder::kPNG_Format;
|
| - }
|
| - return SkImageDecoder::kUnknown_Format;
|
| -}
|
| -
|
| SkImageEncoder* sk_libpng_efactory(SkImageEncoder::Type t) {
|
| return (SkImageEncoder::kPNG_Type == t) ? new SkPNGImageEncoder : nullptr;
|
| }
|
|
|
| -static SkImageDecoder_DecodeReg gDReg(sk_libpng_dfactory);
|
| -static SkImageDecoder_FormatReg gFormatReg(get_format_png);
|
| static SkImageEncoder_EncodeReg gEReg(sk_libpng_efactory);
|
|
|