mirror of
https://github.com/stenzek/duckstation.git
synced 2024-11-23 05:49:43 +00:00
Image: Support rendering SVGs
This commit is contained in:
parent
3d5503612b
commit
3e26b7ab73
@ -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")
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user