static PngInfo read_and_update_info(const png_structp png_ptr, const png_infop info_ptr) { png_uint_32 width, height; int bit_depth, color_type; png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); // Convert transparency to full alpha if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(png_ptr); } // Convert grayscale, if needed. if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { png_set_expand_gray_1_2_4_to_8(png_ptr); } // Convert paletted images, if needed. if (color_type == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png_ptr); } // Add alpha channel, if there is none (rationale: GL_RGBA is faster than GL_RGB on many GPUs) if (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_RGB) { png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER); } // Ensure 8-bit packing if (bit_depth < 8) { png_set_packing(png_ptr); } else if (bit_depth == 16) { png_set_scale_16(png_ptr); } png_read_update_info(png_ptr, info_ptr); // Read the new color type after updates have been made. color_type = png_get_color_type(png_ptr, info_ptr); return (PngInfo) {width, height, color_type}; }
void setformat_rgba8( png_structp png, png_infop info, int bitdepth, int colortype ) { double gamma; if( png_get_gAMA( png, info, &gamma ) ) { png_set_gamma( png, 2.2, gamma ); } else { png_set_gamma( png, 2.2, 0.45455 ); } if( colortype == PNG_COLOR_TYPE_PALETTE ) { png_set_palette_to_rgb( png ); } if( colortype == PNG_COLOR_TYPE_GRAY && bitdepth < 8 ) { png_set_expand_gray_1_2_4_to_8( png ); } if( png_get_valid( png, info, PNG_INFO_tRNS ) ) { png_set_tRNS_to_alpha( png ); } else { int channels = png_get_channels( png, info ); if( channels == 1 || channels == 3 ) { png_set_add_alpha( png, 255, PNG_FILLER_AFTER ); } } if( colortype == PNG_COLOR_TYPE_GRAY || colortype == PNG_COLOR_TYPE_GRAY_ALPHA ) { png_set_gray_to_rgb( png ); } if( bitdepth == 16 ) { png_set_scale_16( png ); } }
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 = nullptr; 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) { png_longjmp(decoder->mPNG, 1); } // Post our size to the superclass decoder->PostSize(width, height); if (decoder->HasError()) { // Setting the size led to an error. png_longjmp(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)) { 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 (bug #428045) if (bit_depth < 16) { png_uint_16 sample_max = (1 << bit_depth) - 1; if ((color_type == PNG_COLOR_TYPE_GRAY && trans_values->gray > sample_max) || (color_type == PNG_COLOR_TYPE_RGB && (trans_values->red > sample_max || trans_values->green > sample_max || 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); num_trans = 0; } } if (num_trans != 0) { png_set_expand(png_ptr); } } if (bit_depth == 16) { png_set_scale_16(png_ptr); } qcms_data_type inType = QCMS_DATA_RGBA_8; uint32_t intent = -1; uint32_t 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 == uint32_t(-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()) // //---------------------------------------------------------------// if (channels == 1 || channels == 3) { decoder->format = gfx::SurfaceFormat::B8G8R8X8; } else if (channels == 2 || channels == 4) { decoder->format = gfx::SurfaceFormat::B8G8R8A8; } else { png_longjmp(decoder->mPNG, 1); // invalid number of channels } #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, nullptr); } if (png_get_first_frame_is_hidden(png_ptr, info_ptr)) { decoder->mFrameIsHidden = 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)) { uint32_t bpp[] = { 0, 3, 4, 3, 4 }; decoder->mCMSLine = (uint8_t*)malloc(bpp[channels] * width); if (!decoder->mCMSLine) { png_longjmp(decoder->mPNG, 5); // NS_ERROR_OUT_OF_MEMORY } } if (interlace_type == PNG_INTERLACE_ADAM7) { if (height < INT32_MAX / (width * channels)) { decoder->interlacebuf = (uint8_t*)malloc(channels * width * height); } if (!decoder->interlacebuf) { png_longjmp(decoder->mPNG, 5); // NS_ERROR_OUT_OF_MEMORY } } if (decoder->NeedsNewFrame()) { // We know that we need a new frame, so pause input so the decoder // infrastructure can give it to us. png_process_data_pause(png_ptr, /* save = */ 1); } }
/* Read a PNG file. You may want to return an error code if the read * fails (depending upon the failure). There are two "prototypes" given * here - one where we are given the filename, and we need to open the * file, and the other where we are given an open file (possibly with * some or all of the magic bytes read - see comments above). */ #ifdef open_file /* prototype 1 */ void read_png(char *file_name) /* We need to open the file */ { png_structp png_ptr; png_infop info_ptr; unsigned int sig_read = 0; png_uint_32 width, height; int bit_depth, color_type, interlace_type; FILE *fp; if ((fp = fopen(file_name, "rb")) == NULL) return (ERROR); #else no_open_file /* prototype 2 */ void read_png(FILE *fp, unsigned int sig_read) /* File is already open */ { png_structp png_ptr; png_infop info_ptr; png_uint_32 width, height; int bit_depth, color_type, interlace_type; #endif no_open_file /* Only use one prototype! */ /* 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 NULL 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. REQUIRED */ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, png_voidp user_error_ptr, user_error_fn, user_warning_fn); if (png_ptr == NULL) { fclose(fp); return (ERROR); } /* Allocate/initialize the memory for image information. REQUIRED. */ info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { fclose(fp); png_destroy_read_struct(&png_ptr, NULL, NULL); return (ERROR); } /* 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))) { /* Free all of the memory associated with the png_ptr and info_ptr */ png_destroy_read_struct(&png_ptr, &info_ptr, NULL); fclose(fp); /* If we get here, we had a problem reading the file */ return (ERROR); } /* One of the following I/O initialization methods is REQUIRED */ #ifdef streams /* PNG file I/O method 1 */ /* Set up the input control if you are using standard C streams */ png_init_io(png_ptr, fp); #else no_streams /* PNG file I/O method 2 */ /* If you are using replacement read functions, instead of calling * png_init_io() here you would call: */ png_set_read_fn(png_ptr, (void *)user_io_ptr, user_read_fn); /* where user_io_ptr is a structure you want available to the callbacks */ #endif no_streams /* Use only one I/O method! */ /* If we have already read some of the signature */ png_set_sig_bytes(png_ptr, sig_read); #ifdef hilevel /* * If you have enough memory to read in the entire image at once, * and you need to specify only transforms that can be controlled * with one of the PNG_TRANSFORM_* bits (this presently excludes * quantizing, filling, setting background, and doing gamma * adjustment), then you can read the entire image (including * pixels) into the info structure with this call: */ png_read_png(png_ptr, info_ptr, png_transforms, NULL); #else /* OK, you're doing it the hard way, with the lower-level functions */ /* The call to png_read_info() gives us all of the information from the * PNG file before the first IDAT (image data chunk). REQUIRED */ png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL); /* Set up the data transformations you want. Note that these are all * optional. Only call them if you want/need them. Many of the * transformations only work on specific types of images, and many * are mutually exclusive. */ /* Tell libpng to strip 16 bit/color files down to 8 bits/color. * Use accurate scaling if it's available, otherwise just chop off the * low byte. */ #ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED png_set_scale_16(png_ptr); #else png_set_strip_16(png_ptr); #endif /* Strip alpha bytes from the input data without combining with the * background (not recommended). */ png_set_strip_alpha(png_ptr); /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single * byte into separate bytes (useful for paletted and grayscale images). */ png_set_packing(png_ptr); /* Change the order of packed pixels to least significant bit first * (not useful if you are using png_set_packing). */ png_set_packswap(png_ptr); /* Expand paletted colors into true RGB triplets */ if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png_ptr); /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */ if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand_gray_1_2_4_to_8(png_ptr); /* Expand paletted or RGB images with transparency to full alpha channels * so the data will be available as RGBA quartets. */ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr); /* Set the background color to draw transparent and alpha images over. * It is possible to set the red, green, and blue components directly * for paletted images instead of supplying a palette index. Note that * even if the PNG file supplies a background, you are not required to * use it - you should use the (solid) application background if it has one. */ png_color_16 my_background, *image_background; if (png_get_bKGD(png_ptr, info_ptr, &image_background)) png_set_background(png_ptr, image_background, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0); else png_set_background(png_ptr, &my_background, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0); /* Some suggestions as to how to get a screen gamma value * * Note that screen gamma is the display_exponent, which includes * the CRT_exponent and any correction for viewing conditions */ if (/* We have a user-defined screen gamma value */) { screen_gamma = user-defined screen_gamma; } /* This is one way that applications share the same screen gamma value */ else if ((gamma_str = getenv("SCREEN_GAMMA")) != NULL)
image_file_t *image_from_file( const char *filename ) /* {{{ */ { unsigned char header[8]; image_file_t *image; png_structp png_ptr; png_infop info_ptr; png_bytepp row_pointers; int tmp, y; image = malloc( sizeof( image_file_t ) ); FILE *fp = fopen( filename, "rb" ); if ( !fp ) die( "Cannot open file '%s'", filename ); fread( header, 1, 8, fp ); if ( png_sig_cmp( header, 0, 8 ) ) die( "File '%s' is not a png", filename ); png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL ); if ( ! png_ptr ) die( "png_create_read_struct failed" ); info_ptr = png_create_info_struct( png_ptr ); if ( ! info_ptr ) die( "png_create_info_struct failed" ); if ( setjmp( png_jmpbuf( png_ptr ) ) ) { /* XXX: handle errors here */ die( "Error during image initialization" ); } png_init_io( png_ptr, fp ); png_set_sig_bytes( png_ptr, 8 ); png_read_info( png_ptr, info_ptr ); png_set_add_alpha( png_ptr, 255, PNG_FILLER_AFTER ); png_set_gray_to_rgb( png_ptr ); png_set_palette_to_rgb( png_ptr ); png_set_expand( png_ptr ); png_set_scale_16( png_ptr ); image->width = png_get_image_width( png_ptr, info_ptr ); image->height = png_get_image_height( png_ptr, info_ptr ); tmp = png_set_interlace_handling( png_ptr ); png_read_update_info( png_ptr, info_ptr ); tmp = png_get_color_type( png_ptr, info_ptr ); if ( tmp != PNG_COLOR_TYPE_RGBA ) die( "Color type is %d, but it should be %d", tmp, PNG_COLOR_TYPE_RGBA ); tmp = png_get_bit_depth( png_ptr, info_ptr ); if ( tmp != BITS_PER_CHANNEL ) die( "Color depth is %d, but is should be %d", tmp, BITS_PER_CHANNEL ); if ( setjmp( png_jmpbuf( png_ptr ) ) ) { /* XXX: handle errors here */ die( "Error during image read" ); } image->row_pointers = row_pointers = malloc( sizeof(png_bytep) * image->height ); if ( !row_pointers ) die( "Cannot allocate pointer memory" ); for ( y = 0; y < image->height; y++ ) { row_pointers[ y ] = malloc( image->width * BYTES_PER_PIXEL ); if ( !row_pointers[ y ] ) die( "Cannot allocate pointer memory for row %d", y ); } png_read_image( png_ptr, row_pointers ); png_read_end( png_ptr, info_ptr ); fclose( fp ); png_destroy_read_struct( &png_ptr, &info_ptr, NULL ); return image; } /* }}} */
void PLPNGDecoder::Open(PLDataSource *pDataSrc) { png_uint_32 width, height; m_png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, user_error_fn, user_warning_fn); PLASSERT(m_png_ptr); m_info_ptr = png_create_info_struct(m_png_ptr); PLASSERT(m_info_ptr); png_set_read_fn(m_png_ptr, (void*)pDataSrc, my_read_data); /* 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(m_png_ptr, m_info_ptr); #ifdef DUMP_PNG_DATA debugLog("%s", toString(*m_png_ptr).cstr()); #endif png_get_IHDR(m_png_ptr, m_info_ptr, &width, &height, &m_bit_depth ,&m_color_type, NULL, NULL, NULL); if( (m_color_type != PNG_COLOR_TYPE_RGB_ALPHA) && (m_color_type != PNG_COLOR_TYPE_GRAY_ALPHA) && ((m_color_type != PNG_COLOR_TYPE_RGB) || (m_bit_depth < 16)) ) { #ifdef PNG_READ_16_TO_8_SUPPORTED if(m_bit_depth == 16) { #ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED png_set_scale_16(png_ptr); #else png_set_strip_16(png_ptr); #endif } #endif if(m_color_type == PNG_COLOR_TYPE_PALETTE) { png_set_expand(m_png_ptr); } if(m_bit_depth < 8) { png_set_expand(m_png_ptr); } if(png_get_valid(m_png_ptr, m_info_ptr, PNG_INFO_tRNS)) { png_set_expand(m_png_ptr); } if((m_color_type == PNG_COLOR_TYPE_GRAY) || (m_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)) { png_set_gray_to_rgb(m_png_ptr); } /* set the background color to draw transparent and alpha images over */ png_color_16 *pBackground; png_color bkgColor = {127, 127, 127}; if(png_get_bKGD(m_png_ptr, m_info_ptr, &pBackground)) { png_set_background(m_png_ptr, pBackground, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0); bkgColor.red = (png_byte)pBackground->red; bkgColor.green = (png_byte)pBackground->green; bkgColor.blue = (png_byte)pBackground->blue; #ifdef DUMP_PNG_DATA debugLog(" Background : (%d,%d,%d)\n", bkgColor.red, bkgColor.green, bkgColor.blue); #endif } /* if required set gamma conversion */ double dGamma; if(png_get_gAMA(m_png_ptr, m_info_ptr, &dGamma)) { png_set_gamma(m_png_ptr, (double)2.2, dGamma); } /* after the transformations are registered, update info_ptr data */ png_read_update_info(m_png_ptr, m_info_ptr); png_get_IHDR(m_png_ptr, m_info_ptr, &width, &height, &m_bit_depth ,&m_color_type, NULL, NULL, NULL); } PLPixelFormat pf; switch(m_color_type) { case PNG_COLOR_TYPE_RGB: pf = PLPixelFormat::R8G8B8; break; case PNG_COLOR_TYPE_RGB_ALPHA: pf = PLPixelFormat::A8R8G8B8; break; case PNG_COLOR_TYPE_GRAY: pf = PLPixelFormat::L8; break; case PNG_COLOR_TYPE_GRAY_ALPHA: png_set_gray_to_rgb(m_png_ptr); png_set_expand(m_png_ptr); pf = PLPixelFormat::A8R8G8B8; break; case PNG_COLOR_TYPE_PALETTE: if(m_bit_depth != 16) { pf = PLPixelFormat::I8; } else { // 16-bit palette image png_set_expand(m_png_ptr); pf = PLPixelFormat::R8G8B8; } break; } if((pf.GetBitsPerPixel() == 32) || (pf.GetBitsPerPixel() == 24)) { png_set_bgr(m_png_ptr); } SetBmpInfo(PLPoint(width, height), PLPoint(0,0), pf); png_uint_32 XRes, YRes; int UnitType; png_get_pHYs(m_png_ptr, m_info_ptr, &XRes, &YRes, &UnitType); if(UnitType == PNG_RESOLUTION_METER) { m_Resolution = PLPoint(int (XRes/39.37f+0.5), int (YRes/39.37f+0.5)); } }
bool loadPngFile( IMAGE_T* image, FILE *file) { png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { return false; } png_infop info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_read_struct(&png_ptr, 0, 0); return false; } if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, 0); return false; } //--------------------------------------------------------------------- png_init_io(png_ptr, file); png_read_info(png_ptr, info_ptr); //--------------------------------------------------------------------- png_byte colour_type = png_get_color_type(png_ptr, info_ptr); png_byte bit_depth = png_get_bit_depth(png_ptr, info_ptr); VC_IMAGE_TYPE_T type = VC_IMAGE_RGB888; if (colour_type & PNG_COLOR_MASK_ALPHA) { type = VC_IMAGE_RGBA32; } initImage(image, type, png_get_image_width(png_ptr, info_ptr), png_get_image_height(png_ptr, info_ptr), false); //--------------------------------------------------------------------- double gamma = 0.0; if (png_get_gAMA(png_ptr, info_ptr, &gamma)) { png_set_gamma(png_ptr, 2.2, gamma); } //--------------------------------------------------------------------- if (colour_type == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png_ptr); } if ((colour_type == PNG_COLOR_TYPE_GRAY) && (bit_depth < 8)) { 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); } if (bit_depth == 16) { #ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED png_set_scale_16(png_ptr); #else png_set_strip_16(png_ptr); #endif } if (colour_type == PNG_COLOR_TYPE_GRAY || colour_type == PNG_COLOR_TYPE_GRAY_ALPHA) { png_set_gray_to_rgb(png_ptr); } //--------------------------------------------------------------------- png_read_update_info(png_ptr, info_ptr); //--------------------------------------------------------------------- png_bytepp row_pointers = malloc(image->height * sizeof(png_bytep)); png_uint_32 j = 0; for (j = 0 ; j < image->height ; ++j) { row_pointers[j] = image->buffer + (j * image->pitch); } //--------------------------------------------------------------------- png_read_image(png_ptr, row_pointers); //--------------------------------------------------------------------- free(row_pointers); png_destroy_read_struct(&png_ptr, &info_ptr, 0); return true; }
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 led to an error. 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_scale_16(png_ptr); qcms_data_type inType; uint32_t intent = -1; uint32_t 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 == uint32_t(-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()) */ /*---------------------------------------------------------------*/ // This code is currently unused, but it will be needed for bug 517713. #if 0 int32_t 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; } } #endif 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 = 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)) { uint32_t bpp[] = { 0, 3, 4, 3, 4 }; decoder->mCMSLine = (uint8_t *)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 < INT32_MAX / (width * channels)) decoder->interlacebuf = (uint8_t *)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; }
static vita2d_texture *_vita2d_load_PNG_generic(const void *io_ptr, png_rw_ptr read_data_fn) { png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { goto error_create_read; } png_infop info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { goto error_create_info; } png_bytep *row_ptrs = NULL; if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)0); if (row_ptrs != NULL) free(row_ptrs); return NULL; } png_set_read_fn(png_ptr, (png_voidp)io_ptr, read_data_fn); png_set_sig_bytes(png_ptr, PNG_SIGSIZE); png_read_info(png_ptr, info_ptr); unsigned int width, height; int bit_depth, color_type; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); if ((color_type == PNG_COLOR_TYPE_PALETTE && bit_depth <= 8) || (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) || png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) || (bit_depth == 16)) { png_set_expand(png_ptr); } if (bit_depth == 16) png_set_scale_16(png_ptr); if (bit_depth == 8 && color_type == PNG_COLOR_TYPE_RGB) png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER); if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); if (color_type == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png_ptr); png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER); } if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) 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); if (bit_depth < 8) png_set_packing(png_ptr); png_read_update_info(png_ptr, info_ptr); row_ptrs = (png_bytep *)malloc(sizeof(png_bytep) * height); if (!row_ptrs) goto error_alloc_rows; vita2d_texture *texture = vita2d_create_empty_texture(width, height); if (!texture) goto error_create_tex; void *texture_data = vita2d_texture_get_datap(texture); unsigned int stride = vita2d_texture_get_stride(texture); int i; for (i = 0; i < height; i++) { row_ptrs[i] = (png_bytep)(texture_data + i*stride); } png_read_image(png_ptr, row_ptrs); png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)0); free(row_ptrs); return texture; error_create_tex: free(row_ptrs); error_alloc_rows: png_destroy_info_struct(png_ptr, &info_ptr); error_create_info: png_destroy_read_struct(&png_ptr, (png_infopp)0, (png_infopp)0); error_create_read: return NULL; }
pictw_t * spng_read(session_t *ps, const char *path) { assert(path); char sig[SPNG_SIGBYTES] = ""; pictw_t *pictw = NULL; png_structp png_ptr = NULL; png_infop info_ptr = NULL; FILE *fp = fopen(path, "rb"); bool need_premultiply = false; if (unlikely(!fp)) { printfef("(\"%s\"): Failed to open file.", path); goto spng_read_end; } if (unlikely(SPNG_SIGBYTES != fread(&sig, 1, SPNG_SIGBYTES, fp))) { printfef("(\"%s\"): Failed to read %d-byte signature.", path, SPNG_SIGBYTES); goto spng_read_end; } if (unlikely(png_sig_cmp((png_bytep) sig, 0, SPNG_SIGBYTES))) { printfef("(\"%s\"): PNG signature invalid.", path); goto spng_read_end; } png_ptr = allocchk(png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)); info_ptr = allocchk(png_create_info_struct(png_ptr)); if (setjmp(png_jmpbuf(png_ptr))) goto spng_read_end; png_init_io(png_ptr, fp); png_set_sig_bytes(png_ptr, SPNG_SIGBYTES); png_read_info(png_ptr, info_ptr); png_uint_32 width = 0, height = 0; // Set transformations int bit_depth = 0, color_type = 0; { int interlace_type = 0; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL); // Scale or strip 16-bit colors if (bit_depth == 16) { printfdf("(\"%s\"): Scaling 16-bit colors.", path); #if PNG_LIBPNG_VER >= 10504 png_set_scale_16(png_ptr); #else png_set_strip_16(png_ptr); #endif bit_depth = 8; } /* if (bit_depth < 8) png_set_packing(png_ptr); */ // No idea why this is needed... png_set_bgr(png_ptr); // Convert palette to RGB if (color_type == PNG_COLOR_TYPE_PALETTE) { printfdf("(\"%s\"): Converting palette PNG to RGB.", path); png_set_palette_to_rgb(png_ptr); color_type = PNG_COLOR_TYPE_RGB; } if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { printfdf("(\"%s\"): Converting rDNS to full alpha.", path); png_set_tRNS_to_alpha(png_ptr); } if (color_type == PNG_COLOR_TYPE_GRAY || PNG_COLOR_TYPE_GRAY_ALPHA == color_type) { printfdf("(\"%s\"): Converting gray (+ alpha) PNG to RGB.", path); png_set_gray_to_rgb(png_ptr); if (PNG_COLOR_TYPE_GRAY == color_type) color_type = PNG_COLOR_TYPE_RGB; else color_type = PNG_COLOR_TYPE_RGB_ALPHA; } /* if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { printfdf("(\"%s\"): Converting 1/2/4 bit gray PNG to 8-bit.", path); #if PNG_LIBPNG_VER >= 10209 png_set_expand_gray_1_2_4_to_8(png_ptr); #else png_set_gray_1_2_4_to_8(png_ptr); #endif bit_depth = 8; } */ // Somehow XImage requires 24-bit visual to use 32 bits per pixel if (color_type == PNG_COLOR_TYPE_RGB) { printfdf("(\"%s\"): Appending filler alpha values.", path); png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); } // Premultiply alpha if (PNG_COLOR_TYPE_RGB_ALPHA == color_type) { #if PNG_LIBPNG_VER >= 10504 png_set_alpha_mode(png_ptr, PNG_ALPHA_STANDARD, 1.0); #else need_premultiply = true; #endif } /* int number_passes = 1; #ifdef PNG_READ_INTERLACING_SUPPORTED number_passes = png_set_interlace_handling(png_ptr); #endif */ if (PNG_INTERLACE_NONE != interlace_type) png_set_interlace_handling(png_ptr); } png_read_update_info(png_ptr, info_ptr); int depth = 0; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); switch (color_type) { case PNG_COLOR_TYPE_GRAY: depth = 1 * bit_depth; break; case PNG_COLOR_TYPE_RGB: depth = 3 * bit_depth; break; case PNG_COLOR_TYPE_RGB_ALPHA: depth = 4 * bit_depth; break; default: assert(0); break; } // Read data and fill to Picture { int rowbytes = png_get_rowbytes(png_ptr, info_ptr); png_bytep row_pointers[height]; memset(row_pointers, 0, sizeof(row_pointers)); row_pointers[0] = png_malloc(png_ptr, rowbytes * height); for (int row = 1; row < height; row++) row_pointers[row] = row_pointers[row - 1] + rowbytes; png_read_image(png_ptr, row_pointers); if (need_premultiply) for (int row = 0; row < height; row++) simg_data32_premultiply(row_pointers[row], width); if (unlikely(!(pictw = simg_data_to_pictw(ps, width, height, depth, row_pointers[0], rowbytes)))) { printfef("(\"%s\"): Failed to create Picture.", path); goto spng_read_end; } } spng_read_end: if (png_ptr) png_destroy_read_struct(&png_ptr, &info_ptr, NULL); if (fp) fclose(fp); return pictw; }
void* PNG_decode(JNIEnv* env, PatchHeadInputStream* patch_head_input_stream, bool partially) { PNG *png = NULL; png_structp png_ptr = NULL; png_infop info_ptr = NULL; bool apng; unsigned int width; unsigned int height; int color_type; int bit_depth; bool is_opaque; unsigned char* buffer = NULL; unsigned int frame_count = 0; bool hide_first_frame = false; PNG_FRAME_INFO* frame_info_array = NULL; int i; png = (PNG *) malloc(sizeof(PNG)); if (png == NULL) { WTF_OM; close_patch_head_input_stream(get_env(), patch_head_input_stream); destroy_patch_head_input_stream(get_env(), &patch_head_input_stream); return NULL; } png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, &user_error_fn, &user_warn_fn); if (png_ptr == NULL) { free(png); png = NULL; close_patch_head_input_stream(get_env(), patch_head_input_stream); destroy_patch_head_input_stream(get_env(), &patch_head_input_stream); return NULL; } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_read_struct(&png_ptr, NULL, NULL); free(png); png = NULL; close_patch_head_input_stream(get_env(), patch_head_input_stream); destroy_patch_head_input_stream(get_env(), &patch_head_input_stream); return NULL; } if (setjmp(png_jmpbuf(png_ptr))) { LOGE(EMSG("Error in png decode")); free_frame_info_array(frame_info_array, frame_count); frame_info_array = NULL; free(buffer); buffer = NULL; png_destroy_read_struct(&png_ptr, &info_ptr, NULL); free(png); png = NULL; close_patch_head_input_stream(get_env(), patch_head_input_stream); destroy_patch_head_input_stream(get_env(), &patch_head_input_stream); return NULL; } // Set custom read function png_set_read_fn(png_ptr, patch_head_input_stream, &user_read_fn); // Get png info png_read_info(png_ptr, info_ptr); // Check apng if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL)) { apng = true; } else { apng = false; } // PNG info width = png_get_image_width(png_ptr, info_ptr); height = png_get_image_height(png_ptr, info_ptr); color_type = png_get_color_type(png_ptr, info_ptr); bit_depth = png_get_bit_depth(png_ptr, info_ptr); // Create buffer buffer = (unsigned char*) malloc(width * height * 4); if (buffer == NULL) { WTF_OM; png_destroy_read_struct(&png_ptr, &info_ptr, NULL); free(png); png = NULL; close_patch_head_input_stream(get_env(), patch_head_input_stream); destroy_patch_head_input_stream(get_env(), &patch_head_input_stream); return NULL; } if (apng) { // Get frame count frame_count = png_get_num_frames(png_ptr, info_ptr); hide_first_frame = png_get_first_frame_is_hidden(png_ptr, info_ptr); if (hide_first_frame) { frame_count--; } // Create frame info array frame_info_array = (PNG_FRAME_INFO*) calloc(frame_count, sizeof(PNG_FRAME_INFO)); if (frame_info_array == NULL) { WTF_OM; free(buffer); buffer = NULL; png_destroy_read_struct(&png_ptr, &info_ptr, NULL); free(png); png = NULL; close_patch_head_input_stream(get_env(), patch_head_input_stream); destroy_patch_head_input_stream(get_env(), &patch_head_input_stream); return NULL; } } // Configure to ARGB png_set_expand(png_ptr); if (bit_depth == 16) { png_set_scale_16(png_ptr); } if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { png_set_gray_to_rgb(png_ptr); } if (!(color_type & PNG_COLOR_MASK_ALPHA)) { is_opaque = true; png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER); } else { is_opaque = false; } if (apng) { if (hide_first_frame) { // Skip first frame read_image(png_ptr, buffer, width, height); } // Read first frame read_frame(png_ptr, info_ptr, frame_info_array); // Fix dop if (frame_info_array->dop == PNG_DISPOSE_OP_PREVIOUS) { frame_info_array->dop = PNG_DISPOSE_OP_BACKGROUND; } if (!partially || frame_count == 1) { // Read all frame for (i = 1; i < frame_count; read_frame(png_ptr, info_ptr, frame_info_array + i++)); // Generate pop generate_pop(frame_info_array, frame_count); // End read png_read_end(png_ptr, info_ptr); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); // Close input stream close_patch_head_input_stream(env, patch_head_input_stream); destroy_patch_head_input_stream(env, &patch_head_input_stream); png->partially = false; png->png_ptr = NULL; png->info_ptr = NULL; png->patch_head_input_stream = NULL; } else { png->partially = true; png->png_ptr = png_ptr; png->info_ptr = info_ptr; png->patch_head_input_stream = patch_head_input_stream; } // Fill PNG png->width = width; png->height = height; png->is_opaque = is_opaque; png->buffer = buffer; png->apng = true; png->buffer_index = -1; png->frame_info_array = frame_info_array; png->frame_count = frame_count; png->backup = NULL; // Render first frame PNG_advance(png); } else { read_image(png_ptr, buffer, width, height); // End read png_read_end(png_ptr, info_ptr); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); // Close input stream close_patch_head_input_stream(env, patch_head_input_stream); destroy_patch_head_input_stream(env, &patch_head_input_stream); // Fill PNG png->width = width; png->height = height; png->buffer = buffer; png->apng = false; png->buffer_index = 0; png->frame_info_array = NULL; png->frame_count = 0; png->backup = NULL; png->partially = false; png->png_ptr = NULL; png->info_ptr = NULL; png->patch_head_input_stream = NULL; } return png; }
bool PNG::Decode (Resource *resource, ImageBuffer *imageBuffer) { unsigned char png_sig[PNG_SIG_SIZE]; png_structp png_ptr; png_infop info_ptr; png_uint_32 width, height; int bit_depth, color_type, interlace_type; FILE_HANDLE *file = NULL; if (resource->path) { file = lime::fopen (resource->path, "rb"); if (!file) return false; int read = lime::fread (png_sig, PNG_SIG_SIZE, 1, file); if (png_sig_cmp (png_sig, 0, PNG_SIG_SIZE)) { lime::fclose (file); return false; } } else { memcpy (png_sig, resource->data->Bytes (), PNG_SIG_SIZE); if (png_sig_cmp (png_sig, 0, PNG_SIG_SIZE)) { return false; } } if ((png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)) == NULL) { if (file) lime::fclose (file); return false; } if ((info_ptr = png_create_info_struct (png_ptr)) == NULL) { png_destroy_read_struct (&png_ptr, (png_infopp)NULL, (png_infopp)NULL); if (file) lime::fclose (file); return false; } // sets the point which libpng will jump back to in the case of an error if (setjmp (png_jmpbuf (png_ptr))) { png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL); if (file) lime::fclose (file); return false; } if (file) { if (file->isFile ()) { png_init_io (png_ptr, file->getFile ()); png_set_sig_bytes (png_ptr, PNG_SIG_SIZE); } else { ByteArray data = ByteArray (resource->path); ReadBuffer buffer (data.Bytes (), data.Size ()); png_set_read_fn (png_ptr, &buffer, user_read_data_fn); } } else { ReadBuffer buffer (resource->data->Bytes (), resource->data->Size ()); png_set_read_fn (png_ptr, &buffer, user_read_data_fn); } png_read_info (png_ptr, info_ptr); png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL); //bool has_alpha = (color_type == PNG_COLOR_TYPE_GRAY_ALPHA || color_type == PNG_COLOR_TYPE_RGB_ALPHA || png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)); png_set_expand (png_ptr); png_set_filler (png_ptr, 0xff, PNG_FILLER_AFTER); //png_set_gray_1_2_4_to_8 (png_ptr); png_set_palette_to_rgb (png_ptr); png_set_gray_to_rgb (png_ptr); if (bit_depth < 8) { png_set_packing (png_ptr); } else if (bit_depth == 16) { png_set_scale_16 (png_ptr); } //png_set_bgr (png_ptr); int bpp = 4; const unsigned int stride = width * bpp; imageBuffer->Resize (width, height, bpp); unsigned char *bytes = imageBuffer->data->Bytes (); int number_of_passes = png_set_interlace_handling (png_ptr); for (int pass = 0; pass < number_of_passes; pass++) { for (int i = 0; i < height; i++) { png_bytep anAddr = (png_bytep)(bytes + i * stride); png_read_rows (png_ptr, (png_bytepp) &anAddr, NULL, 1); } } png_read_end (png_ptr, NULL); png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL); if (file) lime::fclose (file); return true; }
PNGImage::PNGImage(const char *full_file_path) { FILE *file = fopen(full_file_path, "rb"); if(file == nullptr) { throw ReadFileException("PNGImage", "PNG file specified is not valid."); } png_byte png_header[8]; fread(png_header, 1u, 8u, file); if(ferror(file) != 0 || feof(file) != 0) { fclose(file); throw ReadFileException("PNGImage", "Cannot read the PNG file."); } if(png_sig_cmp(png_header, 0, 8) != 0) { fclose(file); throw ReadFileException("PNGImage", "Please select a valid PNG file."); } png_structp png_read_struct = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); if(png_read_struct == nullptr) { fclose(file); throw ReadFileException("PNGImage", "Cannot initialize PNG read struct."); } png_infop png_info_struct = png_create_info_struct(png_read_struct); if(png_info_struct == nullptr) { png_destroy_read_struct(&png_read_struct, nullptr, nullptr); fclose(file); throw ReadFileException("PNGImage", "Cannot initialize PNG info struct."); } png_infop png_end_struct = png_create_info_struct(png_read_struct); if(png_info_struct == nullptr) { png_destroy_read_struct(&png_read_struct, &png_info_struct, nullptr); fclose(file); throw ReadFileException("PNGImage", "Cannot initialize PNG info struct."); } if(setjmp(png_jmpbuf(png_read_struct))) { png_destroy_read_struct(&png_read_struct, &png_info_struct, &png_end_struct); fclose(file); throw ReadFileException("PNGImage", "Error in libpng."); } png_init_io(png_read_struct, file); png_set_sig_bytes(png_read_struct, 8); png_read_info(png_read_struct, png_info_struct); int bit_depth, color_type; png_get_IHDR(png_read_struct, png_info_struct, &width, &height, &bit_depth, &color_type, nullptr, nullptr, nullptr); if(bit_depth == 16) { png_set_scale_16(png_read_struct); bit_depth = 8; } switch(color_type) { case PNG_COLOR_TYPE_RGB: color_space = GL_RGB; break; case PNG_COLOR_TYPE_RGB_ALPHA: color_space = GL_RGBA; break; default: png_destroy_read_struct(&png_read_struct, &png_info_struct, &png_end_struct); fclose(file); throw ReadFileException("PNGImage", "Unsupported color space."); } int row_bytes = png_get_rowbytes(png_read_struct, png_info_struct); row_bytes += 3 - ((row_bytes-1) % 4); output_buffer.reset(new (std::nothrow)png_byte[row_bytes * height * sizeof(png_byte) + 15]); if(output_buffer.get() == nullptr) { png_destroy_read_struct(&png_read_struct, &png_info_struct, &png_end_struct); fclose(file); throw ReadFileException("PNGImage", "Cannot allocate required memory."); } std::unique_ptr<png_byte *> row_pointers(new png_byte *[height]); if(row_pointers.get() == nullptr) { png_destroy_read_struct(&png_read_struct, &png_info_struct, &png_end_struct); fclose(file); throw ReadFileException("PNGImage", "Cannot allocate required memory."); } png_byte **_row_pointers = row_pointers.get(); unsigned char *_output_buffer = output_buffer.get(); for(unsigned int i = 0; i < height; ++i) { _row_pointers[height - 1 - i] = _output_buffer + i * row_bytes; } png_read_image(png_read_struct, _row_pointers); png_destroy_read_struct(&png_read_struct, &png_info_struct, &png_end_struct); fclose(file); }