diff --git a/src/engine/gfx/image_loader.cpp b/src/engine/gfx/image_loader.cpp index 55b1fe757..e9ad79ec4 100644 --- a/src/engine/gfx/image_loader.cpp +++ b/src/engine/gfx/image_loader.cpp @@ -1,6 +1,7 @@ #include "image_loader.h" #include #include +#include #include #include @@ -8,20 +9,22 @@ struct SLibPNGWarningItem { SImageByteBuffer *m_pByteLoader; - const char *pFileName; + const char *m_pFileName; + std::jmp_buf m_Buf; }; -static void LibPNGError(png_structp png_ptr, png_const_charp error_msg) +[[noreturn]] static void LibPNGError(png_structp png_ptr, png_const_charp error_msg) { SLibPNGWarningItem *pUserStruct = (SLibPNGWarningItem *)png_get_error_ptr(png_ptr); pUserStruct->m_pByteLoader->m_Err = -1; - dbg_msg("libpng", "error for file \"%s\": %s", pUserStruct->pFileName, error_msg); + dbg_msg("libpng", "error for file \"%s\": %s", pUserStruct->m_pFileName, error_msg); + std::longjmp(pUserStruct->m_Buf, 1); } static void LibPNGWarning(png_structp png_ptr, png_const_charp warning_msg) { SLibPNGWarningItem *pUserStruct = (SLibPNGWarningItem *)png_get_error_ptr(png_ptr); - dbg_msg("libpng", "warning for file \"%s\": %s", pUserStruct->pFileName, warning_msg); + dbg_msg("libpng", "warning for file \"%s\": %s", pUserStruct->m_pFileName, warning_msg); } static bool FileMatchesImageType(SImageByteBuffer &ByteLoader) @@ -75,7 +78,8 @@ static void LibPNGSetImageFormat(EImageFormat &ImageFormat, int LibPNGColorType) static void LibPNGDeleteReadStruct(png_structp pPNGStruct, png_infop pPNGInfo) { - png_destroy_info_struct(pPNGStruct, &pPNGInfo); + if(pPNGInfo != nullptr) + png_destroy_info_struct(pPNGStruct, &pPNGInfo); png_destroy_read_struct(&pPNGStruct, NULL, NULL); } @@ -127,6 +131,15 @@ static int PngliteIncompatibility(png_structp pPNGStruct, png_infop pPNGInfo) bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &PngliteIncompatible, int &Width, int &Height, uint8_t *&pImageBuff, EImageFormat &ImageFormat) { + png_infop pPNGInfo = nullptr; + int ColorType; + png_byte BitDepth; + int ColorChannelCount; + int BytesInRow; + Height = 0; + png_bytepp pRowPointers = nullptr; + SLibPNGWarningItem UserErrorStruct = {&ByteLoader, pFileName, {}}; + png_structp pPNGStruct = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if(pPNGStruct == NULL) @@ -135,7 +148,22 @@ bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &PngliteIn return false; } - png_infop pPNGInfo = png_create_info_struct(pPNGStruct); + if(setjmp(UserErrorStruct.m_Buf)) + { + if(pRowPointers != nullptr) + { + for(int i = 0; i < Height; ++i) + { + delete[] pRowPointers[i]; + } + } + delete[] pRowPointers; + LibPNGDeleteReadStruct(pPNGStruct, pPNGInfo); + return false; + } + png_set_error_fn(pPNGStruct, &UserErrorStruct, LibPNGError, LibPNGWarning); + + pPNGInfo = png_create_info_struct(pPNGStruct); if(pPNGInfo == NULL) { @@ -144,9 +172,6 @@ bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &PngliteIn return false; } - SLibPNGWarningItem UserErrorStruct = {&ByteLoader, pFileName}; - png_set_error_fn(pPNGStruct, &UserErrorStruct, LibPNGError, LibPNGWarning); - if(!FileMatchesImageType(ByteLoader)) { LibPNGDeleteReadStruct(pPNGStruct, pPNGInfo); @@ -169,12 +194,10 @@ bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &PngliteIn Width = png_get_image_width(pPNGStruct, pPNGInfo); Height = png_get_image_height(pPNGStruct, pPNGInfo); - int ColorType = png_get_color_type(pPNGStruct, pPNGInfo); - png_byte BitDepth = png_get_bit_depth(pPNGStruct, pPNGInfo); + ColorType = png_get_color_type(pPNGStruct, pPNGInfo); + BitDepth = png_get_bit_depth(pPNGStruct, pPNGInfo); PngliteIncompatible = PngliteIncompatibility(pPNGStruct, pPNGInfo); - bool PNGErr = false; - if(BitDepth == 16) { png_set_strip_16(pPNGStruct); @@ -182,64 +205,71 @@ bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &PngliteIn else if(BitDepth > 8) { dbg_msg("png", "non supported bit depth."); - PNGErr = true; + LibPNGDeleteReadStruct(pPNGStruct, pPNGInfo); + return false; } if(Width == 0 || Height == 0 || BitDepth == 0) { dbg_msg("png", "image had width, height or bit depth of 0."); - PNGErr = true; + LibPNGDeleteReadStruct(pPNGStruct, pPNGInfo); + return false; } - if(!PNGErr) + if(ColorType == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb(pPNGStruct); + + if(ColorType == PNG_COLOR_TYPE_GRAY && BitDepth < 8) + png_set_expand_gray_1_2_4_to_8(pPNGStruct); + + if(png_get_valid(pPNGStruct, pPNGInfo, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha(pPNGStruct); + + png_read_update_info(pPNGStruct, pPNGInfo); + + ColorChannelCount = LibPNGGetColorChannelCount(ColorType); + BytesInRow = png_get_rowbytes(pPNGStruct, pPNGInfo); + + if(BytesInRow == Width * ColorChannelCount) { - if(ColorType == PNG_COLOR_TYPE_PALETTE) - png_set_palette_to_rgb(pPNGStruct); - - if(ColorType == PNG_COLOR_TYPE_GRAY && BitDepth < 8) - png_set_expand_gray_1_2_4_to_8(pPNGStruct); - - if(png_get_valid(pPNGStruct, pPNGInfo, PNG_INFO_tRNS)) - png_set_tRNS_to_alpha(pPNGStruct); - - png_read_update_info(pPNGStruct, pPNGInfo); - - int ColorChannelCount = LibPNGGetColorChannelCount(ColorType); - int BytesInRow = png_get_rowbytes(pPNGStruct, pPNGInfo); - - if(BytesInRow == Width * ColorChannelCount) + pRowPointers = new png_bytep[Height]; + for(int y = 0; y < Height; ++y) { - png_bytepp pRowPointers = new png_bytep[Height]; - for(int y = 0; y < Height; ++y) - { - pRowPointers[y] = new png_byte[BytesInRow]; - } - - png_read_image(pPNGStruct, pRowPointers); - - if(ByteLoader.m_Err == 0) - pImageBuff = (uint8_t *)malloc((size_t)Height * (size_t)Width * (size_t)ColorChannelCount * sizeof(uint8_t)); - else - PNGErr = true; - - for(int i = 0; i < Height; ++i) - { - if(ByteLoader.m_Err == 0) - mem_copy(&pImageBuff[i * BytesInRow], pRowPointers[i], BytesInRow); - delete[] pRowPointers[i]; - } - delete[] pRowPointers; - - LibPNGSetImageFormat(ImageFormat, ColorType); + pRowPointers[y] = new png_byte[BytesInRow]; } - else - PNGErr = true; + + png_read_image(pPNGStruct, pRowPointers); + + if(ByteLoader.m_Err == 0) + pImageBuff = (uint8_t *)malloc((size_t)Height * (size_t)Width * (size_t)ColorChannelCount * sizeof(uint8_t)); + + for(int i = 0; i < Height; ++i) + { + if(ByteLoader.m_Err == 0) + mem_copy(&pImageBuff[i * BytesInRow], pRowPointers[i], BytesInRow); + delete[] pRowPointers[i]; + } + delete[] pRowPointers; + pRowPointers = nullptr; + + if(ByteLoader.m_Err != 0) + { + LibPNGDeleteReadStruct(pPNGStruct, pPNGInfo); + return false; + } + + LibPNGSetImageFormat(ImageFormat, ColorType); + } + else + { + LibPNGDeleteReadStruct(pPNGStruct, pPNGInfo); + return false; } png_destroy_info_struct(pPNGStruct, &pPNGInfo); png_destroy_read_struct(&pPNGStruct, NULL, NULL); - return !PNGErr; + return true; } static void WriteDataFromLoadedBytes(png_structp pPNGStruct, png_bytep pOutBytes, png_size_t ByteCountToWrite)