From 10b217e0c283994bb7172ef5e774593e63d1b54d Mon Sep 17 00:00:00 2001 From: Stenzek Date: Mon, 2 Oct 2023 16:58:46 +1000 Subject: [PATCH] Image: Support loading webp files --- PCSX2_qt.sln | 27 ++++++++++++++ cmake/SearchForStuff.cmake | 1 + common/CMakeLists.txt | 1 + common/Image.cpp | 72 ++++++++++++++++++++++++++++++++++++-- common/common.vcxproj | 9 ++++- 5 files changed, 107 insertions(+), 3 deletions(-) diff --git a/PCSX2_qt.sln b/PCSX2_qt.sln index 071582e7ea..bafbd7a08d 100644 --- a/PCSX2_qt.sln +++ b/PCSX2_qt.sln @@ -61,6 +61,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zydis", "3rdparty\zydis\zyd EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "demangler", "3rdparty\demangler\demangler.vcxproj", "{1E3D706C-1D95-4E1B-BDF2-CA3D0007DF7F}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libwebp", "3rdparty\libwebp\libwebp.vcxproj", "{522DAF2A-1F24-4742-B2C4-A956411F6AB2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug AVX2|x64 = Debug AVX2|x64 @@ -737,6 +739,30 @@ Global {1E3D706C-1D95-4E1B-BDF2-CA3D0007DF7F}.Release Clang|x64.Build.0 = Release Clang|x64 {1E3D706C-1D95-4E1B-BDF2-CA3D0007DF7F}.Release|x64.ActiveCfg = Release|x64 {1E3D706C-1D95-4E1B-BDF2-CA3D0007DF7F}.Release|x64.Build.0 = Release|x64 + {522DAF2A-1F24-4742-B2C4-A956411F6AB2}.Debug AVX2|x64.ActiveCfg = Debug|x64 + {522DAF2A-1F24-4742-B2C4-A956411F6AB2}.Debug AVX2|x64.Build.0 = Debug|x64 + {522DAF2A-1F24-4742-B2C4-A956411F6AB2}.Debug Clang AVX2|x64.ActiveCfg = Debug Clang|x64 + {522DAF2A-1F24-4742-B2C4-A956411F6AB2}.Debug Clang AVX2|x64.Build.0 = Debug Clang|x64 + {522DAF2A-1F24-4742-B2C4-A956411F6AB2}.Debug Clang|x64.ActiveCfg = Debug Clang|x64 + {522DAF2A-1F24-4742-B2C4-A956411F6AB2}.Debug Clang|x64.Build.0 = Debug Clang|x64 + {522DAF2A-1F24-4742-B2C4-A956411F6AB2}.Debug|x64.ActiveCfg = Debug|x64 + {522DAF2A-1F24-4742-B2C4-A956411F6AB2}.Debug|x64.Build.0 = Debug|x64 + {522DAF2A-1F24-4742-B2C4-A956411F6AB2}.Devel AVX2|x64.ActiveCfg = Devel|x64 + {522DAF2A-1F24-4742-B2C4-A956411F6AB2}.Devel AVX2|x64.Build.0 = Devel|x64 + {522DAF2A-1F24-4742-B2C4-A956411F6AB2}.Devel Clang AVX2|x64.ActiveCfg = Debug Clang|x64 + {522DAF2A-1F24-4742-B2C4-A956411F6AB2}.Devel Clang AVX2|x64.Build.0 = Debug Clang|x64 + {522DAF2A-1F24-4742-B2C4-A956411F6AB2}.Devel Clang|x64.ActiveCfg = Devel Clang|x64 + {522DAF2A-1F24-4742-B2C4-A956411F6AB2}.Devel Clang|x64.Build.0 = Devel Clang|x64 + {522DAF2A-1F24-4742-B2C4-A956411F6AB2}.Devel|x64.ActiveCfg = Devel|x64 + {522DAF2A-1F24-4742-B2C4-A956411F6AB2}.Devel|x64.Build.0 = Devel|x64 + {522DAF2A-1F24-4742-B2C4-A956411F6AB2}.Release AVX2|x64.ActiveCfg = Release|x64 + {522DAF2A-1F24-4742-B2C4-A956411F6AB2}.Release AVX2|x64.Build.0 = Release|x64 + {522DAF2A-1F24-4742-B2C4-A956411F6AB2}.Release Clang AVX2|x64.ActiveCfg = Release Clang|x64 + {522DAF2A-1F24-4742-B2C4-A956411F6AB2}.Release Clang AVX2|x64.Build.0 = Release Clang|x64 + {522DAF2A-1F24-4742-B2C4-A956411F6AB2}.Release Clang|x64.ActiveCfg = Release Clang|x64 + {522DAF2A-1F24-4742-B2C4-A956411F6AB2}.Release Clang|x64.Build.0 = Release Clang|x64 + {522DAF2A-1F24-4742-B2C4-A956411F6AB2}.Release|x64.ActiveCfg = Release|x64 + {522DAF2A-1F24-4742-B2C4-A956411F6AB2}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -765,6 +791,7 @@ Global {E960DFDF-1BD3-4C29-B251-D1A0919C9B09} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} {67D0160C-0FE4-44B9-AC2E-82BBCF4104DF} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} {1E3D706C-1D95-4E1B-BDF2-CA3D0007DF7F} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} + {522DAF2A-1F24-4742-B2C4-A956411F6AB2} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0BC474EA-3628-45D3-9DBC-E22D0B7E0F77} diff --git a/cmake/SearchForStuff.cmake b/cmake/SearchForStuff.cmake index 17adc7d45f..d4da10daca 100644 --- a/cmake/SearchForStuff.cmake +++ b/cmake/SearchForStuff.cmake @@ -130,6 +130,7 @@ add_library(fast_float INTERFACE) target_include_directories(fast_float INTERFACE 3rdparty/rapidyaml/rapidyaml/ext/c4core/src/c4/ext/fast_float/include) add_subdirectory(3rdparty/jpgd EXCLUDE_FROM_ALL) +add_subdirectory(3rdparty/libwebp EXCLUDE_FROM_ALL) add_subdirectory(3rdparty/simpleini EXCLUDE_FROM_ALL) add_subdirectory(3rdparty/imgui EXCLUDE_FROM_ALL) add_subdirectory(3rdparty/cpuinfo EXCLUDE_FROM_ALL) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 2457b414b0..9c423b8a48 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -190,6 +190,7 @@ target_link_libraries(common PRIVATE ${LIBC_LIBRARIES} PNG::PNG jpgd + WebP::libwebp ) target_link_libraries(common PUBLIC diff --git a/common/Image.cpp b/common/Image.cpp index 100f3ff749..1531155d78 100644 --- a/common/Image.cpp +++ b/common/Image.cpp @@ -24,6 +24,8 @@ #include "jpgd.h" #include "jpge.h" #include +#include +//#include using namespace Common; @@ -37,6 +39,11 @@ static bool JPEGBufferSaver(const RGBA8Image& image, std::vector* buffer, in static bool JPEGFileLoader(RGBA8Image* image, const char* filename, std::FILE* fp); static bool JPEGFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp, int quality); +static bool WebPBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size); +static bool WebPBufferSaver(const RGBA8Image& image, std::vector* buffer, int quality); +static bool WebPFileLoader(RGBA8Image* image, const char* filename, std::FILE* fp); +static bool WebPFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp, int quality); + struct FormatHandler { const char* extension; @@ -50,6 +57,7 @@ static constexpr FormatHandler s_format_handlers[] = { {"png", PNGBufferLoader, PNGBufferSaver, PNGFileLoader, PNGFileSaver}, {"jpg", JPEGBufferLoader, JPEGBufferSaver, JPEGFileLoader, JPEGFileSaver}, {"jpeg", JPEGBufferLoader, JPEGBufferSaver, JPEGFileLoader, JPEGFileSaver}, + {"webp", WebPBufferLoader, WebPBufferSaver, WebPFileLoader, WebPFileSaver}, }; static const FormatHandler* GetFormatHandler(const std::string_view& extension) @@ -155,7 +163,7 @@ bool RGBA8Image::SaveToFile(const char* filename, std::FILE* fp, int quality) co return false; } - if (!handler->file_saver(*this, filename, fp, quality)) + if (!IsValid() || !handler->file_saver(*this, filename, fp, quality)) return false; return (std::fflush(fp) == 0); @@ -175,7 +183,7 @@ std::optional> RGBA8Image::SaveToBuffer(const char* filename, in } ret = std::vector(); - if (!handler->buffer_saver(*this, &ret.value(), quality)) + if (!IsValid() || !handler->buffer_saver(*this, &ret.value(), quality)) ret.reset(); return ret; @@ -543,3 +551,63 @@ bool JPEGFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp, FileStream stream(fp); return JPEGCommonSaver(image, stream, quality); } + +bool WebPBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size) +{ + int width, height; + if (!WebPGetInfo(static_cast(buffer), buffer_size, &width, &height) || width <= 0 || height <= 0) + { + Console.Error("WebPGetInfo() failed"); + return false; + } + + std::vector pixels; + pixels.resize(static_cast(width) * static_cast(height)); + if (!WebPDecodeRGBAInto(static_cast(buffer), buffer_size, + reinterpret_cast(pixels.data()), sizeof(u32) * pixels.size(), + sizeof(u32) * static_cast(width))) + { + Console.Error("WebPDecodeRGBAInto() failed"); + return false; + } + + image->SetPixels(static_cast(width), static_cast(height), std::move(pixels)); + return true; +} + +bool WebPBufferSaver(const RGBA8Image& image, std::vector* buffer, int quality) +{ +#if 0 + u8* encoded_data; + const size_t encoded_size = WebPEncodeRGBA(reinterpret_cast(image.GetPixels()), + image.GetWidth(), image.GetHeight(), image.GetByteStride(), static_cast(quality), &encoded_data); + if (encoded_size == 0) + return false; + + buffer->resize(encoded_size); + std::memcpy(buffer->data(), encoded_data, encoded_size); + WebPFree(encoded_data); + return true; +#else + Console.Error("Not compiled with WebP encoder."); + return false; +#endif +} + +bool WebPFileLoader(RGBA8Image* image, const char* filename, std::FILE* fp) +{ + std::optional> data = FileSystem::ReadBinaryFile(fp); + if (!data.has_value()) + return false; + + return WebPBufferLoader(image, data->data(), data->size()); +} + +bool WebPFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp, int quality) +{ + std::vector buffer; + if (!WebPBufferSaver(image, &buffer, quality)) + return false; + + return (std::fwrite(buffer.data(), buffer.size(), 1, fp) == 1); +} diff --git a/common/common.vcxproj b/common/common.vcxproj index ca4a7b6dbc..11017546fc 100644 --- a/common/common.vcxproj +++ b/common/common.vcxproj @@ -34,9 +34,10 @@ %(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\rapidyaml\rapidyaml\ext\c4core\src\c4\ext\fast_float\include + %(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\fmt\fmt\include %(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\libpng %(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\jpgd - %(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\fmt\fmt\include + %(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\libwebp\libwebp\src Use PrecompiledHeader.h PrecompiledHeader.h @@ -178,6 +179,12 @@ {ed2f21fd-0a36-4a8f-9b90-e7d92a2acb63} + + {d6973076-9317-4ef2-a0b8-b7a18ac0713e} + + + {522daf2a-1f24-4742-b2c4-a956411f6ab2} +