/* * @brief start processing apng frame * @note uses libpng calls, treats apng data as discrete png images */ int apng_ani::_processing_start() { static ubyte png_sig[8] = {137, 80, 78, 71, 13, 10, 26, 10}; _pngp = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); _infop = png_create_info_struct(_pngp); if (_pngp == nullptr || _infop == nullptr) { return 1; } if (setjmp(png_jmpbuf(_pngp))) { png_destroy_read_struct(&_pngp, &_infop, 0); return 1; } png_set_crc_action(_pngp, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE); png_set_progressive_read_fn(_pngp, this, apng::info_callback, apng::row_callback, nullptr); png_process_data(_pngp, _infop, png_sig, 8); memcpy(&_chunk_IHDR.data[8], &_chunk.data[12], 8); // use frame width & height from fcTL png_process_data(_pngp, _infop, &_chunk_IHDR.data[0], _chunk_IHDR.size); for (size_t i = 0; i < _info_chunks.size(); ++i) { // this processes chunks like tRNS / PLTE / etc png_process_data(_pngp, _infop, &_info_chunks.at(i).data[0], _info_chunks.at(i).size); } return 0; }
bool PNGDecoder::loadStream(Common::SeekableReadStream &stream) { #ifdef USE_PNG destroy(); // First, check the PNG signature (if not set to skip it) if (!_skipSignature) { if (stream.readUint32BE() != MKTAG(0x89, 'P', 'N', 'G')) { return false; } if (stream.readUint32BE() != MKTAG(0x0d, 0x0a, 0x1a, 0x0a)) { return false; } } // The following is based on the guide provided in: //http://www.libpng.org/pub/png/libpng-1.2.5-manual.html#section-3 //http://www.libpng.org/pub/png/libpng-1.4.0-manual.pdf // along with the png-loading code used in the sword25-engine. png_structp pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!pngPtr) { return false; } png_infop infoPtr = png_create_info_struct(pngPtr); if (!infoPtr) { png_destroy_read_struct(&pngPtr, NULL, NULL); return false; } png_set_error_fn(pngPtr, NULL, pngError, pngWarning); // TODO: The manual says errors should be handled via setjmp png_set_read_fn(pngPtr, &stream, pngReadFromStream); png_set_crc_action(pngPtr, PNG_CRC_DEFAULT, PNG_CRC_WARN_USE); // We already verified the PNG-header png_set_sig_bytes(pngPtr, 8); // Read PNG header png_read_info(pngPtr, infoPtr); // No handling for unknown chunks yet. int bitDepth, colorType, width, height, interlaceType; png_uint_32 w, h; png_get_IHDR(pngPtr, infoPtr, &w, &h, &bitDepth, &colorType, &interlaceType, NULL, NULL); width = w; height = h; // Allocate memory for the final image data. // To keep memory framentation low this happens before allocating memory for temporary image data. _outputSurface = new Graphics::Surface(); // Images of all color formats except PNG_COLOR_TYPE_PALETTE // will be transformed into ARGB images if (colorType == PNG_COLOR_TYPE_PALETTE && !png_get_valid(pngPtr, infoPtr, PNG_INFO_tRNS)) { int numPalette = 0; png_colorp palette = NULL; uint32 success = png_get_PLTE(pngPtr, infoPtr, &palette, &numPalette); if (success != PNG_INFO_PLTE) { png_destroy_read_struct(&pngPtr, &infoPtr, NULL); return false; } _paletteColorCount = numPalette; _palette = new byte[_paletteColorCount * 3]; for (int i = 0; i < _paletteColorCount; i++) { _palette[(i * 3)] = palette[i].red; _palette[(i * 3) + 1] = palette[i].green; _palette[(i * 3) + 2] = palette[i].blue; } _outputSurface->create(width, height, Graphics::PixelFormat::createFormatCLUT8()); png_set_packing(pngPtr); } else { bool isAlpha = (colorType & PNG_COLOR_MASK_ALPHA); if (png_get_valid(pngPtr, infoPtr, PNG_INFO_tRNS)) { isAlpha = true; png_set_expand(pngPtr); } _outputSurface->create(width, height, Graphics::PixelFormat(4, 8, 8, 8, isAlpha ? 8 : 0, 24, 16, 8, 0)); if (!_outputSurface->getPixels()) { error("Could not allocate memory for output image."); } if (bitDepth == 16) png_set_strip_16(pngPtr); if (bitDepth < 8) png_set_expand(pngPtr); if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(pngPtr); // PNGs are Big-Endian: #ifdef SCUMM_LITTLE_ENDIAN png_set_bgr(pngPtr); png_set_swap_alpha(pngPtr); if (colorType != PNG_COLOR_TYPE_RGB_ALPHA) png_set_filler(pngPtr, 0xff, PNG_FILLER_BEFORE); #else if (colorType != PNG_COLOR_TYPE_RGB_ALPHA) png_set_filler(pngPtr, 0xff, PNG_FILLER_AFTER); #endif } // After the transformations have been registered, the image data is read again. png_set_interlace_handling(pngPtr); png_read_update_info(pngPtr, infoPtr); png_get_IHDR(pngPtr, infoPtr, &w, &h, &bitDepth, &colorType, NULL, NULL, NULL); width = w; height = h; if (interlaceType == PNG_INTERLACE_NONE) { // PNGs without interlacing can simply be read row by row. for (int i = 0; i < height; i++) { png_read_row(pngPtr, (png_bytep)_outputSurface->getBasePtr(0, i), NULL); } } else { // PNGs with interlacing require us to allocate an auxillary // buffer with pointers to all row starts. // Allocate row pointer buffer png_bytep *rowPtr = new png_bytep[height]; if (!rowPtr) { error("Could not allocate memory for row pointers."); } // Initialize row pointers for (int i = 0; i < height; i++) rowPtr[i] = (png_bytep)_outputSurface->getBasePtr(0, i); // Read image data png_read_image(pngPtr, rowPtr); // Free row pointer buffer delete[] rowPtr; } // Read additional data at the end. png_read_end(pngPtr, NULL); // Destroy libpng structures png_destroy_read_struct(&pngPtr, &infoPtr, NULL); return true; #else return false; #endif }
/* This only determines if we have an alpha value */ int xps_png_has_alpha(xps_context_t *ctx, byte *rbuf, int rlen) { png_structp png; png_infop info; struct xps_png_io_s io; int has_alpha; /* * Set up PNG structs and input source */ io.ptr = rbuf; io.lim = rbuf + rlen; png = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL, ctx->memory, xps_png_malloc, xps_png_free); if (!png) { gs_warn("png_create_read_struct"); return 0; } info = png_create_info_struct(png); if (!info) { gs_warn("png_create_info_struct"); return 0; } png_set_read_fn(png, &io, xps_png_read); png_set_crc_action(png, PNG_CRC_WARN_USE, PNG_CRC_WARN_USE); /* * Jump to here on errors. */ if (setjmp(png_jmpbuf(png))) { png_destroy_read_struct(&png, &info, NULL); gs_warn("png reading failed"); return 0; } /* * Read PNG header */ png_read_info(png, info); switch (png_get_color_type(png, info)) { case PNG_COLOR_TYPE_PALETTE: case PNG_COLOR_TYPE_GRAY: case PNG_COLOR_TYPE_RGB: has_alpha = 0; break; case PNG_COLOR_TYPE_GRAY_ALPHA: case PNG_COLOR_TYPE_RGB_ALPHA: has_alpha = 1; break; default: gs_warn("cannot handle this png color type"); has_alpha = 0; break; } /* * Clean up memory. */ png_destroy_read_struct(&png, &info, NULL); return has_alpha; }
int xps_decode_png(xps_context_t *ctx, byte *rbuf, int rlen, xps_image_t *image) { png_structp png; png_infop info; struct xps_png_io_s io; int npasses; int pass; int y; /* * Set up PNG structs and input source */ io.ptr = rbuf; io.lim = rbuf + rlen; png = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL, ctx->memory, xps_png_malloc, xps_png_free); if (!png) return gs_throw(-1, "png_create_read_struct"); info = png_create_info_struct(png); if (!info) return gs_throw(-1, "png_create_info_struct"); png_set_read_fn(png, &io, xps_png_read); png_set_crc_action(png, PNG_CRC_WARN_USE, PNG_CRC_WARN_USE); /* * Jump to here on errors. */ if (setjmp(png_jmpbuf(png))) { png_destroy_read_struct(&png, &info, NULL); return gs_throw(-1, "png reading failed"); } /* * Read PNG header */ png_read_info(png, info); if (png_get_interlace_type(png, info) == PNG_INTERLACE_ADAM7) { npasses = png_set_interlace_handling(png); } else { npasses = 1; } if (png_get_color_type(png, info) == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png); } if (png_get_valid(png, info, PNG_INFO_tRNS)) { /* this will also expand the depth to 8-bits */ png_set_tRNS_to_alpha(png); } png_read_update_info(png, info); image->width = png_get_image_width(png, info); image->height = png_get_image_height(png, info); image->comps = png_get_channels(png, info); image->bits = png_get_bit_depth(png, info); /* See if we have an icc profile */ if (info->iccp_profile != NULL) { image->profilesize = info->iccp_proflen; image->profile = xps_alloc(ctx, info->iccp_proflen); if (image->profile) { /* If we can't create it, just ignore */ memcpy(image->profile, info->iccp_profile, info->iccp_proflen); } } switch (png_get_color_type(png, info)) { case PNG_COLOR_TYPE_GRAY: image->colorspace = ctx->gray; image->hasalpha = 0; break; case PNG_COLOR_TYPE_RGB: image->colorspace = ctx->srgb; image->hasalpha = 0; break; case PNG_COLOR_TYPE_GRAY_ALPHA: image->colorspace = ctx->gray; image->hasalpha = 1; break; case PNG_COLOR_TYPE_RGB_ALPHA: image->colorspace = ctx->srgb; image->hasalpha = 1; break; default: return gs_throw(-1, "cannot handle this png color type"); } /* * Extract DPI, default to 96 dpi */ image->xres = 96; image->yres = 96; if (info->valid & PNG_INFO_pHYs) { png_uint_32 xres, yres; int unit; png_get_pHYs(png, info, &xres, &yres, &unit); if (unit == PNG_RESOLUTION_METER) { image->xres = xres * 0.0254 + 0.5; image->yres = yres * 0.0254 + 0.5; } } /* * Read rows, filling transformed output into image buffer. */ image->stride = (image->width * image->comps * image->bits + 7) / 8; image->samples = xps_alloc(ctx, image->stride * image->height); for (pass = 0; pass < npasses; pass++) { for (y = 0; y < image->height; y++) { png_read_row(png, image->samples + (y * image->stride), NULL); } } /* * Clean up memory. */ png_destroy_read_struct(&png, &info, NULL); return gs_okay; }
void nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr) { /* int number_passes; NOT USED */ png_uint_32 width, height; int bit_depth, color_type, interlace_type, compression_type, filter_type; unsigned int channels; png_bytep trans = NULL; int num_trans = 0; nsPNGDecoder *decoder = static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr)); /* always decode to 24-bit RGB or 32-bit RGBA */ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_type); /* Are we too big? */ if (width > MOZ_PNG_MAX_DIMENSION || height > MOZ_PNG_MAX_DIMENSION) longjmp(png_jmpbuf(decoder->mPNG), 1); // Post our size to the superclass decoder->PostSize(width, height); if (decoder->HasError()) { // Setting the size lead to an error; this can happen when for example // a multipart channel sends an image of a different size. longjmp(png_jmpbuf(decoder->mPNG), 1); } if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_expand(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand(png_ptr); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { int sample_max = (1 << bit_depth); png_color_16p trans_values; png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values); /* libpng doesn't reject a tRNS chunk with out-of-range samples so we check it here to avoid setting up a useless opacity channel or producing unexpected transparent pixels when using libpng-1.2.19 through 1.2.26 (bug #428045) */ if ((color_type == PNG_COLOR_TYPE_GRAY && (int)trans_values->gray > sample_max) || (color_type == PNG_COLOR_TYPE_RGB && ((int)trans_values->red > sample_max || (int)trans_values->green > sample_max || (int)trans_values->blue > sample_max))) { /* clear the tRNS valid flag and release tRNS memory */ png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); } else png_set_expand(png_ptr); } if (bit_depth == 16) png_set_strip_16(png_ptr); qcms_data_type inType; PRUint32 intent = -1; PRUint32 pIntent; if (decoder->mCMSMode != eCMSMode_Off) { intent = gfxPlatform::GetRenderingIntent(); decoder->mInProfile = PNGGetColorProfile(png_ptr, info_ptr, color_type, &inType, &pIntent); /* If we're not mandating an intent, use the one from the image. */ if (intent == PRUint32(-1)) intent = pIntent; } if (decoder->mInProfile && gfxPlatform::GetCMSOutputProfile()) { qcms_data_type outType; if (color_type & PNG_COLOR_MASK_ALPHA || num_trans) outType = QCMS_DATA_RGBA_8; else outType = QCMS_DATA_RGB_8; decoder->mTransform = qcms_transform_create(decoder->mInProfile, inType, gfxPlatform::GetCMSOutputProfile(), outType, (qcms_intent)intent); } else { png_set_gray_to_rgb(png_ptr); // only do gamma correction if CMS isn't entirely disabled if (decoder->mCMSMode != eCMSMode_Off) PNGDoGammaCorrection(png_ptr, info_ptr); if (decoder->mCMSMode == eCMSMode_All) { if (color_type & PNG_COLOR_MASK_ALPHA || num_trans) decoder->mTransform = gfxPlatform::GetCMSRGBATransform(); else decoder->mTransform = gfxPlatform::GetCMSRGBTransform(); } } /* let libpng expand interlaced images */ if (interlace_type == PNG_INTERLACE_ADAM7) { /* number_passes = */ png_set_interlace_handling(png_ptr); } /* now all of those things we set above are used to update various struct * members and whatnot, after which we can get channels, rowbytes, etc. */ png_read_update_info(png_ptr, info_ptr); decoder->mChannels = channels = png_get_channels(png_ptr, info_ptr); /*---------------------------------------------------------------*/ /* copy PNG info into imagelib structs (formerly png_set_dims()) */ /*---------------------------------------------------------------*/ PRInt32 alpha_bits = 1; if (channels == 2 || channels == 4) { /* check if alpha is coming from a tRNS chunk and is binary */ if (num_trans) { /* if it's not an indexed color image, tRNS means binary */ if (color_type == PNG_COLOR_TYPE_PALETTE) { for (int i=0; i<num_trans; i++) { if ((trans[i] != 0) && (trans[i] != 255)) { alpha_bits = 8; break; } } } } else { alpha_bits = 8; } } if (channels == 1 || channels == 3) decoder->format = gfxASurface::ImageFormatRGB24; else if (channels == 2 || channels == 4) decoder->format = gfxASurface::ImageFormatARGB32; #ifdef PNG_APNG_SUPPORTED if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL)) png_set_progressive_frame_fn(png_ptr, nsPNGDecoder::frame_info_callback, NULL); if (png_get_first_frame_is_hidden(png_ptr, info_ptr)) { decoder->mFrameIsHidden = PR_TRUE; } else { #endif decoder->CreateFrame(0, 0, width, height, decoder->format); #ifdef PNG_APNG_SUPPORTED } #endif if (decoder->mTransform && (channels <= 2 || interlace_type == PNG_INTERLACE_ADAM7)) { PRUint32 bpp[] = { 0, 3, 4, 3, 4 }; decoder->mCMSLine = (PRUint8 *)moz_malloc(bpp[channels] * width); if (!decoder->mCMSLine) { longjmp(png_jmpbuf(decoder->mPNG), 5); // NS_ERROR_OUT_OF_MEMORY } } if (interlace_type == PNG_INTERLACE_ADAM7) { if (height < PR_INT32_MAX / (width * channels)) decoder->interlacebuf = (PRUint8 *)moz_malloc(channels * width * height); if (!decoder->interlacebuf) { longjmp(png_jmpbuf(decoder->mPNG), 5); // NS_ERROR_OUT_OF_MEMORY } } /* Reject any ancillary chunk after IDAT with a bad CRC (bug #397593). * It would be better to show the default frame (if one has already been * successfully decoded) before bailing, but it's simpler to just bail * out with an error message. */ png_set_crc_action(png_ptr, PNG_CRC_NO_CHANGE, PNG_CRC_ERROR_QUIT); return; }