Image: Swap stb for libpng/libjpeg

This commit is contained in:
Stenzek 2024-03-06 16:24:25 +10:00
parent e9c4416272
commit c854b8f85e
No known key found for this signature in database
19 changed files with 270 additions and 12288 deletions

View File

@ -358,6 +358,78 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
</pre>
<h3>libjpeg - <a href="https://ijg.org/">https://ijg.org/</a></h3>
<pre>
The authors make NO WARRANTY or representation, either express or implied,
with respect to this software, its quality, accuracy, merchantability, or
fitness for a particular purpose. This software is provided "AS IS", and you,
its user, assume the entire risk as to its quality and accuracy.
This software is copyright (C) 1991-2024, Thomas G. Lane, Guido Vollbeding.
All Rights Reserved except as specified below.
Permission is hereby granted to use, copy, modify, and distribute this
software (or portions thereof) for any purpose, without fee, subject to these
conditions:
(1) If any part of the source code for this software is distributed, then this
README file must be included, with this copyright and no-warranty notice
unaltered; and any additions, deletions, or changes to the original files
must be clearly indicated in accompanying documentation.
(2) If only executable code is distributed, then the accompanying
documentation must state that "this software is based in part on the work of
the Independent JPEG Group".
(3) Permission for use of this software is granted only if the user accepts
full responsibility for any undesirable consequences; the authors accept
NO LIABILITY for damages of any kind.
These conditions apply to any software derived from or based on the IJG code,
not just to the unmodified library. If you use our work, you ought to
acknowledge us.
Permission is NOT granted for the use of any IJG author's name or company name
in advertising or publicity relating to this software or products derived from
it. This software may be referred to only as "the Independent JPEG Group's
software".
We specifically permit and encourage the use of this software as the basis of
commercial products, provided that all warranty or liability claims are
assumed by the product vendor.
</pre>
<h3>libpng - <a href="http://www.libpng.org/pub/png/libpng.html">http://www.libpng.org/pub/png/libpng.html</a></h3>
<pre>
* Copyright (c) 1995-2024 The PNG Reference Library Authors.
* Copyright (c) 2018-2024 Cosmin Truta.
* Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson.
* Copyright (c) 1996-1997 Andreas Dilger.
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
The software is supplied "as is", without warranty of any kind,
express or implied, including, without limitation, the warranties
of merchantability, fitness for a particular purpose, title, and
non-infringement. In no event shall the Copyright owners, or
anyone distributing the software, be liable for any damages or
other liability, whether in contract, tort or otherwise, arising
from, out of, or in connection with the software, or the use or
other dealings in the software, even if advised of the possibility
of such damage.
Permission is hereby granted to use, copy, modify, and distribute
this software, or portions hereof, for any purpose, without fee,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you
must not claim that you wrote the original software. If you
use this software in a product, an acknowledgment in the product
documentation would be appreciated, but is not required.
2. Altered source versions must be plainly marked as such, and must
not be misrepresented as being the original software.
3. This Copyright notice may not be removed or altered from any
source or altered source distribution.
</pre>
<h3>LLVM - <a href="https://github.com/llvm/llvm-project">https://github.com/llvm/llvm-project</a></h3>
<pre>
Apache License
@ -1580,11 +1652,6 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
</pre>
<h3>stb libraries - <a href="https://github.com/nothings/stb">https://github.com/nothings/stb</a></h3>
These libraries are in the public domain. You can do anything you want with them. You have no legal obligation to do anything else, although I appreciate attribution.
They are also licensed under the MIT open source license, if you have lawyers who are unhappy with public domain. Every source file includes an explicit dual-license for you to choose from.
<h3>vixl - <a href="https://git.linaro.org/arm/vixl.git">https://git.linaro.org/arm/vixl.git</a></h3>
<pre>
LICENCE

View File

@ -1,8 +1,6 @@
set(FMT_INSTALL OFF CACHE BOOL "")
add_subdirectory(fmt EXCLUDE_FROM_ALL)
disable_compiler_warnings_for_target(fmt)
add_subdirectory(stb EXCLUDE_FROM_ALL)
disable_compiler_warnings_for_target(stb)
add_subdirectory(minizip EXCLUDE_FROM_ALL)
disable_compiler_warnings_for_target(minizip)
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
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core", "src\core\core.vcxproj", "{868B98C8-65A1-494B-8346-250A73A48C0A}"
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}"
EndProject
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|x64.ActiveCfg = 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.Build.0 = Debug|ARM64
{3773F4CC-614E-4028-8595-22E08CA649E3}.Debug|x64.ActiveCfg = Debug|x64
@ -1140,7 +1106,6 @@ Global
GlobalSection(NestedProjects) = preSolution
{43540154-9E1E-409C-834F-B84BE5621388} = {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}
{72F9423C-91EE-4487-AAC6-555ED6F61AA1} = {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 PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
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)
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">
<Project>{4ba0a6d4-3ae1-42b2-9347-096fd023ff64}</Project>
</ProjectReference>
<ProjectReference Include="..\..\dep\stb\stb.vcxproj">
<Project>{ed601289-ac1a-46b8-a8ed-17db9eb73423}</Project>
</ProjectReference>
<ProjectReference Include="..\..\dep\vixl\vixl.vcxproj" Condition="'$(Platform)'=='ARM64'">
<Project>{8906836e-f06e-46e8-b11a-74e5e8c7b8fb}</Project>
</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 PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
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)
target_sources(util PRIVATE

View File

@ -3,6 +3,7 @@
#include "image.h"
#include "common/bitutils.h"
#include "common/byte_stream.h"
#include "common/file_system.h"
#include "common/log.h"
@ -10,50 +11,41 @@
#include "common/scoped_guard.h"
#include "common/string_util.h"
#include "stb_image.h"
#include "stb_image_resize.h"
#include "stb_image_write.h"
#include <jpeglib.h>
#include <png.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);
#if 0
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 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 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 JPEGFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp, int 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);
static bool JPEGFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp, u8 quality);
struct FormatHandler
{
const char* extension;
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_saver)(const RGBA8Image&, const char*, std::FILE*, int);
bool (*file_saver)(const RGBA8Image&, const char*, std::FILE*, u8);
};
static constexpr FormatHandler s_format_handlers[] = {
#if 0
{"png", PNGBufferLoader, PNGBufferSaver, PNGFileLoader, PNGFileSaver},
{"jpg", 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)
@ -110,7 +102,7 @@ bool RGBA8Image::LoadFromFile(const char* filename)
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");
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);
}
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 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);
}
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;
@ -186,6 +178,8 @@ std::optional<std::vector<u8>> RGBA8Image::SaveToBuffer(const char* filename, in
return ret;
}
#if 0
void RGBA8Image::Resize(u32 new_width, u32 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,
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);
}
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_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);
}
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_infop info_ptr = nullptr;
@ -380,7 +374,7 @@ bool PNGFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp,
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_infop info_ptr = nullptr;
@ -415,245 +409,200 @@ bool PNGBufferSaver(const RGBA8Image& image, std::vector<u8>* buffer, int qualit
return true;
}
bool JPEGBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size)
namespace {
struct JPEGErrorHandler
{
int width, height, file_comps;
u8* data = jpgd::decompress_jpeg_image_from_memory(static_cast<const u8*>(buffer), static_cast<int>(buffer_size),
&width, &height, &file_comps, 4, 0);
if (!data)
jpeg_error_mgr err;
jmp_buf jbuf;
};
} // namespace
static bool HandleJPEGError(JPEGErrorHandler* eh)
{
Console.Error("jpgd::decompress_jpeg_image_from_memory() failed");
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;
}
image->SetPixels(static_cast<u32>(width), static_cast<u32>(height), reinterpret_cast<const u32*>(data));
std::free(data);
return true;
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)
{
Log_ErrorFmt("jpeg_read_header() returned {}", herr);
return false;
}
if (info.image_width == 0 || info.image_height == 0 || info.num_components < 3)
{
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)
{
class FileStream : public jpgd::jpeg_decoder_stream
{
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;
return WrapJPEGDecompress(image, [fp](jpeg_decompress_struct& info) { jpeg_stdio_src(&info, fp); });
}
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)
template<typename T>
static bool WrapJPEGCompress(const RGBA8Image& image, u8 quality, T setup_func)
{
if (std::ferror(m_fp))
{
m_error_flag = true;
return -1;
}
std::vector<u8> scanline;
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)
{
jpge::params params;
params.m_quality = quality;
jpge::jpeg_encoder dst_image;
if (!dst_image.init(&stream, image.GetWidth(), image.GetHeight(), 3, params))
JPEGErrorHandler err;
if (!HandleJPEGError(&err))
return false;
// for RGBA->RGB
std::vector<u8> row;
row.resize(image.GetWidth() * 3);
jpeg_compress_struct info;
info.err = &err.err;
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));
u8* row_out = row.data();
for (u32 j = 0; j < image.GetWidth(); j++)
{
*(row_out++) = *(row_in++);
*(row_out++) = *(row_in++);
*(row_out++) = *(row_in++);
row_in++;
const u32 rgba = *(src_ptr++);
*(dst_ptr++) = Truncate8(rgba);
*(dst_ptr++) = Truncate8(rgba >> 8);
*(dst_ptr++) = Truncate8(rgba >> 16);
}
if (!dst_image.process_scanline(row.data()))
return false;
}
if (!dst_image.process_scanline(NULL))
return false;
}
dst_image.deinit();
return true;
}
bool JPEGBufferSaver(const RGBA8Image& image, std::vector<u8>* buffer, int quality)
if (jpeg_write_scanlines(&info, scanline_buffer, 1) != 1)
{
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;
Log_ErrorFmt("jpeg_write_scanlines() failed at row {}", y);
result = false;
break;
}
}
};
jpeg_finish_compress(&info);
jpeg_destroy_compress(&info);
return result;
}
bool JPEGBufferSaver(const RGBA8Image& image, std::vector<u8>* buffer, u8 quality)
{
// give enough space to avoid reallocs
buffer->reserve(image.GetWidth() * image.GetHeight() * 2);
buffer->resize(image.GetWidth() * image.GetHeight() * 2);
BufferStream stream(buffer);
return JPEGCommonSaver(image, stream, quality);
}
bool JPEGFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp, int quality)
struct MemCallback
{
class FileStream : public jpge::output_stream
{
std::FILE* m_fp;
bool m_error_flag = false;
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;
}
jpeg_destination_mgr mgr;
std::vector<u8>* buffer;
size_t buffer_used;
};
FileStream stream(fp);
return JPEGCommonSaver(image, stream, quality);
}
MemCallback cb;
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)
{
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);
// get final size
cb->buffer->resize(cb->buffer->size() - cb->mgr.free_in_buffer);
};
return (stbi_write_png_to_func(write_func, buffer, image.GetWidth(), image.GetHeight(), 4, image.GetPixels(),
image.GetPitch()) != 0);
return WrapJPEGCompress(image, quality, [&cb](jpeg_compress_struct& info) { info.dest = &cb.mgr; });
}
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) {
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);
return WrapJPEGCompress(image, quality, [fp](jpeg_compress_struct& info) { jpeg_stdio_dest(&info, fp); });
}

View File

@ -114,7 +114,7 @@ protected:
class RGBA8Image : public Image<u32>
{
public:
static constexpr int DEFAULT_SAVE_QUALITY = 85;
static constexpr u8 DEFAULT_SAVE_QUALITY = 85;
RGBA8Image();
RGBA8Image(u32 width, u32 height);
@ -130,10 +130,7 @@ public:
bool LoadFromFile(const char* filename, std::FILE* fp);
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, std::FILE* fp, int quality = DEFAULT_SAVE_QUALITY) const;
std::optional<std::vector<u8>> SaveToBuffer(const char* filename, int 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);
bool SaveToFile(const char* filename, u8 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, u8 quality = DEFAULT_SAVE_QUALITY) const;
};

View File

@ -8,7 +8,7 @@
<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);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>
</ClCompile>
</ItemDefinitionGroup>

View File

@ -253,6 +253,12 @@
<ProjectReference Include="..\..\dep\libchdr\libchdr.vcxproj">
<Project>{425d6c99-d1c8-43c2-b8ac-4d7b1d941017}</Project>
</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">
<Project>{27b8d4bb-4f01-4432-bc14-9bf6ca458eee}</Project>
</ProjectReference>
@ -265,9 +271,6 @@
<ProjectReference Include="..\..\dep\glslang\glslang.vcxproj" Condition="'$(Platform)'!='ARM64'">
<Project>{7f909e29-4808-4bd9-a60c-56c51a3aaec2}</Project>
</ProjectReference>
<ProjectReference Include="..\..\dep\stb\stb.vcxproj">
<Project>{ed601289-ac1a-46b8-a8ed-17db9eb73423}</Project>
</ProjectReference>
<ProjectReference Include="..\..\dep\zstd\zstd.vcxproj">
<Project>{73ee0c55-6ffe-44e7-9c12-baa52434a797}</Project>
</ProjectReference>