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