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,141 +319,180 @@ namespace PNGHelpers } static void JUCE_CDECL warningCallback (png_structp, png_const_charp) {} - #endif -} - -//============================================================================== -PNGImageFormat::PNGImageFormat() {} -PNGImageFormat::~PNGImageFormat() {} - -String PNGImageFormat::getFormatName() { return "PNG"; } -bool PNGImageFormat::usesFileExtension (const File& f) { return f.hasFileExtension ("png"); } - -bool PNGImageFormat::canUnderstand (InputStream& in) -{ - const int bytesNeeded = 4; - char header [bytesNeeded]; - - return in.read (header, bytesNeeded) == bytesNeeded - && header[1] == 'P' - && header[2] == 'N' - && header[3] == 'G'; -} - -#if JUCE_USING_COREIMAGE_LOADER - Image juce_loadWithCoreImage (InputStream& input); -#endif -Image PNGImageFormat::decodeImage (InputStream& in) -{ -#if JUCE_USING_COREIMAGE_LOADER - return juce_loadWithCoreImage (in); -#else - using namespace pnglibNamespace; - Image image; + #if JUCE_MSVC + #pragma warning (push) + #pragma warning (disable: 4611) // (warning about setjmp) + #endif - if (png_structp pngReadStruct = png_create_read_struct (PNG_LIBPNG_VER_STRING, 0, 0, 0)) + 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 (png_infop pngInfoStruct = png_create_info_struct (pngReadStruct)) + if (setjmp (errorJumpBuf) == 0) { - jmp_buf errorJumpBuf; - png_set_error_fn (pngReadStruct, &errorJumpBuf, PNGHelpers::errorCallback, PNGHelpers::warningCallback); + // read the header.. + png_set_read_fn (pngReadStruct, &in, readCallback); - if (setjmp (errorJumpBuf) == 0) - { - // read the header.. - png_set_read_fn (pngReadStruct, &in, PNGHelpers::readCallback); + png_read_info (pngReadStruct, pngInfoStruct); - png_uint_32 width = 0, height = 0; - int bitDepth = 0, colorType = 0, interlaceType; + png_get_IHDR (pngReadStruct, pngInfoStruct, + &width, &height, + &bitDepth, &colorType, + &interlaceType, 0, 0); - png_read_info (pngReadStruct, pngInfoStruct); + if (bitDepth == 16) + png_set_strip_16 (pngReadStruct); - png_get_IHDR (pngReadStruct, pngInfoStruct, - &width, &height, - &bitDepth, &colorType, - &interlaceType, 0, 0); + if (colorType == PNG_COLOR_TYPE_PALETTE) + png_set_expand (pngReadStruct); - if (bitDepth == 16) - png_set_strip_16 (pngReadStruct); + if (bitDepth < 8) + png_set_expand (pngReadStruct); - if (colorType == PNG_COLOR_TYPE_PALETTE) - png_set_expand (pngReadStruct); + if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb (pngReadStruct); - if (bitDepth < 8) - png_set_expand (pngReadStruct); + return true; + } - if (png_get_valid (pngReadStruct, pngInfoStruct, PNG_INFO_tRNS)) - png_set_expand (pngReadStruct); + return false; + } - if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) - png_set_gray_to_rgb (pngReadStruct); + 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_set_add_alpha (pngReadStruct, 0xff, PNG_FILLER_AFTER); - bool hasAlphaChan = (colorType & PNG_COLOR_MASK_ALPHA) != 0 - || pngInfoStruct->num_trans > 0; + png_read_image (pngReadStruct, rows); + png_read_end (pngReadStruct, pngInfoStruct); + return true; + } - // Load the image into a temp buffer in the pnglib format.. - const size_t lineStride = width * 4; - HeapBlock tempBuffer (height * lineStride); + return false; + } - HeapBlock rows (height); - for (size_t y = 0; y < height; ++y) - rows[y] = (png_bytep) (tempBuffer + lineStride * y); + #if JUCE_MSVC + #pragma warning (pop) + #endif - png_read_image (pngReadStruct, rows); - png_read_end (pngReadStruct, pngInfoStruct); - png_destroy_read_struct (&pngReadStruct, &pngInfoStruct, 0); + 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); - // 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) - 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); - 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); - for (int y = 0; y < (int) height; ++y) + if (hasAlphaChan) + { + for (int i = (int) width; --i >= 0;) { - 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; - } - } + ((PixelARGB*) dest)->setARGB (src[3], src[0], src[1], src[2]); + ((PixelARGB*) dest)->premultiply(); + dest += destData.pixelStride; + src += 4; } } else { - png_destroy_read_struct (&pngReadStruct, &pngInfoStruct, 0); + for (int i = (int) width; --i >= 0;) + { + ((PixelRGB*) dest)->setARGB (0, src[0], src[1], src[2]); + dest += destData.pixelStride; + src += 4; + } } } - else + + 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 +} + +//============================================================================== +PNGImageFormat::PNGImageFormat() {} +PNGImageFormat::~PNGImageFormat() {} + +String PNGImageFormat::getFormatName() { return "PNG"; } +bool PNGImageFormat::usesFileExtension (const File& f) { return f.hasFileExtension ("png"); } - return image; +bool PNGImageFormat::canUnderstand (InputStream& in) +{ + const int bytesNeeded = 4; + char header [bytesNeeded]; + + return in.read (header, bytesNeeded) == bytesNeeded + && header[1] == 'P' + && header[2] == 'N' + && header[3] == 'G'; +} + +#if JUCE_USING_COREIMAGE_LOADER + Image juce_loadWithCoreImage (InputStream&); #endif + +Image PNGImageFormat::decodeImage (InputStream& in) +{ + #if JUCE_USING_COREIMAGE_LOADER + return juce_loadWithCoreImage (in); + #else + return PNGHelpers::readImage (in); + #endif } bool PNGImageFormat::writeImageToStream (const Image& image, OutputStream& out)