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