summaryrefslogtreecommitdiff
path: root/Source/Core/Common/Image.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Core/Common/Image.cpp')
-rw-r--r--Source/Core/Common/Image.cpp174
1 files changed, 15 insertions, 159 deletions
diff --git a/Source/Core/Common/Image.cpp b/Source/Core/Common/Image.cpp
index fdbc246efb..87c94d337a 100644
--- a/Source/Core/Common/Image.cpp
+++ b/Source/Core/Common/Image.cpp
@@ -4,181 +4,37 @@
#include "Common/Image.h"
-// Versions of libpng older than 1.5 want us to not include setjmp.h before png.h,
-// so let's include png.h first of all headers
-#include <png.h>
-
-#include <csetjmp>
-#include <cstdlib>
-#include <cstring>
-#include <memory>
#include <vector>
+#include <png.h>
+
#include "Common/CommonTypes.h"
-#include "Common/Logging/Log.h"
namespace Common
{
-static int MultiplyAlpha(int alpha, int color)
-{
- const int temp = (alpha * color) + 0x80;
- return ((temp + (temp >> 8)) >> 8);
-}
-
-static void PremultiplyData(png_structp png, png_row_infop row_info, png_bytep data)
-{
- for (png_size_t i = 0; i < row_info->rowbytes; i += 4, data += 4)
- {
- const png_byte alpha = data[3];
- u32 w;
-
- if (alpha == 0)
- {
- w = 0;
- }
- else
- {
- png_byte red = data[0];
- png_byte green = data[1];
- png_byte blue = data[2];
-
- if (alpha != 0xff)
- {
- red = MultiplyAlpha(alpha, red);
- green = MultiplyAlpha(alpha, green);
- blue = MultiplyAlpha(alpha, blue);
- }
- w = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
- }
-
- std::memcpy(data, &w, sizeof(w));
- }
-}
-
-struct ReadProgress
-{
- const u8* current;
- const u8* end;
-};
-
-static void ReadCallback(png_structp png, png_bytep data, png_size_t size)
-{
- ReadProgress* progress = static_cast<ReadProgress*>(png_get_io_ptr(png));
- for (size_t i = 0; i < size; ++i)
- {
- if (progress->current >= progress->end)
- png_error(png, "Read beyond end of file"); // This makes us longjmp back to LoadPNG
-
- data[i] = *(progress->current);
- ++(progress->current);
- }
-}
-
-static void PNGErrorCallback(png_structp png, png_const_charp error_msg)
-{
- ERROR_LOG(COMMON, "PNG loading error: %s", error_msg);
- longjmp(png_jmpbuf(png), 1);
-}
-
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable : 4611)
-// VS shows the following warning even though no C++ objects are destroyed in LoadPNG:
-// "warning C4611: interaction between '_setjmp' and C++ object destruction is non-portable"
-#endif
-
bool LoadPNG(const std::vector<u8>& input, std::vector<u8>* data_out, u32* width_out,
u32* height_out)
{
- // The const_cast is only required for libpng versions older than 1.5
- const bool is_png = !png_sig_cmp(const_cast<u8*>(input.data()), 0, input.size());
- if (!is_png)
- return false;
+ // Using the 'Simplified API' of libpng; see section V in the libpng manual.
- png_struct* png =
- png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, PNGErrorCallback, nullptr);
- if (!png)
+ // Read header
+ png_image png = {};
+ png.version = PNG_IMAGE_VERSION;
+ if (!png_image_begin_read_from_memory(&png, input.data(), input.size()))
return false;
- png_info* info = png_create_info_struct(png);
- if (!info)
- {
- png_destroy_read_struct(&png, &info, nullptr);
- return false;
- }
-
- // It would've been nice to use std::vector for this, but using setjmp safely
- // means that we have to use this manually managed volatile pointer garbage instead.
- png_byte** volatile row_pointers_volatile = nullptr;
+ // Prepare output vector
+ size_t png_size = PNG_IMAGE_SIZE(png);
+ data_out->resize(png_size);
- if (setjmp(png_jmpbuf(png)))
- {
- free(row_pointers_volatile);
- png_destroy_read_struct(&png, &info, nullptr);
+ // Convert to RGBA and write into output vector
+ png.format = PNG_FORMAT_RGBA;
+ if (!png_image_finish_read(&png, nullptr, data_out->data(), 0, nullptr))
return false;
- }
-
- ReadProgress read_progress{input.data(), input.data() + input.size()};
- png_set_read_fn(png, &read_progress, ReadCallback);
- png_read_info(png, info);
-
- png_uint_32 width, height;
- int depth, color_type, interlace;
- png_get_IHDR(png, info, &width, &height, &depth, &color_type, &interlace, nullptr, nullptr);
-
- if (color_type == PNG_COLOR_TYPE_PALETTE)
- png_set_palette_to_rgb(png);
- else if (color_type == PNG_COLOR_TYPE_GRAY)
- png_set_expand_gray_1_2_4_to_8(png);
-
- if (png_get_valid(png, info, PNG_INFO_tRNS))
- png_set_tRNS_to_alpha(png);
-
- if (depth == 16)
- png_set_strip_16(png);
- else if (depth < 8)
- png_set_packing(png);
-
- if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
- png_set_gray_to_rgb(png);
- if (interlace != PNG_INTERLACE_NONE)
- png_set_interlace_handling(png);
+ *width_out = png.width;
+ *height_out = png.height;
- png_set_bgr(png);
- png_set_filler(png, 0xff, PNG_FILLER_AFTER);
- png_set_read_user_transform_fn(png, PremultiplyData);
- png_read_update_info(png, info);
- png_get_IHDR(png, info, &width, &height, &depth, &color_type, &interlace, nullptr, nullptr);
-
- const size_t stride = width * 4;
- data_out->resize(stride * height);
-
- png_byte** row_pointers = static_cast<png_byte**>(malloc(height * sizeof(png_byte*)));
- if (!row_pointers)
- {
- png_destroy_read_struct(&png, &info, nullptr);
- return false;
- }
-
- for (png_uint_32 i = 0; i < height; i++)
- row_pointers[i] = &(*data_out)[i * stride];
-
- row_pointers_volatile = row_pointers;
-
- png_read_image(png, row_pointers);
- png_read_end(png, info);
-
- free(row_pointers);
- png_destroy_read_struct(&png, &info, nullptr);
-
- *width_out = width;
- *height_out = height;
return true;
}
-
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
-
} // namespace Common