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 255 additions and 12273 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)
{
Console.Error("jpgd::decompress_jpeg_image_from_memory() failed");
return false;
}
image->SetPixels(static_cast<u32>(width), static_cast<u32>(height), reinterpret_cast<const u32*>(data));
std::free(data);
return true;
}
jpeg_error_mgr err;
jmp_buf jbuf;
};
} // namespace
bool JPEGFileLoader(RGBA8Image* image, const char* filename, std::FILE* fp)
static bool HandleJPEGError(JPEGErrorHandler* eh)
{
class FileStream : public jpgd::jpeg_decoder_stream
{
std::FILE* m_fp;
bool m_error_flag = false;
bool m_eof_flag = false;
jpeg_std_error(&eh->err);
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;
}
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);
};
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;
}
if (setjmp(eh->jbuf) == 0)
return true;
image->SetPixels(static_cast<u32>(width), static_cast<u32>(height), reinterpret_cast<const u32*>(data));
std::free(data);
return true;
return false;
}
static bool JPEGCommonSaver(const RGBA8Image& image, jpge::output_stream& stream, int quality)
template<typename T>
static bool WrapJPEGDecompress(RGBA8Image* image, T setup_func)
{
jpge::params params;
params.m_quality = quality;
std::vector<u8> scanline;
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_decompress_struct info;
info.err = &err.err;
jpeg_create_decompress(&info);
setup_func(info);
for (uint pass_index = 0; pass_index < dst_image.get_total_passes(); pass_index++)
const int herr = jpeg_read_header(&info, TRUE);
if (herr != JPEG_HEADER_OK)
{
for (u32 i = 0; i < image.GetHeight(); i++)
{
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++;
}
if (!dst_image.process_scanline(row.data()))
return false;
}
if (!dst_image.process_scanline(NULL))
return false;
Log_ErrorFmt("jpeg_read_header() returned {}", herr);
return false;
}
dst_image.deinit();
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;
}
return true;
}
info.out_color_space = JCS_RGB;
info.out_color_components = 3;
bool JPEGBufferSaver(const RGBA8Image& image, std::vector<u8>* buffer, int quality)
{
class BufferStream : public jpge::output_stream
if (!jpeg_start_decompress(&info))
{
std::vector<u8>* buffer;
Log_ErrorFmt("jpeg_start_decompress() returned failure");
return false;
}
public:
explicit BufferStream(std::vector<u8>* buffer_) : buffer(buffer_) {}
image->SetSize(info.image_width, info.image_height);
scanline.resize(info.image_width * 3);
bool put_buf(const void* Pbuf, int len) override
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)
{
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_read_scanlines() failed at row {}", y);
result = false;
break;
}
};
// give enough space to avoid reallocs
buffer->reserve(image.GetWidth() * image.GetHeight() * 2);
// 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);
}
}
BufferStream stream(buffer);
return JPEGCommonSaver(image, stream, quality);
jpeg_finish_decompress(&info);
jpeg_destroy_decompress(&info);
return result;
}
bool JPEGFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp, int quality)
bool JPEGBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size)
{
class FileStream : public jpge::output_stream
{
std::FILE* m_fp;
bool m_error_flag = false;
return WrapJPEGDecompress(image, [buffer, buffer_size](jpeg_decompress_struct& info) {
jpeg_mem_src(&info, static_cast<const unsigned char*>(buffer), buffer_size);
});
}
public:
explicit FileStream(std::FILE* fp_) : m_fp(fp_) {}
bool JPEGFileLoader(RGBA8Image* image, const char* filename, std::FILE* fp)
{
return WrapJPEGDecompress(image, [fp](jpeg_decompress_struct& info) { jpeg_stdio_src(&info, fp); });
}
bool put_buf(const void* Pbuf, int len) override
{
if (m_error_flag)
return false;
template<typename T>
static bool WrapJPEGCompress(const RGBA8Image& image, u8 quality, T setup_func)
{
std::vector<u8> scanline;
if (std::fwrite(Pbuf, len, 1, m_fp) != 1)
{
m_error_flag = true;
return false;
}
JPEGErrorHandler err;
if (!HandleJPEGError(&err))
return false;
return true;
}
};
jpeg_compress_struct info;
info.err = &err.err;
jpeg_create_compress(&info);
setup_func(info);
FileStream stream(fp);
return JPEGCommonSaver(image, stream, quality);
}
info.image_width = image.GetWidth();
info.image_height = image.GetHeight();
info.in_color_space = JCS_RGB;
info.input_components = 3;
#endif
jpeg_set_defaults(&info);
jpeg_set_quality(&info, quality, TRUE);
jpeg_start_compress(&info, TRUE);
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)
scanline.resize(image.GetWidth() * 3);
u8* scanline_buffer[1] = {scanline.data()};
bool result = true;
for (u32 y = 0; y < info.image_height; y++)
{
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;
}
// RGBA -> RGB
u8* dst_ptr = scanline.data();
const u32* src_ptr = image.GetRowPixels(y);
for (u32 x = 0; x < info.image_width; x++)
{
const u32 rgba = *(src_ptr++);
*(dst_ptr++) = Truncate8(rgba);
*(dst_ptr++) = Truncate8(rgba >> 8);
*(dst_ptr++) = Truncate8(rgba >> 16);
}
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;
if (jpeg_write_scanlines(&info, scanline_buffer, 1) != 1)
{
Log_ErrorFmt("jpeg_write_scanlines() failed at row {}", y);
result = false;
break;
}
}
image->SetPixels(static_cast<u32>(width), static_cast<u32>(height), reinterpret_cast<const u32*>(pixel_data));
stbi_image_free(pixel_data);
return true;
jpeg_finish_compress(&info);
jpeg_destroy_compress(&info);
return result;
}
bool STBBufferSaverPNG(const RGBA8Image& image, std::vector<u8>* buffer, int quality)
bool JPEGBufferSaver(const RGBA8Image& image, std::vector<u8>* buffer, 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_png_to_func(write_func, buffer, image.GetWidth(), image.GetHeight(), 4, image.GetPixels(),
image.GetPitch()) != 0);
}
// give enough space to avoid reallocs
buffer->resize(image.GetWidth() * image.GetHeight() * 2);
bool STBBufferSaverJPEG(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);
struct MemCallback
{
jpeg_destination_mgr mgr;
std::vector<u8>* buffer;
size_t buffer_used;
};
return (stbi_write_jpg_to_func(write_func, buffer, image.GetWidth(), image.GetHeight(), 4, image.GetPixels(),
quality) != 0);
}
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;
// 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 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));
// get final size
cb->buffer->resize(cb->buffer->size() - cb->mgr.free_in_buffer);
};
return (stbi_write_png_to_func(write_func, fp, image.GetWidth(), image.GetHeight(), 4, image.GetPixels(),
image.GetPitch()) != 0);
return WrapJPEGCompress(image, quality, [&cb](jpeg_compress_struct& info) { info.dest = &cb.mgr; });
}
bool STBFileSaverJPEG(const RGBA8Image& image, const char* filename, std::FILE* fp, 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::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>