Image: Swap stb for libpng/libjpeg

This commit is contained in:
Stenzek 2024-03-06 16:24:25 +10:00
parent e9c4416272
commit 8507c21860
No known key found for this signature in database
18 changed files with 198 additions and 12283 deletions

View File

@ -1,8 +1,6 @@
set(FMT_INSTALL OFF CACHE BOOL "") set(FMT_INSTALL OFF CACHE BOOL "")
add_subdirectory(fmt EXCLUDE_FROM_ALL) add_subdirectory(fmt EXCLUDE_FROM_ALL)
disable_compiler_warnings_for_target(fmt) disable_compiler_warnings_for_target(fmt)
add_subdirectory(stb EXCLUDE_FROM_ALL)
disable_compiler_warnings_for_target(stb)
add_subdirectory(minizip EXCLUDE_FROM_ALL) add_subdirectory(minizip EXCLUDE_FROM_ALL)
disable_compiler_warnings_for_target(minizip) disable_compiler_warnings_for_target(minizip)
add_subdirectory(lzma EXCLUDE_FROM_ALL) add_subdirectory(lzma EXCLUDE_FROM_ALL)

View File

@ -1,13 +0,0 @@
set(SRCS
include/stb_image.h
include/stb_image_resize.h
include/stb_image_write.h
src/stb_image.c
src/stb_image_resize.c
src/stb_image_write.c
)
add_library(stb ${SRCS})
target_include_directories(stb PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/src")
target_include_directories(stb INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include")
target_link_libraries(stb ZLIB::ZLIB Threads::Threads "${CMAKE_DL_LIBS}")

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +0,0 @@
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

View File

@ -1,2 +0,0 @@
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include "stb_image_resize.h"

View File

@ -1,28 +0,0 @@
#include <stdlib.h>
#include "zlib.h"
// https://github.com/nothings/stb/issues/113
static unsigned char* compress_for_stbiw(unsigned char* data, int data_len, int* out_len, int quality)
{
uLongf buf_size = compressBound(data_len);
// note that buf will be free'd by stb_image_write.h
// with STBIW_FREE() (plain free() by default)
unsigned char* buf = malloc(buf_size);
if (buf == NULL)
return NULL;
if (compress2(buf, &buf_size, data, data_len, quality) != Z_OK)
{
free(buf);
return NULL;
}
*out_len = buf_size;
return buf;
}
#define STBIW_ZLIB_COMPRESS compress_for_stbiw
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"

View File

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\msvc\vsprops\Configurations.props" />
<ItemGroup>
<ClCompile Include="src\stb_image.c" />
<ClCompile Include="src\stb_image_resize.c" />
<ClCompile Include="src\stb_image_write.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\stb_image.h" />
<ClInclude Include="include\stb_image_resize.h" />
<ClInclude Include="include\stb_image_write.h" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{ED601289-AC1A-46B8-A8ED-17DB9EB73423}</ProjectGuid>
</PropertyGroup>
<Import Project="..\msvc\vsprops\StaticLibrary.props" />
<ItemDefinitionGroup>
<ClCompile>
<WarningLevel>TurnOffAllWarnings</WarningLevel>
<AdditionalIncludeDirectories>$(SolutionDir)dep\zlib\include;$(ProjectDir)include;$(ProjectDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<Import Project="..\msvc\vsprops\Targets.props" />
</Project>

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="src\stb_image_write.c" />
<ClCompile Include="src\stb_image_resize.c" />
<ClCompile Include="src\stb_image.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\stb_image_write.h" />
<ClInclude Include="include\stb_image_resize.h" />
<ClInclude Include="include\stb_image.h" />
</ItemGroup>
</Project>

View File

@ -13,8 +13,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common", "src\common\common
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core", "src\core\core.vcxproj", "{868B98C8-65A1-494B-8346-250A73A48C0A}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core", "src\core\core.vcxproj", "{868B98C8-65A1-494B-8346-250A73A48C0A}"
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stb", "dep\stb\stb.vcxproj", "{ED601289-AC1A-46B8-A8ED-17DB9EB73423}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simpleini", "dep\simpleini\simpleini.vcxproj", "{3773F4CC-614E-4028-8595-22E08CA649E3}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simpleini", "dep\simpleini\simpleini.vcxproj", "{3773F4CC-614E-4028-8595-22E08CA649E3}"
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "duckstation-qt", "src\duckstation-qt\duckstation-qt.vcxproj", "{28F14272-0EC4-41BB-849F-182ADB81AF70}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "duckstation-qt", "src\duckstation-qt\duckstation-qt.vcxproj", "{28F14272-0EC4-41BB-849F-182ADB81AF70}"
@ -223,38 +221,6 @@ Global
{868B98C8-65A1-494B-8346-250A73A48C0A}.ReleaseLTCG-Clang|ARM64.Build.0 = ReleaseLTCG-Clang|ARM64 {868B98C8-65A1-494B-8346-250A73A48C0A}.ReleaseLTCG-Clang|ARM64.Build.0 = ReleaseLTCG-Clang|ARM64
{868B98C8-65A1-494B-8346-250A73A48C0A}.ReleaseLTCG-Clang|x64.ActiveCfg = ReleaseLTCG-Clang|x64 {868B98C8-65A1-494B-8346-250A73A48C0A}.ReleaseLTCG-Clang|x64.ActiveCfg = ReleaseLTCG-Clang|x64
{868B98C8-65A1-494B-8346-250A73A48C0A}.ReleaseLTCG-Clang|x64.Build.0 = ReleaseLTCG-Clang|x64 {868B98C8-65A1-494B-8346-250A73A48C0A}.ReleaseLTCG-Clang|x64.Build.0 = ReleaseLTCG-Clang|x64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.Debug|ARM64.ActiveCfg = Debug|ARM64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.Debug|ARM64.Build.0 = Debug|ARM64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.Debug|x64.ActiveCfg = Debug|x64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.Debug|x64.Build.0 = Debug|x64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.Debug-Clang|ARM64.ActiveCfg = Debug-Clang|ARM64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.Debug-Clang|ARM64.Build.0 = Debug-Clang|ARM64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.Debug-Clang|x64.ActiveCfg = Debug-Clang|x64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.Debug-Clang|x64.Build.0 = Debug-Clang|x64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.DebugFast|ARM64.ActiveCfg = DebugFast|ARM64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.DebugFast|ARM64.Build.0 = DebugFast|ARM64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.DebugFast|x64.ActiveCfg = DebugFast|x64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.DebugFast|x64.Build.0 = DebugFast|x64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.DebugFast-Clang|ARM64.ActiveCfg = DebugFast-Clang|ARM64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.DebugFast-Clang|ARM64.Build.0 = DebugFast-Clang|ARM64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.DebugFast-Clang|x64.ActiveCfg = DebugFast-Clang|x64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.DebugFast-Clang|x64.Build.0 = DebugFast-Clang|x64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.Release|ARM64.ActiveCfg = Release|ARM64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.Release|ARM64.Build.0 = Release|ARM64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.Release|x64.ActiveCfg = Release|x64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.Release|x64.Build.0 = Release|x64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.Release-Clang|ARM64.ActiveCfg = Release-Clang|ARM64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.Release-Clang|ARM64.Build.0 = Release-Clang|ARM64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.Release-Clang|x64.ActiveCfg = Release-Clang|x64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.Release-Clang|x64.Build.0 = Release-Clang|x64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.ReleaseLTCG|ARM64.ActiveCfg = ReleaseLTCG|ARM64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.ReleaseLTCG|ARM64.Build.0 = ReleaseLTCG|ARM64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.ReleaseLTCG|x64.ActiveCfg = ReleaseLTCG|x64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.ReleaseLTCG|x64.Build.0 = ReleaseLTCG|x64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.ReleaseLTCG-Clang|ARM64.ActiveCfg = ReleaseLTCG-Clang|ARM64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.ReleaseLTCG-Clang|ARM64.Build.0 = ReleaseLTCG-Clang|ARM64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.ReleaseLTCG-Clang|x64.ActiveCfg = ReleaseLTCG-Clang|x64
{ED601289-AC1A-46B8-A8ED-17DB9EB73423}.ReleaseLTCG-Clang|x64.Build.0 = ReleaseLTCG-Clang|x64
{3773F4CC-614E-4028-8595-22E08CA649E3}.Debug|ARM64.ActiveCfg = Debug|ARM64 {3773F4CC-614E-4028-8595-22E08CA649E3}.Debug|ARM64.ActiveCfg = Debug|ARM64
{3773F4CC-614E-4028-8595-22E08CA649E3}.Debug|ARM64.Build.0 = Debug|ARM64 {3773F4CC-614E-4028-8595-22E08CA649E3}.Debug|ARM64.Build.0 = Debug|ARM64
{3773F4CC-614E-4028-8595-22E08CA649E3}.Debug|x64.ActiveCfg = Debug|x64 {3773F4CC-614E-4028-8595-22E08CA649E3}.Debug|x64.ActiveCfg = Debug|x64
@ -1140,7 +1106,6 @@ Global
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{43540154-9E1E-409C-834F-B84BE5621388} = {BA490C0E-497D-4634-A21E-E65012006385} {43540154-9E1E-409C-834F-B84BE5621388} = {BA490C0E-497D-4634-A21E-E65012006385}
{BB08260F-6FBC-46AF-8924-090EE71360C6} = {BA490C0E-497D-4634-A21E-E65012006385} {BB08260F-6FBC-46AF-8924-090EE71360C6} = {BA490C0E-497D-4634-A21E-E65012006385}
{ED601289-AC1A-46B8-A8ED-17DB9EB73423} = {BA490C0E-497D-4634-A21E-E65012006385}
{3773F4CC-614E-4028-8595-22E08CA649E3} = {BA490C0E-497D-4634-A21E-E65012006385} {3773F4CC-614E-4028-8595-22E08CA649E3} = {BA490C0E-497D-4634-A21E-E65012006385}
{72F9423C-91EE-4487-AAC6-555ED6F61AA1} = {BA490C0E-497D-4634-A21E-E65012006385} {72F9423C-91EE-4487-AAC6-555ED6F61AA1} = {BA490C0E-497D-4634-A21E-E65012006385}
{8BDA439C-6358-45FB-9994-2FF083BABE06} = {BA490C0E-497D-4634-A21E-E65012006385} {8BDA439C-6358-45FB-9994-2FF083BABE06} = {BA490C0E-497D-4634-A21E-E65012006385}

View File

@ -131,7 +131,7 @@ target_precompile_headers(core PRIVATE "pch.h")
target_include_directories(core PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..") target_include_directories(core PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..")
target_include_directories(core PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..") target_include_directories(core PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
target_link_libraries(core PUBLIC Threads::Threads common util ZLIB::ZLIB) target_link_libraries(core PUBLIC Threads::Threads common util ZLIB::ZLIB)
target_link_libraries(core PRIVATE stb xxhash imgui rapidyaml rcheevos) target_link_libraries(core PRIVATE xxhash imgui rapidyaml rcheevos)
if(CPU_ARCH_X64) if(CPU_ARCH_X64)
target_compile_definitions(core PUBLIC "ENABLE_RECOMPILER=1" "ENABLE_NEWREC=1" "ENABLE_MMAP_FASTMEM=1") target_compile_definitions(core PUBLIC "ENABLE_RECOMPILER=1" "ENABLE_NEWREC=1" "ENABLE_MMAP_FASTMEM=1")

View File

@ -178,9 +178,6 @@
<ProjectReference Include="..\..\dep\rcheevos\rcheevos.vcxproj"> <ProjectReference Include="..\..\dep\rcheevos\rcheevos.vcxproj">
<Project>{4ba0a6d4-3ae1-42b2-9347-096fd023ff64}</Project> <Project>{4ba0a6d4-3ae1-42b2-9347-096fd023ff64}</Project>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\..\dep\stb\stb.vcxproj">
<Project>{ed601289-ac1a-46b8-a8ed-17db9eb73423}</Project>
</ProjectReference>
<ProjectReference Include="..\..\dep\vixl\vixl.vcxproj" Condition="'$(Platform)'=='ARM64'"> <ProjectReference Include="..\..\dep\vixl\vixl.vcxproj" Condition="'$(Platform)'=='ARM64'">
<Project>{8906836e-f06e-46e8-b11a-74e5e8c7b8fb}</Project> <Project>{8906836e-f06e-46e8-b11a-74e5e8c7b8fb}</Project>
</ProjectReference> </ProjectReference>

View File

@ -77,7 +77,7 @@ target_precompile_headers(util PRIVATE "pch.h")
target_include_directories(util PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..") target_include_directories(util PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..")
target_include_directories(util PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..") target_include_directories(util PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
target_link_libraries(util PUBLIC common simpleini imgui) target_link_libraries(util PUBLIC common simpleini imgui)
target_link_libraries(util PRIVATE stb libchdr ZLIB::ZLIB soundtouch xxhash Zstd::Zstd reshadefx) target_link_libraries(util PRIVATE libchdr JPEG::JPEG PNG::PNG ZLIB::ZLIB soundtouch xxhash Zstd::Zstd reshadefx)
if(ENABLE_CUBEB) if(ENABLE_CUBEB)
target_sources(util PRIVATE target_sources(util PRIVATE

View File

@ -3,6 +3,7 @@
#include "image.h" #include "image.h"
#include "common/bitutils.h"
#include "common/byte_stream.h" #include "common/byte_stream.h"
#include "common/file_system.h" #include "common/file_system.h"
#include "common/log.h" #include "common/log.h"
@ -10,50 +11,41 @@
#include "common/scoped_guard.h" #include "common/scoped_guard.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "stb_image.h" #include <jpeglib.h>
#include "stb_image_resize.h" #include <png.h>
#include "stb_image_write.h"
// clang-format off
#ifdef _MSC_VER
#pragma warning(disable : 4611) // warning C4611: interaction between '_setjmp' and C++ object destruction is non-portable
#pragma warning(disable : 4324) // warning C4324: '`anonymous-namespace'::JPEGErrorHandler': structure was padded due to alignment specifier
#endif
// clang-format on
Log_SetChannel(Image); Log_SetChannel(Image);
#if 0
static bool PNGBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size); static bool PNGBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size);
static bool PNGBufferSaver(const RGBA8Image& image, std::vector<u8>* buffer, int quality); static bool PNGBufferSaver(const RGBA8Image& image, std::vector<u8>* buffer, u8 quality);
static bool PNGFileLoader(RGBA8Image* image, const char* filename, std::FILE* fp); static bool PNGFileLoader(RGBA8Image* image, const char* filename, std::FILE* fp);
static bool PNGFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp, int quality); static bool PNGFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp, u8 quality);
static bool JPEGBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size); static bool JPEGBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size);
static bool JPEGBufferSaver(const RGBA8Image& image, std::vector<u8>* buffer, int quality); static bool JPEGBufferSaver(const RGBA8Image& image, std::vector<u8>* buffer, u8 quality);
static bool JPEGFileLoader(RGBA8Image* image, const char* filename, std::FILE* fp); 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 JPEGFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp, u8 quality);
#endif
static bool STBBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size);
static bool STBFileLoader(RGBA8Image* image, const char* filename, std::FILE* fp);
static bool STBBufferSaverPNG(const RGBA8Image& image, std::vector<u8>* buffer, int quality);
static bool STBBufferSaverJPEG(const RGBA8Image& image, std::vector<u8>* buffer, int quality);
static bool STBFileSaverPNG(const RGBA8Image& image, const char* filename, std::FILE* fp, int quality);
static bool STBFileSaverJPEG(const RGBA8Image& image, const char* filename, std::FILE* fp, int quality);
struct FormatHandler struct FormatHandler
{ {
const char* extension; const char* extension;
bool (*buffer_loader)(RGBA8Image*, const void*, size_t); bool (*buffer_loader)(RGBA8Image*, const void*, size_t);
bool (*buffer_saver)(const RGBA8Image&, std::vector<u8>*, int); bool (*buffer_saver)(const RGBA8Image&, std::vector<u8>*, u8);
bool (*file_loader)(RGBA8Image*, const char*, std::FILE*); bool (*file_loader)(RGBA8Image*, const char*, std::FILE*);
bool (*file_saver)(const RGBA8Image&, const char*, std::FILE*, int); bool (*file_saver)(const RGBA8Image&, const char*, std::FILE*, u8);
}; };
static constexpr FormatHandler s_format_handlers[] = { static constexpr FormatHandler s_format_handlers[] = {
#if 0
{"png", PNGBufferLoader, PNGBufferSaver, PNGFileLoader, PNGFileSaver}, {"png", PNGBufferLoader, PNGBufferSaver, PNGFileLoader, PNGFileSaver},
{"jpg", JPEGBufferLoader, JPEGBufferSaver, JPEGFileLoader, JPEGFileSaver}, {"jpg", JPEGBufferLoader, JPEGBufferSaver, JPEGFileLoader, JPEGFileSaver},
{"jpeg", JPEGBufferLoader, JPEGBufferSaver, JPEGFileLoader, JPEGFileSaver}, {"jpeg", JPEGBufferLoader, JPEGBufferSaver, JPEGFileLoader, JPEGFileSaver},
#else
{"png", STBBufferLoader, STBBufferSaverPNG, STBFileLoader, STBFileSaverPNG},
{"jpg", STBBufferLoader, STBBufferSaverJPEG, STBFileLoader, STBFileSaverJPEG},
{"jpeg", STBBufferLoader, STBBufferSaverJPEG, STBFileLoader, STBFileSaverJPEG},
#endif
}; };
static const FormatHandler* GetFormatHandler(const std::string_view& extension) static const FormatHandler* GetFormatHandler(const std::string_view& extension)
@ -110,7 +102,7 @@ bool RGBA8Image::LoadFromFile(const char* filename)
return LoadFromFile(filename, fp.get()); return LoadFromFile(filename, fp.get());
} }
bool RGBA8Image::SaveToFile(const char* filename, int quality) const bool RGBA8Image::SaveToFile(const char* filename, u8 quality) const
{ {
auto fp = FileSystem::OpenManagedCFile(filename, "wb"); auto fp = FileSystem::OpenManagedCFile(filename, "wb");
if (!fp) if (!fp)
@ -151,7 +143,7 @@ bool RGBA8Image::LoadFromBuffer(const char* filename, const void* buffer, size_t
return handler->buffer_loader(this, buffer, buffer_size); return handler->buffer_loader(this, buffer, buffer_size);
} }
bool RGBA8Image::SaveToFile(const char* filename, std::FILE* fp, int quality) const bool RGBA8Image::SaveToFile(const char* filename, std::FILE* fp, u8 quality) const
{ {
const std::string_view extension(Path::GetExtension(filename)); const std::string_view extension(Path::GetExtension(filename));
const FormatHandler* handler = GetFormatHandler(extension); const FormatHandler* handler = GetFormatHandler(extension);
@ -167,7 +159,7 @@ bool RGBA8Image::SaveToFile(const char* filename, std::FILE* fp, int quality) co
return (std::fflush(fp) == 0); return (std::fflush(fp) == 0);
} }
std::optional<std::vector<u8>> RGBA8Image::SaveToBuffer(const char* filename, int quality) const std::optional<std::vector<u8>> RGBA8Image::SaveToBuffer(const char* filename, u8 quality) const
{ {
std::optional<std::vector<u8>> ret; std::optional<std::vector<u8>> ret;
@ -186,6 +178,8 @@ std::optional<std::vector<u8>> RGBA8Image::SaveToBuffer(const char* filename, in
return ret; return ret;
} }
#if 0
void RGBA8Image::Resize(u32 new_width, u32 new_height) void RGBA8Image::Resize(u32 new_width, u32 new_height)
{ {
if (m_width == new_width && m_height == new_height) if (m_width == new_width && m_height == new_height)
@ -222,7 +216,7 @@ void RGBA8Image::Resize(const RGBA8Image* src_image, u32 new_width, u32 new_heig
} }
} }
#if 0 #endif
static bool PNGCommonLoader(RGBA8Image* image, png_structp png_ptr, png_infop info_ptr, std::vector<u32>& new_data, static bool PNGCommonLoader(RGBA8Image* image, png_structp png_ptr, png_infop info_ptr, std::vector<u32>& new_data,
std::vector<png_bytep>& row_pointers) std::vector<png_bytep>& row_pointers)
@ -336,7 +330,7 @@ bool PNGBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size)
return PNGCommonLoader(image, png_ptr, info_ptr, new_data, row_pointers); return PNGCommonLoader(image, png_ptr, info_ptr, new_data, row_pointers);
} }
static void PNGSaveCommon(const RGBA8Image& image, png_structp png_ptr, png_infop info_ptr, int quality) static void PNGSaveCommon(const RGBA8Image& image, png_structp png_ptr, png_infop info_ptr, u8 quality)
{ {
png_set_compression_level(png_ptr, std::clamp(quality / 10, 0, 9)); png_set_compression_level(png_ptr, std::clamp(quality / 10, 0, 9));
png_set_IHDR(png_ptr, info_ptr, image.GetWidth(), image.GetHeight(), 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, png_set_IHDR(png_ptr, info_ptr, image.GetWidth(), image.GetHeight(), 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE,
@ -349,7 +343,7 @@ static void PNGSaveCommon(const RGBA8Image& image, png_structp png_ptr, png_info
png_write_end(png_ptr, nullptr); png_write_end(png_ptr, nullptr);
} }
bool PNGFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp, int quality) bool PNGFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp, u8 quality)
{ {
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
png_infop info_ptr = nullptr; png_infop info_ptr = nullptr;
@ -380,7 +374,7 @@ bool PNGFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp,
return true; return true;
} }
bool PNGBufferSaver(const RGBA8Image& image, std::vector<u8>* buffer, int quality) bool PNGBufferSaver(const RGBA8Image& image, std::vector<u8>* buffer, u8 quality)
{ {
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
png_infop info_ptr = nullptr; png_infop info_ptr = nullptr;
@ -415,245 +409,200 @@ bool PNGBufferSaver(const RGBA8Image& image, std::vector<u8>* buffer, int qualit
return true; return true;
} }
bool JPEGBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size) namespace {
struct JPEGErrorHandler
{ {
int width, height, file_comps; jpeg_error_mgr err;
u8* data = jpgd::decompress_jpeg_image_from_memory(static_cast<const u8*>(buffer), static_cast<int>(buffer_size), jmp_buf jbuf;
&width, &height, &file_comps, 4, 0); };
if (!data) } // namespace
static bool HandleJPEGError(JPEGErrorHandler* eh)
{
jpeg_std_error(&eh->err);
eh->err.error_exit = [](j_common_ptr cinfo) {
JPEGErrorHandler* eh = (JPEGErrorHandler*)cinfo->err;
char msg[JMSG_LENGTH_MAX];
eh->err.format_message(cinfo, msg);
Log_ErrorFmt("libjpeg fatal error: {}", msg);
longjmp(eh->jbuf, 1);
};
if (setjmp(eh->jbuf) == 0)
return true;
return false;
}
template<typename T>
static bool WrapJPEGDecompress(RGBA8Image* image, T setup_func)
{
std::vector<u8> scanline;
JPEGErrorHandler err;
if (!HandleJPEGError(&err))
return false;
jpeg_decompress_struct info;
info.err = &err.err;
jpeg_create_decompress(&info);
setup_func(info);
const int herr = jpeg_read_header(&info, TRUE);
if (herr != JPEG_HEADER_OK)
{ {
Console.Error("jpgd::decompress_jpeg_image_from_memory() failed"); Log_ErrorFmt("jpeg_read_header() returned {}", herr);
return false; return false;
} }
image->SetPixels(static_cast<u32>(width), static_cast<u32>(height), reinterpret_cast<const u32*>(data)); if (info.image_width == 0 || info.image_height == 0 || info.num_components < 3)
std::free(data); {
return true; Log_ErrorFmt("Invalid image dimensions: {}x{}x{}", info.image_width, info.image_height, info.num_components);
return false;
}
info.out_color_space = JCS_RGB;
info.out_color_components = 3;
if (!jpeg_start_decompress(&info))
{
Log_ErrorFmt("jpeg_start_decompress() returned failure");
return false;
}
image->SetSize(info.image_width, info.image_height);
scanline.resize(info.image_width * 3);
u8* scanline_buffer[1] = {scanline.data()};
bool result = true;
for (u32 y = 0; y < info.image_height; y++)
{
if (jpeg_read_scanlines(&info, scanline_buffer, 1) != 1)
{
Log_ErrorFmt("jpeg_read_scanlines() failed at row {}", y);
result = false;
break;
}
// RGB -> RGBA
const u8* src_ptr = scanline.data();
u32* dst_ptr = image->GetRowPixels(y);
for (u32 x = 0; x < info.image_width; x++)
{
*(dst_ptr) =
(ZeroExtend32(src_ptr[0]) | (ZeroExtend32(src_ptr[1]) << 8) | (ZeroExtend32(src_ptr[2]) << 16) | 0xFF000000u);
}
}
jpeg_finish_decompress(&info);
jpeg_destroy_decompress(&info);
return result;
}
bool JPEGBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size)
{
return WrapJPEGDecompress(image, [buffer, buffer_size](jpeg_decompress_struct& info) {
jpeg_mem_src(&info, static_cast<const unsigned char*>(buffer), buffer_size);
});
} }
bool JPEGFileLoader(RGBA8Image* image, const char* filename, std::FILE* fp) bool JPEGFileLoader(RGBA8Image* image, const char* filename, std::FILE* fp)
{ {
class FileStream : public jpgd::jpeg_decoder_stream return WrapJPEGDecompress(image, [fp](jpeg_decompress_struct& info) { jpeg_stdio_src(&info, fp); });
{
std::FILE* m_fp;
bool m_error_flag = false;
bool m_eof_flag = false;
public:
explicit FileStream(std::FILE* fp_) : m_fp(fp_) {}
int read(jpgd::uint8* pBuf, int max_bytes_to_read, bool* pEOF_flag) override
{
if (m_eof_flag)
{
*pEOF_flag = true;
return 0;
}
if (m_error_flag)
return -1;
int bytes_read = static_cast<int>(std::fread(pBuf, 1, max_bytes_to_read, m_fp));
if (bytes_read < max_bytes_to_read)
{
if (std::ferror(m_fp))
{
m_error_flag = true;
return -1;
}
m_eof_flag = true;
*pEOF_flag = true;
}
return bytes_read;
}
};
FileStream stream(fp);
int width, height, file_comps;
u8* data = jpgd::decompress_jpeg_image_from_stream(&stream, &width, &height, &file_comps, 4, 0);
if (!data)
{
Console.Error("jpgd::decompress_jpeg_image_from_stream() failed");
return false;
}
image->SetPixels(static_cast<u32>(width), static_cast<u32>(height), reinterpret_cast<const u32*>(data));
std::free(data);
return true;
} }
static bool JPEGCommonSaver(const RGBA8Image& image, jpge::output_stream& stream, int quality) template<typename T>
static bool WrapJPEGCompress(const RGBA8Image& image, u8 quality, T setup_func)
{ {
jpge::params params; std::vector<u8> scanline;
params.m_quality = quality;
jpge::jpeg_encoder dst_image; JPEGErrorHandler err;
if (!dst_image.init(&stream, image.GetWidth(), image.GetHeight(), 3, params)) if (!HandleJPEGError(&err))
return false; return false;
// for RGBA->RGB jpeg_compress_struct info;
std::vector<u8> row; info.err = &err.err;
row.resize(image.GetWidth() * 3); jpeg_create_compress(&info);
setup_func(info);
for (uint pass_index = 0; pass_index < dst_image.get_total_passes(); pass_index++) info.image_width = image.GetWidth();
info.image_height = image.GetHeight();
info.in_color_space = JCS_RGB;
info.input_components = 3;
jpeg_set_defaults(&info);
jpeg_set_quality(&info, quality, TRUE);
jpeg_start_compress(&info, TRUE);
scanline.resize(image.GetWidth() * 3);
u8* scanline_buffer[1] = {scanline.data()};
bool result = true;
for (u32 y = 0; y < info.image_height; y++)
{ {
for (u32 i = 0; i < image.GetHeight(); i++) // RGBA -> RGB
u8* dst_ptr = scanline.data();
const u32* src_ptr = image.GetRowPixels(y);
for (u32 x = 0; x < info.image_width; x++)
{ {
const u8* row_in = reinterpret_cast<const u8*>(image.GetRowPixels(i)); const u32 rgba = *(src_ptr++);
u8* row_out = row.data(); *(dst_ptr++) = Truncate8(rgba);
for (u32 j = 0; j < image.GetWidth(); j++) *(dst_ptr++) = Truncate8(rgba >> 8);
{ *(dst_ptr++) = Truncate8(rgba >> 16);
*(row_out++) = *(row_in++); }
*(row_out++) = *(row_in++);
*(row_out++) = *(row_in++); if (jpeg_write_scanlines(&info, scanline_buffer, 1) != 1)
row_in++; {
} Log_ErrorFmt("jpeg_write_scanlines() failed at row {}", y);
result = false;
if (!dst_image.process_scanline(row.data())) break;
return false;
} }
if (!dst_image.process_scanline(NULL))
return false;
} }
dst_image.deinit(); jpeg_finish_compress(&info);
jpeg_destroy_compress(&info);
return true; return result;
} }
bool JPEGBufferSaver(const RGBA8Image& image, std::vector<u8>* buffer, int quality) bool JPEGBufferSaver(const RGBA8Image& image, std::vector<u8>* buffer, u8 quality)
{ {
class BufferStream : public jpge::output_stream
{
std::vector<u8>* buffer;
public:
explicit BufferStream(std::vector<u8>* buffer_) : buffer(buffer_) {}
bool put_buf(const void* Pbuf, int len) override
{
const size_t old_size = buffer->size();
buffer->resize(buffer->size() + static_cast<size_t>(len));
std::memcpy(buffer->data() + old_size, Pbuf, static_cast<size_t>(len));
return true;
}
};
// give enough space to avoid reallocs // give enough space to avoid reallocs
buffer->reserve(image.GetWidth() * image.GetHeight() * 2); buffer->resize(image.GetWidth() * image.GetHeight() * 2);
BufferStream stream(buffer); struct MemCallback
return JPEGCommonSaver(image, stream, quality);
}
bool JPEGFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp, int quality)
{
class FileStream : public jpge::output_stream
{ {
std::FILE* m_fp; jpeg_destination_mgr mgr;
bool m_error_flag = false; std::vector<u8>* buffer;
size_t buffer_used;
public:
explicit FileStream(std::FILE* fp_) : m_fp(fp_) {}
bool put_buf(const void* Pbuf, int len) override
{
if (m_error_flag)
return false;
if (std::fwrite(Pbuf, len, 1, m_fp) != 1)
{
m_error_flag = true;
return false;
}
return true;
}
}; };
FileStream stream(fp); MemCallback cb;
return JPEGCommonSaver(image, stream, quality); cb.buffer = buffer;
} cb.buffer_used = 0;
cb.mgr.next_output_byte = buffer->data();
cb.mgr.free_in_buffer = buffer->size();
cb.mgr.init_destination = [](j_compress_ptr cinfo) {};
cb.mgr.empty_output_buffer = [](j_compress_ptr cinfo) -> boolean {
MemCallback* cb = (MemCallback*)cinfo->dest;
#endif // double size
cb->buffer_used = cb->buffer->size();
cb->buffer->resize(cb->buffer->size() * 2);
cb->mgr.next_output_byte = cb->buffer->data() + cb->buffer_used;
cb->mgr.free_in_buffer = cb->buffer->size() - cb->buffer_used;
return TRUE;
};
cb.mgr.term_destination = [](j_compress_ptr cinfo) {
MemCallback* cb = (MemCallback*)cinfo->dest;
bool STBBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size) // get final size
{ cb->buffer->resize(cb->buffer->size() - cb->mgr.free_in_buffer);
int width, height, file_channels;
u8* pixel_data = stbi_load_from_memory(static_cast<const stbi_uc*>(buffer), static_cast<int>(buffer_size), &width,
&height, &file_channels, 4);
if (!pixel_data)
{
const char* error_reason = stbi_failure_reason();
Log_ErrorPrintf("Failed to load image from memory: %s", error_reason ? error_reason : "unknown error");
return false;
}
image->SetPixels(static_cast<u32>(width), static_cast<u32>(height), reinterpret_cast<const u32*>(pixel_data));
stbi_image_free(pixel_data);
return true;
}
bool STBFileLoader(RGBA8Image* image, const char* filename, std::FILE* fp)
{
int width, height, file_channels;
u8* pixel_data = stbi_load_from_file(fp, &width, &height, &file_channels, 4);
if (!pixel_data)
{
const char* error_reason = stbi_failure_reason();
Log_ErrorPrintf("Failed to load image from memory: %s", error_reason ? error_reason : "unknown error");
return false;
}
image->SetPixels(static_cast<u32>(width), static_cast<u32>(height), reinterpret_cast<const u32*>(pixel_data));
stbi_image_free(pixel_data);
return true;
}
bool STBBufferSaverPNG(const RGBA8Image& image, std::vector<u8>* buffer, int quality)
{
const auto write_func = [](void* context, void* data, int size) {
std::vector<u8>* buffer = reinterpret_cast<std::vector<u8>*>(data);
const u32 len = static_cast<u32>(size);
buffer->resize(buffer->size() + len);
std::memcpy(buffer->data(), data, len);
}; };
return (stbi_write_png_to_func(write_func, buffer, image.GetWidth(), image.GetHeight(), 4, image.GetPixels(), return WrapJPEGCompress(image, quality, [&cb](jpeg_compress_struct& info) { info.dest = &cb.mgr; });
image.GetPitch()) != 0);
} }
bool STBBufferSaverJPEG(const RGBA8Image& image, std::vector<u8>* buffer, int quality) bool JPEGFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp, u8 quality)
{ {
const auto write_func = [](void* context, void* data, int size) { return WrapJPEGCompress(image, quality, [fp](jpeg_compress_struct& info) { jpeg_stdio_dest(&info, fp); });
std::vector<u8>* buffer = reinterpret_cast<std::vector<u8>*>(data);
const u32 len = static_cast<u32>(size);
buffer->resize(buffer->size() + len);
std::memcpy(buffer->data(), data, len);
};
return (stbi_write_jpg_to_func(write_func, buffer, image.GetWidth(), image.GetHeight(), 4, image.GetPixels(),
quality) != 0);
}
bool STBFileSaverPNG(const RGBA8Image& image, const char* filename, std::FILE* fp, int quality)
{
const auto write_func = [](void* context, void* data, int size) {
std::fwrite(data, 1, size, static_cast<std::FILE*>(context));
};
return (stbi_write_png_to_func(write_func, fp, image.GetWidth(), image.GetHeight(), 4, image.GetPixels(),
image.GetPitch()) != 0);
}
bool STBFileSaverJPEG(const RGBA8Image& image, const char* filename, std::FILE* fp, int quality)
{
const auto write_func = [](void* context, void* data, int size) {
std::fwrite(data, 1, size, static_cast<std::FILE*>(context));
};
return (stbi_write_jpg_to_func(write_func, fp, image.GetWidth(), image.GetHeight(), 4, image.GetPixels(), quality) !=
0);
} }

View File

@ -114,7 +114,7 @@ protected:
class RGBA8Image : public Image<u32> class RGBA8Image : public Image<u32>
{ {
public: public:
static constexpr int DEFAULT_SAVE_QUALITY = 85; static constexpr u8 DEFAULT_SAVE_QUALITY = 85;
RGBA8Image(); RGBA8Image();
RGBA8Image(u32 width, u32 height); RGBA8Image(u32 width, u32 height);
@ -130,10 +130,7 @@ public:
bool LoadFromFile(const char* filename, std::FILE* fp); bool LoadFromFile(const char* filename, std::FILE* fp);
bool LoadFromBuffer(const char* filename, const void* buffer, size_t buffer_size); bool LoadFromBuffer(const char* filename, const void* buffer, size_t buffer_size);
bool SaveToFile(const char* filename, int quality = DEFAULT_SAVE_QUALITY) const; bool SaveToFile(const char* filename, u8 quality = DEFAULT_SAVE_QUALITY) const;
bool SaveToFile(const char* filename, std::FILE* fp, int quality = DEFAULT_SAVE_QUALITY) const; bool SaveToFile(const char* filename, std::FILE* fp, u8 quality = DEFAULT_SAVE_QUALITY) const;
std::optional<std::vector<u8>> SaveToBuffer(const char* filename, int quality = DEFAULT_SAVE_QUALITY) const; std::optional<std::vector<u8>> SaveToBuffer(const char* filename, u8 quality = DEFAULT_SAVE_QUALITY) const;
void Resize(u32 new_width, u32 new_height);
void Resize(const RGBA8Image* src_image, u32 new_width, u32 new_height);
}; };

View File

@ -8,7 +8,7 @@
<PreprocessorDefinitions>ENABLE_CUBEB=1;ENABLE_SDL2=1;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>ENABLE_CUBEB=1;ENABLE_SDL2=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Platform)'!='ARM64'">%(PreprocessorDefinitions);ENABLE_OPENGL=1;ENABLE_VULKAN=1</PreprocessorDefinitions> <PreprocessorDefinitions Condition="'$(Platform)'!='ARM64'">%(PreprocessorDefinitions);ENABLE_OPENGL=1;ENABLE_VULKAN=1</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Platform)'=='ARM64'">%(PreprocessorDefinitions);SOUNDTOUCH_USE_NEON</PreprocessorDefinitions> <PreprocessorDefinitions Condition="'$(Platform)'=='ARM64'">%(PreprocessorDefinitions);SOUNDTOUCH_USE_NEON</PreprocessorDefinitions>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\soundtouch\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\libchdr\include;$(SolutionDir)dep\cubeb\include;$(SolutionDir)dep\d3d12ma\include;$(SolutionDir)dep\zstd\lib;$(SolutionDir)dep\stb\include</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\soundtouch\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\libchdr\include;$(SolutionDir)dep\cubeb\include;$(SolutionDir)dep\d3d12ma\include;$(SolutionDir)dep\zstd\lib;$(SolutionDir)dep\libpng\include;$(SolutionDir)dep\libjpeg\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Platform)'!='ARM64'">%(AdditionalIncludeDirectories);$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan\include;$(SolutionDir)dep\glslang</AdditionalIncludeDirectories> <AdditionalIncludeDirectories Condition="'$(Platform)'!='ARM64'">%(AdditionalIncludeDirectories);$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan\include;$(SolutionDir)dep\glslang</AdditionalIncludeDirectories>
</ClCompile> </ClCompile>
</ItemDefinitionGroup> </ItemDefinitionGroup>

View File

@ -253,6 +253,12 @@
<ProjectReference Include="..\..\dep\libchdr\libchdr.vcxproj"> <ProjectReference Include="..\..\dep\libchdr\libchdr.vcxproj">
<Project>{425d6c99-d1c8-43c2-b8ac-4d7b1d941017}</Project> <Project>{425d6c99-d1c8-43c2-b8ac-4d7b1d941017}</Project>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\..\dep\libjpeg\libjpeg.vcxproj">
<Project>{ec3b6685-0b6e-4767-84ab-39b75eead2e2}</Project>
</ProjectReference>
<ProjectReference Include="..\..\dep\libpng\libpng.vcxproj">
<Project>{9fd2abcd-2dcd-4302-be5c-df0ba8431fa5}</Project>
</ProjectReference>
<ProjectReference Include="..\..\dep\reshadefx\reshadefx.vcxproj"> <ProjectReference Include="..\..\dep\reshadefx\reshadefx.vcxproj">
<Project>{27b8d4bb-4f01-4432-bc14-9bf6ca458eee}</Project> <Project>{27b8d4bb-4f01-4432-bc14-9bf6ca458eee}</Project>
</ProjectReference> </ProjectReference>
@ -265,9 +271,6 @@
<ProjectReference Include="..\..\dep\glslang\glslang.vcxproj" Condition="'$(Platform)'!='ARM64'"> <ProjectReference Include="..\..\dep\glslang\glslang.vcxproj" Condition="'$(Platform)'!='ARM64'">
<Project>{7f909e29-4808-4bd9-a60c-56c51a3aaec2}</Project> <Project>{7f909e29-4808-4bd9-a60c-56c51a3aaec2}</Project>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\..\dep\stb\stb.vcxproj">
<Project>{ed601289-ac1a-46b8-a8ed-17db9eb73423}</Project>
</ProjectReference>
<ProjectReference Include="..\..\dep\zstd\zstd.vcxproj"> <ProjectReference Include="..\..\dep\zstd\zstd.vcxproj">
<Project>{73ee0c55-6ffe-44e7-9c12-baa52434a797}</Project> <Project>{73ee0c55-6ffe-44e7-9c12-baa52434a797}</Project>
</ProjectReference> </ProjectReference>