From e88ca8ede19d8cd91df0b3bc0dd843290af5b0a7 Mon Sep 17 00:00:00 2001 From: ITotalJustice <47043333+ITotalJustice@users.noreply.github.com> Date: Tue, 3 Jun 2025 02:16:23 +0100 Subject: [PATCH] fix nvjpg icon loading if w*bpp != pitch. add build option to enable/disable nvjpg (for testing). --- sphaira/CMakeLists.txt | 24 ++++++++++++++++-------- sphaira/include/app.hpp | 4 ++++ sphaira/source/app.cpp | 4 ++++ sphaira/source/image.cpp | 34 ++++++++++++++++++++++++++++------ 4 files changed, 52 insertions(+), 14 deletions(-) diff --git a/sphaira/CMakeLists.txt b/sphaira/CMakeLists.txt index 12d9a47..015a4be 100644 --- a/sphaira/CMakeLists.txt +++ b/sphaira/CMakeLists.txt @@ -208,6 +208,8 @@ FetchContent_Declare(nvjpg ) set(USE_NEW_ZSTD ON) +# has issues with some homebrew and game icons (oxenfree, overwatch2). +set(USE_NVJPG ON) set(ZSTD_BUILD_STATIC ON) set(ZSTD_BUILD_SHARED OFF) @@ -310,13 +312,15 @@ add_library(libnxtc ) target_include_directories(libnxtc PUBLIC ${libnxtc_SOURCE_DIR}/include) -add_library(nvjpg - ${nvjpg_SOURCE_DIR}/lib/decoder.cpp - ${nvjpg_SOURCE_DIR}/lib/image.cpp - ${nvjpg_SOURCE_DIR}/lib/surface.cpp -) -target_include_directories(nvjpg PUBLIC ${nvjpg_SOURCE_DIR}/include) -set_target_properties(nvjpg PROPERTIES CXX_STANDARD 26) +if (USE_NVJPG) + add_library(nvjpg + ${nvjpg_SOURCE_DIR}/lib/decoder.cpp + ${nvjpg_SOURCE_DIR}/lib/image.cpp + ${nvjpg_SOURCE_DIR}/lib/surface.cpp + ) + target_include_directories(nvjpg PUBLIC ${nvjpg_SOURCE_DIR}/include) + set_target_properties(nvjpg PROPERTIES CXX_STANDARD 26) +endif() find_package(ZLIB REQUIRED) find_library(minizip_lib minizip REQUIRED) @@ -348,7 +352,6 @@ target_link_libraries(sphaira PRIVATE yyjson # libusbhsfs libnxtc - nvjpg ${minizip_lib} ZLIB::ZLIB @@ -365,6 +368,11 @@ else() target_include_directories(sphaira PRIVATE ${zstd_inc}) endif() +if (USE_NVJPG) + target_link_libraries(sphaira PRIVATE nvjpg) + target_compile_definitions(sphaira PRIVATE USE_NVJPG) +endif() + target_include_directories(sphaira PRIVATE include ${minizip_inc} diff --git a/sphaira/include/app.hpp b/sphaira/include/app.hpp index daae443..e2b583f 100644 --- a/sphaira/include/app.hpp +++ b/sphaira/include/app.hpp @@ -10,7 +10,9 @@ #include "fs.hpp" #include "log.hpp" +#ifdef USE_NVJPG #include +#endif #include #include #include @@ -273,7 +275,9 @@ public: PLSR_PlayerSoundId m_sound_ids[SoundEffect_MAX]{}; +#ifdef USE_NVJPG nj::Decoder m_decoder; +#endif private: // from nanovg decko3d example by adubbz static constexpr unsigned NumFramebuffers = 2; diff --git a/sphaira/source/app.cpp b/sphaira/source/app.cpp index fc89fa9..f8d27b8 100644 --- a/sphaira/source/app.cpp +++ b/sphaira/source/app.cpp @@ -1400,9 +1400,11 @@ App::App(const char* argv0) { curl::Init(); +#ifdef USE_NVJPG // this has to be init before deko3d. nj::initialize(); m_decoder.initialize(); +#endif // get current size of the framebuffer const auto fb = GetFrameBufferSize(); @@ -1883,8 +1885,10 @@ App::~App() { nvgDeleteDk(this->vg); this->renderer.reset(); +#ifdef USE_NVJPG m_decoder.finalize(); nj::finalize(); +#endif // backup hbmenu if it is not sphaira if (App::GetReplaceHbmenuEnable() && !IsHbmenu()) { diff --git a/sphaira/source/image.cpp b/sphaira/source/image.cpp index 47a683d..75c5fec 100644 --- a/sphaira/source/image.cpp +++ b/sphaira/source/image.cpp @@ -22,7 +22,9 @@ #include "app.hpp" #include "log.hpp" +#ifdef USE_NVJPG #include +#endif #include namespace sphaira { @@ -45,6 +47,7 @@ auto ImageLoadInternal(stbi_uc* image_data, int x, int y) -> ImageResult { return {}; } +#ifdef USE_NVJPG auto ImageLoadInternal(nj::Image&& image) -> ImageResult { if (!image.is_valid() || image.parse()) { log_write("[NVJPG] failed to parse image\n"); @@ -68,17 +71,29 @@ auto ImageLoadInternal(nj::Image&& image) -> ImageResult { } ImageResult result{}; - result.w = image.width; - result.h = image.height; - result.data.resize(surf.size()); - std::memcpy(result.data.data(), surf.data(), result.data.size()); + result.w = surf.width; + result.h = surf.height; + result.data.resize(surf.width * surf.height * surf.get_bpp()); + // std::printf("[NVJPG] w: %zu h: %zu bpp: %u pitch: %zu size: %zu size2: %u\n", surf.width, surf.height, surf.get_bpp(), surf.pitch, surf.size(), 256*256*4); + + if (surf.width * surf.get_bpp() == surf.pitch) [[likely]] { + std::memcpy(result.data.data(), surf.data(), result.data.size()); + } else { + for (size_t i = 0; i < surf.height; i++) { + const auto src_pitch = surf.pitch; + const auto dst_pitch = surf.width * surf.get_bpp(); + std::memcpy(result.data.data() + i * dst_pitch, surf.data() + i * src_pitch, dst_pitch); + } + } return result; } +#endif } // namespace auto ImageLoadFromMemory(std::span data, u32 flags) -> ImageResult { +#ifdef USE_NVJPG if (flags & ImageFlag_JPEG) { auto shared_vec = std::make_shared>(data.size()); std::memcpy(shared_vec->data(), data.data(), shared_vec->size()); @@ -86,19 +101,26 @@ auto ImageLoadFromMemory(std::span data, u32 flags) -> ImageResult { auto result = ImageLoadInternal(nj::Image{shared_vec}); // if it failed, try again but without using oss-jpg. return result.data.empty() ? ImageLoadFromMemory(data, 0) : result; - } else { + } + else +#endif + { int x, y, channels; return ImageLoadInternal(stbi_load_from_memory(data.data(), data.size(), &x, &y, &channels, BPP), x, y); } } auto ImageLoadFromFile(const fs::FsPath& file, u32 flags) -> ImageResult { +#ifdef USE_NVJPG if (flags & ImageFlag_JPEG) { // don't make const as it prevents RTO. auto result = ImageLoadInternal(nj::Image{file}); // if it failed, try again but without using oss-jpg. return result.data.empty() ? ImageLoadFromFile(file, 0) : result; - } else { + } + else +#endif + { int x, y, channels; return ImageLoadInternal(stbi_load(file, &x, &y, &channels, BPP), x, y); }