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