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