SkCodec::SkCodec(int width, int height, const SkEncodedInfo& info, SkStream* stream, sk_sp<SkColorSpace> colorSpace, Origin origin) : fEncodedInfo(info) , fSrcInfo(info.makeImageInfo(width, height)) , fStream(stream) , fNeedsRewind(false) , fColorSpace(colorSpace) , fOrigin(origin) , fDstInfo() , fOptions() , fCurrScanline(-1) {}
// Reads the header and initializes the output fields, if not NULL. // // @param stream Input data. Will be read to get enough information to properly // setup the codec. // @param chunkReader SkPngChunkReader, for reading unknown chunks. May be NULL. // If not NULL, png_ptr will hold an *unowned* pointer to it. The caller is // expected to continue to own it for the lifetime of the png_ptr. // @param outCodec Optional output variable. If non-NULL, will be set to a new // SkPngCodec on success. // @param png_ptrp Optional output variable. If non-NULL, will be set to a new // png_structp on success. // @param info_ptrp Optional output variable. If non-NULL, will be set to a new // png_infop on success; // @return true on success, in which case the caller is responsible for calling // png_destroy_read_struct(png_ptrp, info_ptrp). // If it returns false, the passed in fields (except stream) are unchanged. static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader, SkCodec** outCodec, png_structp* png_ptrp, png_infop* info_ptrp) { // The image is known to be a PNG. Decode enough to know the SkImageInfo. png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, sk_error_fn, sk_warning_fn); if (!png_ptr) { return false; } AutoCleanPng autoClean(png_ptr); png_infop info_ptr = png_create_info_struct(png_ptr); if (info_ptr == nullptr) { return false; } autoClean.setInfoPtr(info_ptr); // FIXME: Could we use the return value of setjmp to specify the type of // error? if (setjmp(png_jmpbuf(png_ptr))) { return false; } png_set_read_fn(png_ptr, static_cast<void*>(stream), sk_read_fn); #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED // Hookup our chunkReader so we can see any user-chunks the caller may be interested in. // This needs to be installed before we read the png header. Android may store ninepatch // chunks in the header. if (chunkReader) { png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0); png_set_read_user_chunk_fn(png_ptr, (png_voidp) chunkReader, 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, encodedColorType; png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth, &encodedColorType, nullptr, nullptr, nullptr); // Tell libpng to strip 16 bit/color files down to 8 bits/color. // TODO: Should we handle this in SkSwizzler? Could this also benefit // RAW decodes? if (bitDepth == 16) { SkASSERT(PNG_COLOR_TYPE_PALETTE != encodedColorType); png_set_strip_16(png_ptr); } // Now determine the default colorType and alphaType and set the required transforms. // Often, we depend on SkSwizzler to perform any transforms that we need. However, we // still depend on libpng for many of the rare and PNG-specific cases. SkEncodedInfo::Color color; SkEncodedInfo::Alpha alpha; switch (encodedColorType) { case PNG_COLOR_TYPE_PALETTE: // 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) { // TODO: Should we use SkSwizzler here? png_set_packing(png_ptr); } color = SkEncodedInfo::kPalette_Color; // Set the alpha depending on if a transparency chunk exists. alpha = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? SkEncodedInfo::kUnpremul_Alpha : SkEncodedInfo::kOpaque_Alpha; break; case PNG_COLOR_TYPE_RGB: if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { // Convert to RGBA if transparency chunk exists. png_set_tRNS_to_alpha(png_ptr); color = SkEncodedInfo::kRGBA_Color; alpha = SkEncodedInfo::kBinary_Alpha; } else { color = SkEncodedInfo::kRGB_Color; alpha = SkEncodedInfo::kOpaque_Alpha; } break; case PNG_COLOR_TYPE_GRAY: // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel. if (bitDepth < 8) { // TODO: Should we use SkSwizzler here? png_set_expand_gray_1_2_4_to_8(png_ptr); } if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(png_ptr); color = SkEncodedInfo::kGrayAlpha_Color; alpha = SkEncodedInfo::kBinary_Alpha; } else { color = SkEncodedInfo::kGray_Color; alpha = SkEncodedInfo::kOpaque_Alpha; } break; case PNG_COLOR_TYPE_GRAY_ALPHA: color = SkEncodedInfo::kGrayAlpha_Color; alpha = SkEncodedInfo::kUnpremul_Alpha; break; case PNG_COLOR_TYPE_RGBA: color = SkEncodedInfo::kRGBA_Color; alpha = SkEncodedInfo::kUnpremul_Alpha; break; default: // All the color types have been covered above. SkASSERT(false); color = SkEncodedInfo::kRGBA_Color; alpha = SkEncodedInfo::kUnpremul_Alpha; } int numberPasses = png_set_interlace_handling(png_ptr); autoClean.release(); if (png_ptrp) { *png_ptrp = png_ptr; } if (info_ptrp) { *info_ptrp = info_ptr; } if (outCodec) { sk_sp<SkColorSpace> colorSpace = read_color_space(png_ptr, info_ptr); if (!colorSpace) { // Treat unmarked pngs as sRGB. colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); } SkEncodedInfo encodedInfo = SkEncodedInfo::Make(color, alpha, 8); SkImageInfo imageInfo = encodedInfo.makeImageInfo(origWidth, origHeight, colorSpace); if (SkEncodedInfo::kOpaque_Alpha == alpha) { png_color_8p sigBits; if (png_get_sBIT(png_ptr, info_ptr, &sigBits)) { if (5 == sigBits->red && 6 == sigBits->green && 5 == sigBits->blue) { // Recommend a decode to 565 if the sBIT indicates 565. imageInfo = imageInfo.makeColorType(kRGB_565_SkColorType); } } } if (1 == numberPasses) { *outCodec = new SkPngNormalCodec(encodedInfo, imageInfo, stream, chunkReader, png_ptr, info_ptr, bitDepth); } else { *outCodec = new SkPngInterlacedCodec(encodedInfo, imageInfo, stream, chunkReader, png_ptr, info_ptr, bitDepth, numberPasses); } } return true; }