duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

imgui_freetype.cpp (47134B)


      1 // dear imgui: FreeType font builder (used as a replacement for the stb_truetype builder)
      2 // (code)
      3 
      4 // Get the latest version at https://github.com/ocornut/imgui/tree/master/misc/freetype
      5 // Original code by @vuhdo (Aleksei Skriabin). Improvements by @mikesart. Maintained since 2019 by @ocornut.
      6 
      7 // CHANGELOG
      8 // (minor and older changes stripped away, please see git history for details)
      9 //  2023/11/13: added support for ImFontConfig::RasterizationDensity field for scaling render density without scaling metrics.
     10 //  2023/08/01: added support for SVG fonts, enable by using '#define IMGUI_ENABLE_FREETYPE_LUNASVG' (#6591)
     11 //  2023/01/04: fixed a packing issue which in some occurrences would prevent large amount of glyphs from being packed correctly.
     12 //  2021/08/23: fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL.
     13 //  2021/03/05: added ImGuiFreeTypeBuilderFlags_Bitmap to load bitmap glyphs.
     14 //  2021/03/02: set 'atlas->TexPixelsUseColors = true' to help some backends with deciding of a preferred texture format.
     15 //  2021/01/28: added support for color-layered glyphs via ImGuiFreeTypeBuilderFlags_LoadColor (require Freetype 2.10+).
     16 //  2021/01/26: simplified integration by using '#define IMGUI_ENABLE_FREETYPE'. renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API. removed ImGuiFreeType::BuildFontAtlas().
     17 //  2020/06/04: fix for rare case where FT_Get_Char_Index() succeed but FT_Load_Glyph() fails.
     18 //  2019/02/09: added RasterizerFlags::Monochrome flag to disable font anti-aliasing (combine with ::MonoHinting for best results!)
     19 //  2019/01/15: added support for imgui allocators + added FreeType only override function SetAllocatorFunctions().
     20 //  2019/01/10: re-factored to match big update in STB builder. fixed texture height waste. fixed redundant glyphs when merging. support for glyph padding.
     21 //  2018/06/08: added support for ImFontConfig::GlyphMinAdvanceX, GlyphMaxAdvanceX.
     22 //  2018/02/04: moved to main imgui repository (away from http://www.github.com/ocornut/imgui_club)
     23 //  2018/01/22: fix for addition of ImFontAtlas::TexUvscale member.
     24 //  2017/10/22: minor inconsequential change to match change in master (removed an unnecessary statement).
     25 //  2017/09/26: fixes for imgui internal changes.
     26 //  2017/08/26: cleanup, optimizations, support for ImFontConfig::RasterizerFlags, ImFontConfig::RasterizerMultiply.
     27 //  2017/08/16: imported from https://github.com/Vuhdo/imgui_freetype into http://www.github.com/ocornut/imgui_club, updated for latest changes in ImFontAtlas, minor tweaks.
     28 
     29 // About Gamma Correct Blending:
     30 // - FreeType assumes blending in linear space rather than gamma space.
     31 // - See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Render_Glyph
     32 // - For correct results you need to be using sRGB and convert to linear space in the pixel shader output.
     33 // - The default dear imgui styles will be impacted by this change (alpha values will need tweaking).
     34 
     35 // FIXME: cfg.OversampleH, OversampleV are not supported (but perhaps not so necessary with this rasterizer).
     36 
     37 #include "imgui.h"
     38 #ifndef IMGUI_DISABLE
     39 #include "imgui_freetype.h"
     40 #include "imgui_internal.h"     // ImMin,ImMax,ImFontAtlasBuild*,
     41 #include <stdint.h>
     42 #include <ft2build.h>
     43 #include FT_FREETYPE_H          // <freetype/freetype.h>
     44 #include FT_MODULE_H            // <freetype/ftmodapi.h>
     45 #include FT_GLYPH_H             // <freetype/ftglyph.h>
     46 #include FT_SYNTHESIS_H         // <freetype/ftsynth.h>
     47 
     48 #ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
     49 #include FT_OTSVG_H             // <freetype/otsvg.h>
     50 #include FT_BBOX_H              // <freetype/ftbbox.h>
     51 #include <algorithm>
     52 #include <lunasvg_c.h>
     53 #if !((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12))
     54 #error IMGUI_ENABLE_FREETYPE_LUNASVG requires FreeType version >= 2.12
     55 #endif
     56 #endif
     57 
     58 #ifdef _MSC_VER
     59 #pragma warning (push)
     60 #pragma warning (disable: 4505)     // unreferenced local function has been removed (stb stuff)
     61 #pragma warning (disable: 26812)    // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
     62 #endif
     63 
     64 #ifdef __GNUC__
     65 #pragma GCC diagnostic push
     66 #pragma GCC diagnostic ignored "-Wpragmas"                  // warning: unknown option after '#pragma GCC diagnostic' kind
     67 #pragma GCC diagnostic ignored "-Wunused-function"          // warning: 'xxxx' defined but not used
     68 #ifndef __clang__
     69 #pragma GCC diagnostic ignored "-Wsubobject-linkage"        // warning: 'xxxx' has a field 'xxxx' whose type uses the anonymous namespace
     70 #endif
     71 #endif
     72 
     73 //-------------------------------------------------------------------------
     74 // Data
     75 //-------------------------------------------------------------------------
     76 
     77 // Default memory allocators
     78 static void* ImGuiFreeTypeDefaultAllocFunc(size_t size, void* user_data) { IM_UNUSED(user_data); return IM_ALLOC(size); }
     79 static void  ImGuiFreeTypeDefaultFreeFunc(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_FREE(ptr); }
     80 
     81 // Current memory allocators
     82 static void* (*GImGuiFreeTypeAllocFunc)(size_t size, void* user_data) = ImGuiFreeTypeDefaultAllocFunc;
     83 static void  (*GImGuiFreeTypeFreeFunc)(void* ptr, void* user_data) = ImGuiFreeTypeDefaultFreeFunc;
     84 static void* GImGuiFreeTypeAllocatorUserData = nullptr;
     85 
     86 // Lunasvg support
     87 #ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
     88 static FT_Error ImGuiLunasvgPortInit(FT_Pointer* state);
     89 static void     ImGuiLunasvgPortFree(FT_Pointer* state);
     90 static FT_Error ImGuiLunasvgPortRender(FT_GlyphSlot slot, FT_Pointer* _state);
     91 static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_Pointer* _state);
     92 #endif
     93 
     94 //-------------------------------------------------------------------------
     95 // Code
     96 //-------------------------------------------------------------------------
     97 
     98 namespace
     99 {
    100     // Glyph metrics:
    101     // --------------
    102     //
    103     //                       xmin                     xmax
    104     //                        |                         |
    105     //                        |<-------- width -------->|
    106     //                        |                         |
    107     //              |         +-------------------------+----------------- ymax
    108     //              |         |    ggggggggg   ggggg    |     ^        ^
    109     //              |         |   g:::::::::ggg::::g    |     |        |
    110     //              |         |  g:::::::::::::::::g    |     |        |
    111     //              |         | g::::::ggggg::::::gg    |     |        |
    112     //              |         | g:::::g     g:::::g     |     |        |
    113     //    offsetX  -|-------->| g:::::g     g:::::g     |  offsetY     |
    114     //              |         | g:::::g     g:::::g     |     |        |
    115     //              |         | g::::::g    g:::::g     |     |        |
    116     //              |         | g:::::::ggggg:::::g     |     |        |
    117     //              |         |  g::::::::::::::::g     |     |      height
    118     //              |         |   gg::::::::::::::g     |     |        |
    119     //  baseline ---*---------|---- gggggggg::::::g-----*--------      |
    120     //            / |         |             g:::::g     |              |
    121     //     origin   |         | gggggg      g:::::g     |              |
    122     //              |         | g:::::gg   gg:::::g     |              |
    123     //              |         |  g::::::ggg:::::::g     |              |
    124     //              |         |   gg:::::::::::::g      |              |
    125     //              |         |     ggg::::::ggg        |              |
    126     //              |         |         gggggg          |              v
    127     //              |         +-------------------------+----------------- ymin
    128     //              |                                   |
    129     //              |------------- advanceX ----------->|
    130 
    131     // A structure that describe a glyph.
    132     struct GlyphInfo
    133     {
    134         int         Width;              // Glyph's width in pixels.
    135         int         Height;             // Glyph's height in pixels.
    136         FT_Int      OffsetX;            // The distance from the origin ("pen position") to the left of the glyph.
    137         FT_Int      OffsetY;            // The distance from the origin to the top of the glyph. This is usually a value < 0.
    138         float       AdvanceX;           // The distance from the origin to the origin of the next glyph. This is usually a value > 0.
    139         bool        IsColored;          // The glyph is colored
    140     };
    141 
    142     // Font parameters and metrics.
    143     struct FontInfo
    144     {
    145         uint32_t    PixelHeight;        // Size this font was generated with.
    146         float       Ascender;           // The pixel extents above the baseline in pixels (typically positive).
    147         float       Descender;          // The extents below the baseline in pixels (typically negative).
    148         float       LineSpacing;        // The baseline-to-baseline distance. Note that it usually is larger than the sum of the ascender and descender taken as absolute values. There is also no guarantee that no glyphs extend above or below subsequent baselines when using this distance. Think of it as a value the designer of the font finds appropriate.
    149         float       LineGap;            // The spacing in pixels between one row's descent and the next row's ascent.
    150         float       MaxAdvanceWidth;    // This field gives the maximum horizontal cursor advance for all glyphs in the font.
    151     };
    152 
    153     // FreeType glyph rasterizer.
    154     // NB: No ctor/dtor, explicitly call Init()/Shutdown()
    155     struct FreeTypeFont
    156     {
    157         bool                    InitFont(FT_Library ft_library, const ImFontConfig& cfg, unsigned int extra_user_flags); // Initialize from an external data buffer. Doesn't copy data, and you must ensure it stays valid up to this object lifetime.
    158         void                    CloseFont();
    159         void                    SetPixelHeight(int pixel_height); // Change font pixel size. All following calls to RasterizeGlyph() will use this size
    160         const FT_Glyph_Metrics* LoadGlyph(uint32_t in_codepoint);
    161         const FT_Bitmap*        RenderGlyphAndGetInfo(GlyphInfo* out_glyph_info);
    162         void                    BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch, unsigned char* multiply_table = nullptr);
    163         ~FreeTypeFont()         { CloseFont(); }
    164 
    165         // [Internals]
    166         FontInfo        Info;               // Font descriptor of the current font.
    167         FT_Face         Face;
    168         unsigned int    UserFlags;          // = ImFontConfig::RasterizerFlags
    169         FT_Int32        LoadFlags;
    170         FT_Render_Mode  RenderMode;
    171         float           RasterizationDensity;
    172         float           InvRasterizationDensity;
    173     };
    174 
    175     // From SDL_ttf: Handy routines for converting from fixed point
    176     #define FT_CEIL(X)  (((X + 63) & -64) / 64)
    177 
    178     bool FreeTypeFont::InitFont(FT_Library ft_library, const ImFontConfig& cfg, unsigned int extra_font_builder_flags)
    179     {
    180         FT_Error error = FT_New_Memory_Face(ft_library, (uint8_t*)cfg.FontData, (uint32_t)cfg.FontDataSize, (uint32_t)cfg.FontNo, &Face);
    181         if (error != 0)
    182             return false;
    183         error = FT_Select_Charmap(Face, FT_ENCODING_UNICODE);
    184         if (error != 0)
    185             return false;
    186 
    187         // Convert to FreeType flags (NB: Bold and Oblique are processed separately)
    188         UserFlags = cfg.FontBuilderFlags | extra_font_builder_flags;
    189 
    190         LoadFlags = 0;
    191         if ((UserFlags & ImGuiFreeTypeBuilderFlags_Bitmap) == 0)
    192             LoadFlags |= FT_LOAD_NO_BITMAP;
    193 
    194         if (UserFlags & ImGuiFreeTypeBuilderFlags_NoHinting)
    195             LoadFlags |= FT_LOAD_NO_HINTING;
    196         if (UserFlags & ImGuiFreeTypeBuilderFlags_NoAutoHint)
    197             LoadFlags |= FT_LOAD_NO_AUTOHINT;
    198         if (UserFlags & ImGuiFreeTypeBuilderFlags_ForceAutoHint)
    199             LoadFlags |= FT_LOAD_FORCE_AUTOHINT;
    200         if (UserFlags & ImGuiFreeTypeBuilderFlags_LightHinting)
    201             LoadFlags |= FT_LOAD_TARGET_LIGHT;
    202         else if (UserFlags & ImGuiFreeTypeBuilderFlags_MonoHinting)
    203             LoadFlags |= FT_LOAD_TARGET_MONO;
    204         else
    205             LoadFlags |= FT_LOAD_TARGET_NORMAL;
    206 
    207         if (UserFlags & ImGuiFreeTypeBuilderFlags_Monochrome)
    208             RenderMode = FT_RENDER_MODE_MONO;
    209         else
    210             RenderMode = FT_RENDER_MODE_NORMAL;
    211 
    212         if (UserFlags & ImGuiFreeTypeBuilderFlags_LoadColor)
    213             LoadFlags |= FT_LOAD_COLOR;
    214 
    215         RasterizationDensity = cfg.RasterizerDensity;
    216         InvRasterizationDensity = 1.0f / RasterizationDensity;
    217 
    218         memset(&Info, 0, sizeof(Info));
    219         SetPixelHeight((uint32_t)cfg.SizePixels);
    220 
    221         return true;
    222     }
    223 
    224     void FreeTypeFont::CloseFont()
    225     {
    226         if (Face)
    227         {
    228             FT_Done_Face(Face);
    229             Face = nullptr;
    230         }
    231     }
    232 
    233     void FreeTypeFont::SetPixelHeight(int pixel_height)
    234     {
    235         // Vuhdo: I'm not sure how to deal with font sizes properly. As far as I understand, currently ImGui assumes that the 'pixel_height'
    236         // is a maximum height of an any given glyph, i.e. it's the sum of font's ascender and descender. Seems strange to me.
    237         // NB: FT_Set_Pixel_Sizes() doesn't seem to get us the same result.
    238         FT_Size_RequestRec req;
    239         req.type = (UserFlags & ImGuiFreeTypeBuilderFlags_Bitmap) ? FT_SIZE_REQUEST_TYPE_NOMINAL : FT_SIZE_REQUEST_TYPE_REAL_DIM;
    240         req.width = 0;
    241         req.height = (uint32_t)(pixel_height * 64 * RasterizationDensity);
    242         req.horiResolution = 0;
    243         req.vertResolution = 0;
    244         FT_Request_Size(Face, &req);
    245 
    246         // Update font info
    247         FT_Size_Metrics metrics = Face->size->metrics;
    248         Info.PixelHeight = (uint32_t)(pixel_height * InvRasterizationDensity);
    249         Info.Ascender = (float)FT_CEIL(metrics.ascender) * InvRasterizationDensity;
    250         Info.Descender = (float)FT_CEIL(metrics.descender) * InvRasterizationDensity;
    251         Info.LineSpacing = (float)FT_CEIL(metrics.height) * InvRasterizationDensity;
    252         Info.LineGap = (float)FT_CEIL(metrics.height - metrics.ascender + metrics.descender) * InvRasterizationDensity;
    253         Info.MaxAdvanceWidth = (float)FT_CEIL(metrics.max_advance) * InvRasterizationDensity;
    254     }
    255 
    256     const FT_Glyph_Metrics* FreeTypeFont::LoadGlyph(uint32_t codepoint)
    257     {
    258         uint32_t glyph_index = FT_Get_Char_Index(Face, codepoint);
    259         if (glyph_index == 0)
    260             return nullptr;
    261 
    262 		// If this crash for you: FreeType 2.11.0 has a crash bug on some bitmap/colored fonts.
    263 		// - https://gitlab.freedesktop.org/freetype/freetype/-/issues/1076
    264 		// - https://github.com/ocornut/imgui/issues/4567
    265 		// - https://github.com/ocornut/imgui/issues/4566
    266 		// You can use FreeType 2.10, or the patched version of 2.11.0 in VcPkg, or probably any upcoming FreeType version.
    267         FT_Error error = FT_Load_Glyph(Face, glyph_index, LoadFlags);
    268         if (error)
    269             return nullptr;
    270 
    271         // Need an outline for this to work
    272         FT_GlyphSlot slot = Face->glyph;
    273 #ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
    274         IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP || slot->format == FT_GLYPH_FORMAT_SVG);
    275 #else
    276 #if ((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12))
    277         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");
    278 #endif
    279         IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP);
    280 #endif // IMGUI_ENABLE_FREETYPE_LUNASVG
    281 
    282         // Apply convenience transform (this is not picking from real "Bold"/"Italic" fonts! Merely applying FreeType helper transform. Oblique == Slanting)
    283         if (UserFlags & ImGuiFreeTypeBuilderFlags_Bold)
    284             FT_GlyphSlot_Embolden(slot);
    285         if (UserFlags & ImGuiFreeTypeBuilderFlags_Oblique)
    286         {
    287             FT_GlyphSlot_Oblique(slot);
    288             //FT_BBox bbox;
    289             //FT_Outline_Get_BBox(&slot->outline, &bbox);
    290             //slot->metrics.width = bbox.xMax - bbox.xMin;
    291             //slot->metrics.height = bbox.yMax - bbox.yMin;
    292         }
    293 
    294         return &slot->metrics;
    295     }
    296 
    297     const FT_Bitmap* FreeTypeFont::RenderGlyphAndGetInfo(GlyphInfo* out_glyph_info)
    298     {
    299         FT_GlyphSlot slot = Face->glyph;
    300         FT_Error error = FT_Render_Glyph(slot, RenderMode);
    301         if (error != 0)
    302             return nullptr;
    303 
    304         FT_Bitmap* ft_bitmap = &Face->glyph->bitmap;
    305         out_glyph_info->Width = (int)ft_bitmap->width;
    306         out_glyph_info->Height = (int)ft_bitmap->rows;
    307         out_glyph_info->OffsetX = Face->glyph->bitmap_left;
    308         out_glyph_info->OffsetY = -Face->glyph->bitmap_top;
    309         out_glyph_info->AdvanceX = (float)FT_CEIL(slot->advance.x);
    310         out_glyph_info->IsColored = (ft_bitmap->pixel_mode == FT_PIXEL_MODE_BGRA);
    311 
    312         return ft_bitmap;
    313     }
    314 
    315     void FreeTypeFont::BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch, unsigned char* multiply_table)
    316     {
    317         IM_ASSERT(ft_bitmap != nullptr);
    318         const uint32_t w = ft_bitmap->width;
    319         const uint32_t h = ft_bitmap->rows;
    320         const uint8_t* src = ft_bitmap->buffer;
    321         const uint32_t src_pitch = ft_bitmap->pitch;
    322 
    323         switch (ft_bitmap->pixel_mode)
    324         {
    325         case FT_PIXEL_MODE_GRAY: // Grayscale image, 1 byte per pixel.
    326             {
    327                 if (multiply_table == nullptr)
    328                 {
    329                     for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
    330                         for (uint32_t x = 0; x < w; x++)
    331                             dst[x] = IM_COL32(255, 255, 255, src[x]);
    332                 }
    333                 else
    334                 {
    335                     for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
    336                         for (uint32_t x = 0; x < w; x++)
    337                             dst[x] = IM_COL32(255, 255, 255, multiply_table[src[x]]);
    338                 }
    339                 break;
    340             }
    341         case FT_PIXEL_MODE_MONO: // Monochrome image, 1 bit per pixel. The bits in each byte are ordered from MSB to LSB.
    342             {
    343                 uint8_t color0 = multiply_table ? multiply_table[0] : 0;
    344                 uint8_t color1 = multiply_table ? multiply_table[255] : 255;
    345                 for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
    346                 {
    347                     uint8_t bits = 0;
    348                     const uint8_t* bits_ptr = src;
    349                     for (uint32_t x = 0; x < w; x++, bits <<= 1)
    350                     {
    351                         if ((x & 7) == 0)
    352                             bits = *bits_ptr++;
    353                         dst[x] = IM_COL32(255, 255, 255, (bits & 0x80) ? color1 : color0);
    354                     }
    355                 }
    356                 break;
    357             }
    358         case FT_PIXEL_MODE_BGRA:
    359             {
    360                 // FIXME: Converting pre-multiplied alpha to straight. Doesn't smell good.
    361                 #define DE_MULTIPLY(color, alpha) ImMin((ImU32)(255.0f * (float)color / (float)(alpha + FLT_MIN) + 0.5f), 255u)
    362                 if (multiply_table == nullptr)
    363                 {
    364                     for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
    365                         for (uint32_t x = 0; x < w; x++)
    366                         {
    367                             uint8_t r = src[x * 4 + 2], g = src[x * 4 + 1], b = src[x * 4], a = src[x * 4 + 3];
    368                             dst[x] = IM_COL32(DE_MULTIPLY(r, a), DE_MULTIPLY(g, a), DE_MULTIPLY(b, a), a);
    369                         }
    370                 }
    371                 else
    372                 {
    373                     for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
    374                     {
    375                         for (uint32_t x = 0; x < w; x++)
    376                         {
    377                             uint8_t r = src[x * 4 + 2], g = src[x * 4 + 1], b = src[x * 4], a = src[x * 4 + 3];
    378                             dst[x] = IM_COL32(multiply_table[DE_MULTIPLY(r, a)], multiply_table[DE_MULTIPLY(g, a)], multiply_table[DE_MULTIPLY(b, a)], multiply_table[a]);
    379                         }
    380                     }
    381                 }
    382                 #undef DE_MULTIPLY
    383                 break;
    384             }
    385         default:
    386             IM_ASSERT(0 && "FreeTypeFont::BlitGlyph(): Unknown bitmap pixel mode!");
    387         }
    388     }
    389 } // namespace
    390 
    391 #ifndef STB_RECT_PACK_IMPLEMENTATION                        // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds)
    392 #ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
    393 #define STBRP_ASSERT(x)     do { IM_ASSERT(x); } while (0)
    394 #define STBRP_STATIC
    395 #define STB_RECT_PACK_IMPLEMENTATION
    396 #endif
    397 #ifdef IMGUI_STB_RECT_PACK_FILENAME
    398 #include IMGUI_STB_RECT_PACK_FILENAME
    399 #else
    400 #include "imstb_rectpack.h"
    401 #endif
    402 #endif
    403 
    404 struct ImFontBuildSrcGlyphFT
    405 {
    406     GlyphInfo           Info;
    407     uint32_t            Codepoint;
    408     unsigned int*       BitmapData;         // Point within one of the dst_tmp_bitmap_buffers[] array
    409 
    410     ImFontBuildSrcGlyphFT() { memset((void*)this, 0, sizeof(*this)); }
    411 };
    412 
    413 struct ImFontBuildSrcDataFT
    414 {
    415     FreeTypeFont        Font;
    416     stbrp_rect*         Rects;              // Rectangle to pack. We first fill in their size and the packer will give us their position.
    417     const ImWchar*      SrcRanges;          // Ranges as requested by user (user is allowed to request too much, e.g. 0x0020..0xFFFF)
    418     int                 DstIndex;           // Index into atlas->Fonts[] and dst_tmp_array[]
    419     int                 GlyphsHighest;      // Highest requested codepoint
    420     int                 GlyphsCount;        // Glyph count (excluding missing glyphs and glyphs already set by an earlier source font)
    421     ImBitVector         GlyphsSet;          // Glyph bit map (random access, 1-bit per codepoint. This will be a maximum of 8KB)
    422     ImVector<ImFontBuildSrcGlyphFT>   GlyphsList;
    423 };
    424 
    425 // Temporary data for one destination ImFont* (multiple source fonts can be merged into one destination ImFont)
    426 struct ImFontBuildDstDataFT
    427 {
    428     int                 SrcCount;           // Number of source fonts targeting this destination font.
    429     int                 GlyphsHighest;
    430     int                 GlyphsCount;
    431     ImBitVector         GlyphsSet;          // This is used to resolve collision when multiple sources are merged into a same destination font.
    432 };
    433 
    434 bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, unsigned int extra_flags)
    435 {
    436     IM_ASSERT(atlas->ConfigData.Size > 0);
    437 
    438     ImFontAtlasBuildInit(atlas);
    439 
    440     // Clear atlas
    441     atlas->TexID = 0;
    442     atlas->TexWidth = atlas->TexHeight = 0;
    443     atlas->TexUvScale = ImVec2(0.0f, 0.0f);
    444     atlas->TexUvWhitePixel = ImVec2(0.0f, 0.0f);
    445     atlas->ClearTexData();
    446 
    447     // Temporary storage for building
    448     bool src_load_color = false;
    449     ImVector<ImFontBuildSrcDataFT> src_tmp_array;
    450     ImVector<ImFontBuildDstDataFT> dst_tmp_array;
    451     src_tmp_array.resize(atlas->ConfigData.Size);
    452     dst_tmp_array.resize(atlas->Fonts.Size);
    453     memset((void*)src_tmp_array.Data, 0, (size_t)src_tmp_array.size_in_bytes());
    454     memset((void*)dst_tmp_array.Data, 0, (size_t)dst_tmp_array.size_in_bytes());
    455 
    456     // 1. Initialize font loading structure, check font data validity
    457     for (int src_i = 0; src_i < atlas->ConfigData.Size; src_i++)
    458     {
    459         ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
    460         ImFontConfig& cfg = atlas->ConfigData[src_i];
    461         FreeTypeFont& font_face = src_tmp.Font;
    462         IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == atlas));
    463 
    464         // Find index from cfg.DstFont (we allow the user to set cfg.DstFont. Also it makes casual debugging nicer than when storing indices)
    465         src_tmp.DstIndex = -1;
    466         for (int output_i = 0; output_i < atlas->Fonts.Size && src_tmp.DstIndex == -1; output_i++)
    467             if (cfg.DstFont == atlas->Fonts[output_i])
    468                 src_tmp.DstIndex = output_i;
    469         IM_ASSERT(src_tmp.DstIndex != -1); // cfg.DstFont not pointing within atlas->Fonts[] array?
    470         if (src_tmp.DstIndex == -1)
    471             return false;
    472 
    473         // Load font
    474         if (!font_face.InitFont(ft_library, cfg, extra_flags))
    475             return false;
    476 
    477         // Measure highest codepoints
    478         src_load_color |= (cfg.FontBuilderFlags & ImGuiFreeTypeBuilderFlags_LoadColor) != 0;
    479         ImFontBuildDstDataFT& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
    480         src_tmp.SrcRanges = cfg.GlyphRanges ? cfg.GlyphRanges : atlas->GetGlyphRangesDefault();
    481         for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
    482         {
    483             // Check for valid range. This may also help detect *some* dangling pointers, because a common
    484             // user error is to setup ImFontConfig::GlyphRanges with a pointer to data that isn't persistent.
    485             IM_ASSERT(src_range[0] <= src_range[1]);
    486             src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]);
    487         }
    488         dst_tmp.SrcCount++;
    489         dst_tmp.GlyphsHighest = ImMax(dst_tmp.GlyphsHighest, src_tmp.GlyphsHighest);
    490     }
    491 
    492     // 2. For every requested codepoint, check for their presence in the font data, and handle redundancy or overlaps between source fonts to avoid unused glyphs.
    493     int total_glyphs_count = 0;
    494     for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
    495     {
    496         ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
    497         ImFontBuildDstDataFT& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
    498         src_tmp.GlyphsSet.Create(src_tmp.GlyphsHighest + 1);
    499         if (dst_tmp.GlyphsSet.Storage.empty())
    500             dst_tmp.GlyphsSet.Create(dst_tmp.GlyphsHighest + 1);
    501 
    502         for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
    503             for (int codepoint = src_range[0]; codepoint <= (int)src_range[1]; codepoint++)
    504             {
    505                 if (dst_tmp.GlyphsSet.TestBit(codepoint))    // Don't overwrite existing glyphs. We could make this an option (e.g. MergeOverwrite)
    506                     continue;
    507                 uint32_t glyph_index = FT_Get_Char_Index(src_tmp.Font.Face, codepoint); // It is actually in the font? (FIXME-OPT: We are not storing the glyph_index..)
    508                 if (glyph_index == 0)
    509                     continue;
    510 
    511                 // Add to avail set/counters
    512                 src_tmp.GlyphsCount++;
    513                 dst_tmp.GlyphsCount++;
    514                 src_tmp.GlyphsSet.SetBit(codepoint);
    515                 dst_tmp.GlyphsSet.SetBit(codepoint);
    516                 total_glyphs_count++;
    517             }
    518     }
    519 
    520     // 3. Unpack our bit map into a flat list (we now have all the Unicode points that we know are requested _and_ available _and_ not overlapping another)
    521     for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
    522     {
    523         ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
    524         src_tmp.GlyphsList.reserve(src_tmp.GlyphsCount);
    525 
    526         IM_ASSERT(sizeof(src_tmp.GlyphsSet.Storage.Data[0]) == sizeof(ImU32));
    527         const ImU32* it_begin = src_tmp.GlyphsSet.Storage.begin();
    528         const ImU32* it_end = src_tmp.GlyphsSet.Storage.end();
    529         for (const ImU32* it = it_begin; it < it_end; it++)
    530             if (ImU32 entries_32 = *it)
    531                 for (ImU32 bit_n = 0; bit_n < 32; bit_n++)
    532                     if (entries_32 & ((ImU32)1 << bit_n))
    533                     {
    534                         ImFontBuildSrcGlyphFT src_glyph;
    535                         src_glyph.Codepoint = (ImWchar)(((it - it_begin) << 5) + bit_n);
    536                         //src_glyph.GlyphIndex = 0; // FIXME-OPT: We had this info in the previous step and lost it..
    537                         src_tmp.GlyphsList.push_back(src_glyph);
    538                     }
    539         src_tmp.GlyphsSet.Clear();
    540         IM_ASSERT(src_tmp.GlyphsList.Size == src_tmp.GlyphsCount);
    541     }
    542     for (int dst_i = 0; dst_i < dst_tmp_array.Size; dst_i++)
    543         dst_tmp_array[dst_i].GlyphsSet.Clear();
    544     dst_tmp_array.clear();
    545 
    546     // Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0)
    547     // (We technically don't need to zero-clear buf_rects, but let's do it for the sake of sanity)
    548     ImVector<stbrp_rect> buf_rects;
    549     buf_rects.resize(total_glyphs_count);
    550     memset(buf_rects.Data, 0, (size_t)buf_rects.size_in_bytes());
    551 
    552     // Allocate temporary rasterization data buffers.
    553     // We could not find a way to retrieve accurate glyph size without rendering them.
    554     // (e.g. slot->metrics->width not always matching bitmap->width, especially considering the Oblique transform)
    555     // We allocate in chunks of 256 KB to not waste too much extra memory ahead. Hopefully users of FreeType won't mind the temporary allocations.
    556     const int BITMAP_BUFFERS_CHUNK_SIZE = 256 * 1024;
    557     int buf_bitmap_current_used_bytes = 0;
    558     ImVector<unsigned char*> buf_bitmap_buffers;
    559     buf_bitmap_buffers.push_back((unsigned char*)IM_ALLOC(BITMAP_BUFFERS_CHUNK_SIZE));
    560 
    561     // 4. Gather glyphs sizes so we can pack them in our virtual canvas.
    562     // 8. Render/rasterize font characters into the texture
    563     int total_surface = 0;
    564     int buf_rects_out_n = 0;
    565     for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
    566     {
    567         ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
    568         ImFontConfig& cfg = atlas->ConfigData[src_i];
    569         if (src_tmp.GlyphsCount == 0)
    570             continue;
    571 
    572         src_tmp.Rects = &buf_rects[buf_rects_out_n];
    573         buf_rects_out_n += src_tmp.GlyphsCount;
    574 
    575         // Compute multiply table if requested
    576         const bool multiply_enabled = (cfg.RasterizerMultiply != 1.0f);
    577         unsigned char multiply_table[256];
    578         if (multiply_enabled)
    579             ImFontAtlasBuildMultiplyCalcLookupTable(multiply_table, cfg.RasterizerMultiply);
    580 
    581         // Gather the sizes of all rectangles we will need to pack
    582         const int padding = atlas->TexGlyphPadding;
    583         for (int glyph_i = 0; glyph_i < src_tmp.GlyphsList.Size; glyph_i++)
    584         {
    585             ImFontBuildSrcGlyphFT& src_glyph = src_tmp.GlyphsList[glyph_i];
    586 
    587             const FT_Glyph_Metrics* metrics = src_tmp.Font.LoadGlyph(src_glyph.Codepoint);
    588             if (metrics == nullptr)
    589                 continue;
    590 
    591             // Render glyph into a bitmap (currently held by FreeType)
    592             const FT_Bitmap* ft_bitmap = src_tmp.Font.RenderGlyphAndGetInfo(&src_glyph.Info);
    593             if (ft_bitmap == nullptr)
    594                 continue;
    595 
    596             // Allocate new temporary chunk if needed
    597             const int bitmap_size_in_bytes = src_glyph.Info.Width * src_glyph.Info.Height * 4;
    598             if (buf_bitmap_current_used_bytes + bitmap_size_in_bytes > BITMAP_BUFFERS_CHUNK_SIZE)
    599             {
    600                 buf_bitmap_current_used_bytes = 0;
    601                 buf_bitmap_buffers.push_back((unsigned char*)IM_ALLOC(BITMAP_BUFFERS_CHUNK_SIZE));
    602             }
    603             IM_ASSERT(buf_bitmap_current_used_bytes + bitmap_size_in_bytes <= BITMAP_BUFFERS_CHUNK_SIZE); // We could probably allocate custom-sized buffer instead.
    604 
    605             // Blit rasterized pixels to our temporary buffer and keep a pointer to it.
    606             src_glyph.BitmapData = (unsigned int*)(buf_bitmap_buffers.back() + buf_bitmap_current_used_bytes);
    607             buf_bitmap_current_used_bytes += bitmap_size_in_bytes;
    608             src_tmp.Font.BlitGlyph(ft_bitmap, src_glyph.BitmapData, src_glyph.Info.Width, multiply_enabled ? multiply_table : nullptr);
    609 
    610             src_tmp.Rects[glyph_i].w = (stbrp_coord)(src_glyph.Info.Width + padding);
    611             src_tmp.Rects[glyph_i].h = (stbrp_coord)(src_glyph.Info.Height + padding);
    612             total_surface += src_tmp.Rects[glyph_i].w * src_tmp.Rects[glyph_i].h;
    613         }
    614     }
    615 
    616     // We need a width for the skyline algorithm, any width!
    617     // The exact width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height.
    618     // User can override TexDesiredWidth and TexGlyphPadding if they wish, otherwise we use a simple heuristic to select the width based on expected surface.
    619     const int surface_sqrt = (int)ImSqrt((float)total_surface) + 1;
    620     atlas->TexHeight = 0;
    621     if (atlas->TexDesiredWidth > 0)
    622         atlas->TexWidth = atlas->TexDesiredWidth;
    623     else
    624         atlas->TexWidth = (surface_sqrt >= 4096 * 0.7f) ? 4096 : (surface_sqrt >= 2048 * 0.7f) ? 2048 : (surface_sqrt >= 1024 * 0.7f) ? 1024 : 512;
    625 
    626     // 5. Start packing
    627     // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values).
    628     const int TEX_HEIGHT_MAX = 1024 * 32;
    629     const int num_nodes_for_packing_algorithm = atlas->TexWidth - atlas->TexGlyphPadding;
    630     ImVector<stbrp_node> pack_nodes;
    631     pack_nodes.resize(num_nodes_for_packing_algorithm);
    632     stbrp_context pack_context;
    633     stbrp_init_target(&pack_context, atlas->TexWidth - atlas->TexGlyphPadding, TEX_HEIGHT_MAX - atlas->TexGlyphPadding, pack_nodes.Data, pack_nodes.Size);
    634     ImFontAtlasBuildPackCustomRects(atlas, &pack_context);
    635 
    636     // 6. Pack each source font. No rendering yet, we are working with rectangles in an infinitely tall texture at this point.
    637     for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
    638     {
    639         ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
    640         if (src_tmp.GlyphsCount == 0)
    641             continue;
    642 
    643         stbrp_pack_rects(&pack_context, src_tmp.Rects, src_tmp.GlyphsCount);
    644 
    645         // Extend texture height and mark missing glyphs as non-packed so we won't render them.
    646         // FIXME: We are not handling packing failure here (would happen if we got off TEX_HEIGHT_MAX or if a single if larger than TexWidth?)
    647         for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
    648             if (src_tmp.Rects[glyph_i].was_packed)
    649                 atlas->TexHeight = ImMax(atlas->TexHeight, src_tmp.Rects[glyph_i].y + src_tmp.Rects[glyph_i].h);
    650     }
    651 
    652     // 7. Allocate texture
    653     atlas->TexHeight = (atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (atlas->TexHeight + 1) : ImUpperPowerOfTwo(atlas->TexHeight);
    654     atlas->TexUvScale = ImVec2(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight);
    655     if (src_load_color)
    656     {
    657         size_t tex_size = (size_t)atlas->TexWidth * atlas->TexHeight * 4;
    658         atlas->TexPixelsRGBA32 = (unsigned int*)IM_ALLOC(tex_size);
    659         memset(atlas->TexPixelsRGBA32, 0, tex_size);
    660     }
    661     else
    662     {
    663         size_t tex_size = (size_t)atlas->TexWidth * atlas->TexHeight * 1;
    664         atlas->TexPixelsAlpha8 = (unsigned char*)IM_ALLOC(tex_size);
    665         memset(atlas->TexPixelsAlpha8, 0, tex_size);
    666     }
    667 
    668     // 8. Copy rasterized font characters back into the main texture
    669     // 9. Setup ImFont and glyphs for runtime
    670     bool tex_use_colors = false;
    671     for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
    672     {
    673         ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
    674         if (src_tmp.GlyphsCount == 0)
    675             continue;
    676 
    677         // When merging fonts with MergeMode=true:
    678         // - We can have multiple input fonts writing into a same destination font.
    679         // - dst_font->ConfigData is != from cfg which is our source configuration.
    680         ImFontConfig& cfg = atlas->ConfigData[src_i];
    681         ImFont* dst_font = cfg.DstFont;
    682 
    683         const float ascent = src_tmp.Font.Info.Ascender;
    684         const float descent = src_tmp.Font.Info.Descender;
    685         ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent);
    686         const float font_off_x = cfg.GlyphOffset.x;
    687         const float font_off_y = cfg.GlyphOffset.y + IM_ROUND(dst_font->Ascent);
    688 
    689         const int padding = atlas->TexGlyphPadding;
    690         for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
    691         {
    692             ImFontBuildSrcGlyphFT& src_glyph = src_tmp.GlyphsList[glyph_i];
    693             stbrp_rect& pack_rect = src_tmp.Rects[glyph_i];
    694             IM_ASSERT(pack_rect.was_packed);
    695             if (pack_rect.w == 0 && pack_rect.h == 0)
    696                 continue;
    697 
    698             GlyphInfo& info = src_glyph.Info;
    699             IM_ASSERT(info.Width + padding <= pack_rect.w);
    700             IM_ASSERT(info.Height + padding <= pack_rect.h);
    701             const int tx = pack_rect.x + padding;
    702             const int ty = pack_rect.y + padding;
    703 
    704             // Register glyph
    705             float x0 = info.OffsetX * src_tmp.Font.InvRasterizationDensity + font_off_x;
    706             float y0 = info.OffsetY * src_tmp.Font.InvRasterizationDensity + font_off_y;
    707             float x1 = x0 + info.Width * src_tmp.Font.InvRasterizationDensity;
    708             float y1 = y0 + info.Height * src_tmp.Font.InvRasterizationDensity;
    709             float u0 = (tx) / (float)atlas->TexWidth;
    710             float v0 = (ty) / (float)atlas->TexHeight;
    711             float u1 = (tx + info.Width) / (float)atlas->TexWidth;
    712             float v1 = (ty + info.Height) / (float)atlas->TexHeight;
    713             dst_font->AddGlyph(&cfg, (ImWchar)src_glyph.Codepoint, x0, y0, x1, y1, u0, v0, u1, v1, info.AdvanceX * src_tmp.Font.InvRasterizationDensity);
    714 
    715             ImFontGlyph* dst_glyph = &dst_font->Glyphs.back();
    716             IM_ASSERT(dst_glyph->Codepoint == src_glyph.Codepoint);
    717             if (src_glyph.Info.IsColored)
    718                 dst_glyph->Colored = tex_use_colors = true;
    719 
    720             // Blit from temporary buffer to final texture
    721             size_t blit_src_stride = (size_t)src_glyph.Info.Width;
    722             size_t blit_dst_stride = (size_t)atlas->TexWidth;
    723             unsigned int* blit_src = src_glyph.BitmapData;
    724             if (atlas->TexPixelsAlpha8 != nullptr)
    725             {
    726                 unsigned char* blit_dst = atlas->TexPixelsAlpha8 + (ty * blit_dst_stride) + tx;
    727                 for (int y = 0; y < info.Height; y++, blit_dst += blit_dst_stride, blit_src += blit_src_stride)
    728                     for (int x = 0; x < info.Width; x++)
    729                         blit_dst[x] = (unsigned char)((blit_src[x] >> IM_COL32_A_SHIFT) & 0xFF);
    730             }
    731             else
    732             {
    733                 unsigned int* blit_dst = atlas->TexPixelsRGBA32 + (ty * blit_dst_stride) + tx;
    734                 for (int y = 0; y < info.Height; y++, blit_dst += blit_dst_stride, blit_src += blit_src_stride)
    735                     for (int x = 0; x < info.Width; x++)
    736                         blit_dst[x] = blit_src[x];
    737             }
    738         }
    739 
    740         src_tmp.Rects = nullptr;
    741     }
    742     atlas->TexPixelsUseColors = tex_use_colors;
    743 
    744     // Cleanup
    745     for (int buf_i = 0; buf_i < buf_bitmap_buffers.Size; buf_i++)
    746         IM_FREE(buf_bitmap_buffers[buf_i]);
    747     src_tmp_array.clear_destruct();
    748 
    749     ImFontAtlasBuildFinish(atlas);
    750 
    751     return true;
    752 }
    753 
    754 // FreeType memory allocation callbacks
    755 static void* FreeType_Alloc(FT_Memory /*memory*/, long size)
    756 {
    757     return GImGuiFreeTypeAllocFunc((size_t)size, GImGuiFreeTypeAllocatorUserData);
    758 }
    759 
    760 static void FreeType_Free(FT_Memory /*memory*/, void* block)
    761 {
    762     GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
    763 }
    764 
    765 static void* FreeType_Realloc(FT_Memory /*memory*/, long cur_size, long new_size, void* block)
    766 {
    767     // Implement realloc() as we don't ask user to provide it.
    768     if (block == nullptr)
    769         return GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData);
    770 
    771     if (new_size == 0)
    772     {
    773         GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
    774         return nullptr;
    775     }
    776 
    777     if (new_size > cur_size)
    778     {
    779         void* new_block = GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData);
    780         memcpy(new_block, block, (size_t)cur_size);
    781         GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
    782         return new_block;
    783     }
    784 
    785     return block;
    786 }
    787 
    788 static bool ImFontAtlasBuildWithFreeType(ImFontAtlas* atlas)
    789 {
    790     // FreeType memory management: https://www.freetype.org/freetype2/docs/design/design-4.html
    791     FT_MemoryRec_ memory_rec = {};
    792     memory_rec.user = nullptr;
    793     memory_rec.alloc = &FreeType_Alloc;
    794     memory_rec.free = &FreeType_Free;
    795     memory_rec.realloc = &FreeType_Realloc;
    796 
    797     // https://www.freetype.org/freetype2/docs/reference/ft2-module_management.html#FT_New_Library
    798     FT_Library ft_library;
    799     FT_Error error = FT_New_Library(&memory_rec, &ft_library);
    800     if (error != 0)
    801         return false;
    802 
    803     // If you don't call FT_Add_Default_Modules() the rest of code may work, but FreeType won't use our custom allocator.
    804     FT_Add_Default_Modules(ft_library);
    805 
    806 #ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
    807     // Install svg hooks for FreeType
    808     // https://freetype.org/freetype2/docs/reference/ft2-properties.html#svg-hooks
    809     // https://freetype.org/freetype2/docs/reference/ft2-svg_fonts.html#svg_fonts
    810     SVG_RendererHooks hooks = { ImGuiLunasvgPortInit, ImGuiLunasvgPortFree, ImGuiLunasvgPortRender, ImGuiLunasvgPortPresetSlot };
    811     FT_Property_Set(ft_library, "ot-svg", "svg-hooks", &hooks);
    812 #endif // IMGUI_ENABLE_FREETYPE_LUNASVG
    813 
    814     bool ret = ImFontAtlasBuildWithFreeTypeEx(ft_library, atlas, atlas->FontBuilderFlags);
    815     FT_Done_Library(ft_library);
    816 
    817     return ret;
    818 }
    819 
    820 const ImFontBuilderIO* ImGuiFreeType::GetBuilderForFreeType()
    821 {
    822     static ImFontBuilderIO io;
    823     io.FontBuilder_Build = ImFontAtlasBuildWithFreeType;
    824     return &io;
    825 }
    826 
    827 void ImGuiFreeType::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data)
    828 {
    829     GImGuiFreeTypeAllocFunc = alloc_func;
    830     GImGuiFreeTypeFreeFunc = free_func;
    831     GImGuiFreeTypeAllocatorUserData = user_data;
    832 }
    833 
    834 #ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
    835 // For more details, see https://gitlab.freedesktop.org/freetype/freetype-demos/-/blob/master/src/rsvg-port.c
    836 // 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)
    837 struct LunasvgPortState
    838 {
    839     LunasvgPortState();
    840     ~LunasvgPortState();
    841 
    842     FT_Error err = FT_Err_Ok;
    843     lunasvg_matrix* matrix = nullptr;
    844     lunasvg_document* svg = nullptr;
    845 };
    846 
    847 LunasvgPortState::LunasvgPortState()
    848 {
    849   matrix = lunasvg_matrix_create();
    850 }
    851 
    852 LunasvgPortState::~LunasvgPortState()
    853 {
    854   lunasvg_matrix_destroy(matrix);
    855   if (svg)
    856     lunasvg_document_destroy(svg);
    857 }
    858 
    859 static FT_Error ImGuiLunasvgPortInit(FT_Pointer* _state)
    860 {
    861     *_state = IM_NEW(LunasvgPortState)();
    862     return FT_Err_Ok;
    863 }
    864 
    865 static void ImGuiLunasvgPortFree(FT_Pointer* _state)
    866 {
    867     IM_DELETE(*(LunasvgPortState**)_state);
    868 }
    869 
    870 static FT_Error ImGuiLunasvgPortRender(FT_GlyphSlot slot, FT_Pointer* _state)
    871 {
    872     LunasvgPortState* state = *(LunasvgPortState**)_state;
    873 
    874     // If there was an error while loading the svg in ImGuiLunasvgPortPresetSlot(), the renderer hook still get called, so just returns the error.
    875     if (state->err != FT_Err_Ok)
    876         return state->err;
    877 
    878     // rows is height, pitch (or stride) equals to width * sizeof(int32)
    879     lunasvg_bitmap* bitmap = lunasvg_bitmap_create_with_data((uint8_t*)slot->bitmap.buffer, slot->bitmap.width, slot->bitmap.rows, slot->bitmap.pitch);
    880     lunasvg_document_set_identity_matrix(state->svg); // Reset the svg matrix to the default value
    881     lunasvg_document_render(state->svg, bitmap, state->matrix);              // state->matrix is already scaled and translated
    882     lunasvg_bitmap_destroy(bitmap);
    883     state->err = FT_Err_Ok;
    884     return state->err;
    885 }
    886 
    887 static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_Pointer* _state)
    888 {
    889     FT_SVG_Document   document = (FT_SVG_Document)slot->other;
    890     LunasvgPortState* state = *(LunasvgPortState**)_state;
    891     FT_Size_Metrics&  metrics = document->metrics;
    892 
    893     // This function is called twice, once in the FT_Load_Glyph() and another right before ImGuiLunasvgPortRender().
    894     // If it's the latter, don't do anything because it's // already done in the former.
    895     if (cache)
    896         return state->err;
    897 
    898     if (state->svg)
    899       lunasvg_document_destroy(state->svg);
    900     state->svg = lunasvg_document_load_from_data(document->svg_document, document->svg_document_length);
    901     if (state->svg == nullptr)
    902     {
    903         state->err = FT_Err_Invalid_SVG_Document;
    904         return state->err;
    905     }
    906 
    907     lunasvg_box* box = lunasvg_box_create();
    908     double box_x, box_y, box_w, box_h;
    909     lunasvg_document_get_box(state->svg, box);
    910     lunasvg_box_get_values(box, &box_x, &box_y, &box_w, &box_h);
    911 
    912     double scale = std::min(metrics.x_ppem / box_w, metrics.y_ppem / box_h);
    913     double xx = (double)document->transform.xx / (1 << 16);
    914     double xy = -(double)document->transform.xy / (1 << 16);
    915     double yx = -(double)document->transform.yx / (1 << 16);
    916     double yy = (double)document->transform.yy / (1 << 16);
    917     double x0 = (double)document->delta.x / 64 * box_w / metrics.x_ppem;
    918     double y0 = -(double)document->delta.y / 64 * box_h / metrics.y_ppem;
    919 
    920     // Scale and transform, we don't translate the svg yet
    921     lunasvg_matrix_identity(state->matrix);
    922     lunasvg_matrix_scale(state->matrix, scale, scale);
    923     lunasvg_matrix_transform(state->matrix, xx, xy, yx, yy, x0, y0);
    924     lunasvg_document_set_matrix(state->svg, state->matrix);
    925 
    926     // Pre-translate the matrix for the rendering step
    927     lunasvg_matrix_translate(state->matrix, -box_x, -box_y);
    928 
    929     // Get the box again after the transformation
    930     lunasvg_document_get_box(state->svg, box);
    931     lunasvg_box_get_values(box, &box_x, &box_y, &box_w, &box_h);
    932     lunasvg_box_destroy(box);
    933 
    934     // Calculate the bitmap size
    935     slot->bitmap_left = FT_Int(box_x);
    936     slot->bitmap_top = FT_Int(-box_y);
    937     slot->bitmap.rows = (unsigned int)(ImCeil((float)box_h));
    938     slot->bitmap.width = (unsigned int)(ImCeil((float)box_w));
    939     slot->bitmap.pitch = slot->bitmap.width * 4;
    940     slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA;
    941 
    942     // Compute all the bearings and set them correctly. The outline is scaled already, we just need to use the bounding box.
    943     double metrics_width = box_w;
    944     double metrics_height = box_h;
    945     double horiBearingX = box_x;
    946     double horiBearingY = -box_y;
    947     double vertBearingX = slot->metrics.horiBearingX / 64.0 - slot->metrics.horiAdvance / 64.0 / 2.0;
    948     double vertBearingY = (slot->metrics.vertAdvance / 64.0 - slot->metrics.height / 64.0) / 2.0;
    949     slot->metrics.width = FT_Pos(IM_ROUND(metrics_width * 64.0));   // Using IM_ROUND() assume width and height are positive
    950     slot->metrics.height = FT_Pos(IM_ROUND(metrics_height * 64.0));
    951     slot->metrics.horiBearingX = FT_Pos(horiBearingX * 64);
    952     slot->metrics.horiBearingY = FT_Pos(horiBearingY * 64);
    953     slot->metrics.vertBearingX = FT_Pos(vertBearingX * 64);
    954     slot->metrics.vertBearingY = FT_Pos(vertBearingY * 64);
    955 
    956     if (slot->metrics.vertAdvance == 0)
    957         slot->metrics.vertAdvance = FT_Pos(metrics_height * 1.2 * 64.0);
    958 
    959     state->err = FT_Err_Ok;
    960     return state->err;
    961 }
    962 
    963 #endif // #ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
    964 
    965 //-----------------------------------------------------------------------------
    966 
    967 #ifdef __GNUC__
    968 #pragma GCC diagnostic pop
    969 #endif
    970 
    971 #ifdef _MSC_VER
    972 #pragma warning (pop)
    973 #endif
    974 
    975 #endif // #ifndef IMGUI_DISABLE