This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

@@ -0,0 +1,9 @@
cmake_minimum_required(VERSION 3.0)

project(spng C)

add_library(spng STATIC ${CMAKE_CURRENT_LIST_DIR}/libspng/spng/spng.c)
target_compile_definitions(spng PUBLIC SPNG_STATIC)
target_link_libraries(spng PUBLIC ZLIB::ZLIB)
target_include_directories(spng PUBLIC ${CMAKE_CURRENT_LIST_DIR}/libspng/spng/)
dolphin_disable_warnings_msvc(spng)
Submodule libspng added at dc5b10
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<Import Project="..\..\Source\VSProps\Base.Macros.props" />
<Import Project="$(VSPropsDir)Base.Targets.props" />
<PropertyGroup Label="Globals">
<ProjectGuid>{447B7B1E-1D74-4AEF-B2B9-6EB41C5D5313}</ProjectGuid>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<Import Project="$(VSPropsDir)Configuration.StaticLibrary.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings" />
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VSPropsDir)Base.props" />
<Import Project="$(VSPropsDir)ClDisableAllWarnings.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>libspng\spng;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="libspng\spng\spng.c" />
</ItemGroup>
<ItemGroup>
<Text Include="CMakeLists.txt" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="libspng\spng\spng.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
@@ -15,13 +15,13 @@ Dolphin includes or links code of the following third-party software projects:
- [ENet](http://enet.bespin.org/):
[MIT](http://enet.bespin.org/License.html)
- [FatFs](http://elm-chan.org/fsw/ff/00index_e.html):
[BSD-1-Clause](http://elm-chan.org/fsw/ff/doc/appnote.html#license)
[BSD 1-Clause](http://elm-chan.org/fsw/ff/doc/appnote.html#license)
- [GCEmu](http://sourceforge.net/projects/gcemu-project/):
GPLv2+
- [gettext](https://www.gnu.org/software/gettext/):
[GPLv3+](http://git.savannah.gnu.org/cgit/gettext.git/tree/COPYING)
- [googletest](https://github.com/google/googletest):
[3-clause BSD](https://github.com/google/googletest/blob/master/LICENSE)
[BSD 3-Clause](https://github.com/google/googletest/blob/master/LICENSE)
- [libao](https://www.xiph.org/ao/):
[GPLv2+](https://trac.xiph.org/browser/trunk/ao/README)
- [libav](https://libav.org/):
@@ -32,8 +32,8 @@ Dolphin includes or links code of the following third-party software projects:
[LGPLv2+](http://git.savannah.gnu.org/cgit/libiconv.git/tree/COPYING.LIB)
- [liblzma](https://tukaani.org/xz/):
[Public domain](https://git.tukaani.org/?p=xz.git;a=blob_plain;f=COPYING;hb=HEAD)
- [libpng](http://www.libpng.org/pub/png/libpng.html):
[libpng license](http://www.libpng.org/pub/png/src/libpng-LICENSE.txt)
- [libspng](https://github.com/randy408/libspng):
[BSD 2-Clause](https://github.com/randy408/libspng/blob/master/LICENSE)
- [libusb](http://libusb.info/):
[LGPLv2.1+](https://github.com/libusb/libusb/blob/master/COPYING)
- [LLVM](http://llvm.org/):
@@ -43,7 +43,7 @@ Dolphin includes or links code of the following third-party software projects:
- [mGBA](http://mgba.io)
[MPL 2.0](https://github.com/mgba-emu/mgba/blob/master/LICENSE)
- [MiniUPnPc](http://miniupnp.free.fr/):
[3-clause BSD](https://github.com/miniupnp/miniupnp/blob/master/miniupnpc/LICENSE)
[BSD 3-Clause](https://github.com/miniupnp/miniupnp/blob/master/miniupnpc/LICENSE)
- [Microsoft Visual C++ Runtime Library](http://www.microsoft.com/en-us/download/details.aspx?id=40784):
[System Library if not distributed](https://www.gnu.org/licenses/gpl-faq.html#WindowsRuntimeAndGPL)
- [OpenAL Soft](http://kcat.strangesoft.net/openal.html):
@@ -73,8 +73,8 @@ Dolphin includes or links code of the following third-party software projects:
- [Windows Implementation Libraries](https://github.com/microsoft/wil):
[MIT](https://github.com/microsoft/wil/blob/master/LICENSE)
- [xxHash](https://github.com/Cyan4973/xxHash):
[2-clause BSD](https://github.com/Cyan4973/xxHash/blob/master/LICENSE)
[BSD 2-Clause](https://github.com/Cyan4973/xxHash/blob/master/LICENSE)
- [zlib-ng](https://github.com/zlib-ng/zlib-ng):
[zlib license](https://github.com/zlib-ng/zlib-ng/blob/develop/LICENSE.md)
- [Zstandard](https://facebook.github.io/zstd/):
[3-clause BSD](https://github.com/facebook/zstd/blob/dev/LICENSE) or [GPLv2](https://github.com/facebook/zstd/blob/dev/COPYING)
[BSD 3-Clause](https://github.com/facebook/zstd/blob/dev/LICENSE) or [GPLv2](https://github.com/facebook/zstd/blob/dev/COPYING)
@@ -64,8 +64,6 @@ add_library(common
HttpRequest.h
Image.cpp
Image.h
ImageC.c
ImageC.h
IniFile.cpp
IniFile.h
Inline.h
@@ -148,7 +146,7 @@ PRIVATE
${CURL_LIBRARIES}
FatFs
${ICONV_LIBRARIES}
png
spng
${VTUNE_LIBRARIES}
)

@@ -315,9 +313,4 @@ endif()
if(MSVC)
# Add precompiled header
target_link_libraries(common PRIVATE use_pch)

# We need to disable PCH for this one file, because it's C and our PCH is C++
set_source_files_properties(
${CMAKE_CURRENT_SOURCE_DIR}/ImageC.c
PROPERTIES COMPILE_FLAGS "/Y- /I${CMAKE_SOURCE_DIR}/Source/PCH/nopch")
endif()
@@ -3,159 +3,121 @@

#include "Common/Image.h"

#include <memory>
#include <string>
#include <vector>

#include <png.h>
#include <spng.h>

#include "Common/Assert.h"
#include "Common/CommonTypes.h"
#include "Common/IOFile.h"
#include "Common/ImageC.h"
#include "Common/Logging/Log.h"
#include "Common/Timer.h"

namespace Common
{
static void spng_free(spng_ctx* ctx)
{
if (ctx)
spng_ctx_free(ctx);
}

static auto make_spng_ctx(int flags)
{
return std::unique_ptr<spng_ctx, decltype(&spng_free)>(spng_ctx_new(flags), spng_free);
}

bool LoadPNG(const std::vector<u8>& input, std::vector<u8>* data_out, u32* width_out,
u32* height_out)
{
// Using the 'Simplified API' of libpng; see section V in the libpng manual.
auto ctx = make_spng_ctx(0);
if (!ctx)
return false;

// Read header
png_image png = {};
png.version = PNG_IMAGE_VERSION;
if (!png_image_begin_read_from_memory(&png, input.data(), input.size()))
if (spng_set_png_buffer(ctx.get(), input.data(), input.size()))
return false;

// Prepare output vector
png.format = PNG_FORMAT_RGBA;
size_t png_size = PNG_IMAGE_SIZE(png);
data_out->resize(png_size);
spng_ihdr ihdr{};
if (spng_get_ihdr(ctx.get(), &ihdr))
return false;

// Convert to RGBA and write into output vector
if (!png_image_finish_read(&png, nullptr, data_out->data(), 0, nullptr))
const int format = SPNG_FMT_RGBA8;
size_t decoded_len = 0;
if (spng_decoded_image_size(ctx.get(), format, &decoded_len))
return false;

*width_out = png.width;
*height_out = png.height;
data_out->resize(decoded_len);
if (spng_decode_image(ctx.get(), data_out->data(), decoded_len, format, SPNG_DECODE_TRNS))
return false;

*width_out = ihdr.width;
*height_out = ihdr.height;
return true;
}

static void WriteCallback(png_structp png_ptr, png_bytep data, size_t length)
{
std::vector<u8>* buffer = static_cast<std::vector<u8>*>(png_get_io_ptr(png_ptr));
buffer->insert(buffer->end(), data, data + length);
}

static void ErrorCallback(ErrorHandler* self, const char* msg)
{
std::vector<std::string>* errors = static_cast<std::vector<std::string>*>(self->error_list);
errors->emplace_back(msg);
}

static void WarningCallback(ErrorHandler* self, const char* msg)
{
std::vector<std::string>* warnings = static_cast<std::vector<std::string>*>(self->warning_list);
warnings->emplace_back(msg);
}

bool SavePNG(const std::string& path, const u8* input, ImageByteFormat format, u32 width,
u32 height, int stride, int level)
u32 height, u32 stride, int level)
{
Common::Timer timer;
timer.Start();

size_t byte_per_pixel;
int color_type;
spng_color_type color_type;
switch (format)
{
case ImageByteFormat::RGB:
color_type = PNG_COLOR_TYPE_RGB;
byte_per_pixel = 3;
color_type = SPNG_COLOR_TYPE_TRUECOLOR;
break;
case ImageByteFormat::RGBA:
color_type = PNG_COLOR_TYPE_RGBA;
byte_per_pixel = 4;
color_type = SPNG_COLOR_TYPE_TRUECOLOR_ALPHA;
break;
default:
ASSERT_MSG(FRAMEDUMP, false, "Invalid format {}", static_cast<int>(format));
return false;
}

// libpng doesn't handle non-ASCII characters in path, so write in two steps:
// first to memory, then to file
std::vector<u8> buffer;
buffer.reserve(byte_per_pixel * width * height);

std::vector<std::string> warnings;
std::vector<std::string> errors;
ErrorHandler error_handler;
error_handler.error_list = &errors;
error_handler.warning_list = &warnings;
error_handler.StoreError = ErrorCallback;
error_handler.StoreWarning = WarningCallback;

std::vector<const u8*> rows;
rows.reserve(height);
for (u32 row = 0; row < height; row++)
{
rows.push_back(&input[row * stride]);
}
auto ctx = make_spng_ctx(SPNG_CTX_ENCODER);
if (!ctx)
return false;

png_structp png_ptr =
png_create_write_struct(PNG_LIBPNG_VER_STRING, &error_handler, PngError, PngWarning);
png_infop info_ptr = png_create_info_struct(png_ptr);
auto outfile = File::IOFile(path, "wb");
if (spng_set_png_file(ctx.get(), outfile.GetHandle()))
return false;

bool success = false;
if (png_ptr != nullptr && info_ptr != nullptr)
{
success = SavePNG0(png_ptr, info_ptr, color_type, width, height, level, &buffer, WriteCallback,
const_cast<u8**>(rows.data()));
}
png_destroy_write_struct(&png_ptr, &info_ptr);
if (spng_set_option(ctx.get(), SPNG_IMG_COMPRESSION_LEVEL, level))
return false;

if (success)
{
File::IOFile outfile(path, "wb");
if (!outfile)
return false;
success = outfile.WriteBytes(buffer.data(), buffer.size());
spng_ihdr ihdr{};
ihdr.width = width;
ihdr.height = height;
ihdr.color_type = color_type;
ihdr.bit_depth = 8;
if (spng_set_ihdr(ctx.get(), &ihdr))
return false;

timer.Stop();
INFO_LOG_FMT(FRAMEDUMP, "{} byte {} by {} image saved to {} at level {} in {}", buffer.size(),
width, height, path, level, timer.GetTimeElapsedFormatted());
ASSERT(errors.size() == 0);
if (warnings.size() != 0)
if (spng_encode_image(ctx.get(), nullptr, 0, SPNG_FMT_PNG, SPNG_ENCODE_PROGRESSIVE))
return false;
for (u32 row = 0; row < height; row++)
{
const int err = spng_encode_row(ctx.get(), &input[row * stride], stride);
if (err == SPNG_EOI)
break;
if (err)
{
WARN_LOG_FMT(FRAMEDUMP, "Saved with {} warnings:", warnings.size());
for (auto& warning : warnings)
WARN_LOG_FMT(FRAMEDUMP, "libpng warning: {}", warning);
ERROR_LOG_FMT(FRAMEDUMP, "Failed to save {} by {} image to {} at level {}: error {}", width,
height, path, level, err);
return false;
}
}
else
{
ERROR_LOG_FMT(FRAMEDUMP,
"Failed to save {} by {} image to {} at level {}: {} warnings, {} errors", width,
height, path, level, warnings.size(), errors.size());
for (auto& error : errors)
ERROR_LOG_FMT(FRAMEDUMP, "libpng error: {}", error);
for (auto& warning : warnings)
WARN_LOG_FMT(FRAMEDUMP, "libpng warning: {}", warning);
}

return success;
}

bool ConvertRGBAToRGBAndSavePNG(const std::string& path, const u8* input, u32 width, u32 height,
int stride, int level)
{
const std::vector<u8> data = RGBAToRGB(input, width, height, stride);
return SavePNG(path, data.data(), ImageByteFormat::RGB, width, height, width * 3, level);
size_t image_len = 0;
spng_decoded_image_size(ctx.get(), SPNG_FMT_PNG, &image_len);
INFO_LOG_FMT(FRAMEDUMP, "{} byte {} by {} image saved to {} at level {} in {}", image_len, width,
height, path, level, timer.GetTimeElapsedFormatted());
return true;
}

std::vector<u8> RGBAToRGB(const u8* input, u32 width, u32 height, int row_stride)
static std::vector<u8> RGBAToRGB(const u8* input, u32 width, u32 height, u32 row_stride)
{
std::vector<u8> buffer;
buffer.reserve(width * height * 3);
@@ -172,4 +134,11 @@ std::vector<u8> RGBAToRGB(const u8* input, u32 width, u32 height, int row_stride
}
return buffer;
}

bool ConvertRGBAToRGBAndSavePNG(const std::string& path, const u8* input, u32 width, u32 height,
u32 stride, int level)
{
const std::vector<u8> data = RGBAToRGB(input, width, height, stride);
return SavePNG(path, data.data(), ImageByteFormat::RGB, width, height, width * 3, level);
}
} // namespace Common
@@ -20,10 +20,7 @@ enum class ImageByteFormat
};

bool SavePNG(const std::string& path, const u8* input, ImageByteFormat format, u32 width,
u32 height, int stride, int level = 6);
u32 height, u32 stride, int level = 6);
bool ConvertRGBAToRGBAndSavePNG(const std::string& path, const u8* input, u32 width, u32 height,
int stride, int level);

std::vector<u8> RGBAToRGB(const u8* input, u32 width, u32 height, int row_stride = 0);

u32 stride, int level);
} // namespace Common