From 5cef7c8e7982dea7b49f00a6247dd8c45bac85c4 Mon Sep 17 00:00:00 2001 From: jules Date: Fri, 11 Sep 2015 09:48:51 +0100 Subject: [PATCH] Reworked the PNG loader --- .../image_formats/juce_PNGLoader.cpp | 255 ++++++++++-------- 1 file changed, 147 insertions(+), 108 deletions(-) diff --git a/modules/juce_graphics/image_formats/juce_PNGLoader.cpp b/modules/juce_graphics/image_formats/juce_PNGLoader.cpp index 8ce62199b5..173849b950 100644 --- a/modules/juce_graphics/image_formats/juce_PNGLoader.cpp +++ b/modules/juce_graphics/image_formats/juce_PNGLoader.cpp @@ -319,6 +319,148 @@ namespace PNGHelpers } static void JUCE_CDECL warningCallback (png_structp, png_const_charp) {} + + #if JUCE_MSVC + #pragma warning (push) + #pragma warning (disable: 4611) // (warning about setjmp) + #endif + + static bool readHeader (InputStream& in, png_structp pngReadStruct, png_infop pngInfoStruct, jmp_buf& errorJumpBuf, + png_uint_32& width, png_uint_32& height, int& bitDepth, int& colorType, int& interlaceType) noexcept + { + if (setjmp (errorJumpBuf) == 0) + { + // read the header.. + png_set_read_fn (pngReadStruct, &in, readCallback); + + png_read_info (pngReadStruct, pngInfoStruct); + + png_get_IHDR (pngReadStruct, pngInfoStruct, + &width, &height, + &bitDepth, &colorType, + &interlaceType, 0, 0); + + if (bitDepth == 16) + png_set_strip_16 (pngReadStruct); + + if (colorType == PNG_COLOR_TYPE_PALETTE) + png_set_expand (pngReadStruct); + + if (bitDepth < 8) + png_set_expand (pngReadStruct); + + if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb (pngReadStruct); + + return true; + } + + return false; + } + + static bool readImageData (png_structp pngReadStruct, png_infop pngInfoStruct, jmp_buf& errorJumpBuf, png_bytepp rows) noexcept + { + if (setjmp (errorJumpBuf) == 0) + { + if (png_get_valid (pngReadStruct, pngInfoStruct, PNG_INFO_tRNS)) + png_set_expand (pngReadStruct); + + png_set_add_alpha (pngReadStruct, 0xff, PNG_FILLER_AFTER); + + png_read_image (pngReadStruct, rows); + png_read_end (pngReadStruct, pngInfoStruct); + return true; + } + + return false; + } + + #if JUCE_MSVC + #pragma warning (pop) + #endif + + static Image createImageFromData (bool hasAlphaChan, int width, int height, png_bytepp rows) + { + // now convert the data to a juce image format.. + Image image (hasAlphaChan ? Image::ARGB : Image::RGB, width, height, hasAlphaChan); + + image.getProperties()->set ("originalImageHadAlpha", image.hasAlphaChannel()); + hasAlphaChan = image.hasAlphaChannel(); // (the native image creator may not give back what we expect) + + const Image::BitmapData destData (image, Image::BitmapData::writeOnly); + + for (int y = 0; y < (int) height; ++y) + { + const uint8* src = rows[y]; + uint8* dest = destData.getLinePointer (y); + + if (hasAlphaChan) + { + for (int i = (int) width; --i >= 0;) + { + ((PixelARGB*) dest)->setARGB (src[3], src[0], src[1], src[2]); + ((PixelARGB*) dest)->premultiply(); + dest += destData.pixelStride; + src += 4; + } + } + else + { + for (int i = (int) width; --i >= 0;) + { + ((PixelRGB*) dest)->setARGB (0, src[0], src[1], src[2]); + dest += destData.pixelStride; + src += 4; + } + } + } + + return image; + } + + static Image readImage (InputStream& in, png_structp pngReadStruct, png_infop pngInfoStruct) + { + jmp_buf errorJumpBuf; + png_set_error_fn (pngReadStruct, &errorJumpBuf, errorCallback, warningCallback); + + png_uint_32 width = 0, height = 0; + int bitDepth = 0, colorType = 0, interlaceType = 0; + + if (readHeader (in, pngReadStruct, pngInfoStruct, errorJumpBuf, + width, height, bitDepth, colorType, interlaceType)) + { + // Load the image into a temp buffer.. + const size_t lineStride = width * 4; + HeapBlock tempBuffer (height * lineStride); + HeapBlock rows (height); + + for (size_t y = 0; y < height; ++y) + rows[y] = (png_bytep) (tempBuffer + lineStride * y); + + if (readImageData (pngReadStruct, pngInfoStruct, errorJumpBuf, rows)) + return createImageFromData ((colorType & PNG_COLOR_MASK_ALPHA) != 0 || pngInfoStruct->num_trans > 0, + (int) width, (int) height, rows); + } + + return Image(); + } + + static Image readImage (InputStream& in) + { + if (png_structp pngReadStruct = png_create_read_struct (PNG_LIBPNG_VER_STRING, 0, 0, 0)) + { + if (png_infop pngInfoStruct = png_create_info_struct (pngReadStruct)) + { + Image image (readImage (in, pngReadStruct, pngInfoStruct)); + png_destroy_read_struct (&pngReadStruct, &pngInfoStruct, 0); + return image; + } + + png_destroy_read_struct (&pngReadStruct, 0, 0); + } + + return Image(); + } #endif } @@ -341,119 +483,16 @@ bool PNGImageFormat::canUnderstand (InputStream& in) } #if JUCE_USING_COREIMAGE_LOADER - Image juce_loadWithCoreImage (InputStream& input); + Image juce_loadWithCoreImage (InputStream&); #endif Image PNGImageFormat::decodeImage (InputStream& in) { -#if JUCE_USING_COREIMAGE_LOADER + #if JUCE_USING_COREIMAGE_LOADER return juce_loadWithCoreImage (in); -#else - using namespace pnglibNamespace; - Image image; - - if (png_structp pngReadStruct = png_create_read_struct (PNG_LIBPNG_VER_STRING, 0, 0, 0)) - { - if (png_infop pngInfoStruct = png_create_info_struct (pngReadStruct)) - { - jmp_buf errorJumpBuf; - png_set_error_fn (pngReadStruct, &errorJumpBuf, PNGHelpers::errorCallback, PNGHelpers::warningCallback); - - if (setjmp (errorJumpBuf) == 0) - { - // read the header.. - png_set_read_fn (pngReadStruct, &in, PNGHelpers::readCallback); - - png_uint_32 width = 0, height = 0; - int bitDepth = 0, colorType = 0, interlaceType; - - png_read_info (pngReadStruct, pngInfoStruct); - - png_get_IHDR (pngReadStruct, pngInfoStruct, - &width, &height, - &bitDepth, &colorType, - &interlaceType, 0, 0); - - if (bitDepth == 16) - png_set_strip_16 (pngReadStruct); - - if (colorType == PNG_COLOR_TYPE_PALETTE) - png_set_expand (pngReadStruct); - - if (bitDepth < 8) - png_set_expand (pngReadStruct); - - if (png_get_valid (pngReadStruct, pngInfoStruct, PNG_INFO_tRNS)) - png_set_expand (pngReadStruct); - - if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) - png_set_gray_to_rgb (pngReadStruct); - - png_set_add_alpha (pngReadStruct, 0xff, PNG_FILLER_AFTER); - - bool hasAlphaChan = (colorType & PNG_COLOR_MASK_ALPHA) != 0 - || pngInfoStruct->num_trans > 0; - - // Load the image into a temp buffer in the pnglib format.. - const size_t lineStride = width * 4; - HeapBlock tempBuffer (height * lineStride); - - HeapBlock rows (height); - for (size_t y = 0; y < height; ++y) - rows[y] = (png_bytep) (tempBuffer + lineStride * y); - - png_read_image (pngReadStruct, rows); - png_read_end (pngReadStruct, pngInfoStruct); - png_destroy_read_struct (&pngReadStruct, &pngInfoStruct, 0); - - // now convert the data to a juce image format.. - image = Image (hasAlphaChan ? Image::ARGB : Image::RGB, - (int) width, (int) height, hasAlphaChan); - - image.getProperties()->set ("originalImageHadAlpha", image.hasAlphaChannel()); - hasAlphaChan = image.hasAlphaChannel(); // (the native image creator may not give back what we expect) - - const Image::BitmapData destData (image, Image::BitmapData::writeOnly); - - for (int y = 0; y < (int) height; ++y) - { - const uint8* src = rows[y]; - uint8* dest = destData.getLinePointer (y); - - if (hasAlphaChan) - { - for (int i = (int) width; --i >= 0;) - { - ((PixelARGB*) dest)->setARGB (src[3], src[0], src[1], src[2]); - ((PixelARGB*) dest)->premultiply(); - dest += destData.pixelStride; - src += 4; - } - } - else - { - for (int i = (int) width; --i >= 0;) - { - ((PixelRGB*) dest)->setARGB (0, src[0], src[1], src[2]); - dest += destData.pixelStride; - src += 4; - } - } - } - } - else - { - png_destroy_read_struct (&pngReadStruct, &pngInfoStruct, 0); - } - } - else - { - png_destroy_read_struct (&pngReadStruct, 0, 0); - } - } - - return image; -#endif + #else + return PNGHelpers::readImage (in); + #endif } bool PNGImageFormat::writeImageToStream (const Image& image, OutputStream& out)