diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index c47acf614..38e1e9a19 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -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 libchdr JPEG::JPEG PNG::PNG WebP::libwebp ZLIB::ZLIB SoundTouch::SoundTouchDLL xxhash Zstd::Zstd reshadefx) +target_link_libraries(util PRIVATE libchdr JPEG::JPEG PNG::PNG WebP::libwebp lunasvg::lunasvg ZLIB::ZLIB SoundTouch::SoundTouchDLL xxhash Zstd::Zstd reshadefx) if(ENABLE_X11) target_compile_definitions(util PRIVATE "-DENABLE_X11=1") diff --git a/src/util/image.cpp b/src/util/image.cpp index b70db3075..5524a8f47 100644 --- a/src/util/image.cpp +++ b/src/util/image.cpp @@ -8,12 +8,15 @@ #include "common/error.h" #include "common/fastjmp.h" #include "common/file_system.h" +#include "common/gsvector.h" #include "common/heap_array.h" #include "common/log.h" #include "common/path.h" #include "common/scoped_guard.h" #include "common/string_util.h" +#include "lunasvg_c.h" + #include #include #include @@ -154,6 +157,35 @@ bool RGBA8Image::LoadFromBuffer(std::string_view filename, std::span d return handler->buffer_loader(this, data, error); } +bool RGBA8Image::RasterizeSVG(const std::span data, u32 width, u32 height, Error* error) +{ + if (width == 0 || height == 0) + { + Error::SetStringFmt(error, "Invalid dimensions: {}x{}", width, height); + return false; + } + + std::unique_ptr doc( + lunasvg_document_load_from_data(data.data(), data.size()), lunasvg_document_destroy); + if (!doc) + { + Error::SetStringView(error, "lunasvg_document_load_from_data() failed"); + return false; + } + + std::unique_ptr bitmap( + lunasvg_document_render_to_bitmap(doc.get(), width, height, 0), lunasvg_bitmap_destroy); + if (!bitmap) + { + Error::SetStringView(error, "lunasvg_document_render_to_bitmap() failed"); + return false; + } + + SetPixels(width, height, lunasvg_bitmap_data(bitmap.get()), lunasvg_bitmap_stride(bitmap.get())); + SwapBGRAToRGBA(m_pixels.data(), m_width, m_height, GetPitch()); + return true; +} + bool RGBA8Image::SaveToFile(std::string_view filename, std::FILE* fp, u8 quality /* = DEFAULT_SAVE_QUALITY */, Error* error /* = nullptr */) const { @@ -198,6 +230,41 @@ std::optional> RGBA8Image::SaveToBuffer(std::string_view fi return ret; } +void RGBA8Image::SwapBGRAToRGBA(void* pixels, u32 width, u32 height, u32 pitch) +{ +#ifdef GSVECTOR_HAS_FAST_INT_SHUFFLE8 + constexpr u32 pixels_per_vec = sizeof(GSVector4i) / 4; + const u32 aligned_width = Common::AlignDownPow2(width, pixels_per_vec); +#endif + + u8* pixels_ptr = static_cast(pixels); + for (u32 y = 0; y < height; y++) + { + u8* row_pixels_ptr = pixels_ptr; + u32 x; + +#ifdef GSVECTOR_HAS_FAST_INT_SHUFFLE8 + for (x = 0; x < aligned_width; x += pixels_per_vec) + { + static constexpr GSVector4i mask = GSVector4i::cxpr8(2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15); + GSVector4i::store(row_pixels_ptr, GSVector4i::load(row_pixels_ptr).shuffle8(mask)); + row_pixels_ptr += sizeof(GSVector4i); + } +#endif + + for (; x < width; x++) + { + u32 pixel; + std::memcpy(&pixel, row_pixels_ptr, sizeof(pixel)); + pixel = (pixel & 0xFF00FF00) | ((pixel & 0xFF) << 16) | ((pixel >> 16) & 0xFF); + std::memcpy(row_pixels_ptr, &pixel, sizeof(pixel)); + row_pixels_ptr += sizeof(pixel); + } + + pixels_ptr += pitch; + } +} + #if 0 void RGBA8Image::Resize(u32 new_width, u32 new_height) diff --git a/src/util/image.h b/src/util/image.h index 6bc915b38..efc272eda 100644 --- a/src/util/image.h +++ b/src/util/image.h @@ -102,6 +102,28 @@ public: m_pixels = std::move(pixels); } + void SetPixels(u32 width, u32 height, const void* data, u32 stride) + { + const u32 copy_width = width * sizeof(PixelType); + if (stride == copy_width) + { + SetPixels(width, height, static_cast(data)); + return; + } + + m_width = width; + m_height = height; + m_pixels.resize(width, height); + PixelType* out_ptr = m_pixels.data(); + const u8* in_ptr = static_cast(data); + for (u32 row = 0; row < height; row++) + { + std::memcpy(out_ptr, in_ptr, copy_width); + out_ptr += width; + in_ptr += stride; + } + } + std::vector TakePixels() { m_width = 0; @@ -134,9 +156,13 @@ public: bool LoadFromFile(std::string_view filename, std::FILE* fp, Error* error = nullptr); bool LoadFromBuffer(std::string_view filename, std::span data, Error* error = nullptr); + bool RasterizeSVG(const std::span data, u32 width, u32 height, Error* error = nullptr); + bool SaveToFile(const char* filename, u8 quality = DEFAULT_SAVE_QUALITY, Error* error = nullptr) const; bool SaveToFile(std::string_view filename, std::FILE* fp, u8 quality = DEFAULT_SAVE_QUALITY, Error* error = nullptr) const; std::optional> SaveToBuffer(std::string_view filename, u8 quality = DEFAULT_SAVE_QUALITY, Error* error = nullptr) const; + + static void SwapBGRAToRGBA(void* pixels, u32 width, u32 height, u32 pitch); };