Image: Support rendering SVGs

This commit is contained in:
Stenzek 2024-10-11 22:32:51 +10:00
parent 3d5503612b
commit 3e26b7ab73
No known key found for this signature in database
3 changed files with 94 additions and 1 deletions

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 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")

View File

@ -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 <jpeglib.h>
#include <png.h>
#include <webp/decode.h>
@ -154,6 +157,35 @@ bool RGBA8Image::LoadFromBuffer(std::string_view filename, std::span<const u8> d
return handler->buffer_loader(this, data, error);
}
bool RGBA8Image::RasterizeSVG(const std::span<const u8> 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<lunasvg_document, void (*)(lunasvg_document*)> 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<lunasvg_bitmap, void (*)(lunasvg_bitmap*)> 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<DynamicHeapArray<u8>> 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<u8*>(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<false>(row_pixels_ptr, GSVector4i::load<false>(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)

View File

@ -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<const PixelType*>(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<const u8*>(data);
for (u32 row = 0; row < height; row++)
{
std::memcpy(out_ptr, in_ptr, copy_width);
out_ptr += width;
in_ptr += stride;
}
}
std::vector<PixelType> 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<const u8> data, Error* error = nullptr);
bool RasterizeSVG(const std::span<const u8> 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<DynamicHeapArray<u8>> 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);
};