imgui

FORK: Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies
git clone https://git.neptards.moe/neptards/imgui.git
Log | Files | Refs

imgui_freetype.cpp (37549B)


      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 //  2021/03/05: added ImGuiFreeTypeBuilderFlags_Bitmap to load bitmap glyphs.
     10 //  2021/03/02: set 'atlas->TexPixelsUseColors = true' to help some backends with deciding of a prefered texture format.
     11 //  2021/01/28: added support for color-layered glyphs via ImGuiFreeTypeBuilderFlags_LoadColor (require Freetype 2.10+).
     12 //  2021/01/26: simplified integration by using '#define IMGUI_ENABLE_FREETYPE'.
     13 //              renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API. removed ImGuiFreeType::BuildFontAtlas().
     14 //  2020/06/04: fix for rare case where FT_Get_Char_Index() succeed but FT_Load_Glyph() fails.
     15 //  2019/02/09: added RasterizerFlags::Monochrome flag to disable font anti-aliasing (combine with ::MonoHinting for best results!)
     16 //  2019/01/15: added support for imgui allocators + added FreeType only override function SetAllocatorFunctions().
     17 //  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.
     18 //  2018/06/08: added support for ImFontConfig::GlyphMinAdvanceX, GlyphMaxAdvanceX.
     19 //  2018/02/04: moved to main imgui repository (away from http://www.github.com/ocornut/imgui_club)
     20 //  2018/01/22: fix for addition of ImFontAtlas::TexUvscale member.
     21 //  2017/10/22: minor inconsequential change to match change in master (removed an unnecessary statement).
     22 //  2017/09/26: fixes for imgui internal changes.
     23 //  2017/08/26: cleanup, optimizations, support for ImFontConfig::RasterizerFlags, ImFontConfig::RasterizerMultiply.
     24 //  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.
     25 
     26 // About Gamma Correct Blending:
     27 // - FreeType assumes blending in linear space rather than gamma space.
     28 // - See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Render_Glyph
     29 // - For correct results you need to be using sRGB and convert to linear space in the pixel shader output.
     30 // - The default dear imgui styles will be impacted by this change (alpha values will need tweaking).
     31 
     32 // FIXME: cfg.OversampleH, OversampleV are not supported (but perhaps not so necessary with this rasterizer).
     33 
     34 #include "imgui_freetype.h"
     35 #include "imgui_internal.h"     // ImMin,ImMax,ImFontAtlasBuild*,
     36 #include <stdint.h>
     37 #include <ft2build.h>
     38 #include FT_FREETYPE_H          // <freetype/freetype.h>
     39 #include FT_MODULE_H            // <freetype/ftmodapi.h>
     40 #include FT_GLYPH_H             // <freetype/ftglyph.h>
     41 #include FT_SYNTHESIS_H         // <freetype/ftsynth.h>
     42 
     43 #ifdef _MSC_VER
     44 #pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
     45 #endif
     46 
     47 #if defined(__GNUC__)
     48 #pragma GCC diagnostic ignored "-Wpragmas"                  // warning: unknown option after '#pragma GCC diagnostic' kind
     49 #pragma GCC diagnostic ignored "-Wunused-function"          // warning: 'xxxx' defined but not used
     50 #endif
     51 
     52 //-------------------------------------------------------------------------
     53 // Data
     54 //-------------------------------------------------------------------------
     55 
     56 // Default memory allocators
     57 static void* ImGuiFreeTypeDefaultAllocFunc(size_t size, void* user_data) { IM_UNUSED(user_data); return IM_ALLOC(size); }
     58 static void  ImGuiFreeTypeDefaultFreeFunc(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_FREE(ptr); }
     59 
     60 // Current memory allocators
     61 static void* (*GImGuiFreeTypeAllocFunc)(size_t size, void* user_data) = ImGuiFreeTypeDefaultAllocFunc;
     62 static void  (*GImGuiFreeTypeFreeFunc)(void* ptr, void* user_data) = ImGuiFreeTypeDefaultFreeFunc;
     63 static void* GImGuiFreeTypeAllocatorUserData = NULL;
     64 
     65 //-------------------------------------------------------------------------
     66 // Code
     67 //-------------------------------------------------------------------------
     68 
     69 namespace
     70 {
     71     // Glyph metrics:
     72     // --------------
     73     //
     74     //                       xmin                     xmax
     75     //                        |                         |
     76     //                        |<-------- width -------->|
     77     //                        |                         |
     78     //              |         +-------------------------+----------------- ymax
     79     //              |         |    ggggggggg   ggggg    |     ^        ^
     80     //              |         |   g:::::::::ggg::::g    |     |        |
     81     //              |         |  g:::::::::::::::::g    |     |        |
     82     //              |         | g::::::ggggg::::::gg    |     |        |
     83     //              |         | g:::::g     g:::::g     |     |        |
     84     //    offsetX  -|-------->| g:::::g     g:::::g     |  offsetY     |
     85     //              |         | g:::::g     g:::::g     |     |        |
     86     //              |         | g::::::g    g:::::g     |     |        |
     87     //              |         | g:::::::ggggg:::::g     |     |        |
     88     //              |         |  g::::::::::::::::g     |     |      height
     89     //              |         |   gg::::::::::::::g     |     |        |
     90     //  baseline ---*---------|---- gggggggg::::::g-----*--------      |
     91     //            / |         |             g:::::g     |              |
     92     //     origin   |         | gggggg      g:::::g     |              |
     93     //              |         | g:::::gg   gg:::::g     |              |
     94     //              |         |  g::::::ggg:::::::g     |              |
     95     //              |         |   gg:::::::::::::g      |              |
     96     //              |         |     ggg::::::ggg        |              |
     97     //              |         |         gggggg          |              v
     98     //              |         +-------------------------+----------------- ymin
     99     //              |                                   |
    100     //              |------------- advanceX ----------->|
    101 
    102     // A structure that describe a glyph.
    103     struct GlyphInfo
    104     {
    105         int         Width;              // Glyph's width in pixels.
    106         int         Height;             // Glyph's height in pixels.
    107         FT_Int      OffsetX;            // The distance from the origin ("pen position") to the left of the glyph.
    108         FT_Int      OffsetY;            // The distance from the origin to the top of the glyph. This is usually a value < 0.
    109         float       AdvanceX;           // The distance from the origin to the origin of the next glyph. This is usually a value > 0.
    110         bool        IsColored;          // The glyph is colored
    111     };
    112 
    113     // Font parameters and metrics.
    114     struct FontInfo
    115     {
    116         uint32_t    PixelHeight;        // Size this font was generated with.
    117         float       Ascender;           // The pixel extents above the baseline in pixels (typically positive).
    118         float       Descender;          // The extents below the baseline in pixels (typically negative).
    119         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.
    120         float       LineGap;            // The spacing in pixels between one row's descent and the next row's ascent.
    121         float       MaxAdvanceWidth;    // This field gives the maximum horizontal cursor advance for all glyphs in the font.
    122     };
    123 
    124     // FreeType glyph rasterizer.
    125     // NB: No ctor/dtor, explicitly call Init()/Shutdown()
    126     struct FreeTypeFont
    127     {
    128         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.
    129         void                    CloseFont();
    130         void                    SetPixelHeight(int pixel_height); // Change font pixel size. All following calls to RasterizeGlyph() will use this size
    131         const FT_Glyph_Metrics* LoadGlyph(uint32_t in_codepoint);
    132         const FT_Bitmap*        RenderGlyphAndGetInfo(GlyphInfo* out_glyph_info);
    133         void                    BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch, unsigned char* multiply_table = NULL);
    134         ~FreeTypeFont()         { CloseFont(); }
    135 
    136         // [Internals]
    137         FontInfo        Info;               // Font descriptor of the current font.
    138         FT_Face         Face;
    139         unsigned int    UserFlags;          // = ImFontConfig::RasterizerFlags
    140         FT_Int32        LoadFlags;
    141         FT_Render_Mode  RenderMode;
    142     };
    143 
    144     // From SDL_ttf: Handy routines for converting from fixed point
    145     #define FT_CEIL(X)  (((X + 63) & -64) / 64)
    146 
    147     bool FreeTypeFont::InitFont(FT_Library ft_library, const ImFontConfig& cfg, unsigned int extra_font_builder_flags)
    148     {
    149         FT_Error error = FT_New_Memory_Face(ft_library, (uint8_t*)cfg.FontData, (uint32_t)cfg.FontDataSize, (uint32_t)cfg.FontNo, &Face);
    150         if (error != 0)
    151             return false;
    152         error = FT_Select_Charmap(Face, FT_ENCODING_UNICODE);
    153         if (error != 0)
    154             return false;
    155 
    156         // Convert to FreeType flags (NB: Bold and Oblique are processed separately)
    157         UserFlags = cfg.FontBuilderFlags | extra_font_builder_flags;
    158 
    159         LoadFlags = 0;
    160         if ((UserFlags & ImGuiFreeTypeBuilderFlags_Bitmap) == 0)
    161             LoadFlags |= FT_LOAD_NO_BITMAP;
    162 
    163         if (UserFlags & ImGuiFreeTypeBuilderFlags_NoHinting)
    164             LoadFlags |= FT_LOAD_NO_HINTING;
    165         if (UserFlags & ImGuiFreeTypeBuilderFlags_NoAutoHint)
    166             LoadFlags |= FT_LOAD_NO_AUTOHINT;
    167         if (UserFlags & ImGuiFreeTypeBuilderFlags_ForceAutoHint)
    168             LoadFlags |= FT_LOAD_FORCE_AUTOHINT;
    169         if (UserFlags & ImGuiFreeTypeBuilderFlags_LightHinting)
    170             LoadFlags |= FT_LOAD_TARGET_LIGHT;
    171         else if (UserFlags & ImGuiFreeTypeBuilderFlags_MonoHinting)
    172             LoadFlags |= FT_LOAD_TARGET_MONO;
    173         else
    174             LoadFlags |= FT_LOAD_TARGET_NORMAL;
    175 
    176         if (UserFlags & ImGuiFreeTypeBuilderFlags_Monochrome)
    177             RenderMode = FT_RENDER_MODE_MONO;
    178         else
    179             RenderMode = FT_RENDER_MODE_NORMAL;
    180 
    181         if (UserFlags & ImGuiFreeTypeBuilderFlags_LoadColor)
    182             LoadFlags |= FT_LOAD_COLOR;
    183 
    184         memset(&Info, 0, sizeof(Info));
    185         SetPixelHeight((uint32_t)cfg.SizePixels);
    186 
    187         return true;
    188     }
    189 
    190     void FreeTypeFont::CloseFont()
    191     {
    192         if (Face)
    193         {
    194             FT_Done_Face(Face);
    195             Face = NULL;
    196         }
    197     }
    198 
    199     void FreeTypeFont::SetPixelHeight(int pixel_height)
    200     {
    201         // Vuhdo: I'm not sure how to deal with font sizes properly. As far as I understand, currently ImGui assumes that the 'pixel_height'
    202         // 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.
    203         // NB: FT_Set_Pixel_Sizes() doesn't seem to get us the same result.
    204         FT_Size_RequestRec req;
    205         req.type = (UserFlags & ImGuiFreeTypeBuilderFlags_Bitmap) ? FT_SIZE_REQUEST_TYPE_NOMINAL : FT_SIZE_REQUEST_TYPE_REAL_DIM;
    206         req.width = 0;
    207         req.height = (uint32_t)pixel_height * 64;
    208         req.horiResolution = 0;
    209         req.vertResolution = 0;
    210         FT_Request_Size(Face, &req);
    211 
    212         // Update font info
    213         FT_Size_Metrics metrics = Face->size->metrics;
    214         Info.PixelHeight = (uint32_t)pixel_height;
    215         Info.Ascender = (float)FT_CEIL(metrics.ascender);
    216         Info.Descender = (float)FT_CEIL(metrics.descender);
    217         Info.LineSpacing = (float)FT_CEIL(metrics.height);
    218         Info.LineGap = (float)FT_CEIL(metrics.height - metrics.ascender + metrics.descender);
    219         Info.MaxAdvanceWidth = (float)FT_CEIL(metrics.max_advance);
    220     }
    221 
    222     const FT_Glyph_Metrics* FreeTypeFont::LoadGlyph(uint32_t codepoint)
    223     {
    224         uint32_t glyph_index = FT_Get_Char_Index(Face, codepoint);
    225         if (glyph_index == 0)
    226             return NULL;
    227         FT_Error error = FT_Load_Glyph(Face, glyph_index, LoadFlags);
    228         if (error)
    229             return NULL;
    230 
    231         // Need an outline for this to work
    232         FT_GlyphSlot slot = Face->glyph;
    233         IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP);
    234 
    235         // Apply convenience transform (this is not picking from real "Bold"/"Italic" fonts! Merely applying FreeType helper transform. Oblique == Slanting)
    236         if (UserFlags & ImGuiFreeTypeBuilderFlags_Bold)
    237             FT_GlyphSlot_Embolden(slot);
    238         if (UserFlags & ImGuiFreeTypeBuilderFlags_Oblique)
    239         {
    240             FT_GlyphSlot_Oblique(slot);
    241             //FT_BBox bbox;
    242             //FT_Outline_Get_BBox(&slot->outline, &bbox);
    243             //slot->metrics.width = bbox.xMax - bbox.xMin;
    244             //slot->metrics.height = bbox.yMax - bbox.yMin;
    245         }
    246 
    247         return &slot->metrics;
    248     }
    249 
    250     const FT_Bitmap* FreeTypeFont::RenderGlyphAndGetInfo(GlyphInfo* out_glyph_info)
    251     {
    252         FT_GlyphSlot slot = Face->glyph;
    253         FT_Error error = FT_Render_Glyph(slot, RenderMode);
    254         if (error != 0)
    255             return NULL;
    256 
    257         FT_Bitmap* ft_bitmap = &Face->glyph->bitmap;
    258         out_glyph_info->Width = (int)ft_bitmap->width;
    259         out_glyph_info->Height = (int)ft_bitmap->rows;
    260         out_glyph_info->OffsetX = Face->glyph->bitmap_left;
    261         out_glyph_info->OffsetY = -Face->glyph->bitmap_top;
    262         out_glyph_info->AdvanceX = (float)FT_CEIL(slot->advance.x);
    263         out_glyph_info->IsColored = (ft_bitmap->pixel_mode == FT_PIXEL_MODE_BGRA);
    264 
    265         return ft_bitmap;
    266     }
    267 
    268     void FreeTypeFont::BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch, unsigned char* multiply_table)
    269     {
    270         IM_ASSERT(ft_bitmap != NULL);
    271         const uint32_t w = ft_bitmap->width;
    272         const uint32_t h = ft_bitmap->rows;
    273         const uint8_t* src = ft_bitmap->buffer;
    274         const uint32_t src_pitch = ft_bitmap->pitch;
    275 
    276         switch (ft_bitmap->pixel_mode)
    277         {
    278         case FT_PIXEL_MODE_GRAY: // Grayscale image, 1 byte per pixel.
    279             {
    280                 if (multiply_table == NULL)
    281                 {
    282                     for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
    283                         for (uint32_t x = 0; x < w; x++)
    284                             dst[x] = IM_COL32(255, 255, 255, src[x]);
    285                 }
    286                 else
    287                 {
    288                     for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
    289                         for (uint32_t x = 0; x < w; x++)
    290                             dst[x] = IM_COL32(255, 255, 255, multiply_table[src[x]]);
    291                 }
    292                 break;
    293             }
    294         case FT_PIXEL_MODE_MONO: // Monochrome image, 1 bit per pixel. The bits in each byte are ordered from MSB to LSB.
    295             {
    296                 uint8_t color0 = multiply_table ? multiply_table[0] : 0;
    297                 uint8_t color1 = multiply_table ? multiply_table[255] : 255;
    298                 for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
    299                 {
    300                     uint8_t bits = 0;
    301                     const uint8_t* bits_ptr = src;
    302                     for (uint32_t x = 0; x < w; x++, bits <<= 1)
    303                     {
    304                         if ((x & 7) == 0)
    305                             bits = *bits_ptr++;
    306                         dst[x] = IM_COL32(255, 255, 255, (bits & 0x80) ? color1 : color0);
    307                     }
    308                 }
    309                 break;
    310             }
    311         case FT_PIXEL_MODE_BGRA:
    312             {
    313                 // FIXME: Converting pre-multiplied alpha to straight. Doesn't smell good.
    314                 #define DE_MULTIPLY(color, alpha) (ImU32)(255.0f * (float)color / (float)alpha + 0.5f)
    315                 if (multiply_table == NULL)
    316                 {
    317                     for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
    318                         for (uint32_t x = 0; x < w; x++)
    319                         {
    320                             uint8_t r = src[x * 4 + 2], g = src[x * 4 + 1], b = src[x * 4], a = src[x * 4 + 3];
    321                             dst[x] = IM_COL32(DE_MULTIPLY(r, a), DE_MULTIPLY(g, a), DE_MULTIPLY(b, a), a);
    322                         }
    323                 }
    324                 else
    325                 {
    326                     for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
    327                     {
    328                         for (uint32_t x = 0; x < w; x++)
    329                         {
    330                             uint8_t r = src[x * 4 + 2], g = src[x * 4 + 1], b = src[x * 4], a = src[x * 4 + 3];
    331                             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]);
    332                         }
    333                     }
    334                 }
    335                 #undef DE_MULTIPLY
    336                 break;
    337             }
    338         default:
    339             IM_ASSERT(0 && "FreeTypeFont::BlitGlyph(): Unknown bitmap pixel mode!");
    340         }
    341     }
    342 }
    343 
    344 #ifndef STB_RECT_PACK_IMPLEMENTATION                        // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds)
    345 #ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
    346 #define STBRP_ASSERT(x)     do { IM_ASSERT(x); } while (0)
    347 #define STBRP_STATIC
    348 #define STB_RECT_PACK_IMPLEMENTATION
    349 #endif
    350 #ifdef IMGUI_STB_RECT_PACK_FILENAME
    351 #include IMGUI_STB_RECT_PACK_FILENAME
    352 #else
    353 #include "imstb_rectpack.h"
    354 #endif
    355 #endif
    356 
    357 struct ImFontBuildSrcGlyphFT
    358 {
    359     GlyphInfo           Info;
    360     uint32_t            Codepoint;
    361     unsigned int*       BitmapData;         // Point within one of the dst_tmp_bitmap_buffers[] array
    362 
    363     ImFontBuildSrcGlyphFT() { memset(this, 0, sizeof(*this)); }
    364 };
    365 
    366 struct ImFontBuildSrcDataFT
    367 {
    368     FreeTypeFont        Font;
    369     stbrp_rect*         Rects;              // Rectangle to pack. We first fill in their size and the packer will give us their position.
    370     const ImWchar*      SrcRanges;          // Ranges as requested by user (user is allowed to request too much, e.g. 0x0020..0xFFFF)
    371     int                 DstIndex;           // Index into atlas->Fonts[] and dst_tmp_array[]
    372     int                 GlyphsHighest;      // Highest requested codepoint
    373     int                 GlyphsCount;        // Glyph count (excluding missing glyphs and glyphs already set by an earlier source font)
    374     ImBitVector         GlyphsSet;          // Glyph bit map (random access, 1-bit per codepoint. This will be a maximum of 8KB)
    375     ImVector<ImFontBuildSrcGlyphFT>   GlyphsList;
    376 };
    377 
    378 // Temporary data for one destination ImFont* (multiple source fonts can be merged into one destination ImFont)
    379 struct ImFontBuildDstDataFT
    380 {
    381     int                 SrcCount;           // Number of source fonts targeting this destination font.
    382     int                 GlyphsHighest;
    383     int                 GlyphsCount;
    384     ImBitVector         GlyphsSet;          // This is used to resolve collision when multiple sources are merged into a same destination font.
    385 };
    386 
    387 bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, unsigned int extra_flags)
    388 {
    389     IM_ASSERT(atlas->ConfigData.Size > 0);
    390 
    391     ImFontAtlasBuildInit(atlas);
    392 
    393     // Clear atlas
    394     atlas->TexID = (ImTextureID)NULL;
    395     atlas->TexWidth = atlas->TexHeight = 0;
    396     atlas->TexUvScale = ImVec2(0.0f, 0.0f);
    397     atlas->TexUvWhitePixel = ImVec2(0.0f, 0.0f);
    398     atlas->ClearTexData();
    399 
    400     // Temporary storage for building
    401     bool src_load_color = false;
    402     ImVector<ImFontBuildSrcDataFT> src_tmp_array;
    403     ImVector<ImFontBuildDstDataFT> dst_tmp_array;
    404     src_tmp_array.resize(atlas->ConfigData.Size);
    405     dst_tmp_array.resize(atlas->Fonts.Size);
    406     memset((void*)src_tmp_array.Data, 0, (size_t)src_tmp_array.size_in_bytes());
    407     memset((void*)dst_tmp_array.Data, 0, (size_t)dst_tmp_array.size_in_bytes());
    408 
    409     // 1. Initialize font loading structure, check font data validity
    410     for (int src_i = 0; src_i < atlas->ConfigData.Size; src_i++)
    411     {
    412         ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
    413         ImFontConfig& cfg = atlas->ConfigData[src_i];
    414         FreeTypeFont& font_face = src_tmp.Font;
    415         IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == atlas));
    416 
    417         // Find index from cfg.DstFont (we allow the user to set cfg.DstFont. Also it makes casual debugging nicer than when storing indices)
    418         src_tmp.DstIndex = -1;
    419         for (int output_i = 0; output_i < atlas->Fonts.Size && src_tmp.DstIndex == -1; output_i++)
    420             if (cfg.DstFont == atlas->Fonts[output_i])
    421                 src_tmp.DstIndex = output_i;
    422         IM_ASSERT(src_tmp.DstIndex != -1); // cfg.DstFont not pointing within atlas->Fonts[] array?
    423         if (src_tmp.DstIndex == -1)
    424             return false;
    425 
    426         // Load font
    427         if (!font_face.InitFont(ft_library, cfg, extra_flags))
    428             return false;
    429 
    430         // Measure highest codepoints
    431         src_load_color |= (cfg.FontBuilderFlags & ImGuiFreeTypeBuilderFlags_LoadColor) != 0;
    432         ImFontBuildDstDataFT& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
    433         src_tmp.SrcRanges = cfg.GlyphRanges ? cfg.GlyphRanges : atlas->GetGlyphRangesDefault();
    434         for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
    435             src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]);
    436         dst_tmp.SrcCount++;
    437         dst_tmp.GlyphsHighest = ImMax(dst_tmp.GlyphsHighest, src_tmp.GlyphsHighest);
    438     }
    439 
    440     // 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.
    441     int total_glyphs_count = 0;
    442     for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
    443     {
    444         ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
    445         ImFontBuildDstDataFT& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
    446         src_tmp.GlyphsSet.Create(src_tmp.GlyphsHighest + 1);
    447         if (dst_tmp.GlyphsSet.Storage.empty())
    448             dst_tmp.GlyphsSet.Create(dst_tmp.GlyphsHighest + 1);
    449 
    450         for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
    451             for (int codepoint = src_range[0]; codepoint <= (int)src_range[1]; codepoint++)
    452             {
    453                 if (dst_tmp.GlyphsSet.TestBit(codepoint))    // Don't overwrite existing glyphs. We could make this an option (e.g. MergeOverwrite)
    454                     continue;
    455                 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..)
    456                 if (glyph_index == 0)
    457                     continue;
    458 
    459                 // Add to avail set/counters
    460                 src_tmp.GlyphsCount++;
    461                 dst_tmp.GlyphsCount++;
    462                 src_tmp.GlyphsSet.SetBit(codepoint);
    463                 dst_tmp.GlyphsSet.SetBit(codepoint);
    464                 total_glyphs_count++;
    465             }
    466     }
    467 
    468     // 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)
    469     for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
    470     {
    471         ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
    472         src_tmp.GlyphsList.reserve(src_tmp.GlyphsCount);
    473 
    474         IM_ASSERT(sizeof(src_tmp.GlyphsSet.Storage.Data[0]) == sizeof(ImU32));
    475         const ImU32* it_begin = src_tmp.GlyphsSet.Storage.begin();
    476         const ImU32* it_end = src_tmp.GlyphsSet.Storage.end();
    477         for (const ImU32* it = it_begin; it < it_end; it++)
    478             if (ImU32 entries_32 = *it)
    479                 for (ImU32 bit_n = 0; bit_n < 32; bit_n++)
    480                     if (entries_32 & ((ImU32)1 << bit_n))
    481                     {
    482                         ImFontBuildSrcGlyphFT src_glyph;
    483                         src_glyph.Codepoint = (ImWchar)(((it - it_begin) << 5) + bit_n);
    484                         //src_glyph.GlyphIndex = 0; // FIXME-OPT: We had this info in the previous step and lost it..
    485                         src_tmp.GlyphsList.push_back(src_glyph);
    486                     }
    487         src_tmp.GlyphsSet.Clear();
    488         IM_ASSERT(src_tmp.GlyphsList.Size == src_tmp.GlyphsCount);
    489     }
    490     for (int dst_i = 0; dst_i < dst_tmp_array.Size; dst_i++)
    491         dst_tmp_array[dst_i].GlyphsSet.Clear();
    492     dst_tmp_array.clear();
    493 
    494     // Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0)
    495     // (We technically don't need to zero-clear buf_rects, but let's do it for the sake of sanity)
    496     ImVector<stbrp_rect> buf_rects;
    497     buf_rects.resize(total_glyphs_count);
    498     memset(buf_rects.Data, 0, (size_t)buf_rects.size_in_bytes());
    499 
    500     // Allocate temporary rasterization data buffers.
    501     // We could not find a way to retrieve accurate glyph size without rendering them.
    502     // (e.g. slot->metrics->width not always matching bitmap->width, especially considering the Oblique transform)
    503     // We allocate in chunks of 256 KB to not waste too much extra memory ahead. Hopefully users of FreeType won't find the temporary allocations.
    504     const int BITMAP_BUFFERS_CHUNK_SIZE = 256 * 1024;
    505     int buf_bitmap_current_used_bytes = 0;
    506     ImVector<unsigned char*> buf_bitmap_buffers;
    507     buf_bitmap_buffers.push_back((unsigned char*)IM_ALLOC(BITMAP_BUFFERS_CHUNK_SIZE));
    508 
    509     // 4. Gather glyphs sizes so we can pack them in our virtual canvas.
    510     // 8. Render/rasterize font characters into the texture
    511     int total_surface = 0;
    512     int buf_rects_out_n = 0;
    513     for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
    514     {
    515         ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
    516         ImFontConfig& cfg = atlas->ConfigData[src_i];
    517         if (src_tmp.GlyphsCount == 0)
    518             continue;
    519 
    520         src_tmp.Rects = &buf_rects[buf_rects_out_n];
    521         buf_rects_out_n += src_tmp.GlyphsCount;
    522 
    523         // Compute multiply table if requested
    524         const bool multiply_enabled = (cfg.RasterizerMultiply != 1.0f);
    525         unsigned char multiply_table[256];
    526         if (multiply_enabled)
    527             ImFontAtlasBuildMultiplyCalcLookupTable(multiply_table, cfg.RasterizerMultiply);
    528 
    529         // Gather the sizes of all rectangles we will need to pack
    530         const int padding = atlas->TexGlyphPadding;
    531         for (int glyph_i = 0; glyph_i < src_tmp.GlyphsList.Size; glyph_i++)
    532         {
    533             ImFontBuildSrcGlyphFT& src_glyph = src_tmp.GlyphsList[glyph_i];
    534 
    535             const FT_Glyph_Metrics* metrics = src_tmp.Font.LoadGlyph(src_glyph.Codepoint);
    536             if (metrics == NULL)
    537                 continue;
    538 
    539             // Render glyph into a bitmap (currently held by FreeType)
    540             const FT_Bitmap* ft_bitmap = src_tmp.Font.RenderGlyphAndGetInfo(&src_glyph.Info);
    541             IM_ASSERT(ft_bitmap);
    542 
    543             // Allocate new temporary chunk if needed
    544             const int bitmap_size_in_bytes = src_glyph.Info.Width * src_glyph.Info.Height * 4;
    545             if (buf_bitmap_current_used_bytes + bitmap_size_in_bytes > BITMAP_BUFFERS_CHUNK_SIZE)
    546             {
    547                 buf_bitmap_current_used_bytes = 0;
    548                 buf_bitmap_buffers.push_back((unsigned char*)IM_ALLOC(BITMAP_BUFFERS_CHUNK_SIZE));
    549             }
    550 
    551             // Blit rasterized pixels to our temporary buffer and keep a pointer to it.
    552             src_glyph.BitmapData = (unsigned int*)(buf_bitmap_buffers.back() + buf_bitmap_current_used_bytes);
    553             buf_bitmap_current_used_bytes += bitmap_size_in_bytes;
    554             src_tmp.Font.BlitGlyph(ft_bitmap, src_glyph.BitmapData, src_glyph.Info.Width, multiply_enabled ? multiply_table : NULL);
    555 
    556             src_tmp.Rects[glyph_i].w = (stbrp_coord)(src_glyph.Info.Width + padding);
    557             src_tmp.Rects[glyph_i].h = (stbrp_coord)(src_glyph.Info.Height + padding);
    558             total_surface += src_tmp.Rects[glyph_i].w * src_tmp.Rects[glyph_i].h;
    559         }
    560     }
    561 
    562     // We need a width for the skyline algorithm, any width!
    563     // The exact width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height.
    564     // User can override TexDesiredWidth and TexGlyphPadding if they wish, otherwise we use a simple heuristic to select the width based on expected surface.
    565     const int surface_sqrt = (int)ImSqrt((float)total_surface) + 1;
    566     atlas->TexHeight = 0;
    567     if (atlas->TexDesiredWidth > 0)
    568         atlas->TexWidth = atlas->TexDesiredWidth;
    569     else
    570         atlas->TexWidth = (surface_sqrt >= 4096 * 0.7f) ? 4096 : (surface_sqrt >= 2048 * 0.7f) ? 2048 : (surface_sqrt >= 1024 * 0.7f) ? 1024 : 512;
    571 
    572     // 5. Start packing
    573     // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values).
    574     const int TEX_HEIGHT_MAX = 1024 * 32;
    575     const int num_nodes_for_packing_algorithm = atlas->TexWidth - atlas->TexGlyphPadding;
    576     ImVector<stbrp_node> pack_nodes;
    577     pack_nodes.resize(num_nodes_for_packing_algorithm);
    578     stbrp_context pack_context;
    579     stbrp_init_target(&pack_context, atlas->TexWidth, TEX_HEIGHT_MAX, pack_nodes.Data, pack_nodes.Size);
    580     ImFontAtlasBuildPackCustomRects(atlas, &pack_context);
    581 
    582     // 6. Pack each source font. No rendering yet, we are working with rectangles in an infinitely tall texture at this point.
    583     for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
    584     {
    585         ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
    586         if (src_tmp.GlyphsCount == 0)
    587             continue;
    588 
    589         stbrp_pack_rects(&pack_context, src_tmp.Rects, src_tmp.GlyphsCount);
    590 
    591         // Extend texture height and mark missing glyphs as non-packed so we won't render them.
    592         // 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?)
    593         for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
    594             if (src_tmp.Rects[glyph_i].was_packed)
    595                 atlas->TexHeight = ImMax(atlas->TexHeight, src_tmp.Rects[glyph_i].y + src_tmp.Rects[glyph_i].h);
    596     }
    597 
    598     // 7. Allocate texture
    599     atlas->TexHeight = (atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (atlas->TexHeight + 1) : ImUpperPowerOfTwo(atlas->TexHeight);
    600     atlas->TexUvScale = ImVec2(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight);
    601     if (src_load_color)
    602     {
    603         atlas->TexPixelsRGBA32 = (unsigned int*)IM_ALLOC(atlas->TexWidth * atlas->TexHeight * 4);
    604         memset(atlas->TexPixelsRGBA32, 0, atlas->TexWidth * atlas->TexHeight * 4);
    605     }
    606     else
    607     {
    608         atlas->TexPixelsAlpha8 = (unsigned char*)IM_ALLOC(atlas->TexWidth * atlas->TexHeight);
    609         memset(atlas->TexPixelsAlpha8, 0, atlas->TexWidth * atlas->TexHeight);
    610     }
    611 
    612     // 8. Copy rasterized font characters back into the main texture
    613     // 9. Setup ImFont and glyphs for runtime
    614     bool tex_use_colors = false;
    615     for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
    616     {
    617         ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
    618         if (src_tmp.GlyphsCount == 0)
    619             continue;
    620 
    621         // When merging fonts with MergeMode=true:
    622         // - We can have multiple input fonts writing into a same destination font.
    623         // - dst_font->ConfigData is != from cfg which is our source configuration.
    624         ImFontConfig& cfg = atlas->ConfigData[src_i];
    625         ImFont* dst_font = cfg.DstFont;
    626 
    627         const float ascent = src_tmp.Font.Info.Ascender;
    628         const float descent = src_tmp.Font.Info.Descender;
    629         ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent);
    630         const float font_off_x = cfg.GlyphOffset.x;
    631         const float font_off_y = cfg.GlyphOffset.y + IM_ROUND(dst_font->Ascent);
    632 
    633         const int padding = atlas->TexGlyphPadding;
    634         for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
    635         {
    636             ImFontBuildSrcGlyphFT& src_glyph = src_tmp.GlyphsList[glyph_i];
    637             stbrp_rect& pack_rect = src_tmp.Rects[glyph_i];
    638             IM_ASSERT(pack_rect.was_packed);
    639             if (pack_rect.w == 0 && pack_rect.h == 0)
    640                 continue;
    641 
    642             GlyphInfo& info = src_glyph.Info;
    643             IM_ASSERT(info.Width + padding <= pack_rect.w);
    644             IM_ASSERT(info.Height + padding <= pack_rect.h);
    645             const int tx = pack_rect.x + padding;
    646             const int ty = pack_rect.y + padding;
    647 
    648             // Blit from temporary buffer to final texture
    649             size_t blit_src_stride = (size_t)src_glyph.Info.Width;
    650             size_t blit_dst_stride = (size_t)atlas->TexWidth;
    651             unsigned int* blit_src = src_glyph.BitmapData;
    652             if (atlas->TexPixelsAlpha8 != NULL)
    653             {
    654                 unsigned char* blit_dst = atlas->TexPixelsAlpha8 + (ty * blit_dst_stride) + tx;
    655                 for (int y = 0; y < info.Height; y++, blit_dst += blit_dst_stride, blit_src += blit_src_stride)
    656                     for (int x = 0; x < info.Width; x++)
    657                         blit_dst[x] = (unsigned char)((blit_src[x] >> IM_COL32_A_SHIFT) & 0xFF);
    658             }
    659             else
    660             {
    661                 unsigned int* blit_dst = atlas->TexPixelsRGBA32 + (ty * blit_dst_stride) + tx;
    662                 for (int y = 0; y < info.Height; y++, blit_dst += blit_dst_stride, blit_src += blit_src_stride)
    663                     for (int x = 0; x < info.Width; x++)
    664                         blit_dst[x] = blit_src[x];
    665             }
    666 
    667             // Register glyph
    668             float x0 = info.OffsetX + font_off_x;
    669             float y0 = info.OffsetY + font_off_y;
    670             float x1 = x0 + info.Width;
    671             float y1 = y0 + info.Height;
    672             float u0 = (tx) / (float)atlas->TexWidth;
    673             float v0 = (ty) / (float)atlas->TexHeight;
    674             float u1 = (tx + info.Width) / (float)atlas->TexWidth;
    675             float v1 = (ty + info.Height) / (float)atlas->TexHeight;
    676             dst_font->AddGlyph(&cfg, (ImWchar)src_glyph.Codepoint, x0, y0, x1, y1, u0, v0, u1, v1, info.AdvanceX);
    677 
    678             ImFontGlyph* dst_glyph = &dst_font->Glyphs.back();
    679             IM_ASSERT(dst_glyph->Codepoint == src_glyph.Codepoint);
    680             if (src_glyph.Info.IsColored)
    681                 dst_glyph->Colored = tex_use_colors = true;
    682         }
    683 
    684         src_tmp.Rects = NULL;
    685     }
    686     atlas->TexPixelsUseColors = tex_use_colors;
    687 
    688     // Cleanup
    689     for (int buf_i = 0; buf_i < buf_bitmap_buffers.Size; buf_i++)
    690         IM_FREE(buf_bitmap_buffers[buf_i]);
    691     for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
    692         src_tmp_array[src_i].~ImFontBuildSrcDataFT();
    693 
    694     ImFontAtlasBuildFinish(atlas);
    695 
    696     return true;
    697 }
    698 
    699 // FreeType memory allocation callbacks
    700 static void* FreeType_Alloc(FT_Memory /*memory*/, long size)
    701 {
    702     return GImGuiFreeTypeAllocFunc((size_t)size, GImGuiFreeTypeAllocatorUserData);
    703 }
    704 
    705 static void FreeType_Free(FT_Memory /*memory*/, void* block)
    706 {
    707     GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
    708 }
    709 
    710 static void* FreeType_Realloc(FT_Memory /*memory*/, long cur_size, long new_size, void* block)
    711 {
    712     // Implement realloc() as we don't ask user to provide it.
    713     if (block == NULL)
    714         return GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData);
    715 
    716     if (new_size == 0)
    717     {
    718         GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
    719         return NULL;
    720     }
    721 
    722     if (new_size > cur_size)
    723     {
    724         void* new_block = GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData);
    725         memcpy(new_block, block, (size_t)cur_size);
    726         GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
    727         return new_block;
    728     }
    729 
    730     return block;
    731 }
    732 
    733 static bool ImFontAtlasBuildWithFreeType(ImFontAtlas* atlas)
    734 {
    735     // FreeType memory management: https://www.freetype.org/freetype2/docs/design/design-4.html
    736     FT_MemoryRec_ memory_rec = {};
    737     memory_rec.user = NULL;
    738     memory_rec.alloc = &FreeType_Alloc;
    739     memory_rec.free = &FreeType_Free;
    740     memory_rec.realloc = &FreeType_Realloc;
    741 
    742     // https://www.freetype.org/freetype2/docs/reference/ft2-module_management.html#FT_New_Library
    743     FT_Library ft_library;
    744     FT_Error error = FT_New_Library(&memory_rec, &ft_library);
    745     if (error != 0)
    746         return false;
    747 
    748     // If you don't call FT_Add_Default_Modules() the rest of code may work, but FreeType won't use our custom allocator.
    749     FT_Add_Default_Modules(ft_library);
    750 
    751     bool ret = ImFontAtlasBuildWithFreeTypeEx(ft_library, atlas, atlas->FontBuilderFlags);
    752     FT_Done_Library(ft_library);
    753 
    754     return ret;
    755 }
    756 
    757 const ImFontBuilderIO* ImGuiFreeType::GetBuilderForFreeType()
    758 {
    759     static ImFontBuilderIO io;
    760     io.FontBuilder_Build = ImFontAtlasBuildWithFreeType;
    761     return &io;
    762 }
    763 
    764 void ImGuiFreeType::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data)
    765 {
    766     GImGuiFreeTypeAllocFunc = alloc_func;
    767     GImGuiFreeTypeFreeFunc = free_func;
    768     GImGuiFreeTypeAllocatorUserData = user_data;
    769 }