Example #1
0
void PNGImageDecoder::headerAvailable()
{
    png_structp png = reader()->pngPtr();
    png_infop info = reader()->infoPtr();
    png_uint_32 width = png->width;
    png_uint_32 height = png->height;
    
    // Protect against large images.
    if (png->width > cMaxPNGSize || png->height > cMaxPNGSize) {
        m_failed = true;
        longjmp(png->jmpbuf, 1);
        return;
    }
    
    // We can fill in the size now that the header is available.
    if (!ImageDecoder::isSizeAvailable()) {
        if (!setSize(width, height)) {
            // Size unreasonable, bail out.
            longjmp(png->jmpbuf, 1);
            return;
        }
    }

    int bitDepth, colorType, interlaceType, compressionType, filterType, channels;
    png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType,
                 &interlaceType, &compressionType, &filterType);

    // The options we set here match what Mozilla does.

    // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA.
    if (colorType == PNG_COLOR_TYPE_PALETTE ||
        (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8))
        png_set_expand(png);
    
    png_bytep trns = 0;
    int trnsCount = 0;
    if (png_get_valid(png, info, PNG_INFO_tRNS)) {
        png_get_tRNS(png, info, &trns, &trnsCount, 0);
        png_set_expand(png);
    }

    if (bitDepth == 16)
        png_set_strip_16(png);

    if (colorType == PNG_COLOR_TYPE_GRAY ||
        colorType == PNG_COLOR_TYPE_GRAY_ALPHA)
        png_set_gray_to_rgb(png);

    // Deal with gamma and keep it under our control.
    double gamma;
    if (png_get_gAMA(png, info, &gamma)) {
        if ((gamma <= 0.0) || (gamma > cMaxGamma)) {
            gamma = cInverseGamma;
            png_set_gAMA(png, info, gamma);
        }
        png_set_gamma(png, cDefaultGamma, gamma);
    }
    else
        png_set_gamma(png, cDefaultGamma, cInverseGamma);

    // Tell libpng to send us rows for interlaced pngs.
    if (interlaceType == PNG_INTERLACE_ADAM7)
        png_set_interlace_handling(png);

    // Update our info now
    png_read_update_info(png, info);
    channels = png_get_channels(png, info);
    assert(channels == 3 || channels == 4);

    reader()->setHasAlpha(channels == 4);

    if (reader()->decodingSizeOnly()) {
        // If we only needed the size, halt the reader.     
        reader()->setReadOffset(m_data->size() - png->buffer_size);
        png->buffer_size = 0;
    }
}
Example #2
0
bool PNGDecoder::loadStream(Common::SeekableReadStream &stream) {
#ifdef USE_PNG
	destroy();

	_stream = &stream;

	// First, check the PNG signature
	if (_stream->readUint32BE() != MKTAG(0x89, 'P', 'N', 'G')) {
		delete _stream;
		return false;
	}
	if (_stream->readUint32BE() != MKTAG(0x0d, 0x0a, 0x1a, 0x0a)) {
		delete _stream;
		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) {
		delete _stream;
		return false;
	}
	png_infop infoPtr = png_create_info_struct(pngPtr);
	if (!infoPtr) {
		png_destroy_read_struct(&pngPtr, NULL, NULL);
		delete _stream;
		return false;
	}
	png_infop endInfo = png_create_info_struct(pngPtr);
	if (!endInfo) {
		png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
		delete _stream;
		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, &endInfo);

	// We no longer need the file stream, thus close it here
	_stream = 0;

	return true;
#else
	return false;
#endif
}
Example #3
0
void PNGAPI
png_read_png(png_structp png_ptr, png_infop info_ptr,
             int transforms,
             voidp params)
{
    int row;

#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED)
    /* invert the alpha channel from opacity to transparency */
    if (transforms & PNG_TRANSFORM_INVERT_ALPHA)
        png_set_invert_alpha(png_ptr);
#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);

    if (info_ptr->height > PNG_UINT_32_MAX/sizeof(png_bytep))
        png_error(png_ptr,"Image is too high to process with png_read_png()");

    /* -------------- image transformations start here ------------------- */

#if defined(PNG_READ_16_TO_8_SUPPORTED)
    /* tell libpng to strip 16 bit/color files down to 8 bits/color */
    if (transforms & PNG_TRANSFORM_STRIP_16)
        png_set_strip_16(png_ptr);
#endif

#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
    /* Strip alpha bytes from the input data without combining with the
     * background (not recommended).
     */
    if (transforms & PNG_TRANSFORM_STRIP_ALPHA)
        png_set_strip_alpha(png_ptr);
#endif

#if defined(PNG_READ_PACK_SUPPORTED) && !defined(PNG_READ_EXPAND_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 (transforms & PNG_TRANSFORM_PACKING)
        png_set_packing(png_ptr);
#endif

#if defined(PNG_READ_PACKSWAP_SUPPORTED)
    /* Change the order of packed pixels to least significant bit first
     * (not useful if you are using png_set_packing). */
    if (transforms & PNG_TRANSFORM_PACKSWAP)
        png_set_packswap(png_ptr);
#endif

#if defined(PNG_READ_EXPAND_SUPPORTED)
    /* Expand paletted colors into true RGB triplets
     * Expand grayscale images to full 8 bits from 1, 2, or 4 bits/pixel
     * Expand paletted or RGB images with transparency to full alpha
     * channels so the data will be available as RGBA quartets.
     */
    if (transforms & PNG_TRANSFORM_EXPAND)
        if ((png_ptr->bit_depth < 8) ||
                (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) ||
                (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)))
            png_set_expand(png_ptr);
#endif

    /* We don't handle background color or gamma transformation or dithering. */

#if defined(PNG_READ_INVERT_SUPPORTED)
    /* invert monochrome files to have 0 as white and 1 as black */
    if (transforms & PNG_TRANSFORM_INVERT_MONO)
        png_set_invert_mono(png_ptr);
#endif

#if defined(PNG_READ_SHIFT_SUPPORTED)
    /* If you want to shift the pixel values from the range [0,255] or
     * [0,65535] to the original [0,7] or [0,31], or whatever range the
     * colors were originally in:
     */
    if ((transforms & PNG_TRANSFORM_SHIFT)
            && png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT))
    {
        png_color_8p sig_bit;

        png_get_sBIT(png_ptr, info_ptr, &sig_bit);
        png_set_shift(png_ptr, sig_bit);
    }
#endif

#if defined(PNG_READ_BGR_SUPPORTED)
    /* flip the RGB pixels to BGR (or RGBA to BGRA) */
    if (transforms & PNG_TRANSFORM_BGR)
        png_set_bgr(png_ptr);
#endif

#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED)
    /* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */
    if (transforms & PNG_TRANSFORM_SWAP_ALPHA)
        png_set_swap_alpha(png_ptr);
#endif

#if defined(PNG_READ_SWAP_SUPPORTED)
    /* swap bytes of 16 bit files to least significant byte first */
    if (transforms & PNG_TRANSFORM_SWAP_ENDIAN)
        png_set_swap(png_ptr);
#endif

    /* We don't handle adding filler bytes */

    /* 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 (i.e., you selected such a transform above).
     */
    png_read_update_info(png_ptr, info_ptr);

    /* -------------- image transformations end here ------------------- */

#ifdef PNG_FREE_ME_SUPPORTED
    png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0);
#endif
    if(info_ptr->row_pointers == NULL)
    {
        info_ptr->row_pointers = (png_bytepp)png_malloc(png_ptr,
                                 info_ptr->height * sizeof(png_bytep));
#ifdef PNG_FREE_ME_SUPPORTED
        info_ptr->free_me |= PNG_FREE_ROWS;
#endif
        for (row = 0; row < (int)info_ptr->height; row++)
        {
            info_ptr->row_pointers[row] = (png_bytep)png_malloc(png_ptr,
                                          png_get_rowbytes(png_ptr, info_ptr));
        }
    }

    png_read_image(png_ptr, info_ptr->row_pointers);
    info_ptr->valid |= PNG_INFO_IDAT;

    /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
    png_read_end(png_ptr, info_ptr);

    if(transforms == 0 || params == NULL)
        /* quiet compiler warnings */ return;

}
Example #4
0
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;
}
Example #5
0
void __fastcall TPngDecoder::Init(){
    pfFile=fopen(FFileName.c_str(),"rb");
    if(!pfFile)
        throw Exception ( "Can't open file" );
    
    // first check the eight byte PNG signature
    fread(pbSig, 1, 8, pfFile);
    if (!png_check_sig(pbSig, 8))
        throw Exception ( "Bad file format" );

    // create the two png(-info) structures
    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
        NULL, NULL);
    if (!png_ptr)
        throw Exception( "Not enough memory" );

    info_ptr = png_create_info_struct(png_ptr);
    if (!info_ptr)
    {
        png_destroy_read_struct(&png_ptr, NULL, NULL);
        throw Exception( "Not enough memory" );
    }
    /*точка возврата после обработки ошибки или предупреждения*/
    if (setjmp(png_jmpbuf(png_ptr)))
    {
    //    awp_res=*((long*)png_ptr->error_ptr);
        if(row_pointers){
            for (i = 0; i < FNumScanLines; i++){
                if(row_pointers[i])
                    free(row_pointers[i]);
            }
            free(row_pointers);
        }
        png_destroy_read_struct(&png_ptr, &info_ptr,NULL);
        if(pfFile) fclose(pfFile);
        throw Exception( "" );;
    }
    
    // initialize the input png file
    png_init_io(png_ptr, pfFile);

    //8 байт сигнатуры уже прочитанно
    png_set_sig_bytes(png_ptr, 8);

   
    /* 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);
    unsigned long width, hight;
    png_get_IHDR(png_ptr, info_ptr, &width, &hight, &iBitDepth, &iColorType,
        NULL, NULL, NULL);
    FWidth = width;
    FNumScanLines = hight;

   /**** 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 */
    //может это не нужно?
    if (iBitDepth == 16)
        png_set_strip_16(png_ptr);
    if (iBitDepth < 8)
        png_set_expand(png_ptr);
    
    
    /* Expand paletted colors into true RGB triplets */
    if (iColorType == PNG_COLOR_TYPE_PALETTE)
        png_set_expand(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_expand(png_ptr);
    
    /* tell libpng convert grayscale images to true rgd */
    if (iColorType == PNG_COLOR_TYPE_GRAY||
        iColorType == PNG_COLOR_TYPE_GRAY_ALPHA)
        png_set_gray_to_rgb(png_ptr);
    /*remove alpha channel instead combine with background*/
    if (iColorType & PNG_COLOR_MASK_ALPHA)
        png_set_strip_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.
    */

    // set the background color to draw transparent and alpha images over.
    if (png_get_bKGD(png_ptr, info_ptr, &pBackground))
        png_set_background(png_ptr, pBackground, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
    /* flip the RGB pixels to BGR (or RGBA to BGRA) */
    if (iColorType & PNG_COLOR_MASK_COLOR)
        png_set_bgr(png_ptr);

    png_read_update_info(png_ptr, info_ptr);
    png_get_IHDR(png_ptr, info_ptr, &width, &hight, &iBitDepth, &iColorType,
        NULL, NULL, NULL);
    FWidth = width; FNumScanLines = hight;
    FBufferSize = png_get_rowbytes(png_ptr, info_ptr);
    FScanBuffer = new char[FBufferSize];
    if(width>0x7fff||hight>0x7fff)
        throw Exception( "Format not supported" );

}
Example #6
0
void LoadPNG(const char *name, byte ** pic, int *width, int *height, byte alphaByte)
{
	int             bit_depth;
	int             color_type;
	png_uint_32     w;
	png_uint_32     h;
	unsigned int    row;
//	size_t          rowbytes;
	png_infop       info;
	png_structp     png;
	png_bytep      *row_pointers;
	byte           *data;
	byte           *out;
	int             size;

	// load png
	size = ri.FS_ReadFile(name, (void **)&data);

	if(!data)
		return;

	//png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	png = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp) NULL, png_user_error_fn, png_user_warning_fn);

	if(!png)
	{
		ri.Printf(PRINT_WARNING, "LoadPNG: png_create_write_struct() failed for (%s)\n", name);
		ri.FS_FreeFile(data);
		return;
	}

	// allocate/initialize the memory for image information.  REQUIRED
	info = png_create_info_struct(png);
	if(!info)
	{
		ri.Printf(PRINT_WARNING, "LoadPNG: png_create_info_struct() failed for (%s)\n", name);
		ri.FS_FreeFile(data);
		png_destroy_read_struct(&png, (png_infopp) NULL, (png_infopp) NULL);
		return;
	}

	/*
	 * Set error handling if you are using the setjmp/longjmp method (this is
	 * the common 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)))
	{
		// if we get here, we had a problem reading the file
		ri.Printf(PRINT_WARNING, "LoadPNG: first exception handler called for (%s)\n", name);
		ri.FS_FreeFile(data);
		png_destroy_read_struct(&png, (png_infopp) & info, (png_infopp) NULL);
		return;
	}

	//png_set_write_fn(png, buffer, png_write_data, png_flush_data);
	png_set_read_fn(png, data, png_read_data);

	png_set_sig_bytes(png, 0);

	// 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, info);

	// get picture info
	png_get_IHDR(png, info, (png_uint_32 *) & w, (png_uint_32 *) & h, &bit_depth, &color_type, NULL, NULL, NULL);

	// tell libpng to strip 16 bit/color files down to 8 bits/color
	png_set_strip_16(png);

	// expand paletted images to RGB triplets
	if(color_type & PNG_COLOR_MASK_PALETTE)
		png_set_expand(png);

	// expand gray-scaled images to RGB triplets
	if(!(color_type & PNG_COLOR_MASK_COLOR))
		png_set_gray_to_rgb(png);

	// 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_gray_1_2_4_to_8(png);

	// 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, info, PNG_INFO_tRNS))
		png_set_tRNS_to_alpha(png);

	// if there is no alpha information, fill with alphaByte
	if(!(color_type & PNG_COLOR_MASK_ALPHA))
		png_set_filler(png, alphaByte, PNG_FILLER_AFTER);

	// expand pictures with less than 8bpp to 8bpp
	if(bit_depth < 8)
		png_set_packing(png);

	// update structure with the above settings
	png_read_update_info(png, info);

	// allocate the memory to hold the image
	*width = w;
	*height = h;
	*pic = out = (byte *) ri.Z_Malloc(w * h * 4);

	row_pointers = (png_bytep *) ri.Hunk_AllocateTempMemory(sizeof(png_bytep) * h);

	// set a new exception handler
	if(setjmp(png_jmpbuf(png)))
	{
		ri.Printf(PRINT_WARNING, "LoadPNG: second exception handler called for (%s)\n", name);
		ri.Hunk_FreeTempMemory(row_pointers);
		ri.FS_FreeFile(data);
		png_destroy_read_struct(&png, (png_infopp) & info, (png_infopp) NULL);
		return;
	}

	//rowbytes = png_get_rowbytes(png, info);

	for(row = 0; row < h; row++)
		row_pointers[row] = (png_bytep) (out + (row * 4 * w));

	// read image data
	png_read_image(png, row_pointers);

	// read rest of file, and get additional chunks in info
	png_read_end(png, info);

	// clean up after the read, and free any memory allocated
	png_destroy_read_struct(&png, &info, (png_infopp) NULL);

	ri.Hunk_FreeTempMemory(row_pointers);
	ri.FS_FreeFile(data);
}
Example #7
0
bool vtImage::_ReadPNG(const char *filename)
{
	FILE *fp = NULL;

	uchar header[8];
	png_structp png;
	png_infop   info;
	png_infop   endinfo;
	png_bytep  *row_p;

	png_uint_32 width, height;
	int depth, color;

	png_uint_32 i;
	png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	if (!png)
	{
		// We compiled against the headers of one version of libpng, but
		// linked against the libraries from another version.  If you get
		// this, fix the paths in your development environment.
		return false;
	}
	info = png_create_info_struct(png);
	endinfo = png_create_info_struct(png);

	fp = vtFileOpen(filename, "rb");
	if (fp && fread(header, 1, 8, fp) && png_check_sig(header, 8))
		png_init_io(png, fp);
	else
	{
		png_destroy_read_struct(&png, &info, &endinfo);
		return false;
	}
	png_set_sig_bytes(png, 8);

	png_read_info(png, info);
	png_get_IHDR(png, info, &width, &height, &depth, &color, NULL, NULL, NULL);

	if (color == PNG_COLOR_TYPE_GRAY || color == PNG_COLOR_TYPE_GRAY_ALPHA)
		png_set_gray_to_rgb(png);

	// never strip alpha
//	{
//		png_set_strip_alpha(png);
//		color &= ~PNG_COLOR_MASK_ALPHA;
//	}

	// Always expand paletted images
	if (color == PNG_COLOR_TYPE_PALETTE)
		png_set_expand(png);

	/*--GAMMA--*/
//	checkForGammaEnv();
	double screenGamma = 2.2 / 1.0;
#if 0
	// Getting the gamma from the PNG file is disabled here, since
	// PhotoShop writes bizarre gamma values like .227 (PhotoShop 5.0)
	// or .45 (newer versions)
	double	fileGamma;
	if (png_get_gAMA(png, info, &fileGamma))
		png_set_gamma(png, screenGamma, fileGamma);
	else
#endif
		png_set_gamma(png, screenGamma, 1.0/2.2);

	png_read_update_info(png, info);

	m_pPngData = (png_bytep) malloc(png_get_rowbytes(png, info)*height);
	row_p = (png_bytep *) malloc(sizeof(png_bytep)*height);

	bool StandardOrientation = true;
	for (i = 0; i < height; i++) {
		if (StandardOrientation)
			row_p[height - 1 - i] = &m_pPngData[png_get_rowbytes(png, info)*i];
		else
			row_p[i] = &m_pPngData[png_get_rowbytes(png, info)*i];
	}

	png_read_image(png, row_p);
	free(row_p);

	int iBitCount;

	switch (color)
	{
		case PNG_COLOR_TYPE_GRAY:
		case PNG_COLOR_TYPE_RGB:
		case PNG_COLOR_TYPE_PALETTE:
			iBitCount = 24;
			break;

		case PNG_COLOR_TYPE_GRAY_ALPHA:
		case PNG_COLOR_TYPE_RGB_ALPHA:
			iBitCount = 32;
			break;

		default:
			return false;
	}

	png_read_end(png, endinfo);
	png_destroy_read_struct(&png, &info, &endinfo);

	// Don't free the data, we're going to pass it to OSG
//	free(m_pPngData);

	if (fp)
		fclose(fp);

	int pixelFormat;
	uint internalFormat;

	if (iBitCount == 24)
		pixelFormat = GL_RGB;
	else if (iBitCount == 32)
		pixelFormat = GL_RGBA;

	if (m_internalformat == -1)
		internalFormat = pixelFormat;	// use default
	else
		internalFormat = m_internalformat;	// use specific

	setImage(width, height, 1,
	   internalFormat,		// int internalFormat,
	   pixelFormat,			// uint pixelFormat
	   GL_UNSIGNED_BYTE,	// uint dataType
	   m_pPngData,
	   osg::Image::USE_MALLOC_FREE);

	return true;
}
Example #8
0
static unsigned char* _load_image_RGBA_png(const char *fileName, int *width, int *height)
{
    // open the file
    FILE *fp = fopen(fileName, "rb");
    if (!fp)
        return _load_img_error(width, height);

    // read the header
    const size_t HEADER_LENGTH = 8;
    png_byte header[HEADER_LENGTH];
    size_t n = fread(header, 1, HEADER_LENGTH, fp);
    if (n != HEADER_LENGTH || png_sig_cmp(header, 0, HEADER_LENGTH))
        return _load_img_error(width, height);

    // try to create the loading structures
    png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0,
      0);
    if (!png_ptr) {
        fclose(fp);
    return _load_img_error(width, height);
    }

    png_infop info_ptr = png_create_info_struct(png_ptr);
    if (!info_ptr) {
        png_destroy_read_struct(&png_ptr, (png_infopp) 0, (png_infopp) 0);
        fclose(fp);
        return _load_img_error(width, height);
    }

    png_infop end_info = png_create_info_struct(png_ptr);
    if (!end_info) {
        png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)0);
        fclose(fp);
        return _load_img_error(width, height);
    }

    if (setjmp(png_ptr->jmpbuf)) {
        png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
        fclose(fp);
        return _load_img_error(width, height);
    }

    // start the io
    png_init_io(png_ptr, fp);

    // indicate that we have already read some of the hearder
    png_set_sig_bytes(png_ptr, HEADER_LENGTH);

    // read the image info, get some info
    png_read_info(png_ptr, info_ptr);
    *width = png_get_image_width(png_ptr, info_ptr);
    *height = png_get_image_height(png_ptr, info_ptr);
    int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
    png_byte color_type = png_get_color_type(png_ptr, info_ptr);

    // force the image into RGBA, 8 bits per channel
    if (color_type != PNG_COLOR_TYPE_RGBA)
        png_set_expand(png_ptr);
    if (color_type == PNG_COLOR_TYPE_GRAY ||
      color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
        png_set_gray_to_rgb(png_ptr);
    if (bit_depth < 8)
        png_set_packing(png_ptr);
    else if (bit_depth == 16)
        png_set_strip_16(png_ptr);
    if (color_type != PNG_COLOR_TYPE_RGBA)
        png_set_filler(png_ptr, 255, PNG_FILLER_AFTER);
    png_read_update_info(png_ptr, info_ptr);

    // make sure we're actually in rgba mode
    if ((int)png_get_rowbytes(png_ptr, info_ptr) != ((*width) * 4)) {
        png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
        fclose(fp);
        return _load_img_error(width, height);
    }

    // finally, read the file
    unsigned char *buffer = (unsigned char *) malloc((*width) * (*height) * 4);
    png_bytep *row_pointers = new png_bytep[*height];
    for (int y = 0 ; y < (*height) ; y++) {
        row_pointers[y] = (png_byte *) (buffer + ((*height) - 1 - y) *
          (*width) * 4);
    }
    png_read_rows(png_ptr, row_pointers, 0, (long unsigned int) (*height));

    // deallocate memory and return
    fclose(fp);
    png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
    return buffer;
}
Example #9
0
bool GR_Win32Image::_convertFromPNG(const UT_ByteBuf* pBB, UT_sint32 iDisplayWidth, UT_sint32 iDisplayHeight)
{
	png_structp png_ptr;
	png_infop info_ptr;
	png_uint_32 width, height;
	int bit_depth, color_type, interlace_type;

	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (void*) NULL,
									 NULL, NULL);

	if (png_ptr == NULL)
	{
		return false;
	}

	/* Allocate/initialize the memory for image information.  REQUIRED. */
	info_ptr = png_create_info_struct(png_ptr);
	if (info_ptr == NULL)
	{
		png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
		return false;
	}

	/* 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, (png_infopp)NULL);

		/* If we get here, we had a problem reading the file */
		return false;
	}

	struct _bb myBB;
	myBB.pBB = pBB;
	myBB.iCurPos = 0;
	
	png_set_read_fn(png_ptr, (void *)&myBB, _png_read);

	/* 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);

	/* 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);

	/* Expand paletted colors into true RGB triplets */
	png_set_expand(png_ptr);

	/*  If we've got images with 16 bits per channel, we don't need that
		much precision.  We'll do fine with 8 bits per channel */
	png_set_strip_16(png_ptr);

	/*  For simplicity, treat grayscale as RGB */
	png_set_gray_to_rgb(png_ptr);

	/*  For simplicity, we'll ignore alpha */
	png_set_strip_alpha(png_ptr);
	
	/*  We want libpng to deinterlace the image for us */
	UT_uint32 iInterlacePasses = png_set_interlace_handling(png_ptr);

	/* flip the RGB pixels to BGR (or RGBA to BGRA) */
	png_set_bgr(png_ptr);

	UT_uint32 iBytesInRow = width * 3;
	if (iBytesInRow % 4)
	{
		iBytesInRow += (4 - (iBytesInRow % 4));
	}

	m_pDIB = (BITMAPINFO*) g_try_malloc(sizeof(BITMAPINFOHEADER) + height * iBytesInRow);
	if (!m_pDIB)
	{
		png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
		return false;
	}

	/*
	  Note that we do NOT create a DIB of iDisplayWidth,iDisplayHeight, since
	  DIBs can be stretched automatically by the Win32 API.  So we simply remember
	  the display size for drawing later.
	*/

	setDisplaySize(iDisplayWidth, iDisplayHeight);

	m_pDIB->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	m_pDIB->bmiHeader.biWidth = width;
	m_pDIB->bmiHeader.biHeight = height;
	m_pDIB->bmiHeader.biPlanes = 1;
	m_pDIB->bmiHeader.biBitCount = 24;
	m_pDIB->bmiHeader.biCompression = BI_RGB;
	m_pDIB->bmiHeader.biSizeImage = 0;
	m_pDIB->bmiHeader.biXPelsPerMeter = 0;
	m_pDIB->bmiHeader.biYPelsPerMeter = 0;
	m_pDIB->bmiHeader.biClrUsed = 0;
	m_pDIB->bmiHeader.biClrImportant = 0;

	UT_Byte* pBits = ((unsigned char*) m_pDIB) + m_pDIB->bmiHeader.biSize;

	for (; iInterlacePasses; iInterlacePasses--)
	{
		for (UT_uint32 iRow = 0; iRow < height; iRow++)
		{
			UT_Byte* pRow = pBits + (height - iRow - 1) * iBytesInRow;

			png_read_rows(png_ptr, &pRow, NULL, 1);
		}
	}

	/* read rest of file, and get additional chunks in info_ptr - REQUIRED */
	png_read_end(png_ptr, info_ptr);

	/* clean up after the read, and g_free any memory allocated - REQUIRED */
	png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);

	return true;
}
Example #10
0
void readPng(GImage* pImage, const unsigned char* pData, size_t nDataSize)
{
	// Check for the PNG signature
	if(nDataSize < 8 || png_sig_cmp((png_bytep)pData, 0, 8) != 0)
		throw Ex("not a png file");

	// Read all PNG data up until the image data chunk.
	GPNGReader reader(pData);
	png_set_read_fn(reader.m_pReadStruct, (png_voidp)&reader, (png_rw_ptr)readFunc);
	png_read_info(reader.m_pReadStruct, reader.m_pInfoStruct);

	// Get the image data
	int depth, color;
	png_uint_32 width, height;
	png_get_IHDR(reader.m_pReadStruct, reader.m_pInfoStruct, &width, &height, &depth, &color, NULL, NULL, NULL);
	if(depth != 8)
		throw Ex("unexpected depth");
	pImage->resize(width, height);

	// Set gamma correction
	double dGamma;
	if (png_get_gAMA(reader.m_pReadStruct, reader.m_pInfoStruct, &dGamma))
		png_set_gamma(reader.m_pReadStruct, 2.2, dGamma);
	else
		png_set_gamma(reader.m_pReadStruct, 2.2, 1.0 / 2.2); // 1.0 = viewing gamma, 2.2 = screen gamma

	// Update the 'info' struct with the gamma information
	png_read_update_info(reader.m_pReadStruct, reader.m_pInfoStruct);

	// Tell it to expand palettes to full channels
	png_set_expand(reader.m_pReadStruct);
	png_set_gray_to_rgb(reader.m_pReadStruct);

	// Allocate the row pointers
	unsigned long rowbytes = png_get_rowbytes(reader.m_pReadStruct, reader.m_pInfoStruct);
	unsigned long channels = rowbytes / width;
	png_bytep pRawData = (png_bytep)new unsigned char[rowbytes * height];
	unsigned int i;
	{
		png_bytep* pRows = (png_bytep*)new unsigned char[sizeof(png_bytep) * height];
		for(i = 0; i < height; i++)
			pRows[i] = pRawData + i * rowbytes;
		png_read_image(reader.m_pReadStruct, pRows);
		delete[] pRows;
	}

	// Copy to the GImage
	unsigned long nPixels = width * height;
	unsigned int* pRGBQuads = pImage->m_pPixels;
	unsigned char *pBytes = pRawData;
	if(channels > 3)
	{
		if(channels != 4)
			throw Ex("Unexpected number of channels");
		for(i = 0; i < nPixels; i++)
		{
			*pRGBQuads = gARGB(pBytes[3], pBytes[0], pBytes[1], pBytes[2]);
			pBytes += channels;
			pRGBQuads++;
		}
	}
	else if(channels == 3)
	{
		for(i = 0; i < nPixels; i++)
		{
			*pRGBQuads = gARGB(0xff, pBytes[0], pBytes[1], pBytes[2]);
			pBytes += channels;
			pRGBQuads++;
		}
	}
	else
	{
		delete[] pRawData;
		throw Ex("Sorry, loading ", to_str(channels), "-channel png files is not yet supported");
	}
	delete[] pRawData;

	// Check for additional tags
	png_read_end(reader.m_pReadStruct, reader.m_pEndInfoStruct);
}
SDL_Surface *TCOD_sys_read_png(const char *filename) {
	png_uint_32 png_width,png_height,y;
	int png_bit_depth,png_color_type,png_interlace_type;
	png_structp png_ptr;
	png_infop info_ptr;
	FILE *fp;
	SDL_Surface *bitmap;
	png_bytep *row_pointers;

	if ((fp = fopen(filename, "rb")) == NULL)
		return NULL;
	/* 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,NULL, NULL, NULL);

	if (png_ptr == NULL)
	{
		fclose(fp);
		return NULL;
	}

	/* 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, png_infopp_NULL, png_infopp_NULL);
		return NULL;
	}

	/* 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, png_infopp_NULL);
		fclose(fp);
		/* If we get here, we had a problem reading the file */
		return NULL;
	}

	png_init_io(png_ptr, fp);

	/*
	* 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
	* dithering, 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_TRANSFORM_IDENTITY, png_voidp_NULL);

	// get info about the image
	png_read_info(png_ptr,info_ptr);
	png_get_IHDR(png_ptr,info_ptr,&png_width,&png_height,&png_bit_depth,&png_color_type,
		&png_interlace_type,NULL,NULL);

	// convert the image to a format suitable with libtcod
	png_set_strip_16(png_ptr); // 16 bits channels => 8 bits channels
	png_set_packing(png_ptr); // 1,2,4 bits depth => 24/32 bits depth
	if ( png_color_type == PNG_COLOR_TYPE_GRAY ) png_set_expand(png_ptr); // grayscale => color
	if ( png_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ) png_set_gray_to_rgb(png_ptr);

	// update the image information
	png_read_update_info(png_ptr,info_ptr);
	png_get_IHDR(png_ptr,info_ptr,&png_width,&png_height,&png_bit_depth,&png_color_type,
		&png_interlace_type,NULL,NULL);

	// create the SDL surface
	bitmap=TCOD_sys_get_surface(png_width,png_height,info_ptr->channels == 4);

	// get row data
	row_pointers=(png_bytep *)malloc(sizeof(png_bytep)*png_height);
	for (y=0; y<  png_height; y++ ) {
		row_pointers[y]=(png_bytep)(Uint8 *)(bitmap->pixels) + y * bitmap->pitch;
	}

	// read png data directly into the SDL surface
	png_read_image(png_ptr,row_pointers);

	/* clean up after the read, and free any memory allocated - REQUIRED */
	png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
	free(row_pointers);

	/* close the file */
	fclose(fp);
	return bitmap;
}
Example #12
0
void PNGImageDecoder::headerAvailable()
{
    png_structp png = m_reader->pngPtr();
    png_infop info = m_reader->infoPtr();
    png_uint_32 width = png_get_image_width(png, info);
    png_uint_32 height = png_get_image_height(png, info);

    // Protect against large PNGs. See http://bugzil.la/251381 for more details.
    const unsigned long maxPNGSize = 1000000UL;
    if (width > maxPNGSize || height > maxPNGSize) {
        longjmp(JMPBUF(png), 1);
        return;
    }

    // Set the image size now that the image header is available.
    if (!setSize(width, height)) {
        longjmp(JMPBUF(png), 1);
        return;
    }

    int bitDepth, colorType, interlaceType, compressionType, filterType, channels;
    png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, &interlaceType, &compressionType, &filterType);

    // The options we set here match what Mozilla does.

    // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA.
    if (colorType == PNG_COLOR_TYPE_PALETTE || (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8))
        png_set_expand(png);

    png_bytep trns = 0;
    int trnsCount = 0;
    if (png_get_valid(png, info, PNG_INFO_tRNS)) {
        png_get_tRNS(png, info, &trns, &trnsCount, 0);
        png_set_expand(png);
    }

    if (bitDepth == 16)
        png_set_strip_16(png);

    if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA)
        png_set_gray_to_rgb(png);

#if USE(QCMSLIB)
    if ((colorType & PNG_COLOR_MASK_COLOR) && !m_ignoreGammaAndColorProfile) {
        // We only support color profiles for color PALETTE and RGB[A] PNG. Supporting
        // color profiles for gray-scale images is slightly tricky, at least using the
        // CoreGraphics ICC library, because we expand gray-scale images to RGB but we
        // do not similarly transform the color profile. We'd either need to transform
        // the color profile or we'd need to decode into a gray-scale image buffer and
        // hand that to CoreGraphics.
        bool sRGB = false;
        ColorProfile colorProfile;
        getColorProfile(png, info, colorProfile, sRGB);
        bool imageHasAlpha = (colorType & PNG_COLOR_MASK_ALPHA) || trnsCount;
        m_reader->createColorTransform(colorProfile, imageHasAlpha, sRGB);
        m_hasColorProfile = !!m_reader->colorTransform();
    }
#endif

    if (!m_hasColorProfile) {
        // Deal with gamma and keep it under our control.
        const double inverseGamma = 0.45455;
        const double defaultGamma = 2.2;
        double gamma;
        if (!m_ignoreGammaAndColorProfile && png_get_gAMA(png, info, &gamma)) {
            const double maxGamma = 21474.83;
            if ((gamma <= 0.0) || (gamma > maxGamma)) {
                gamma = inverseGamma;
                png_set_gAMA(png, info, gamma);
            }
            png_set_gamma(png, defaultGamma, gamma);
        } else {
            png_set_gamma(png, defaultGamma, inverseGamma);
        }
    }

    // Tell libpng to send us rows for interlaced pngs.
    if (interlaceType == PNG_INTERLACE_ADAM7)
        png_set_interlace_handling(png);

    // Update our info now.
    png_read_update_info(png, info);
    channels = png_get_channels(png, info);
    ASSERT(channels == 3 || channels == 4);

    m_reader->setHasAlpha(channels == 4);

    if (m_reader->decodingSizeOnly()) {
        // If we only needed the size, halt the reader.
#if PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5)
        // '0' argument to png_process_data_pause means: Do not cache unprocessed data.
        m_reader->setReadOffset(m_reader->currentBufferSize() - png_process_data_pause(png, 0));
#else
        m_reader->setReadOffset(m_reader->currentBufferSize() - png->buffer_size);
        png->buffer_size = 0;
#endif
    }
}
Example #13
0
bool ossimPngReader::initReader()
{
   bool result = true;
   
   ossim_uint32 height    = png_get_image_height(m_pngReadPtr, m_pngReadInfoPtr);
   ossim_uint32 width     = png_get_image_width(m_pngReadPtr, m_pngReadInfoPtr);
   m_bitDepth            = png_get_bit_depth(m_pngReadPtr, m_pngReadInfoPtr);
   m_pngColorType        = png_get_color_type(m_pngReadPtr, m_pngReadInfoPtr);
   
   m_imageRect = ossimIrect(0, 0, width  - 1, height - 1);
   
   if (m_bitDepth == 16)
   {
      // png_set_strip_16 (m_pngReadPtr);
      m_bytePerPixelPerBand = 2;
      m_outputScalarType = OSSIM_UINT16;
   }
   else
   {
      m_bytePerPixelPerBand = 1;
   }

   // Set the read mode from scalar and color type.
   if (m_outputScalarType == OSSIM_UINT8)
   {
      if ( (m_pngColorType == PNG_COLOR_TYPE_RGB_ALPHA) ||
           (m_pngColorType == PNG_COLOR_TYPE_GRAY_ALPHA) )
      {
         m_readMode = ossimPngRead8a;
      }
      else
      {
         m_readMode = ossimPngRead8;
      }
   }
   else
   {
      if ( (m_pngColorType == PNG_COLOR_TYPE_RGB_ALPHA) ||
           (m_pngColorType == PNG_COLOR_TYPE_GRAY_ALPHA) )
      {
         m_readMode = ossimPngRead16a;
      }
      else
      {
         m_readMode = ossimPngRead16;
      }

      // Set the swap flag.  PNG stores data in network byte order(big endian).
      if(ossim::byteOrder() == OSSIM_LITTLE_ENDIAN)
      {
         m_swapFlag = true;
      }
   }
   
   //---
   // If png_set_expand used:
   // Expand data to 24-bit RGB, or 8-bit grayscale,
   // with alpha if available.
   //---
   bool expandFlag = false;

   if ( m_pngColorType == PNG_COLOR_TYPE_PALETTE )
   {
      expandFlag = true;
   }
   if ( (m_pngColorType == PNG_COLOR_TYPE_GRAY) && (m_bitDepth < 8) )
   {
      expandFlag = true;
   }
   if ( png_get_valid(m_pngReadPtr, m_pngReadInfoPtr, PNG_INFO_tRNS) )
   {
      expandFlag = true;
   }

   //---
   // If png_set_packing used:
   // Use 1 byte per pixel in 1, 2, or 4-bit depth files. */
   //---
   bool packingFlag = false;

   if ( (m_bitDepth < 8) && (m_pngColorType == PNG_COLOR_TYPE_GRAY) )
   {
      packingFlag = true;
   }

   if (expandFlag)
   {
       png_set_expand(m_pngReadPtr);
   }
   if (packingFlag)
   {
      png_set_packing(m_pngReadPtr);
   }

   // Gamma correction.
   // ossim_float64 gamma;
   // if (png_get_gAMA(m_pngReadPtr, m_pngReadInfoPtr, &gamma))
   // {
   //    png_set_gamma(png_ptr, display_exponent, gamma);
   // }

   //---
   // Turn on interlace handling... libpng returns just 1 (ie single pass)
   //  if the image is not interlaced
   //---
   m_interlacePasses = png_set_interlace_handling (m_pngReadPtr);

   //---
   // Update the info structures after the transformations take effect
   //---
   png_read_update_info (m_pngReadPtr, m_pngReadInfoPtr);

   // TODO:
   // Add check for image offsets.
   // Add check for resolution.
   // Add check for colormap.

   switch (m_pngColorType)
   {
      case PNG_COLOR_TYPE_RGB:           /* RGB */
      {
         m_numberOfInputBands  = 3;
         m_numberOfOutputBands = 3;
         break;
      }  
      case PNG_COLOR_TYPE_RGB_ALPHA:     /* RGBA */
      {
         m_numberOfInputBands  = 4;
         if (m_useAlphaChannelFlag)
         {
            m_numberOfOutputBands = 4;     
         }
         else
         {
            m_numberOfOutputBands = 3;    
         }         
         break;
      }
      case PNG_COLOR_TYPE_GRAY:          /* Grayscale */
      {
         m_numberOfInputBands = 1;
         m_numberOfOutputBands = 1;
         break;
      }  
      case PNG_COLOR_TYPE_GRAY_ALPHA:    /* Grayscale + alpha */
      {
         m_numberOfInputBands = 2;
         if (m_useAlphaChannelFlag)
         {
            m_numberOfOutputBands = 2;     
         }
         else
         {
            m_numberOfOutputBands = 1;
         }
        break;
      }
     case PNG_COLOR_TYPE_PALETTE:       /* Indexed */
     {
        m_numberOfInputBands  = 3;
        m_numberOfOutputBands = 3;
        break;
     }  
     default:                   /* Unknown type */
     {
        result = false;
     }
   }

   if ( result )
   {
      m_lineBufferSizeInBytes = png_get_rowbytes(m_pngReadPtr, m_pngReadInfoPtr);
      
      // Set the max pixel value.
      setMaxPixelValue();
      
      // Set to OSSIM_USHORT11 for use of specialized tile.
      if (m_maxPixelValue[0] == 2047.0)
      {
         m_outputScalarType = OSSIM_USHORT11;
      }

      // We're on row 0 or first line.
      m_currentRow = 0;
      
      if (traceDebug())
      {
         ossimNotify(ossimNotifyLevel_DEBUG)
            << "ossimPngReader::initReader DEBUG:"
            << "\nm_imageRect:                     " << m_imageRect
            << "\nm_bitDepth:                      " << int(m_bitDepth)
            << "\nm_pngColorType:                  "
            <<  getPngColorTypeString().c_str()
            << "\nm_numberOfInputBands:            " << m_numberOfInputBands
            << "\nm_numberOfOutputBands:           " << m_numberOfOutputBands
            << "\nm_bytePerPixelPerBand:           " << m_bytePerPixelPerBand
            << "\nm_lineBufferSizeInBytes:         " << m_lineBufferSizeInBytes
            << "\nm_interlacePasses:               " << m_interlacePasses
            << "\npalette expansion:                "
            << (expandFlag?"on":"off")
            << "\npacking (1,2,4 bit to one byte):  "
            << (packingFlag?"on":"off")
            << "\nm_readMode:                      " << m_readMode
            << "\nm_swapFlag:                      " << m_swapFlag
            << std::endl;
         
         for (ossim_uint32 band = 0; band < m_numberOfInputBands; ++band)
         {
            ossimNotify(ossimNotifyLevel_DEBUG)
               << "max[" << band << "]:  " << m_maxPixelValue[band]
               << std::endl;
         }
      }
   }

   return result;
   
} // End: ossimPngReader::initReader()
Example #14
0
void ossimPngReader::restart()
{
   if ( m_str )
   {
      // Destroy the existing memory associated with png structs.
      if (m_pngReadPtr && m_pngReadInfoPtr)
      {
         png_destroy_read_struct(&m_pngReadPtr, &m_pngReadInfoPtr, NULL);
      }

      m_pngReadPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
                                            NULL,
                                            NULL,
                                            NULL);
      m_pngReadInfoPtr = png_create_info_struct(m_pngReadPtr);

      if ( setjmp( png_jmpbuf(m_pngReadPtr) ) )
      {
         ossimNotify(ossimNotifyLevel_WARN)
            << "Error while reading.  File corrupted?  "
            << theImageFile
            << std::endl;
      
         return;
      }

      // Reset the file pointer.
      m_str->seekg( m_restartPosition, std::ios_base::beg );
   
      //---
      // Pass the static read method to libpng to allow us to use our
      // c++ stream instead of doing "png_init_io (pp, ...);" with
      // c stream.
      //---
      png_set_read_fn( m_pngReadPtr,
                       (png_voidp)m_str,
                       (png_rw_ptr)&ossimPngReader::pngReadData );

      //---
      // Note we won't do png_set_sig_bytes(png_ptr, 8) here because we are not
      // rechecking for png signature.
      //---
      png_read_info(m_pngReadPtr, m_pngReadInfoPtr);

      //---
      // If png_set_expand used:
      // Expand data to 24-bit RGB, or 8-bit grayscale,
      // with alpha if available.
      //---
      bool expandFlag = false;

      if ( m_pngColorType == PNG_COLOR_TYPE_PALETTE )
      {
         expandFlag = true;
      }
      if ( (m_pngColorType == PNG_COLOR_TYPE_GRAY) && (m_bitDepth < 8) )
      {
         expandFlag = true;
      }
      if ( png_get_valid(m_pngReadPtr, m_pngReadInfoPtr, PNG_INFO_tRNS) )
      {
         expandFlag = true;
      }

      //---
      // If png_set_packing used:
      // Use 1 byte per pixel in 1, 2, or 4-bit depth files. */
      //---
      bool packingFlag = false;

      if ( (m_bitDepth < 8) && (m_pngColorType == PNG_COLOR_TYPE_GRAY) )
      {
         packingFlag = true;
      }

      if (expandFlag)
      {
         png_set_expand(m_pngReadPtr);
      }
      if (packingFlag)
      {
         png_set_packing(m_pngReadPtr);
      }

      // Gamma correction.
      //    ossim_float64 gamma;
      //    if (png_get_gAMA(m_pngReadPtr, m_pngReadInfoPtr, &gamma))
      //    {
      //       png_set_gamma(m_pngReadPtr, display_exponent, gamma);
      //    }

      //---
      // Turn on interlace handling... libpng returns just 1 (ie single pass)
      //  if the image is not interlaced
      //---
      png_set_interlace_handling (m_pngReadPtr);

      //---
      // Update the info structures after the transformations take effect
      //---
      png_read_update_info (m_pngReadPtr, m_pngReadInfoPtr);
   
      // We're back on row 0 or first line.
      m_currentRow = 0;
   }
}
Example #15
0
void* replaceBootImage(AbstractFile* imageWrapper, const unsigned int* key, const unsigned int* iv, AbstractFile* png, size_t *fileSize) {
	AbstractFile* imageFile;
	unsigned char header[8];
	InfoIBootIM* info;
	png_uint_32 i;
	png_bytepp row_pointers;
	
	uint8_t* imageBuffer;
	void* buffer;

	png->read(png, header, 8);
	if(png_sig_cmp(header, 0, 8) != 0) {
		XLOG(0, "error: not a valid png file\n");
		return NULL;
	}
	png->seek(png, 0);

	png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, pngError, pngWarn);
	if (!png_ptr) {
		return NULL;
	}

	png_infop info_ptr = png_create_info_struct(png_ptr);
	if (!info_ptr)
	{
		png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
		return NULL;
	}

	png_infop end_info = png_create_info_struct(png_ptr);
	if (!end_info)
	{
		png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
		return NULL;
	}

	if (setjmp(png_jmpbuf(png_ptr)))
	{
		XLOG(0, "error reading png\n");
		png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
		free(buffer);
		return NULL;
	}

	png_set_read_fn(png_ptr, png, pngRead);

	png_read_info(png_ptr, info_ptr);
	
	if(png_get_bit_depth(png_ptr, info_ptr) > 8) {
		XLOG(0, "warning: bit depth per channel is greater than 8 (%d). Attempting to strip, but image quality will be degraded.\n", png_get_bit_depth(png_ptr, info_ptr));
	}
	
	if(png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_GRAY || png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_RGB) {
		XLOG(0, "notice: attempting to add dummy transparency channel\n");
	}
	
	if(png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_PALETTE) {
		XLOG(0, "notice: attempting to expand palette into full rgb\n");
	}
	
	png_set_expand(png_ptr);
	png_set_strip_16(png_ptr);
	png_set_bgr(png_ptr);
	png_set_add_alpha(png_ptr, 0x0, PNG_FILLER_AFTER);
	png_set_invert_alpha(png_ptr);
	
	png_read_update_info(png_ptr, info_ptr);
	

	if(png_get_image_width(png_ptr, info_ptr) > 320 || png_get_image_height(png_ptr, info_ptr) > 480) {
		XLOG(0, "error: dimensions out of range, must be within 320x480, not %lux%lu\n", png_get_image_width(png_ptr, info_ptr), png_get_image_height(png_ptr, info_ptr));
		png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
		return NULL;
	}

	if(png_get_bit_depth(png_ptr, info_ptr) != 8) {
		XLOG(0, "error: bit depth per channel must be 8 not %d!\n", png_get_bit_depth(png_ptr, info_ptr));
		png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
		return NULL;
	}

	if(png_get_color_type(png_ptr, info_ptr) != PNG_COLOR_TYPE_GRAY_ALPHA && png_get_color_type(png_ptr, info_ptr) != PNG_COLOR_TYPE_RGB_ALPHA) {
		XLOG(0, "error: incorrect color type, must be greyscale with alpha, or rgb with alpha\n");
		if(png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_GRAY || png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_RGB) {
			XLOG(0, "It appears you're missing an alpha channel. Add transparency to your image\n");
		}
		if(png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_PALETTE) {
			XLOG(0, "This PNG is saved with the palette color type rather than ARGB.\n");
		}
		png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);

		return NULL;
	}

	row_pointers = (png_bytepp) malloc(sizeof(png_bytep) * png_get_image_height(png_ptr, info_ptr));
	imageBuffer = malloc(png_get_image_height(png_ptr, info_ptr) * png_get_rowbytes(png_ptr, info_ptr));
	for(i = 0; i < png_get_image_height(png_ptr, info_ptr); i++) {
		row_pointers[i] = imageBuffer + (png_get_rowbytes(png_ptr, info_ptr) * i);
	}

	png_read_image(png_ptr, row_pointers);
	png_read_end(png_ptr, end_info);
	
	buffer = malloc(1);
	*fileSize = 0;

	if(key != NULL) {
		imageFile = duplicateAbstractFile2(imageWrapper, createAbstractFileFromMemoryFile((void**)&buffer, fileSize), key, iv, NULL);
	} else {	
		imageFile = duplicateAbstractFile(imageWrapper, createAbstractFileFromMemoryFile((void**)&buffer, fileSize));
	}
	info = (InfoIBootIM*) (imageFile->data);
	
	info->header.width = (uint16_t) png_get_image_width(png_ptr, info_ptr);
	info->header.height = (uint16_t) png_get_image_height(png_ptr, info_ptr);
	if(png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_GRAY_ALPHA) {
		info->header.format = IBOOTIM_GREY;
	} else {
		info->header.format = IBOOTIM_ARGB;
	}
	
	imageFile->write(imageFile, imageBuffer, png_get_image_height(png_ptr, info_ptr) * png_get_rowbytes(png_ptr, info_ptr));
	
	imageFile->close(imageFile);

	png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);

	png->close(png);
	
	free(row_pointers);

	free(imageBuffer);

	return buffer;
}
Example #16
0
// This is the interesting function here. It sets up a PNG image as a texture.
void loadPngTex(char * fname , struct tImagePNG * pImagePNG , int *hasAlpha )
{
  // The header of the file will be saved in here
  char           buf[PNG_BYTES_TO_CHECK];
  
  // images infos
  int            bit_depth;
  int            cType;
  double         gamma;
  FILE          *fp;

  // Open the file and check correct opening
  /* Open the prospective PNG file. */
  if ((fp = fopen(fname, "rb")) == NULL) {
    printf( "Error: Could not open the texture file [%s]!" , fname ); throw 336;
  }
  
  // Read the PNG header, which is 8 bytes long.
  /* Read in some of the signature bytes */
  if (fread(buf, 1, PNG_BYTES_TO_CHECK, fp) != PNG_BYTES_TO_CHECK) {
    printf( "Error: Incorrect PNG texture file [%s]!" , fname ); throw 336;
  }

  // Check whether the file is a PNG file
  // png_sig_cmp() checks the given PNG header and returns 0 if it could
  // be the start of a PNG file. We can use 8 bytes at max for
  // this comparison.
  if(png_sig_cmp((png_byte*)buf, (png_size_t)0, PNG_BYTES_TO_CHECK) != 0) {
    fclose( fp );
    printf( "Error: Not a PNG file [%s]!" , fname ); throw 337;
  }
  
  // Create / initialize the png_struct
  // The png_struct isn't directly accessed by the user (us).
  // We will later create a png_info from this to get access to the
  // PNG's infos.
  // The three 0's in the arg list could be pointers to user defined error
  // handling functions. 0 means we don't want to specify them, but use
  // libPNG's default error handling instead.
  png_infop info_ptr;
  png_structp png_ptr
    = png_create_read_struct(PNG_LIBPNG_VER_STRING,
			     NULL , NULL , NULL );
  if(!png_ptr) {
    fclose( fp );
    printf( "Error: Couldn't create PNG read structure [%s]!" , fname ); throw 338;
  }

  // Create / initialize the png_info
  // The png_info grants the user access to the PNG infos.
  info_ptr = png_create_info_struct(png_ptr);
  if(!info_ptr) {
    // We need to destroy the read_struct
    png_destroy_read_struct(&png_ptr, NULL, NULL);
    fclose( fp );
    printf( "Error: Couldn't create PNG info structure [%s]!" , fname ); throw 339;
  }
  
  // Setup error handler
  // This sets the point libPNG jumps back to is an error occurs.
  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, png_infopp_NULL);
      fclose(fp);
      /* If we get here, we had a problem reading the file */
      printf( "Error: Couldn't setup PNG error handler [%s]!" , fname ); throw 340;
   }
  
  /* Set up the input control if you are using standard C streams */
  png_init_io(png_ptr, fp);
  
  // This tells libPNG that we have already read 8 bytes from the start
  // of the file (for the header check above).
  png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK);

  // This reads the PNG file into the read and info structs
  /*
   * 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
   * dithering, 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_info(png_ptr, info_ptr);

  // Get some basic infos about the image from png_info structure
  // width & height in px, bit depth
  // interlace_method, compression_method, & filter_method are ignored
  png_get_IHDR(png_ptr, info_ptr, &(pImagePNG->sizeX), &(pImagePNG->sizeY), 
	       &bit_depth, &cType, 0, 0, 0);

  // COLOR TYPE read and possible corrections - then reread
  // Color type: we handle RGB and RGB_ALPHA (with Alpha) 
  // GRAY (luminance) and GRAY_ALPHA (luminance with Alpha)
  cType = png_get_color_type(png_ptr, info_ptr);
  // strip the pixels of a PNG stream with 16 bits per channel to 8 bits per channel
  if (bit_depth == 16) {
    png_set_strip_16(png_ptr);
  }
  // set transformation in png_ptr such that paletted images are expanded to RGB, 
  // grayscale images of bit-depth less than 8 are expanded to 8-bit images
  // tRNS chunks are expanded to alpha channels
  if (cType == PNG_COLOR_TYPE_PALETTE) {
    png_set_expand(png_ptr);
  }
  if (bit_depth < 8) {
    png_set_expand(png_ptr);
  }
  if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
    png_set_expand(png_ptr);
  }
  // if required set gamma conversion
  if (png_get_gAMA(png_ptr, info_ptr, &gamma)) {
    png_set_gamma(png_ptr, (double) 2.2, gamma);
  }

  // After the image transformations have been registered update info_ptr data
  png_read_update_info(png_ptr, info_ptr);
  // Gets again width, height and the new bit-depth and color-type
  png_get_IHDR(png_ptr, info_ptr, &(pImagePNG->sizeX), &(pImagePNG->sizeY), 
	       &bit_depth, &cType, 0, 0, 0);

  // We now calculate the *bytes* per pixel from the color type and the
  // bits per pixel.
  if((cType == PNG_COLOR_TYPE_RGB) && (bit_depth == 8)) {
    pImagePNG->bytesPerPixel = 3;
    *hasAlpha = false;
  }
  else if((cType == PNG_COLOR_TYPE_RGB_ALPHA) && (bit_depth == 8)) {
    pImagePNG->bytesPerPixel = 4;
    *hasAlpha = true;
  }
  else if((cType == PNG_COLOR_TYPE_GRAY) && (bit_depth == 8)) {
    pImagePNG->bytesPerPixel = 1;
    *hasAlpha = false;
  }
  else if((cType == PNG_COLOR_TYPE_GRAY_ALPHA) && (bit_depth == 8)) {
    pImagePNG->bytesPerPixel = 2;
    *hasAlpha = true;
  }
  else {
    /* clean up after the read, and free any memory allocated - REQUIRED */
    png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
    fclose( fp );
    printf( "Error: PNG image [%s] type (%d) bit depth (%d) is unsupported!" , fname , cType , bit_depth ); throw 336;
  }
  
  // rowbytes is the width x number of channels. channels is not used currently
  unsigned int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
  // unsigned int channels = png_get_channels(png_ptr, info_ptr);

  // Allocates memory to store the image
  pImagePNG->data = new GLubyte[pImagePNG->sizeY * rowbytes];

//   printf( "PNG image %ldx%ld (%d bitsperpixel/%d bytesPerPixel) size row %d total %ld\n" , 
// 	  pImagePNG->sizeX ,
// 	  pImagePNG->sizeY , bit_depth , pImagePNG->bytesPerPixel,
// 	  rowbytes , pImagePNG->sizeY * rowbytes);

  ////  Alternate solution without explicit row pointers
  //// and image read through png_get_rows
  // Returns a pointer to the array of array of png_bytes that holds the
  // image data.
  // png_byte **imageData = png_get_rows(png_ptr, info_ptr);
  //// must read row after row
  //   for( unsigned int i = 0 ; i < pImagePNG->sizeY ; i++ ) {
  //     printf( "Ind %d\n" , i );
  //     memcpy(&pImagePNG->data[(pImagePNG->sizeY - i - 1) * rowbytes],
  // 	   row[i], rowbytes);
  //  }

  ////  Alternate solution with explicit row pointers
  //// and image read through png_read_image
  // Allocates memory for an array of row-pointers
  png_byte ** row = new GLubyte * [pImagePNG->sizeY];
  // Sets the row-pointers to point at the correct offsets	
  for (unsigned int i = 0; i < pImagePNG->sizeY; i++) {
    row[i] = pImagePNG->data + (pImagePNG->sizeY - i - 1) * rowbytes;
  }
  // Reads the whole image
  png_read_image(png_ptr, row);
  // deallocate the now unuseful row pointers
  delete [] row;

  // Free the memory we used - we don't need it anymore
  /* clean up after the read, and free any memory allocated - REQUIRED */
  png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
  fclose( fp );
}
Example #17
0
uint8 *readpng_get_image(void * strsv, uint32 *pChannels, uint32 *pRowbytes, uint32 *pWidth, uint32 *pHeight)
{
    readpng_structs_t * strs = (readpng_structs_t *)strsv;

  png_uint_32  width, height;
  int  bit_depth, color_type;
  uint8  *image_data = NULL;
  png_uint_32  i, rowbytes;
  png_bytepp  row_pointers = NULL;


    /* alternatively, could make separate calls to png_get_image_width(),
     * etc., but want bit_depth and color_type for later [don't care about
     * compression_type and filter_type => NULLs] */

  png_get_IHDR(strs->png_ptr, strs->info_ptr, &width, &height, &bit_depth, &color_type,
               NULL, NULL, NULL);

  *pWidth = width;
  *pHeight = height;

    /* expand palette images to RGB, low-bit-depth grayscale images to 8 bits,
     * transparency chunks to full alpha channel; strip 16-bit-per-sample
     * images to 8 bits per sample; and convert grayscale to RGB[A] */

    if (color_type == PNG_COLOR_TYPE_PALETTE)
        png_set_expand(strs->png_ptr);
    if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
        png_set_expand(strs->png_ptr);
    if (png_get_valid(strs->png_ptr, strs->info_ptr, PNG_INFO_tRNS))
        png_set_expand(strs->png_ptr);
    if (bit_depth == 16)
        png_set_strip_16(strs->png_ptr);
    if (color_type == PNG_COLOR_TYPE_GRAY ||
        color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
        png_set_gray_to_rgb(strs->png_ptr);

    /* all transformations have been registered; now update info_ptr data,
     * get rowbytes and channels, and allocate image memory */

    png_read_update_info(strs->png_ptr, strs->info_ptr);

    *pRowbytes = rowbytes = png_get_rowbytes(strs->png_ptr, strs->info_ptr);
    *pChannels = (int)png_get_channels(strs->png_ptr, strs->info_ptr);

    if ((image_data = (uint8 *)malloc(rowbytes*height)) == NULL) {
        png_destroy_read_struct(&strs->png_ptr, &strs->info_ptr, NULL);
        return NULL;
    }
    if ((row_pointers = (png_bytepp)malloc(height*sizeof(png_bytep))) == NULL) {
        png_destroy_read_struct(&strs->png_ptr, &strs->info_ptr, NULL);
        free(image_data);
        image_data = NULL;
        return NULL;
    }

    Trace((stderr, "readpng_get_image:  channels = %d, rowbytes = %ld, height = %ld\n", *pChannels, rowbytes, height));


    /* set the individual row_pointers to point at the correct offsets */

    for (i = 0;  i < height;  ++i)
        row_pointers[i] = image_data + i*rowbytes;

    /* now we can go ahead and just read the whole image */

    png_read_image(strs->png_ptr, row_pointers);

    free(row_pointers);
    row_pointers = NULL;

    png_read_end(strs->png_ptr, NULL);

    return image_data;
}
Example #18
0
int read_png_(char *file_name, int *w, int *h,  int *rw, int *rh, unsigned char *pixout[], bool forgl, int *numchannel)
{
   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, compression_type, filter_type;
   FILE *fp;

   if ((fp = fopen(file_name, "rb")) == NULL)
      return (ERROR);

   /* 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,
      NULL, NULL, NULL);

   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, png_infopp_NULL, png_infopp_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, png_infopp_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 */
   /* Set up the input control if you are using standard C streams */
   png_init_io(png_ptr, fp);

   /* If we have already read some of the signature */
   png_set_sig_bytes(png_ptr, sig_read);

   png_read_info(png_ptr, info_ptr);
   png_get_IHDR(png_ptr, info_ptr, &width, &height,
       &bit_depth, &color_type, &interlace_type,
       &compression_type, &filter_type);

   // The following code transforms grayscale images of less than 8 to 8 bits,
   // changes paletted images to RGB, and adds a full alpha channel if there
   // is transparency information in a tRNS chunk. 
   if (color_type == PNG_COLOR_TYPE_PALETTE && bit_depth <= 8) 
     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_set_expand(png_ptr);

   if (bit_depth == 16) //ensure 8-bit
     png_set_strip_16(png_ptr);
   if (bit_depth < 8) //ensure 8-bit
     png_set_packing(png_ptr);

   if (forgl) { //width and height must be powers of 2.
     *rh = height; //set the real return vals first
     *rw = width;
     //these are probably the only likely ranges
     if (width > 512)
       width = 1024;
     else if (width > 256)
       width = 512;
     else 
       width = 256;
     if (height > 512)
       height = 1024;
     else if (height > 256)
       height = 512;
     else 
       height = 256;
   }
   
   //printf("colortype %d %d\n",color_type,PNG_COLOR_TYPE_RGBA); 
   *numchannel=3;
   if(color_type==PNG_COLOR_TYPE_RGBA||color_type==PNG_COLOR_TYPE_RGB_ALPHA)
     *numchannel=4;

   unsigned char *pixels = new unsigned char[width*height*sizeof(unsigned char)*(*numchannel)]; //for rgba
   png_byte **row_pointers = new png_byte*[height];
   for (unsigned int k = 0; k < height; k++)
     row_pointers[k] = pixels + (k)* 
       width*sizeof(unsigned char)*(*numchannel); //for r,g,b,a

   png_read_image(png_ptr, row_pointers);
   png_read_end(png_ptr, NULL);

   /* At this point you have read the entire image */

   //set return values
   *h = height;
   *w = width;
   *pixout = pixels;

   /* clean up after the read, and free any memory allocated - REQUIRED */
   png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);

   /* close the file */
   fclose(fp);

   /* that's it */
   return (OK);
}
Example #19
0
/* read png image */
uch *read_png (EIF_POINTER p_file_name, EIF_POINTER pChannels, EIF_POINTER pRowbytes,
		EIF_POINTER pWidth, EIF_POINTER pHeight,EIF_POINTER pBitDepth,EIF_POINTER pColorType,EIF_POINTER pInterlaceType) {
	//parameters
	char *file_name;

	//locals
	//int cha
	png_structp		png_ptr;
	png_infop		info_ptr;
	png_uint_32 	width;
	png_uint_32		height;
    	png_uint_32  i, rowbytes;
	int				bit_depth;
	int				color_type;
	int				interlace_type;
	FILE 			*fp;			/* Current opened file */
   	png_color_16 my_background, *image_background;
	double gamma;

    	png_bytepp  row_pointers = NULL;

	// open file
	file_name=(char*)p_file_name;
	fp = fopen (file_name, "rb");
	if (fp == NULL) {
		return NULL;
	}
	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	if (png_ptr == NULL){
		fclose (fp);
		return NULL;
	}

	/* Allocate/initialize the memory for image information.  REQUIRED. */
	info_ptr = png_create_info_struct(png_ptr);
	if (info_ptr == NULL) {
		png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
		fclose (fp);
		return NULL;
	}

	/* 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_ptr->jmpbuf)) {
		/* Free all of the memory associated with the png_ptr and info_ptr */
		png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);

		/* If we get here, we had a problem reading the file */
		fclose (fp);
		return NULL;
	}

	/* One of the following I/O initialization methods is REQUIRED */
	/* Set up the input control if you are using standard C streams */
	png_init_io(png_ptr, fp);

	/* 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.                                            */

	/* 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);

	/* Expand paletted colors into true RGB triplets */
	if (color_type == PNG_COLOR_TYPE_PALETTE)
		png_set_expand(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(png_ptr);

    if (bit_depth == 16)
        png_set_strip_16(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_expand(png_ptr);

	/* invert monocrome files to have 0 as white and 1 as black */
	//png_set_invert_mono(png_ptr);

	/* Add filler (or alpha) byte (before/after each RGB triplet) */
	if (color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
		png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
	}

        // after the transformations have been registered update info_ptr data

	png_read_update_info(png_ptr, info_ptr);

	// get again width, height and the new bit-depth and color-type

	png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
		&interlace_type, NULL, NULL);

	rowbytes = png_get_rowbytes(png_ptr, info_ptr);
    	*((EIF_INTEGER*)pRowbytes) = (EIF_INTEGER) rowbytes;
	*((EIF_INTEGER*)pChannels) = (EIF_INTEGER) png_get_channels(png_ptr, info_ptr);


	*((EIF_INTEGER*)pWidth) = (EIF_INTEGER) width;
	*((EIF_INTEGER*)pHeight) = (EIF_INTEGER) height;
	*((EIF_INTEGER*)pBitDepth) = (EIF_INTEGER) bit_depth;
	*((EIF_INTEGER*)pColorType) = (EIF_INTEGER) color_type;
	*((EIF_INTEGER*)pInterlaceType) = (EIF_INTEGER) interlace_type;

    if ((image_data = (uch *)malloc(rowbytes*height)) == NULL) {
        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
		fclose (fp);
        return NULL;
    }
    if ((row_pointers = (png_bytepp)malloc(height*sizeof(png_bytep))) == NULL) {
        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
        free(image_data);
        image_data = NULL;
		fclose (fp);
        return NULL;
    }

//    Trace((stderr, "readpng_get_image:  channels = %d, rowbytes = %ld, height = %ld\n", *pChannels, rowbytes, height));


    /* set the individual row_pointers to point at the correct offsets */

    for (i = 0;  i < height;  ++i)
        row_pointers[i] = image_data + i*rowbytes;


    /* now we can go ahead and just read the whole image */

    png_read_image(png_ptr, row_pointers);


    /* and we're done!  (png_read_end() can be omitted if no processing of
     * post-IDAT text/time/etc. is desired) */

    free(row_pointers);
    row_pointers = NULL;

    png_read_end(png_ptr, NULL);
	/* clean up after the read, and free any memory allocated - REQUIRED */
	png_destroy_read_struct(&png_ptr, &info_ptr, NULL);

	fclose (fp);

    return (EIF_POINTER) image_data;
}
Example #20
0
void PNGImageDecoder::headerAvailable()
{
    png_structp png = m_reader->pngPtr();
    png_infop info = m_reader->infoPtr();
    png_uint_32 width = png_get_image_width(png, info);
    png_uint_32 height = png_get_image_height(png, info);

    // Protect against large images.
    if (width > cMaxPNGSize || height > cMaxPNGSize) {
        longjmp(JMPBUF(png), 1);
        return;
    }

    // We can fill in the size now that the header is available.  Avoid memory
    // corruption issues by neutering setFailed() during this call; if we don't
    // do this, failures will cause |m_reader| to be deleted, and our jmpbuf
    // will cease to exist.  Note that we'll still properly set the failure flag
    // in this case as soon as we longjmp().
    m_doNothingOnFailure = true;
    bool result = setSize(width, height);
#if ENABLE(IMAGE_DECODER_DOWN_SAMPLING)
    prepareScaleDataIfNecessary(FloatSize(requestedFrameSize().width(), requestedFrameSize().height()));
#endif
    m_doNothingOnFailure = false;
    if (!result) {
        longjmp(JMPBUF(png), 1);
        return;
    }

    int bitDepth, colorType, interlaceType, compressionType, filterType, channels;
    png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, &interlaceType, &compressionType, &filterType);

    // The options we set here match what Mozilla does.

    // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA.
    if (colorType == PNG_COLOR_TYPE_PALETTE || (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8))
        png_set_expand(png);

    png_bytep trns = 0;
    int trnsCount = 0;
    if (png_get_valid(png, info, PNG_INFO_tRNS)) {
        png_get_tRNS(png, info, &trns, &trnsCount, 0);
        png_set_expand(png);
    }

    if (bitDepth == 16)
        png_set_strip_16(png);

    if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA)
        png_set_gray_to_rgb(png);

    if ((colorType & PNG_COLOR_MASK_COLOR) && !m_ignoreGammaAndColorProfile) {
        // We only support color profiles for color PALETTE and RGB[A] PNG. Supporting
        // color profiles for gray-scale images is slightly tricky, at least using the
        // CoreGraphics ICC library, because we expand gray-scale images to RGB but we
        // do not similarly transform the color profile. We'd either need to transform
        // the color profile or we'd need to decode into a gray-scale image buffer and
        // hand that to CoreGraphics.
        readColorProfile(png, info, m_colorProfile);
#if USE(QCMSLIB)
        bool decodedImageHasAlpha = (colorType & PNG_COLOR_MASK_ALPHA) || trnsCount;
        m_reader->createColorTransform(m_colorProfile, decodedImageHasAlpha);
        m_colorProfile.clear();
#endif
    }

    // Deal with gamma and keep it under our control.
    double gamma;
    if (!m_ignoreGammaAndColorProfile && png_get_gAMA(png, info, &gamma)) {
        if ((gamma <= 0.0) || (gamma > cMaxGamma)) {
            gamma = cInverseGamma;
            png_set_gAMA(png, info, gamma);
        }
        png_set_gamma(png, cDefaultGamma, gamma);
    } else
        png_set_gamma(png, cDefaultGamma, cInverseGamma);

    // Tell libpng to send us rows for interlaced pngs.
    if (interlaceType == PNG_INTERLACE_ADAM7)
        png_set_interlace_handling(png);

    // Update our info now.
    png_read_update_info(png, info);
    channels = png_get_channels(png, info);
    ASSERT(channels == 3 || channels == 4);

    m_reader->setHasAlpha(channels == 4);

    if (m_reader->decodingSizeOnly()) {
        // If we only needed the size, halt the reader.
#if defined(PNG_LIBPNG_VER_MAJOR) && defined(PNG_LIBPNG_VER_MINOR) && (PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5))
        // '0' argument to png_process_data_pause means: Do not cache unprocessed data.
        m_reader->setReadOffset(m_reader->currentBufferSize() - png_process_data_pause(png, 0));
#else
        m_reader->setReadOffset(m_reader->currentBufferSize() - png->buffer_size);
        png->buffer_size = 0;
#endif
    }
}
    void png_reader::read(unsigned x0, unsigned y0,image_data_32& image) 
    {
        FILE *fp=fopen(fileName_.c_str(),"rb");
        if (!fp) throw image_reader_exception("cannot open image file "+fileName_);

        png_structp png_ptr = png_create_read_struct
            (PNG_LIBPNG_VER_STRING,0,0,0);

        if (!png_ptr) 
        {
            fclose(fp);
            throw image_reader_exception("failed to allocate png_ptr");
        }

        png_infop info_ptr = png_create_info_struct(png_ptr);
        if (!info_ptr)
        {
            png_destroy_read_struct(&png_ptr,0,0);
            fclose(fp);
            throw image_reader_exception("failed to create info_ptr");
        }

        png_set_read_fn(png_ptr, (png_voidp)fp, png_read_data);
        png_read_info(png_ptr, info_ptr);

        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_set_expand(png_ptr);
        if (bit_depth_ == 16)
            png_set_strip_16(png_ptr);
        if (color_type_ == PNG_COLOR_TYPE_GRAY ||
            color_type_ == PNG_COLOR_TYPE_GRAY_ALPHA)
            png_set_gray_to_rgb(png_ptr);

        // quick hack -- only work in >=libpng 1.2.7
        png_set_add_alpha(png_ptr,0xff,PNG_FILLER_AFTER); //rgba
        
        double gamma;
        if (png_get_gAMA(png_ptr, info_ptr, &gamma))
            png_set_gamma(png_ptr, 2.2, gamma);

        png_read_update_info(png_ptr, info_ptr);

        //START read image rows
        unsigned w=std::min(unsigned(image.width()),width_);
        unsigned h=std::min(unsigned(image.height()),height_);
        unsigned rowbytes=png_get_rowbytes(png_ptr, info_ptr);
        boost::scoped_array<png_byte> row(new png_byte[rowbytes]);
	for (unsigned i=0;i<height_;++i)
        {
            png_read_row(png_ptr,row.get(),0);
            if (i>=y0 && i<h) 
            {
                image.setRow(i-y0,reinterpret_cast<unsigned*>(&row[x0]),w);
            } 
        }
        //END
        png_read_end(png_ptr,0);
        png_destroy_read_struct(&png_ptr, &info_ptr,0);
        fclose(fp);
    }
Example #22
0
//--------------------------------------
static bool sReadPNG(Stream &stream, GBitmap *bitmap)
{
   static const U32 cs_headerBytesChecked = 8;

   U8 header[cs_headerBytesChecked];
   stream.read(cs_headerBytesChecked, header);

   bool isPng = png_check_sig(header, cs_headerBytesChecked) != 0;
   if (isPng == false) 
   {
      AssertWarn(false, "GBitmap::readPNG: stream doesn't contain a PNG");
      return false;
   }

   U32 prevWaterMark = FrameAllocator::getWaterMark();
   png_structp png_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING,
      NULL,
      pngFatalErrorFn,
      pngWarningFn,
      NULL,
      pngRealMallocFn,
      pngRealFreeFn);

   if (png_ptr == NULL) 
   {
      FrameAllocator::setWaterMark(prevWaterMark);
      return false;
   }

   png_infop info_ptr = png_create_info_struct(png_ptr);
   if (info_ptr == NULL) 
   {
      png_destroy_read_struct(&png_ptr,
         (png_infopp)NULL,
         (png_infopp)NULL);

      FrameAllocator::setWaterMark(prevWaterMark);
      return false;
   }

   png_infop end_info = png_create_info_struct(png_ptr);
   if (end_info == NULL) 
   {
      png_destroy_read_struct(&png_ptr,
         &info_ptr,
         (png_infopp)NULL);

      FrameAllocator::setWaterMark(prevWaterMark);
      return false;
   }

   png_set_read_fn(png_ptr, &stream, pngReadDataFn);

   // Read off the info on the image.
   png_set_sig_bytes(png_ptr, cs_headerBytesChecked);
   png_read_info(png_ptr, info_ptr);

   // OK, at this point, if we have reached it ok, then we can reset the
   //  image to accept the new data...
   //
   bitmap->deleteImage();

   png_uint_32 width;
   png_uint_32 height;
   S32 bit_depth;
   S32 color_type;

   png_get_IHDR(png_ptr, info_ptr,
      &width, &height,             // obv.
      &bit_depth, &color_type,     // obv.
      NULL,                        // interlace
      NULL,                        // compression_type
      NULL);                       // filter_type

   // First, handle the color transformations.  We need this to read in the
   //  data as RGB or RGBA, _always_, with a maximal channel width of 8 bits.
   //
   bool transAlpha     = false;
   GFXFormat format = GFXFormatR8G8B8;

   // Strip off any 16 bit info
   //
   if (bit_depth == 16 && color_type != PNG_COLOR_TYPE_GRAY) 
   {
      png_set_strip_16(png_ptr);
   }

   // Expand a transparency channel into a full alpha channel...
   //
   if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) 
   {
      png_set_expand(png_ptr);
      transAlpha = true;
   }

   if (color_type == PNG_COLOR_TYPE_PALETTE) 
   {
      png_set_expand(png_ptr);
      format = transAlpha ? GFXFormatR8G8B8A8 : GFXFormatR8G8B8;
   }
   else if (color_type == PNG_COLOR_TYPE_GRAY) 
   {
      png_set_expand(png_ptr);

      if (bit_depth == 16)
         format = GFXFormatR5G6B5;
      else
         format = GFXFormatA8;
   }
   else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) 
   {
      png_set_expand(png_ptr);
      png_set_gray_to_rgb(png_ptr);
      format = GFXFormatR8G8B8A8;
   }
   else if (color_type == PNG_COLOR_TYPE_RGB) 
   {
      format = transAlpha ? GFXFormatR8G8B8A8 : GFXFormatR8G8B8;
      png_set_expand(png_ptr);
   }
   else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) 
   {
      png_set_expand(png_ptr);
      format = GFXFormatR8G8B8A8;
   }

   // Update the info pointer with the result of the transformations
   //  above...
   png_read_update_info(png_ptr, info_ptr);

   const png_size_t rowBytes = png_get_rowbytes(png_ptr, info_ptr);
   if (format == GFXFormatR8G8B8) 
   {
      AssertFatal(rowBytes == width * 3,
         "Error, our rowbytes are incorrect for this transform... (3)");
   }
   else if (format == GFXFormatR8G8B8A8) 
   {
      AssertFatal(rowBytes == width * 4,
         "Error, our rowbytes are incorrect for this transform... (4)");
   }
   else if (format == GFXFormatR5G6B5) 
   {
      AssertFatal(rowBytes == width * 2,
         "Error, our rowbytes are incorrect for this transform... (2)");
   }

   // actually allocate the bitmap space...
   bitmap->allocateBitmap(width, height,
      false,            // don't extrude miplevels...
      format);          // use determined format...

   // Set up the row pointers...
   png_bytep* rowPointers = new png_bytep[ height ];
   U8* pBase = (U8*)bitmap->getBits();
   
   for (U32 i = 0; i < height; i++)
      rowPointers[i] = pBase + (i * rowBytes);

   // And actually read the image!
   png_read_image(png_ptr, rowPointers);

   // We're outta here, destroy the png structs, and release the lock
   //  as quickly as possible...
   //png_read_end(png_ptr, end_info);
   delete [] rowPointers;
   png_read_end(png_ptr, NULL);
   png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);

   // Ok, the image is read in, now we need to finish up the initialization,
   //  which means: setting up the detailing members, init'ing the palette
   //  key, etc...
   //
   // actually, all of that was handled by allocateBitmap, so we're outta here
   //

   // Check this bitmap for transparency
   bitmap->checkForTransparency();

   FrameAllocator::setWaterMark(prevWaterMark);

   return true;
}
Example #23
0
static void LoadPNGBuffer(byte * buffer, int size, byte ** pixels, int *width, int *height)
{
	png_struct     *png;
	png_info       *info, *end;
	pngBuffer_t     pb;
	int             i, bitDepth, colorType, channels;
	png_uint_32     w, h;
	byte          **rowPointers;


	/* dummy check */
	if(buffer == NULL || size <= 0 || pixels == NULL || width == NULL || height == NULL)
		return;

	/* null out */
	*pixels = 0;
	*width = 0;
	*height = 0;

	/* determine if this is a png file */
	if(png_sig_cmp(buffer, 0, 8) != 0)
	{
		Sys_Printf("WARNING: Invalid PNG file\n");
		return;
	}

	/* create png structs */
	png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	if(png == NULL)
	{
		Sys_Printf("WARNING: Unable to create PNG read struct\n");
		return;
	}

	info = png_create_info_struct(png);
	if(info == NULL)
	{
		Sys_Printf("WARNING: Unable to create PNG info struct\n");
		png_destroy_read_struct(&png, NULL, NULL);
		return;
	}

	end = png_create_info_struct(png);
	if(end == NULL)
	{
		Sys_Printf("WARNING: Unable to create PNG end info struct\n");
		png_destroy_read_struct(&png, &info, NULL);
		return;
	}

	/* set read callback */
	pb.buffer = buffer;
	pb.size = size;
	pb.offset = 0;
	png_set_read_fn(png, &pb, PNGReadData);
	png->io_ptr = &pb;			/* hack! */

	/* set error longjmp */
	if(setjmp(png->jmpbuf))
	{
		Sys_Printf("WARNING: An error occurred reading PNG image\n");
		png_destroy_read_struct(&png, &info, &end);
		return;
	}

	/* fixme: add proper i/o stuff here */

	/* read png info */
	png_read_info(png, info);

	/* read image header chunk */
	png_get_IHDR(png, info, &w, &h, &bitDepth, &colorType, NULL, NULL, NULL);

	/* read number of channels */
	channels = png_get_channels(png, info);

	/* the following will probably bork on certain types of png images, but hey... */

	/* force indexed/gray/trans chunk to rgb */
	if((colorType == PNG_COLOR_TYPE_PALETTE && bitDepth <= 8) ||
	   (colorType == PNG_COLOR_TYPE_GRAY && bitDepth <= 8) || png_get_valid(png, info, PNG_INFO_tRNS))
		png_set_expand(png);

	/* strip 16bpc -> 8bpc */
	if(bitDepth == 16)
		png_set_strip_16(png);

	/* pad rgb to rgba */
	if(bitDepth == 8 && colorType == PNG_COLOR_TYPE_RGB)
		png_set_filler(png, 255, PNG_FILLER_AFTER);

	/* create image pixel buffer */
	*width = w;
	*height = h;
	*pixels = safe_malloc(w * h * 4);

	/* create row pointers */
	rowPointers = safe_malloc(h * sizeof(byte *));
	for(i = 0; i < h; i++)
		rowPointers[i] = *pixels + (i * w * 4);

	/* read the png */
	png_read_image(png, rowPointers);

	/* clean up */
	free(rowPointers);
	png_destroy_read_struct(&png, &info, &end);

}
Example #24
0
//-----------------------------------------------------------------------------
bool abiword_document::garble_png( void*& data, size_t& size ) {

	png_bytep * dib;
	png_uint_32 width;
	png_uint_32 height;
	int compression_type;
	int filter_type;
	int interlace_type;
	int bit_depth;
	int color_type;
	png_uint_32 rowbytes;

	// read PNG data
	{
		png_structp png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, (void*) NULL, NULL, NULL );
		if (!png_ptr)
			return false;
		png_infop info_ptr = png_create_info_struct(png_ptr);
		if (!info_ptr) {
			png_destroy_read_struct( &png_ptr, (png_infopp)NULL, (png_infopp)NULL );
			return false;
		}
		png_read_data _png_read_data = { data, size, 0 };
		png_set_read_fn( png_ptr, (void*)&_png_read_data, &_png_read );
		png_read_info( png_ptr, info_ptr );
		png_get_IHDR( png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_type );
		png_set_packing( png_ptr );
		png_set_expand( png_ptr );
		png_set_strip_16( png_ptr );
		png_set_gray_to_rgb( png_ptr );
		png_set_strip_alpha( png_ptr );
		png_set_interlace_handling( png_ptr );
		png_set_bgr( png_ptr );
		rowbytes = png_get_rowbytes(png_ptr, info_ptr);
		png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
	}

	// we don't care about the image data itself, we just want a random garbled
	// image of the same size
	dib = (png_bytep*) malloc( sizeof(png_bytep) * height );
	for (size_t i=0; i<height; ++i) {
		dib[i] = (png_byte*) malloc( rowbytes );
		garble_image_line( reinterpret_cast<char*>( dib[i] ), rowbytes );
	}

	bool result = false;
	{
		// write it back
		png_structp png_ptrw = png_create_write_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL );
		if (png_ptrw)
		{
			png_infop info_ptrw = png_create_info_struct( png_ptrw );
			png_set_IHDR( png_ptrw, info_ptrw, width, height, bit_depth, color_type, interlace_type, compression_type, filter_type );
			string newdata;
			png_set_write_fn( png_ptrw, (void*)&newdata, &_png_write, NULL );
			png_write_info( png_ptrw, info_ptrw );
			png_write_image( png_ptrw, dib );
			png_write_end( png_ptrw, NULL );
			png_destroy_write_struct( &png_ptrw, NULL );

			free(data);
			size = newdata.size();
			data = malloc( size );
			memcpy( data, &newdata[0], size );
			result = true;
		}
	}

	// cleanup
	for (size_t i=0; i<height; i++)
		free( dib[i] );
	free( dib );
	return result;
}
Example #25
0
/* really_load_png:
 *  Worker routine, used by load_png and load_memory_png.
 */
static BITMAP *really_load_png(png_structp png_ptr, png_infop info_ptr, RGB *pal)
{
    BITMAP *bmp;
    PALETTE tmppal;
    png_uint_32 width, height, rowbytes;
    int bit_depth, color_type, interlace_type;
    double image_gamma, screen_gamma;
    int intent;
    int bpp, dest_bpp;
    int tRNS_to_alpha = FALSE;
    int number_passes, pass;

    ASSERT(png_ptr && info_ptr);

    /* 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_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
                 &interlace_type, NULL, NULL);

    /* 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);

    /* 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(png_ptr);

    /* Adds a full alpha channel if there is transparency information
     * in a tRNS chunk. */
    if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
        png_set_tRNS_to_alpha(png_ptr);
        tRNS_to_alpha = TRUE;
    }

    /* Convert 16-bits per colour component to 8-bits per colour component. */
    if (bit_depth == 16)
        png_set_strip_16(png_ptr);

    /* Convert grayscale to RGB triplets */
    if ((color_type == PNG_COLOR_TYPE_GRAY) ||
        (color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
        png_set_gray_to_rgb(png_ptr);

    /* Optionally, tell libpng to handle the gamma correction for us. */
    if (_png_screen_gamma != 0.0) {
        screen_gamma = get_gamma();

        if (png_get_sRGB(png_ptr, info_ptr, &intent))
            png_set_gamma(png_ptr, screen_gamma, 0.45455);
        else {
            if (png_get_gAMA(png_ptr, info_ptr, &image_gamma))
                png_set_gamma(png_ptr, screen_gamma, image_gamma);
            else
                png_set_gamma(png_ptr, screen_gamma, 0.45455);
        }
    }

    /* Turn on interlace handling. */
    number_passes = png_set_interlace_handling(png_ptr);

    /* Call to gamma correct and add the background to the palette
     * and update info structure.
     */
    png_read_update_info(png_ptr, info_ptr);

    /* Even if the user doesn't supply space for a palette, we want
     * one for the load process.
     */
    if (!pal)
        pal = tmppal;

    /* Palettes. */
    if (color_type & PNG_COLOR_MASK_PALETTE) {
        int num_palette, i;
        png_colorp palette;

        if (png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)) {
            /* We don't actually dither, we just copy the palette. */
            for (i = 0; ((i < num_palette) && (i < 256)); i++) {
                pal[i].r = palette[i].red >> 2;         /* 256 -> 64 */
                pal[i].g = palette[i].green >> 2;
                pal[i].b = palette[i].blue >> 2;
            }

            for (; i < 256; i++)
                pal[i].r = pal[i].g = pal[i].b = 0;
        }
    }
Example #26
0
static int loadpng(const char *path, img_t *image)
{
	png_structp png_ptr;
	png_infop info_ptr;
	FILE *fp;
	uint8_t *buf, header[8];
	int w, h, cp, i, bit_depth;
	png_byte color_type;

	lprintf("loadpng %s\n", path);
	fp = fopen(path, "rb");
	if(!fp)
	{
		lprintf("Failed to open file %s\n", path);
		return -1;
	}
	if(fread(header, 8, 1, fp) != 1)
	{
		lprintf("Error reading header\n");
		fclose(fp);
		return -1;
	}
	if(png_sig_cmp(header, 0, 8))
	{
		lprintf("Not a png file\n");
		fclose(fp);
		return -1;
	}
	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	info_ptr = png_create_info_struct(png_ptr);
	if(setjmp(png_jmpbuf(png_ptr)))
	{
		lprintf("Error reading png file header\n");
		fclose(fp);
		return -1;
	}
	png_init_io(png_ptr, fp);
	png_set_sig_bytes(png_ptr, 8);
	png_read_info(png_ptr, info_ptr);
	w = png_get_image_width(png_ptr, info_ptr);
	h = 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);
	png_read_update_info(png_ptr, info_ptr);
	switch(color_type)
	{
	case PNG_COLOR_TYPE_RGBA:
		cp = 3;
		png_set_strip_alpha(png_ptr);
		break;
	case PNG_COLOR_TYPE_RGB:
		cp = 3;
		break;
	case PNG_COLOR_TYPE_GRAY:
		if(bit_depth < 8)
			png_set_expand_gray_1_2_4_to_8(png_ptr);
		cp = 1;
		break;
	case PNG_COLOR_TYPE_GA:
		cp = 1;
		png_set_strip_alpha(png_ptr);
		break;	
	case PNG_COLOR_TYPE_PALETTE:
		cp = 3;
		png_set_expand(png_ptr);
		break;
	default:
		cp = 0;
	}
	if(cp != 3 && cp != 1)
	{
		fclose(fp);
		lprintf("Unsupported number of color planes %d\n", cp);
		return -1;
	}
	if(bit_depth == 16)
		png_set_strip_16(png_ptr);
	buf = (uint8_t *)malloc(w * h * cp);
	png_bytep *rows = (png_bytep *)malloc(sizeof(png_bytep) * h);
	if(setjmp(png_jmpbuf(png_ptr)))
	{
		lprintf("Error reading png file\n");
		fclose(fp);
		free(buf);
		free(rows);
		return -1;
	}
	for(i = 0; i < h; i++)
		rows[i] = buf + i * cp * w;
	png_read_image(png_ptr, rows);
	png_read_end(png_ptr, NULL);
	free(rows);
	fclose(fp);
	png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
	lprintf("PNG (%d,%d,%d) loaded\n", w, h, cp);
	image->bitmap = buf;
	image->width = w;
	image->height = h;
	image->cp = cp;
	return 0;
}
Example #27
0
static
void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, float screen_gamma=0.0)
{
    if (screen_gamma != 0.0 && png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) {
        double file_gamma;
        png_get_gAMA(png_ptr, info_ptr, &file_gamma);
        png_set_gamma(png_ptr, screen_gamma, file_gamma);
    }

    png_uint_32 width;
    png_uint_32 height;
    int bit_depth;
    int color_type;
    png_bytep trans_alpha = 0;
    png_color_16p trans_color_p = 0;
    int num_trans;
    png_colorp palette = 0;
    int num_palette;
    png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);
    png_set_interlace_handling(png_ptr);

    if (color_type == PNG_COLOR_TYPE_GRAY) {
        // Black & White or 8-bit grayscale
        if (bit_depth == 1 && png_get_channels(png_ptr, info_ptr) == 1) {
            png_set_invert_mono(png_ptr);
            png_read_update_info(png_ptr, info_ptr);
            if (image.size() != QSize(width, height) || image.format() != QImage::Format_Mono) {
                image = QImage(width, height, QImage::Format_Mono);
                if (image.isNull())
                    return;
            }
            image.setColorCount(2);
            image.setColor(1, qRgb(0,0,0));
            image.setColor(0, qRgb(255,255,255));
        } else if (bit_depth == 16 && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
            png_set_expand(png_ptr);
            png_set_strip_16(png_ptr);
            png_set_gray_to_rgb(png_ptr);
            if (image.size() != QSize(width, height) || image.format() != QImage::Format_ARGB32) {
                image = QImage(width, height, QImage::Format_ARGB32);
                if (image.isNull())
                    return;
            }
            if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
                png_set_swap_alpha(png_ptr);

            png_read_update_info(png_ptr, info_ptr);
        } else {
            if (bit_depth == 16)
                png_set_strip_16(png_ptr);
            else if (bit_depth < 8)
                png_set_packing(png_ptr);
            int ncols = bit_depth < 8 ? 1 << bit_depth : 256;
            png_read_update_info(png_ptr, info_ptr);
            if (image.size() != QSize(width, height) || image.format() != QImage::Format_Indexed8) {
                image = QImage(width, height, QImage::Format_Indexed8);
                if (image.isNull())
                    return;
            }
            image.setColorCount(ncols);
            for (int i=0; i<ncols; i++) {
                int c = i*255/(ncols-1);
                image.setColor(i, qRgba(c,c,c,0xff));
            }
            if (png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color_p) && trans_color_p) {
                const int g = trans_color_p->gray;
                if (g < ncols) {
                    image.setColor(g, 0);
                }
            }
        }
    } else if (color_type == PNG_COLOR_TYPE_PALETTE
               && png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)
               && num_palette <= 256)
    {
        // 1-bit and 8-bit color
        if (bit_depth != 1)
            png_set_packing(png_ptr);
        png_read_update_info(png_ptr, info_ptr);
        png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);
        QImage::Format format = bit_depth == 1 ? QImage::Format_Mono : QImage::Format_Indexed8;
        if (image.size() != QSize(width, height) || image.format() != format) {
            image = QImage(width, height, format);
            if (image.isNull())
                return;
        }
        png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
        image.setColorCount(num_palette);
        int i = 0;
        if (png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color_p) && trans_alpha) {
            while (i < num_trans) {
                image.setColor(i, qRgba(
                    palette[i].red,
                    palette[i].green,
                    palette[i].blue,
                    trans_alpha[i]
                   )
               );
                i++;
            }
        }
        while (i < num_palette) {
            image.setColor(i, qRgba(
                palette[i].red,
                palette[i].green,
                palette[i].blue,
                0xff
               )
           );
            i++;
        }
    } else {
        // 32-bit
        if (bit_depth == 16)
            png_set_strip_16(png_ptr);

        png_set_expand(png_ptr);

        if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
            png_set_gray_to_rgb(png_ptr);

        QImage::Format format = QImage::Format_ARGB32;
        // Only add filler if no alpha, or we can get 5 channel data.
        if (!(color_type & PNG_COLOR_MASK_ALPHA)
            && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
            png_set_filler(png_ptr, 0xff, QSysInfo::ByteOrder == QSysInfo::BigEndian ?
                           PNG_FILLER_BEFORE : PNG_FILLER_AFTER);
            // We want 4 bytes, but it isn't an alpha channel
            format = QImage::Format_RGB32;
        }
        if (image.size() != QSize(width, height) || image.format() != format) {
            image = QImage(width, height, format);
            if (image.isNull())
                return;
        }

        if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
            png_set_swap_alpha(png_ptr);

        png_read_update_info(png_ptr, info_ptr);
    }

    // Qt==ARGB==Big(ARGB)==Little(BGRA)
    if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
        png_set_bgr(png_ptr);
    }
}
Example #28
0
int					/* O - Read status */
_cupsImageReadPNG(
    cups_image_t    *img,		/* IO - cupsImage */
    FILE            *fp,		/* I - cupsImage file */
    cups_icspace_t  primary,		/* I - Primary choice for colorspace */
    cups_icspace_t  secondary,		/* I - Secondary choice for colorspace */
    int             saturation,		/* I - Color saturation (%) */
    int             hue,		/* I - Color hue (degrees) */
    const cups_ib_t *lut)		/* I - Lookup table for gamma/brightness */
{
  int		y;			/* Looping var */
  png_structp	pp;			/* PNG read pointer */
  png_infop	info;			/* PNG info pointers */
  png_uint_32	width,			/* Width of image */
		height;			/* Height of image */
  int		bit_depth,		/* Bit depth */
		color_type,		/* Color type */
		interlace_type,		/* Interlace type */
		compression_type,	/* Compression type */
		filter_type;		/* Filter type */
  png_uint_32	xppm,			/* X pixels per meter */
		yppm;			/* Y pixels per meter */
  int		bpp;			/* Bytes per pixel */
  int		pass,			/* Current pass */
		passes;			/* Number of passes required */
  cups_ib_t	*in,			/* Input pixels */
		*inptr,			/* Pointer into pixels */
		*out;			/* Output pixels */
  png_color_16	bg;			/* Background color */


 /*
  * Setup the PNG data structures...
  */

  pp   = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  info = png_create_info_struct(pp);

 /*
  * Initialize the PNG read "engine"...
  */

  png_init_io(pp, fp);

 /*
  * Get the image dimensions and load the output image...
  */

  png_read_info(pp, info);

  png_get_IHDR(pp, info, &width, &height, &bit_depth, &color_type,
               &interlace_type, &compression_type, &filter_type);

  fprintf(stderr, "DEBUG: PNG image: %dx%dx%d, color_type=%x (%s%s%s)\n",
          (int)width, (int)height, bit_depth, color_type,
	  (color_type & PNG_COLOR_MASK_COLOR) ? "RGB" : "GRAYSCALE",
	  (color_type & PNG_COLOR_MASK_ALPHA) ? "+ALPHA" : "",
	  (color_type & PNG_COLOR_MASK_PALETTE) ? "+PALETTE" : "");

  if (color_type & PNG_COLOR_MASK_PALETTE)
    png_set_expand(pp);
  else if (bit_depth < 8)
  {
    png_set_packing(pp);
    png_set_expand(pp);
  }
  else if (bit_depth == 16)
    png_set_strip_16(pp);

  if (color_type & PNG_COLOR_MASK_COLOR)
    img->colorspace = (primary == CUPS_IMAGE_RGB_CMYK) ? CUPS_IMAGE_RGB :
                                                         primary;
  else
    img->colorspace = secondary;

  if (width == 0 || width > CUPS_IMAGE_MAX_WIDTH ||
      height == 0 || height > CUPS_IMAGE_MAX_HEIGHT)
  {
    fprintf(stderr, "DEBUG: PNG image has invalid dimensions %ux%u!\n",
            (unsigned)width, (unsigned)height);
    fclose(fp);
    return (1);
  }

  img->xsize = width;
  img->ysize = height;

  if ((xppm = png_get_x_pixels_per_meter(pp, info)) != 0 &&
      (yppm = png_get_y_pixels_per_meter(pp, info)) != 0)
  {
    img->xppi = (int)((float)xppm * 0.0254);
    img->yppi = (int)((float)yppm * 0.0254);

    if (img->xppi == 0 || img->yppi == 0)
    {
      fprintf(stderr, "DEBUG: PNG image has invalid resolution %dx%d PPI\n",
              img->xppi, img->yppi);

      img->xppi = img->yppi = 128;
    }
  }

  cupsImageSetMaxTiles(img, 0);

  passes = png_set_interlace_handling(pp);

 /*
  * Handle transparency...
  */

  if (png_get_valid(pp, info, PNG_INFO_tRNS))
    png_set_tRNS_to_alpha(pp);

  bg.red   = 65535;
  bg.green = 65535;
  bg.blue  = 65535;

  png_set_background(pp, &bg, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);

  if (passes == 1)
  {
   /*
    * Load one row at a time...
    */

    if (color_type == PNG_COLOR_TYPE_GRAY ||
	color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
      in = malloc(img->xsize);
    else
      in = malloc(img->xsize * 3);
  }
  else
  {
   /*
    * Interlaced images must be loaded all at once...
    */

    size_t bufsize;			/* Size of buffer */


    if (color_type == PNG_COLOR_TYPE_GRAY ||
	color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
    {
      bufsize = img->xsize * img->ysize;

      if ((bufsize / img->xsize) != img->ysize)
      {
	fprintf(stderr, "DEBUG: PNG image dimensions (%ux%u) too large!\n",
		(unsigned)width, (unsigned)height);
	fclose(fp);
	return (1);
      }
    }
    else
    {
      bufsize = img->xsize * img->ysize * 3;

      if ((bufsize / (img->xsize * 3)) != img->ysize)
      {
	fprintf(stderr, "DEBUG: PNG image dimensions (%ux%u) too large!\n",
		(unsigned)width, (unsigned)height);
	fclose(fp);
	return (1);
      }
    }

    in = malloc(bufsize);
  }

  bpp = cupsImageGetDepth(img);
  out = malloc(img->xsize * bpp);

  if (!in || !out)
  {
    fputs("DEBUG: Unable to allocate memory for PNG image!\n", stderr);

    if (in)
      free(in);

    if (out)
      free(out);

    fclose(fp);

    return (1);
  }

 /*
  * Read the image, interlacing as needed...
  */

  for (pass = 1; pass <= passes; pass ++)
    for (inptr = in, y = 0; y < img->ysize; y ++)
    {
      png_read_row(pp, (png_bytep)inptr, NULL);

      if (pass == passes)
      {
       /*
        * Output this row...
	*/

	if (color_type & PNG_COLOR_MASK_COLOR)
	{
	  if ((saturation != 100 || hue != 0) && bpp > 1)
	    cupsImageRGBAdjust(inptr, img->xsize, saturation, hue);

	  switch (img->colorspace)
	  {
	    case CUPS_IMAGE_WHITE :
		cupsImageRGBToWhite(inptr, out, img->xsize);
		break;
	    case CUPS_IMAGE_RGB :
	    case CUPS_IMAGE_RGB_CMYK :
		cupsImageRGBToRGB(inptr, out, img->xsize);
		break;
	    case CUPS_IMAGE_BLACK :
		cupsImageRGBToBlack(inptr, out, img->xsize);
		break;
	    case CUPS_IMAGE_CMY :
		cupsImageRGBToCMY(inptr, out, img->xsize);
		break;
	    case CUPS_IMAGE_CMYK :
		cupsImageRGBToCMYK(inptr, out, img->xsize);
		break;
	  }
	}
	else
	{
	  switch (img->colorspace)
	  {
	    case CUPS_IMAGE_WHITE :
		memcpy(out, inptr, img->xsize);
		break;
	    case CUPS_IMAGE_RGB :
	    case CUPS_IMAGE_RGB_CMYK :
		cupsImageWhiteToRGB(inptr, out, img->xsize);
		break;
	    case CUPS_IMAGE_BLACK :
		cupsImageWhiteToBlack(inptr, out, img->xsize);
		break;
	    case CUPS_IMAGE_CMY :
		cupsImageWhiteToCMY(inptr, out, img->xsize);
		break;
	    case CUPS_IMAGE_CMYK :
		cupsImageWhiteToCMYK(inptr, out, img->xsize);
		break;
	  }
	}

	if (lut)
	  cupsImageLut(out, img->xsize * bpp, lut);

	_cupsImagePutRow(img, 0, y, img->xsize, out);
      }

      if (passes > 1)
      {
	if (color_type & PNG_COLOR_MASK_COLOR)
          inptr += img->xsize * 3;
	else
          inptr += img->xsize;
      }
    }

  png_read_end(pp, info);
  png_destroy_read_struct(&pp, &info, NULL);

  fclose(fp);
  free(in);
  free(out);

  return (0);
}
PremultipliedImage decodePNG(const uint8_t* data, size_t size) {
    util::CharArrayBuffer dataBuffer { reinterpret_cast<const char*>(data), size };
    std::istream stream(&dataBuffer);

    png_byte header[8] = { 0 };
    stream.read(reinterpret_cast<char*>(header), 8);
    if (stream.gcount() != 8)
        throw std::runtime_error("PNG reader: Could not read image");

    int is_png = !png_sig_cmp(header, 0, 8);
    if (!is_png)
        throw std::runtime_error("File or stream is not a png");

    png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
    if (!png_ptr)
        throw std::runtime_error("failed to allocate png_ptr");

    // catch errors in a custom way to avoid the need for setjmp
    png_set_error_fn(png_ptr, png_get_error_ptr(png_ptr), user_error_fn, user_warning_fn);

    png_infop info_ptr;
    png_struct_guard sguard(&png_ptr, &info_ptr);
    info_ptr = png_create_info_struct(png_ptr);
    if (!info_ptr)
        throw std::runtime_error("failed to create info_ptr");

    png_set_read_fn(png_ptr, &stream, png_read_data);
    png_set_sig_bytes(png_ptr, 8);
    png_read_info(png_ptr, info_ptr);

    png_uint_32 width = 0;
    png_uint_32 height = 0;
    int bit_depth = 0;
    int color_type = 0;
    png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, nullptr, nullptr, nullptr);

    UnassociatedImage image { static_cast<uint16_t>(width), static_cast<uint16_t>(height) };

    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_set_expand(png_ptr);

    if (bit_depth == 16)
        png_set_strip_16(png_ptr);

    if (color_type == PNG_COLOR_TYPE_GRAY ||
        color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
        png_set_gray_to_rgb(png_ptr);

    png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER);

    if (png_get_interlace_type(png_ptr,info_ptr) == PNG_INTERLACE_ADAM7) {
        png_set_interlace_handling(png_ptr); // FIXME: libpng bug?
        // according to docs png_read_image
        // "..automatically handles interlacing,
        // so you don't need to call png_set_interlace_handling()"
    }

    png_read_update_info(png_ptr, info_ptr);

    // we can read whole image at once
    // alloc row pointers
    const std::unique_ptr<png_bytep[]> rows(new png_bytep[height]);
    for (unsigned row = 0; row < height; ++row)
        rows[row] = image.data.get() + row * width * 4;
    png_read_image(png_ptr, rows.get());

    png_read_end(png_ptr, nullptr);

    return util::premultiply(std::move(image));
}
//==============================================================================
static bool _decompressPNG(png_structp a_png_ptr, png_infop a_info_ptr, cImage* a_image)
{
    int j;
    int bpp;

    // read in the header
    png_read_info(a_png_ptr, a_info_ptr);
    int width  = png_get_image_width (a_png_ptr, a_info_ptr);
    int height = png_get_image_height(a_png_ptr, a_info_ptr);

    // get image info
    png_uint_32 tmpw, tmph;
    int bit_depth, color_type, interlace_type;
    png_get_IHDR(a_png_ptr, a_info_ptr, &tmpw, &tmph, &bit_depth, &color_type, &interlace_type, NULL, NULL);

    // strip 16 bit/color files down to 8 bits/color
    png_set_strip_16(a_png_ptr);

    // extract multiple pixels with bit depths of 1, 2, and 4 into separate bytes
    png_set_packing(a_png_ptr);

    // expand palette to RGB, grayscale to 8 bits, transparency to alpha
    png_set_expand(a_png_ptr);

    // set the bytes per pixel count
    if (color_type & PNG_COLOR_MASK_COLOR)
        bpp = 3;
    else
        bpp = 1;

    // if there is an alpha channel or some transparency, act accordingly
    if (color_type & PNG_COLOR_MASK_ALPHA || png_get_valid(a_png_ptr, a_info_ptr, PNG_INFO_tRNS))
        bpp += 1;

    // allocate row buffers
    size_t    rowsize = bpp*width;
    png_bytep *row_pointers = new png_bytep[height];
    for (j=0; j<height; j++)
        row_pointers[j] = (png_bytep)png_malloc(a_png_ptr, rowsize);

    // read the entire image in one go
    png_read_image(a_png_ptr, row_pointers);

    // we allocate memory for image. By default we shall use OpenGL's RGB mode.
    if (bpp == 1 && !a_image->allocate(width, height, GL_LUMINANCE))
        return false;
    if (bpp == 2 && !a_image->allocate(width, height, GL_LUMINANCE_ALPHA))
        return false;
    if (bpp == 3 && !a_image->allocate(width, height, GL_RGB))
        return false;
    if (bpp == 4 && !a_image->allocate(width, height, GL_RGBA))
        return false;

    // retrieve pointer to image data
    unsigned char* data = a_image->getData();

    // put rows neatly in the destination buffer
    for (j=0; j<height; j++)
        memcpy(data+bpp*j*width, row_pointers[height-1-j], rowsize);

    // clean up after the read and free any memory allocated
    delete [] row_pointers;
    png_read_end(a_png_ptr, NULL);

    // success
    return true;
}