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 PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..")
|
||||||
target_include_directories(util PUBLIC "${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 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)
|
if(ENABLE_X11)
|
||||||
target_compile_definitions(util PRIVATE "-DENABLE_X11=1")
|
target_compile_definitions(util PRIVATE "-DENABLE_X11=1")
|
||||||
|
|
|
@ -8,12 +8,15 @@
|
||||||
#include "common/error.h"
|
#include "common/error.h"
|
||||||
#include "common/fastjmp.h"
|
#include "common/fastjmp.h"
|
||||||
#include "common/file_system.h"
|
#include "common/file_system.h"
|
||||||
|
#include "common/gsvector.h"
|
||||||
#include "common/heap_array.h"
|
#include "common/heap_array.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "common/path.h"
|
#include "common/path.h"
|
||||||
#include "common/scoped_guard.h"
|
#include "common/scoped_guard.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
|
|
||||||
|
#include "lunasvg_c.h"
|
||||||
|
|
||||||
#include <jpeglib.h>
|
#include <jpeglib.h>
|
||||||
#include <png.h>
|
#include <png.h>
|
||||||
#include <webp/decode.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);
|
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 */,
|
bool RGBA8Image::SaveToFile(std::string_view filename, std::FILE* fp, u8 quality /* = DEFAULT_SAVE_QUALITY */,
|
||||||
Error* error /* = nullptr */) const
|
Error* error /* = nullptr */) const
|
||||||
{
|
{
|
||||||
|
@ -198,6 +230,41 @@ std::optional<DynamicHeapArray<u8>> RGBA8Image::SaveToBuffer(std::string_view fi
|
||||||
return ret;
|
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
|
#if 0
|
||||||
|
|
||||||
void RGBA8Image::Resize(u32 new_width, u32 new_height)
|
void RGBA8Image::Resize(u32 new_width, u32 new_height)
|
||||||
|
|
|
@ -102,6 +102,28 @@ public:
|
||||||
m_pixels = std::move(pixels);
|
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()
|
std::vector<PixelType> TakePixels()
|
||||||
{
|
{
|
||||||
m_width = 0;
|
m_width = 0;
|
||||||
|
@ -134,9 +156,13 @@ public:
|
||||||
bool LoadFromFile(std::string_view filename, std::FILE* fp, Error* error = nullptr);
|
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 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(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,
|
bool SaveToFile(std::string_view filename, std::FILE* fp, u8 quality = DEFAULT_SAVE_QUALITY,
|
||||||
Error* error = nullptr) const;
|
Error* error = nullptr) const;
|
||||||
std::optional<DynamicHeapArray<u8>> SaveToBuffer(std::string_view filename, u8 quality = DEFAULT_SAVE_QUALITY,
|
std::optional<DynamicHeapArray<u8>> SaveToBuffer(std::string_view filename, u8 quality = DEFAULT_SAVE_QUALITY,
|
||||||
Error* error = nullptr) const;
|
Error* error = nullptr) const;
|
||||||
|
|
||||||
|
static void SwapBGRAToRGBA(void* pixels, u32 width, u32 height, u32 pitch);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue