|
@@ -6,6 +6,7 @@
|
|
|
|
|
|
// CHANGELOG
|
|
// CHANGELOG
|
|
// (minor and older changes stripped away, please see git history for details)
|
|
// (minor and older changes stripped away, please see git history for details)
|
|
|
|
+// 2023/07/12: added support for SVG fonts, enable by using '#define IMGUI_ENABLE_FREETYPE_LUNASVG'
|
|
// 2023/01/04: fixed a packing issue which in some occurrences would prevent large amount of glyphs from being packed correctly.
|
|
// 2023/01/04: fixed a packing issue which in some occurrences would prevent large amount of glyphs from being packed correctly.
|
|
// 2021/08/23: fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL.
|
|
// 2021/08/23: fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL.
|
|
// 2021/03/05: added ImGuiFreeTypeBuilderFlags_Bitmap to load bitmap glyphs.
|
|
// 2021/03/05: added ImGuiFreeTypeBuilderFlags_Bitmap to load bitmap glyphs.
|
|
@@ -44,6 +45,13 @@
|
|
#include FT_GLYPH_H // <freetype/ftglyph.h>
|
|
#include FT_GLYPH_H // <freetype/ftglyph.h>
|
|
#include FT_SYNTHESIS_H // <freetype/ftsynth.h>
|
|
#include FT_SYNTHESIS_H // <freetype/ftsynth.h>
|
|
|
|
|
|
|
|
+#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
|
|
|
|
+#include FT_OTSVG_H // <freetype/otsvg.h>
|
|
|
|
+#include FT_BBOX_H // <freetype/ftbbox.h>
|
|
|
|
+#include <lunasvg.h>
|
|
|
|
+#include <memory>
|
|
|
|
+#endif
|
|
|
|
+
|
|
#ifdef _MSC_VER
|
|
#ifdef _MSC_VER
|
|
#pragma warning (push)
|
|
#pragma warning (push)
|
|
#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
|
|
#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
|
|
@@ -70,6 +78,14 @@ static void* (*GImGuiFreeTypeAllocFunc)(size_t size, void* user_data) = ImGuiFre
|
|
static void (*GImGuiFreeTypeFreeFunc)(void* ptr, void* user_data) = ImGuiFreeTypeDefaultFreeFunc;
|
|
static void (*GImGuiFreeTypeFreeFunc)(void* ptr, void* user_data) = ImGuiFreeTypeDefaultFreeFunc;
|
|
static void* GImGuiFreeTypeAllocatorUserData = nullptr;
|
|
static void* GImGuiFreeTypeAllocatorUserData = nullptr;
|
|
|
|
|
|
|
|
+
|
|
|
|
+#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
|
|
|
|
+FT_Error ImGuiLunasvgPortInit(FT_Pointer* state);
|
|
|
|
+void ImGuiLunasvgPortFree(FT_Pointer* state);
|
|
|
|
+FT_Error ImGuiLunasvgPortRender(FT_GlyphSlot slot, FT_Pointer* _state);
|
|
|
|
+FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_Pointer* _state);
|
|
|
|
+#endif
|
|
|
|
+
|
|
//-------------------------------------------------------------------------
|
|
//-------------------------------------------------------------------------
|
|
// Code
|
|
// Code
|
|
//-------------------------------------------------------------------------
|
|
//-------------------------------------------------------------------------
|
|
@@ -244,7 +260,19 @@ namespace
|
|
|
|
|
|
// Need an outline for this to work
|
|
// Need an outline for this to work
|
|
FT_GlyphSlot slot = Face->glyph;
|
|
FT_GlyphSlot slot = Face->glyph;
|
|
|
|
+
|
|
|
|
+#if ((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12))
|
|
|
|
+#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
|
|
|
|
+ IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP || slot->format == FT_GLYPH_FORMAT_SVG);
|
|
|
|
+#else
|
|
|
|
+ IM_ASSERT(slot->format != FT_GLYPH_FORMAT_SVG &&
|
|
|
|
+ "The font contains SVG glyphs, you'll need to enable IMGUI_ENABLE_FREETYPE_LUNASVG"
|
|
|
|
+ "in imconfig.h and install required libraries in order to use this font");
|
|
|
|
+ IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP);
|
|
|
|
+#endif // IMGUI_ENABLE_FREETYPE_LUNASVG
|
|
|
|
+#else
|
|
IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP);
|
|
IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP);
|
|
|
|
+#endif // ((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12))
|
|
|
|
|
|
// Apply convenience transform (this is not picking from real "Bold"/"Italic" fonts! Merely applying FreeType helper transform. Oblique == Slanting)
|
|
// Apply convenience transform (this is not picking from real "Bold"/"Italic" fonts! Merely applying FreeType helper transform. Oblique == Slanting)
|
|
if (UserFlags & ImGuiFreeTypeBuilderFlags_Bold)
|
|
if (UserFlags & ImGuiFreeTypeBuilderFlags_Bold)
|
|
@@ -770,6 +798,18 @@ static bool ImFontAtlasBuildWithFreeType(ImFontAtlas* atlas)
|
|
// If you don't call FT_Add_Default_Modules() the rest of code may work, but FreeType won't use our custom allocator.
|
|
// If you don't call FT_Add_Default_Modules() the rest of code may work, but FreeType won't use our custom allocator.
|
|
FT_Add_Default_Modules(ft_library);
|
|
FT_Add_Default_Modules(ft_library);
|
|
|
|
|
|
|
|
+#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
|
|
|
|
+#if ((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12))
|
|
|
|
+ // Install svg hooks for FreeType
|
|
|
|
+ // https://freetype.org/freetype2/docs/reference/ft2-properties.html#svg-hooks
|
|
|
|
+ // https://freetype.org/freetype2/docs/reference/ft2-svg_fonts.html#svg_fonts
|
|
|
|
+ SVG_RendererHooks hooks = {ImGuiLunasvgPortInit, ImGuiLunasvgPortFree, ImGuiLunasvgPortRender, ImGuiLunasvgPortPresetSlot};
|
|
|
|
+ FT_Property_Set(ft_library, "ot-svg", "svg-hooks", &hooks);
|
|
|
|
+#else
|
|
|
|
+ IM_ASSERT(!"IMGUI_ENABLE_FREETYPE_LUNASVG requires FreeType version >= 2.12");
|
|
|
|
+#endif // ((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12))
|
|
|
|
+#endif // IMGUI_ENABLE_FREETYPE_LUNASVG
|
|
|
|
+
|
|
bool ret = ImFontAtlasBuildWithFreeTypeEx(ft_library, atlas, atlas->FontBuilderFlags);
|
|
bool ret = ImFontAtlasBuildWithFreeTypeEx(ft_library, atlas, atlas->FontBuilderFlags);
|
|
FT_Done_Library(ft_library);
|
|
FT_Done_Library(ft_library);
|
|
|
|
|
|
@@ -790,6 +830,123 @@ void ImGuiFreeType::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* u
|
|
GImGuiFreeTypeAllocatorUserData = user_data;
|
|
GImGuiFreeTypeAllocatorUserData = user_data;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
|
|
|
|
+// For more details, see https://gitlab.freedesktop.org/freetype/freetype-demos/-/blob/master/src/rsvg-port.c
|
|
|
|
+// The original code from the demo is licensed under CeCILL-C Free Software License Agreement
|
|
|
|
+// https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/LICENSE.TXT
|
|
|
|
+typedef struct LunasvgPortState_
|
|
|
|
+{
|
|
|
|
+ FT_Error err = FT_Err_Ok;
|
|
|
|
+ std::unique_ptr<lunasvg::Document> svg = nullptr;
|
|
|
|
+ lunasvg::Matrix matrix; // Scaled and translated matrix for rendering
|
|
|
|
+} LunasvgPortState, *PLunasvgPortState;
|
|
|
|
+
|
|
|
|
+FT_Error ImGuiLunasvgPortInit(FT_Pointer* _state)
|
|
|
|
+{
|
|
|
|
+ *_state = new LunasvgPortState();
|
|
|
|
+ return FT_Err_Ok;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void ImGuiLunasvgPortFree(FT_Pointer* _state)
|
|
|
|
+{
|
|
|
|
+ delete *_state;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+FT_Error ImGuiLunasvgPortRender(FT_GlyphSlot slot, FT_Pointer* _state)
|
|
|
|
+{
|
|
|
|
+ PLunasvgPortState state = *(PLunasvgPortState*)_state;
|
|
|
|
+
|
|
|
|
+ // If there was an error while loading the svg in
|
|
|
|
+ // `ImGuiLunasvgPortPresetSlot`, the renderer hook
|
|
|
|
+ // still get called, so just returns the error.
|
|
|
|
+ if (state->err != FT_Err_Ok) return state->err;
|
|
|
|
+
|
|
|
|
+ // rows is height, pitch (or stride) equals to width * sizeof(int32)
|
|
|
|
+ lunasvg::Bitmap bitmap((uint8_t*)slot->bitmap.buffer, slot->bitmap.width, slot->bitmap.rows, slot->bitmap.pitch);
|
|
|
|
+
|
|
|
|
+ state->svg->setMatrix(state->svg->matrix().identity()); // Reset the svg matrix to the default value
|
|
|
|
+ state->svg->render(bitmap, state->matrix); // state->matrix is already scaled and translated
|
|
|
|
+
|
|
|
|
+ state->err = FT_Err_Ok;
|
|
|
|
+ return state->err;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_Pointer* _state)
|
|
|
|
+{
|
|
|
|
+ FT_SVG_Document document = (FT_SVG_Document)slot->other;
|
|
|
|
+ PLunasvgPortState state = *(PLunasvgPortState*)_state;
|
|
|
|
+ FT_Size_Metrics& metrics = document->metrics;
|
|
|
|
+
|
|
|
|
+ // This function is called twice, once in the `FT_Load_Glyph`
|
|
|
|
+ // and another right before `ImGuiLunasvgPortRender`.
|
|
|
|
+ // If it's the latter, don't do anything because it's
|
|
|
|
+ // already done in the former.
|
|
|
|
+ if (cache) return state->err;
|
|
|
|
+
|
|
|
|
+ state->svg = lunasvg::Document::loadFromData((const char*)document->svg_document, document->svg_document_length);
|
|
|
|
+ if (state->svg == nullptr)
|
|
|
|
+ {
|
|
|
|
+ state->err = FT_Err_Invalid_SVG_Document;
|
|
|
|
+ return state->err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ lunasvg::Box box = state->svg->box();
|
|
|
|
+
|
|
|
|
+ double scale = std::min(metrics.x_ppem / box.w, metrics.y_ppem / box.h);
|
|
|
|
+ double xx = (double)document->transform.xx / (1 << 16);
|
|
|
|
+ double xy = -(double)document->transform.xy / (1 << 16);
|
|
|
|
+ double yx = -(double)document->transform.yx / (1 << 16);
|
|
|
|
+ double yy = (double)document->transform.yy / (1 << 16);
|
|
|
|
+ double x0 = (double)document->delta.x / 64 * box.w / metrics.x_ppem;
|
|
|
|
+ double y0 = -(double)document->delta.y / 64 * box.h / metrics.y_ppem;
|
|
|
|
+
|
|
|
|
+ // This reset the matrix to its default value
|
|
|
|
+ state->matrix.identity();
|
|
|
|
+
|
|
|
|
+ // Scale and transform, we don't translate the svg yet
|
|
|
|
+ state->matrix.scale(scale, scale);
|
|
|
|
+ state->matrix.transform(xx, xy, yx, yy, x0, y0);
|
|
|
|
+ state->svg->setMatrix(state->matrix);
|
|
|
|
+
|
|
|
|
+ // Pre-translate the matrix for the rendering step
|
|
|
|
+ state->matrix.translate(-box.x, -box.y);
|
|
|
|
+
|
|
|
|
+ // Get the box again after the transformation
|
|
|
|
+ box = state->svg->box();
|
|
|
|
+
|
|
|
|
+ // Calculate the bitmap size
|
|
|
|
+ slot->bitmap_left = FT_Int(box.x);
|
|
|
|
+ slot->bitmap_top = FT_Int(-box.y);
|
|
|
|
+ slot->bitmap.rows = (unsigned int)(ceil(box.h));
|
|
|
|
+ slot->bitmap.width = (unsigned int)(ceil(box.w));
|
|
|
|
+ slot->bitmap.pitch = slot->bitmap.width * 4;
|
|
|
|
+ slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA;
|
|
|
|
+
|
|
|
|
+ // Compute all the bearings and set them correctly. The outline is
|
|
|
|
+ // scaled already, we just need to use the bounding box.
|
|
|
|
+ double metrics_width = box.w;
|
|
|
|
+ double metrics_height = box.h;
|
|
|
|
+ double horiBearingX = box.x;
|
|
|
|
+ double horiBearingY = -box.y;
|
|
|
|
+
|
|
|
|
+ double vertBearingX = slot->metrics.horiBearingX / 64.0 - slot->metrics.horiAdvance / 64.0 / 2.0;
|
|
|
|
+ double vertBearingY = (slot->metrics.vertAdvance / 64.0 - slot->metrics.height / 64.0) / 2.0;
|
|
|
|
+
|
|
|
|
+ slot->metrics.width = FT_Pos(round(metrics_width * 64.0));
|
|
|
|
+ slot->metrics.height = FT_Pos(round(metrics_height * 64.0));
|
|
|
|
+
|
|
|
|
+ slot->metrics.horiBearingX = FT_Pos(horiBearingX * 64);
|
|
|
|
+ slot->metrics.horiBearingY = FT_Pos(horiBearingY * 64);
|
|
|
|
+ slot->metrics.vertBearingX = FT_Pos(vertBearingX * 64);
|
|
|
|
+ slot->metrics.vertBearingY = FT_Pos(vertBearingY * 64);
|
|
|
|
+
|
|
|
|
+ if (slot->metrics.vertAdvance == 0)
|
|
|
|
+ slot->metrics.vertAdvance = FT_Pos(metrics_height * 1.2 * 64.0);
|
|
|
|
+
|
|
|
|
+ state->err = FT_Err_Ok;
|
|
|
|
+ return state->err;
|
|
|
|
+}
|
|
|
|
+#endif // !IMGUI_ENABLE_FREETYPE_LUNASVG
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
#ifdef __GNUC__
|
|
#ifdef __GNUC__
|