fix nvjpg icon loading if w*bpp != pitch. add build option to enable/disable nvjpg (for testing).

This commit is contained in:
ITotalJustice
2025-06-03 02:16:23 +01:00
parent 7a83269d98
commit e88ca8ede1
4 changed files with 52 additions and 14 deletions

View File

@@ -208,6 +208,8 @@ FetchContent_Declare(nvjpg
) )
set(USE_NEW_ZSTD ON) 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_STATIC ON)
set(ZSTD_BUILD_SHARED OFF) set(ZSTD_BUILD_SHARED OFF)
@@ -310,13 +312,15 @@ add_library(libnxtc
) )
target_include_directories(libnxtc PUBLIC ${libnxtc_SOURCE_DIR}/include) target_include_directories(libnxtc PUBLIC ${libnxtc_SOURCE_DIR}/include)
add_library(nvjpg if (USE_NVJPG)
${nvjpg_SOURCE_DIR}/lib/decoder.cpp add_library(nvjpg
${nvjpg_SOURCE_DIR}/lib/image.cpp ${nvjpg_SOURCE_DIR}/lib/decoder.cpp
${nvjpg_SOURCE_DIR}/lib/surface.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) target_include_directories(nvjpg PUBLIC ${nvjpg_SOURCE_DIR}/include)
set_target_properties(nvjpg PROPERTIES CXX_STANDARD 26)
endif()
find_package(ZLIB REQUIRED) find_package(ZLIB REQUIRED)
find_library(minizip_lib minizip REQUIRED) find_library(minizip_lib minizip REQUIRED)
@@ -348,7 +352,6 @@ target_link_libraries(sphaira PRIVATE
yyjson yyjson
# libusbhsfs # libusbhsfs
libnxtc libnxtc
nvjpg
${minizip_lib} ${minizip_lib}
ZLIB::ZLIB ZLIB::ZLIB
@@ -365,6 +368,11 @@ else()
target_include_directories(sphaira PRIVATE ${zstd_inc}) target_include_directories(sphaira PRIVATE ${zstd_inc})
endif() endif()
if (USE_NVJPG)
target_link_libraries(sphaira PRIVATE nvjpg)
target_compile_definitions(sphaira PRIVATE USE_NVJPG)
endif()
target_include_directories(sphaira PRIVATE target_include_directories(sphaira PRIVATE
include include
${minizip_inc} ${minizip_inc}

View File

@@ -10,7 +10,9 @@
#include "fs.hpp" #include "fs.hpp"
#include "log.hpp" #include "log.hpp"
#ifdef USE_NVJPG
#include <nvjpg.hpp> #include <nvjpg.hpp>
#endif
#include <switch.h> #include <switch.h>
#include <vector> #include <vector>
#include <string> #include <string>
@@ -273,7 +275,9 @@ public:
PLSR_PlayerSoundId m_sound_ids[SoundEffect_MAX]{}; PLSR_PlayerSoundId m_sound_ids[SoundEffect_MAX]{};
#ifdef USE_NVJPG
nj::Decoder m_decoder; nj::Decoder m_decoder;
#endif
private: // from nanovg decko3d example by adubbz private: // from nanovg decko3d example by adubbz
static constexpr unsigned NumFramebuffers = 2; static constexpr unsigned NumFramebuffers = 2;

View File

@@ -1400,9 +1400,11 @@ App::App(const char* argv0) {
curl::Init(); curl::Init();
#ifdef USE_NVJPG
// this has to be init before deko3d. // this has to be init before deko3d.
nj::initialize(); nj::initialize();
m_decoder.initialize(); m_decoder.initialize();
#endif
// get current size of the framebuffer // get current size of the framebuffer
const auto fb = GetFrameBufferSize(); const auto fb = GetFrameBufferSize();
@@ -1883,8 +1885,10 @@ App::~App() {
nvgDeleteDk(this->vg); nvgDeleteDk(this->vg);
this->renderer.reset(); this->renderer.reset();
#ifdef USE_NVJPG
m_decoder.finalize(); m_decoder.finalize();
nj::finalize(); nj::finalize();
#endif
// backup hbmenu if it is not sphaira // backup hbmenu if it is not sphaira
if (App::GetReplaceHbmenuEnable() && !IsHbmenu()) { if (App::GetReplaceHbmenuEnable() && !IsHbmenu()) {

View File

@@ -22,7 +22,9 @@
#include "app.hpp" #include "app.hpp"
#include "log.hpp" #include "log.hpp"
#ifdef USE_NVJPG
#include <nvjpg.hpp> #include <nvjpg.hpp>
#endif
#include <cstring> #include <cstring>
namespace sphaira { namespace sphaira {
@@ -45,6 +47,7 @@ auto ImageLoadInternal(stbi_uc* image_data, int x, int y) -> ImageResult {
return {}; return {};
} }
#ifdef USE_NVJPG
auto ImageLoadInternal(nj::Image&& image) -> ImageResult { auto ImageLoadInternal(nj::Image&& image) -> ImageResult {
if (!image.is_valid() || image.parse()) { if (!image.is_valid() || image.parse()) {
log_write("[NVJPG] failed to parse image\n"); log_write("[NVJPG] failed to parse image\n");
@@ -68,17 +71,29 @@ auto ImageLoadInternal(nj::Image&& image) -> ImageResult {
} }
ImageResult result{}; ImageResult result{};
result.w = image.width; result.w = surf.width;
result.h = image.height; result.h = surf.height;
result.data.resize(surf.size()); result.data.resize(surf.width * surf.height * surf.get_bpp());
std::memcpy(result.data.data(), surf.data(), result.data.size()); // 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; return result;
} }
#endif
} // namespace } // namespace
auto ImageLoadFromMemory(std::span<const u8> data, u32 flags) -> ImageResult { auto ImageLoadFromMemory(std::span<const u8> data, u32 flags) -> ImageResult {
#ifdef USE_NVJPG
if (flags & ImageFlag_JPEG) { if (flags & ImageFlag_JPEG) {
auto shared_vec = std::make_shared<std::vector<u8>>(data.size()); auto shared_vec = std::make_shared<std::vector<u8>>(data.size());
std::memcpy(shared_vec->data(), data.data(), shared_vec->size()); std::memcpy(shared_vec->data(), data.data(), shared_vec->size());
@@ -86,19 +101,26 @@ auto ImageLoadFromMemory(std::span<const u8> data, u32 flags) -> ImageResult {
auto result = ImageLoadInternal(nj::Image{shared_vec}); auto result = ImageLoadInternal(nj::Image{shared_vec});
// if it failed, try again but without using oss-jpg. // if it failed, try again but without using oss-jpg.
return result.data.empty() ? ImageLoadFromMemory(data, 0) : result; return result.data.empty() ? ImageLoadFromMemory(data, 0) : result;
} else { }
else
#endif
{
int x, y, channels; int x, y, channels;
return ImageLoadInternal(stbi_load_from_memory(data.data(), data.size(), &x, &y, &channels, BPP), x, y); 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 { auto ImageLoadFromFile(const fs::FsPath& file, u32 flags) -> ImageResult {
#ifdef USE_NVJPG
if (flags & ImageFlag_JPEG) { if (flags & ImageFlag_JPEG) {
// don't make const as it prevents RTO. // don't make const as it prevents RTO.
auto result = ImageLoadInternal(nj::Image{file}); auto result = ImageLoadInternal(nj::Image{file});
// if it failed, try again but without using oss-jpg. // if it failed, try again but without using oss-jpg.
return result.data.empty() ? ImageLoadFromFile(file, 0) : result; return result.data.empty() ? ImageLoadFromFile(file, 0) : result;
} else { }
else
#endif
{
int x, y, channels; int x, y, channels;
return ImageLoadInternal(stbi_load(file, &x, &y, &channels, BPP), x, y); return ImageLoadInternal(stbi_load(file, &x, &y, &channels, BPP), x, y);
} }