| @@ -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<uint8> tempBuffer (height * lineStride); | |||
| return false; | |||
| } | |||
| HeapBlock<png_bytep> 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<uint8> tempBuffer (height * lineStride); | |||
| HeapBlock<png_bytep> 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) | |||