From deee29a5c2dac1048945960f6bc954ce7d76957b Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Tue, 5 Dec 2023 08:17:51 -0800 Subject: [PATCH] WIP: BLF: Support OpenType SVG Work in progress in supporting the OpenType SVG font type, which could be used to display icons. FreeType supplies callbacks for this type that are used to render the SVG to a bitmpap, here using NanoSVG. Currently works as proof of concept, but can crash while resizing. --- source/blender/blenfont/CMakeLists.txt | 1 + source/blender/blenfont/intern/blf_font.cc | 155 ++++++++++++++++++++ source/blender/blenfont/intern/blf_glyph.cc | 2 +- 3 files changed, 157 insertions(+), 1 deletion(-) diff --git a/source/blender/blenfont/CMakeLists.txt b/source/blender/blenfont/CMakeLists.txt index eb548c2522b..a7616a99e22 100644 --- a/source/blender/blenfont/CMakeLists.txt +++ b/source/blender/blenfont/CMakeLists.txt @@ -35,6 +35,7 @@ set(LIB bf_gpu PRIVATE bf::imbuf PRIVATE bf::intern::guardedalloc + PRIVATE bf::extern::nanosvg ${FREETYPE_LIBRARIES} ${BROTLI_LIBRARIES} ) diff --git a/source/blender/blenfont/intern/blf_font.cc b/source/blender/blenfont/intern/blf_font.cc index 4044933efc3..d76402600af 100644 --- a/source/blender/blenfont/intern/blf_font.cc +++ b/source/blender/blenfont/intern/blf_font.cc @@ -23,6 +23,11 @@ #include FT_MULTIPLE_MASTERS_H /* Variable font support. */ #include FT_TRUETYPE_IDS_H /* Code-point coverage constants. */ #include FT_TRUETYPE_TABLES_H /* For TT_OS2 */ +#include FT_OTSVG_H /* For SVG fonts */ +#include FT_MODULE_H /* To change driver settings. */ + +#include "nanosvg.h" +#include "nanosvgrast.h" #include "MEM_guardedalloc.h" @@ -1296,6 +1301,144 @@ char *blf_display_name(FontBLF *font) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name OpenType SVG Processing + * \{ */ + +typedef struct Rsvg_Port_StateRec_ { + uint glyph_id; + NSVGimage *image; + NSVGrasterizer *rast; + float x; + float y; + float scale; +} Rsvg_Port_StateRec; + +typedef struct Rsvg_Port_StateRec_ *Rsvg_Port_State; + +/* Called just once, the first time an SVG is encountered. */ +FT_Error rsvg_port_init(FT_Pointer *state) +{ + *state = (FT_Pointer *)MEM_mallocN(sizeof(Rsvg_Port_StateRec), __func__); + if (*state) { + Rsvg_Port_State _state = *(Rsvg_Port_State *)state; + _state->glyph_id = 0; + _state->image = nullptr; + _state->rast = nsvgCreateRasterizer(); + _state->x = 0; + _state->y = 0; + _state->scale = 0; + return FT_Err_Ok; + } + return FT_Err_Cannot_Open_Resource; +} + +/* Only called once at shutdown. */ +void rsvg_port_free(FT_Pointer *state) +{ + Rsvg_Port_State _state = *(Rsvg_Port_State *)state; + if (_state->image) { + nsvgDelete(_state->image); + _state->image = nullptr; + } + if (_state->rast) { + nsvgDeleteRasterizer(_state->rast); + } + MEM_SAFE_FREE(*state); +} + +/* Called twice. On Load and just before Render. */ +FT_Error rsvg_port_preset_slot(FT_GlyphSlot slot, FT_Bool cache, FT_Pointer *_state) +{ + if (cache) { + /* We can do everything in the earlier of the two calls. */ + return FT_Err_Ok; + } + + Rsvg_Port_State state = *(Rsvg_Port_State *)_state; + state->glyph_id = slot->glyph_index; + + //FT_Face face = slot->face; + //FontBLF *font = (FontBLF *)face->generic.data; + + FT_SVG_Document document = (FT_SVG_Document)slot->other; + if (!document || !document->svg_document) { + return FT_Err_Invalid_SVG_Document; + } + + if (state->image) { + return FT_Err_Invalid_SVG_Document; + } + + try { + state->image = nsvgParse((char *)document->svg_document, "px", 96.0f); + } + catch (...) { + state->image = nullptr; + } + + if (state->image && state->image->width && state->image->height) { + + const float ratio = state->image->width / state->image->height; + const int height_64 = document->metrics.ascender - document->metrics.descender; + const int width_64 = int(ceil(height_64 / ratio)); + + state->scale = float(height_64) / 64.0f / state->image->height * 0.8f; + state->x = 0.0f; + state->y = float(document->metrics.ascender / 64.0f); + + slot->bitmap_left = 0; + slot->bitmap_top = int(state->y); + slot->bitmap.width = int(ceil(width_64 / 64.0f)); + slot->bitmap.rows = int(ceil(height_64 / 64.0f)); + slot->bitmap.pitch = int(slot->bitmap.width * 4); + slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA; + slot->metrics.width = height_64; + slot->metrics.height = height_64; + slot->advance.x = width_64; + slot->metrics.horiAdvance = slot->advance.x * 1024; + + return FT_Err_Ok; + } + + return FT_Err_Invalid_SVG_Document; +} + +FT_Error rsvg_port_render(FT_GlyphSlot slot, FT_Pointer *_state) +{ + Rsvg_Port_State state = *(Rsvg_Port_State *)_state; + if (!state->image || state->image->width == 0 || state->image->height == 0) { + return FT_Err_Invalid_SVG_Document; + } + + if (state->glyph_id != slot->glyph_index) { + return FT_Err_Invalid_SVG_Document; + } + + //FT_Face face = slot->face; + //FontBLF *font = (FontBLF *)face->generic.data; + + nsvgRasterize(state->rast, + state->image, + state->x, + state->y, + state->scale, + slot->bitmap.buffer, + slot->bitmap.width, + slot->bitmap.rows, + slot->bitmap.pitch); + + slot->bitmap.num_grays = 256; + slot->format = FT_GLYPH_FORMAT_BITMAP; + + nsvgDelete(state->image); + state->image = nullptr; + + return FT_Err_Ok; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Font Subsystem Init/Exit * \{ */ @@ -1318,6 +1461,18 @@ int blf_font_init() /* Create a character-map cache to speed up glyph index lookups. */ err = FTC_CMapCache_New(ftc_manager, &ftc_charmap_cache); } +#ifdef FT_CONFIG_OPTION_SVG + if (err == FT_Err_Ok) { + SVG_RendererHooks hooks = {(SVG_Lib_Init_Func)rsvg_port_init, + (SVG_Lib_Free_Func)rsvg_port_free, + (SVG_Lib_Render_Func)rsvg_port_render, + (SVG_Lib_Preset_Slot_Func)rsvg_port_preset_slot}; + err = FT_Property_Set(ft_lib, "ot-svg", "svg-hooks", &hooks); + if (err != FT_Err_Ok) { + printf("Error setting up SVG hooks \n"); + } + } +#endif } return err; } diff --git a/source/blender/blenfont/intern/blf_glyph.cc b/source/blender/blenfont/intern/blf_glyph.cc index feabc01d34e..8d9d7fb3d2d 100644 --- a/source/blender/blenfont/intern/blf_glyph.cc +++ b/source/blender/blenfont/intern/blf_glyph.cc @@ -333,7 +333,7 @@ static GlyphBLF *blf_glyph_cache_add_glyph(FontBLF *font, } } } - else if (glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) { + else if (glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA && !FT_HAS_SVG(font->face)){ /* Convert from BGRA to RGBA. */ for (size_t y = 0; y < size_t(g->dims[1]); y++) { for (size_t x = 0; x < size_t(g->dims[0]); x++) { -- 2.30.2