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