openMSX
imgui_freetype.cc
Go to the documentation of this file.
1// dear imgui: FreeType font builder (used as a replacement for the stb_truetype builder)
2// (code)
3
4// Get the latest version at https://github.com/ocornut/imgui/tree/master/misc/freetype
5// Original code by @vuhdo (Aleksei Skriabin). Improvements by @mikesart. Maintained since 2019 by @ocornut.
6
7// CHANGELOG
8// (minor and older changes stripped away, please see git history for details)
9// 2023/11/13: added support for ImFontConfig::RasterizationDensity field for scaling render density without scaling metrics.
10// 2023/08/01: added support for SVG fonts, enable by using '#define IMGUI_ENABLE_FREETYPE_LUNASVG' (#6591)
11// 2023/01/04: fixed a packing issue which in some occurrences would prevent large amount of glyphs from being packed correctly.
12// 2021/08/23: fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL.
13// 2021/03/05: added ImGuiFreeTypeBuilderFlags_Bitmap to load bitmap glyphs.
14// 2021/03/02: set 'atlas->TexPixelsUseColors = true' to help some backends with deciding of a prefered texture format.
15// 2021/01/28: added support for color-layered glyphs via ImGuiFreeTypeBuilderFlags_LoadColor (require Freetype 2.10+).
16// 2021/01/26: simplified integration by using '#define IMGUI_ENABLE_FREETYPE'. renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API. removed ImGuiFreeType::BuildFontAtlas().
17// 2020/06/04: fix for rare case where FT_Get_Char_Index() succeed but FT_Load_Glyph() fails.
18// 2019/02/09: added RasterizerFlags::Monochrome flag to disable font anti-aliasing (combine with ::MonoHinting for best results!)
19// 2019/01/15: added support for imgui allocators + added FreeType only override function SetAllocatorFunctions().
20// 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.
21// 2018/06/08: added support for ImFontConfig::GlyphMinAdvanceX, GlyphMaxAdvanceX.
22// 2018/02/04: moved to main imgui repository (away from http://www.github.com/ocornut/imgui_club)
23// 2018/01/22: fix for addition of ImFontAtlas::TexUvscale member.
24// 2017/10/22: minor inconsequential change to match change in master (removed an unnecessary statement).
25// 2017/09/26: fixes for imgui internal changes.
26// 2017/08/26: cleanup, optimizations, support for ImFontConfig::RasterizerFlags, ImFontConfig::RasterizerMultiply.
27// 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.
28
29// About Gamma Correct Blending:
30// - FreeType assumes blending in linear space rather than gamma space.
31// - See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Render_Glyph
32// - For correct results you need to be using sRGB and convert to linear space in the pixel shader output.
33// - The default dear imgui styles will be impacted by this change (alpha values will need tweaking).
34
35// FIXME: cfg.OversampleH, OversampleV are not supported (but perhaps not so necessary with this rasterizer).
36
37#include "imgui.h"
38#ifndef IMGUI_DISABLE
39#include "imgui_freetype.h"
40#include "imgui_internal.h" // ImMin,ImMax,ImFontAtlasBuild*,
41#include <stdint.h>
42#include <ft2build.h>
43#include FT_FREETYPE_H // <freetype/freetype.h>
44#include FT_MODULE_H // <freetype/ftmodapi.h>
45#include FT_GLYPH_H // <freetype/ftglyph.h>
46#include FT_SYNTHESIS_H // <freetype/ftsynth.h>
47
48#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
49#include FT_OTSVG_H // <freetype/otsvg.h>
50#include FT_BBOX_H // <freetype/ftbbox.h>
51#include <lunasvg.h>
52#if !((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12))
53#error IMGUI_ENABLE_FREETYPE_LUNASVG requires FreeType version >= 2.12
54#endif
55#endif
56
57#ifdef _MSC_VER
58#pragma warning (push)
59#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
60#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
61#endif
62
63#ifdef __GNUC__
64#pragma GCC diagnostic push
65#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
66#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
67#ifndef __clang__
68#pragma GCC diagnostic ignored "-Wsubobject-linkage" // warning: 'xxxx' has a field 'xxxx' whose type uses the anonymous namespace
69#endif
70#endif
71
72//-------------------------------------------------------------------------
73// Data
74//-------------------------------------------------------------------------
75
76// Default memory allocators
77static void* ImGuiFreeTypeDefaultAllocFunc(size_t size, void* user_data) { IM_UNUSED(user_data); return IM_ALLOC(size); }
78static void ImGuiFreeTypeDefaultFreeFunc(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_FREE(ptr); }
79
80// Current memory allocators
81static void* (*GImGuiFreeTypeAllocFunc)(size_t size, void* user_data) = ImGuiFreeTypeDefaultAllocFunc;
82static void (*GImGuiFreeTypeFreeFunc)(void* ptr, void* user_data) = ImGuiFreeTypeDefaultFreeFunc;
83static void* GImGuiFreeTypeAllocatorUserData = nullptr;
84
85// Lunasvg support
86#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
87static FT_Error ImGuiLunasvgPortInit(FT_Pointer* state);
88static void ImGuiLunasvgPortFree(FT_Pointer* state);
89static FT_Error ImGuiLunasvgPortRender(FT_GlyphSlot slot, FT_Pointer* _state);
90static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_Pointer* _state);
91#endif
92
93//-------------------------------------------------------------------------
94// Code
95//-------------------------------------------------------------------------
96
97namespace
98{
99 // Glyph metrics:
100 // --------------
101 //
102 // xmin xmax
103 // | |
104 // |<-------- width -------->|
105 // | |
106 // | +-------------------------+----------------- ymax
107 // | | ggggggggg ggggg | ^ ^
108 // | | g:::::::::ggg::::g | | |
109 // | | g:::::::::::::::::g | | |
110 // | | g::::::ggggg::::::gg | | |
111 // | | g:::::g g:::::g | | |
112 // offsetX -|-------->| g:::::g g:::::g | offsetY |
113 // | | g:::::g g:::::g | | |
114 // | | g::::::g g:::::g | | |
115 // | | g:::::::ggggg:::::g | | |
116 // | | g::::::::::::::::g | | height
117 // | | gg::::::::::::::g | | |
118 // baseline ---*---------|---- gggggggg::::::g-----*-------- |
119 // / | | g:::::g | |
120 // origin | | gggggg g:::::g | |
121 // | | g:::::gg gg:::::g | |
122 // | | g::::::ggg:::::::g | |
123 // | | gg:::::::::::::g | |
124 // | | ggg::::::ggg | |
125 // | | gggggg | v
126 // | +-------------------------+----------------- ymin
127 // | |
128 // |------------- advanceX ----------->|
129
130 // A structure that describe a glyph.
131 struct GlyphInfo
132 {
133 int Width; // Glyph's width in pixels.
134 int Height; // Glyph's height in pixels.
135 FT_Int OffsetX; // The distance from the origin ("pen position") to the left of the glyph.
136 FT_Int OffsetY; // The distance from the origin to the top of the glyph. This is usually a value < 0.
137 float AdvanceX; // The distance from the origin to the origin of the next glyph. This is usually a value > 0.
138 bool IsColored; // The glyph is colored
139 };
140
141 // Font parameters and metrics.
142 struct FontInfo
143 {
144 uint32_t PixelHeight; // Size this font was generated with.
145 float Ascender; // The pixel extents above the baseline in pixels (typically positive).
146 float Descender; // The extents below the baseline in pixels (typically negative).
147 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.
148 float LineGap; // The spacing in pixels between one row's descent and the next row's ascent.
149 float MaxAdvanceWidth; // This field gives the maximum horizontal cursor advance for all glyphs in the font.
150 };
151
152 // FreeType glyph rasterizer.
153 // NB: No ctor/dtor, explicitly call Init()/Shutdown()
154 struct FreeTypeFont
155 {
156 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.
157 void CloseFont();
158 void SetPixelHeight(int pixel_height); // Change font pixel size. All following calls to RasterizeGlyph() will use this size
159 const FT_Glyph_Metrics* LoadGlyph(uint32_t in_codepoint);
160 const FT_Bitmap* RenderGlyphAndGetInfo(GlyphInfo* out_glyph_info);
161 void BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch, unsigned char* multiply_table = nullptr);
162 ~FreeTypeFont() { CloseFont(); }
163
164 // [Internals]
165 FontInfo Info; // Font descriptor of the current font.
166 FT_Face Face;
167 unsigned int UserFlags; // = ImFontConfig::RasterizerFlags
168 FT_Int32 LoadFlags;
169 FT_Render_Mode RenderMode;
170 float RasterizationDensity;
171 float InvRasterizationDensity;
172 };
173
174 // From SDL_ttf: Handy routines for converting from fixed point
175 #define FT_CEIL(X) (((X + 63) & -64) / 64)
176
177 bool FreeTypeFont::InitFont(FT_Library ft_library, const ImFontConfig& cfg, unsigned int extra_font_builder_flags)
178 {
179 FT_Error error = FT_New_Memory_Face(ft_library, (uint8_t*)cfg.FontData, (uint32_t)cfg.FontDataSize, (uint32_t)cfg.FontNo, &Face);
180 if (error != 0)
181 return false;
182 error = FT_Select_Charmap(Face, FT_ENCODING_UNICODE);
183 if (error != 0)
184 return false;
185
186 // Convert to FreeType flags (NB: Bold and Oblique are processed separately)
187 UserFlags = cfg.FontBuilderFlags | extra_font_builder_flags;
188
189 LoadFlags = 0;
190 if ((UserFlags & ImGuiFreeTypeBuilderFlags_Bitmap) == 0)
191 LoadFlags |= FT_LOAD_NO_BITMAP;
192
193 if (UserFlags & ImGuiFreeTypeBuilderFlags_NoHinting)
194 LoadFlags |= FT_LOAD_NO_HINTING;
195 if (UserFlags & ImGuiFreeTypeBuilderFlags_NoAutoHint)
196 LoadFlags |= FT_LOAD_NO_AUTOHINT;
197 if (UserFlags & ImGuiFreeTypeBuilderFlags_ForceAutoHint)
198 LoadFlags |= FT_LOAD_FORCE_AUTOHINT;
199 if (UserFlags & ImGuiFreeTypeBuilderFlags_LightHinting)
200 LoadFlags |= FT_LOAD_TARGET_LIGHT;
201 else if (UserFlags & ImGuiFreeTypeBuilderFlags_MonoHinting)
202 LoadFlags |= FT_LOAD_TARGET_MONO;
203 else
204 LoadFlags |= FT_LOAD_TARGET_NORMAL;
205
206 if (UserFlags & ImGuiFreeTypeBuilderFlags_Monochrome)
207 RenderMode = FT_RENDER_MODE_MONO;
208 else
209 RenderMode = FT_RENDER_MODE_NORMAL;
210
211 if (UserFlags & ImGuiFreeTypeBuilderFlags_LoadColor)
212 LoadFlags |= FT_LOAD_COLOR;
213
214 RasterizationDensity = cfg.RasterizerDensity;
215 InvRasterizationDensity = 1.0f / RasterizationDensity;
216
217 memset(&Info, 0, sizeof(Info));
218 SetPixelHeight((uint32_t)cfg.SizePixels);
219
220 return true;
221 }
222
223 void FreeTypeFont::CloseFont()
224 {
225 if (Face)
226 {
227 FT_Done_Face(Face);
228 Face = nullptr;
229 }
230 }
231
232 void FreeTypeFont::SetPixelHeight(int pixel_height)
233 {
234 // Vuhdo: I'm not sure how to deal with font sizes properly. As far as I understand, currently ImGui assumes that the 'pixel_height'
235 // 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.
236 // NB: FT_Set_Pixel_Sizes() doesn't seem to get us the same result.
237 FT_Size_RequestRec req;
238 req.type = (UserFlags & ImGuiFreeTypeBuilderFlags_Bitmap) ? FT_SIZE_REQUEST_TYPE_NOMINAL : FT_SIZE_REQUEST_TYPE_REAL_DIM;
239 req.width = 0;
240 req.height = (uint32_t)(pixel_height * 64 * RasterizationDensity);
241 req.horiResolution = 0;
242 req.vertResolution = 0;
243 FT_Request_Size(Face, &req);
244
245 // Update font info
246 FT_Size_Metrics metrics = Face->size->metrics;
247 Info.PixelHeight = (uint32_t)(pixel_height * InvRasterizationDensity);
248 Info.Ascender = (float)FT_CEIL(metrics.ascender) * InvRasterizationDensity;
249 Info.Descender = (float)FT_CEIL(metrics.descender) * InvRasterizationDensity;
250 Info.LineSpacing = (float)FT_CEIL(metrics.height) * InvRasterizationDensity;
251 Info.LineGap = (float)FT_CEIL(metrics.height - metrics.ascender + metrics.descender) * InvRasterizationDensity;
252 Info.MaxAdvanceWidth = (float)FT_CEIL(metrics.max_advance) * InvRasterizationDensity;
253 }
254
255 const FT_Glyph_Metrics* FreeTypeFont::LoadGlyph(uint32_t codepoint)
256 {
257 uint32_t glyph_index = FT_Get_Char_Index(Face, codepoint);
258 if (glyph_index == 0)
259 return nullptr;
260
261 // If this crash for you: FreeType 2.11.0 has a crash bug on some bitmap/colored fonts.
262 // - https://gitlab.freedesktop.org/freetype/freetype/-/issues/1076
263 // - https://github.com/ocornut/imgui/issues/4567
264 // - https://github.com/ocornut/imgui/issues/4566
265 // You can use FreeType 2.10, or the patched version of 2.11.0 in VcPkg, or probably any upcoming FreeType version.
266 FT_Error error = FT_Load_Glyph(Face, glyph_index, LoadFlags);
267 if (error)
268 return nullptr;
269
270 // Need an outline for this to work
271 FT_GlyphSlot slot = Face->glyph;
272#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
273 IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP || slot->format == FT_GLYPH_FORMAT_SVG);
274#else
275#if ((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12))
276 IM_ASSERT(slot->format != FT_GLYPH_FORMAT_SVG && "The font contains SVG glyphs, you'll need to enable IMGUI_ENABLE_FREETYPE_LUNASVG in imconfig.h and install required libraries in order to use this font");
277#endif
278 IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP);
279#endif // IMGUI_ENABLE_FREETYPE_LUNASVG
280
281 // Apply convenience transform (this is not picking from real "Bold"/"Italic" fonts! Merely applying FreeType helper transform. Oblique == Slanting)
282 if (UserFlags & ImGuiFreeTypeBuilderFlags_Bold)
283 FT_GlyphSlot_Embolden(slot);
284 if (UserFlags & ImGuiFreeTypeBuilderFlags_Oblique)
285 {
286 FT_GlyphSlot_Oblique(slot);
287 //FT_BBox bbox;
288 //FT_Outline_Get_BBox(&slot->outline, &bbox);
289 //slot->metrics.width = bbox.xMax - bbox.xMin;
290 //slot->metrics.height = bbox.yMax - bbox.yMin;
291 }
292
293 return &slot->metrics;
294 }
295
296 const FT_Bitmap* FreeTypeFont::RenderGlyphAndGetInfo(GlyphInfo* out_glyph_info)
297 {
298 FT_GlyphSlot slot = Face->glyph;
299 FT_Error error = FT_Render_Glyph(slot, RenderMode);
300 if (error != 0)
301 return nullptr;
302
303 FT_Bitmap* ft_bitmap = &Face->glyph->bitmap;
304 out_glyph_info->Width = (int)ft_bitmap->width;
305 out_glyph_info->Height = (int)ft_bitmap->rows;
306 out_glyph_info->OffsetX = Face->glyph->bitmap_left;
307 out_glyph_info->OffsetY = -Face->glyph->bitmap_top;
308 out_glyph_info->AdvanceX = (float)FT_CEIL(slot->advance.x);
309 out_glyph_info->IsColored = (ft_bitmap->pixel_mode == FT_PIXEL_MODE_BGRA);
310
311 return ft_bitmap;
312 }
313
314 void FreeTypeFont::BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch, unsigned char* multiply_table)
315 {
316 IM_ASSERT(ft_bitmap != nullptr);
317 const uint32_t w = ft_bitmap->width;
318 const uint32_t h = ft_bitmap->rows;
319 const uint8_t* src = ft_bitmap->buffer;
320 const uint32_t src_pitch = ft_bitmap->pitch;
321
322 switch (ft_bitmap->pixel_mode)
323 {
324 case FT_PIXEL_MODE_GRAY: // Grayscale image, 1 byte per pixel.
325 {
326 if (multiply_table == nullptr)
327 {
328 for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
329 for (uint32_t x = 0; x < w; x++)
330 dst[x] = IM_COL32(255, 255, 255, src[x]);
331 }
332 else
333 {
334 for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
335 for (uint32_t x = 0; x < w; x++)
336 dst[x] = IM_COL32(255, 255, 255, multiply_table[src[x]]);
337 }
338 break;
339 }
340 case FT_PIXEL_MODE_MONO: // Monochrome image, 1 bit per pixel. The bits in each byte are ordered from MSB to LSB.
341 {
342 uint8_t color0 = multiply_table ? multiply_table[0] : 0;
343 uint8_t color1 = multiply_table ? multiply_table[255] : 255;
344 for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
345 {
346 uint8_t bits = 0;
347 const uint8_t* bits_ptr = src;
348 for (uint32_t x = 0; x < w; x++, bits <<= 1)
349 {
350 if ((x & 7) == 0)
351 bits = *bits_ptr++;
352 dst[x] = IM_COL32(255, 255, 255, (bits & 0x80) ? color1 : color0);
353 }
354 }
355 break;
356 }
357 case FT_PIXEL_MODE_BGRA:
358 {
359 // FIXME: Converting pre-multiplied alpha to straight. Doesn't smell good.
360 #define DE_MULTIPLY(color, alpha) (ImU32)(255.0f * (float)color / (float)alpha + 0.5f)
361 if (multiply_table == nullptr)
362 {
363 for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
364 for (uint32_t x = 0; x < w; x++)
365 {
366 uint8_t r = src[x * 4 + 2], g = src[x * 4 + 1], b = src[x * 4], a = src[x * 4 + 3];
367 dst[x] = IM_COL32(DE_MULTIPLY(r, a), DE_MULTIPLY(g, a), DE_MULTIPLY(b, a), a);
368 }
369 }
370 else
371 {
372 for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
373 {
374 for (uint32_t x = 0; x < w; x++)
375 {
376 uint8_t r = src[x * 4 + 2], g = src[x * 4 + 1], b = src[x * 4], a = src[x * 4 + 3];
377 dst[x] = IM_COL32(multiply_table[DE_MULTIPLY(r, a)], multiply_table[DE_MULTIPLY(g, a)], multiply_table[DE_MULTIPLY(b, a)], multiply_table[a]);
378 }
379 }
380 }
381 #undef DE_MULTIPLY
382 break;
383 }
384 default:
385 IM_ASSERT(0 && "FreeTypeFont::BlitGlyph(): Unknown bitmap pixel mode!");
386 }
387 }
388} // namespace
389
390#ifndef STB_RECT_PACK_IMPLEMENTATION // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds)
391#ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
392#define STBRP_ASSERT(x) do { IM_ASSERT(x); } while (0)
393#define STBRP_STATIC
394#define STB_RECT_PACK_IMPLEMENTATION
395#endif
396#ifdef IMGUI_STB_RECT_PACK_FILENAME
397#include IMGUI_STB_RECT_PACK_FILENAME
398#else
399#include "imstb_rectpack.h"
400#endif
401#endif
402
404{
405 GlyphInfo Info;
406 uint32_t Codepoint;
407 unsigned int* BitmapData; // Point within one of the dst_tmp_bitmap_buffers[] array
408
409 ImFontBuildSrcGlyphFT() { memset((void*)this, 0, sizeof(*this)); }
410};
411
413{
414 FreeTypeFont Font;
415 stbrp_rect* Rects; // Rectangle to pack. We first fill in their size and the packer will give us their position.
416 const ImWchar* SrcRanges; // Ranges as requested by user (user is allowed to request too much, e.g. 0x0020..0xFFFF)
417 int DstIndex; // Index into atlas->Fonts[] and dst_tmp_array[]
418 int GlyphsHighest; // Highest requested codepoint
419 int GlyphsCount; // Glyph count (excluding missing glyphs and glyphs already set by an earlier source font)
420 ImBitVector GlyphsSet; // Glyph bit map (random access, 1-bit per codepoint. This will be a maximum of 8KB)
421 ImVector<ImFontBuildSrcGlyphFT> GlyphsList;
422};
423
424// Temporary data for one destination ImFont* (multiple source fonts can be merged into one destination ImFont)
426{
427 int SrcCount; // Number of source fonts targeting this destination font.
430 ImBitVector GlyphsSet; // This is used to resolve collision when multiple sources are merged into a same destination font.
431};
432
433bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, unsigned int extra_flags)
434{
435 IM_ASSERT(atlas->ConfigData.Size > 0);
436
438
439 // Clear atlas
440 atlas->TexID = 0;
441 atlas->TexWidth = atlas->TexHeight = 0;
442 atlas->TexUvScale = ImVec2(0.0f, 0.0f);
443 atlas->TexUvWhitePixel = ImVec2(0.0f, 0.0f);
444 atlas->ClearTexData();
445
446 // Temporary storage for building
447 bool src_load_color = false;
448 ImVector<ImFontBuildSrcDataFT> src_tmp_array;
449 ImVector<ImFontBuildDstDataFT> dst_tmp_array;
450 src_tmp_array.resize(atlas->ConfigData.Size);
451 dst_tmp_array.resize(atlas->Fonts.Size);
452 memset((void*)src_tmp_array.Data, 0, (size_t)src_tmp_array.size_in_bytes());
453 memset((void*)dst_tmp_array.Data, 0, (size_t)dst_tmp_array.size_in_bytes());
454
455 // 1. Initialize font loading structure, check font data validity
456 for (int src_i = 0; src_i < atlas->ConfigData.Size; src_i++)
457 {
458 ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
459 ImFontConfig& cfg = atlas->ConfigData[src_i];
460 FreeTypeFont& font_face = src_tmp.Font;
461 IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == atlas));
462
463 // Find index from cfg.DstFont (we allow the user to set cfg.DstFont. Also it makes casual debugging nicer than when storing indices)
464 src_tmp.DstIndex = -1;
465 for (int output_i = 0; output_i < atlas->Fonts.Size && src_tmp.DstIndex == -1; output_i++)
466 if (cfg.DstFont == atlas->Fonts[output_i])
467 src_tmp.DstIndex = output_i;
468 IM_ASSERT(src_tmp.DstIndex != -1); // cfg.DstFont not pointing within atlas->Fonts[] array?
469 if (src_tmp.DstIndex == -1)
470 return false;
471
472 // Load font
473 if (!font_face.InitFont(ft_library, cfg, extra_flags))
474 return false;
475
476 // Measure highest codepoints
477 src_load_color |= (cfg.FontBuilderFlags & ImGuiFreeTypeBuilderFlags_LoadColor) != 0;
478 ImFontBuildDstDataFT& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
479 src_tmp.SrcRanges = cfg.GlyphRanges ? cfg.GlyphRanges : atlas->GetGlyphRangesDefault();
480 for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
481 {
482 // Check for valid range. This may also help detect *some* dangling pointers, because a common
483 // user error is to setup ImFontConfig::GlyphRanges with a pointer to data that isn't persistent.
484 IM_ASSERT(src_range[0] <= src_range[1]);
485 src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]);
486 }
487 dst_tmp.SrcCount++;
488 dst_tmp.GlyphsHighest = ImMax(dst_tmp.GlyphsHighest, src_tmp.GlyphsHighest);
489 }
490
491 // 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.
492 int total_glyphs_count = 0;
493 for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
494 {
495 ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
496 ImFontBuildDstDataFT& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
497 src_tmp.GlyphsSet.Create(src_tmp.GlyphsHighest + 1);
498 if (dst_tmp.GlyphsSet.Storage.empty())
499 dst_tmp.GlyphsSet.Create(dst_tmp.GlyphsHighest + 1);
500
501 for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
502 for (int codepoint = src_range[0]; codepoint <= (int)src_range[1]; codepoint++)
503 {
504 if (dst_tmp.GlyphsSet.TestBit(codepoint)) // Don't overwrite existing glyphs. We could make this an option (e.g. MergeOverwrite)
505 continue;
506 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..)
507 if (glyph_index == 0)
508 continue;
509
510 // Add to avail set/counters
511 src_tmp.GlyphsCount++;
512 dst_tmp.GlyphsCount++;
513 src_tmp.GlyphsSet.SetBit(codepoint);
514 dst_tmp.GlyphsSet.SetBit(codepoint);
515 total_glyphs_count++;
516 }
517 }
518
519 // 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)
520 for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
521 {
522 ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
523 src_tmp.GlyphsList.reserve(src_tmp.GlyphsCount);
524
525 IM_ASSERT(sizeof(src_tmp.GlyphsSet.Storage.Data[0]) == sizeof(ImU32));
526 const ImU32* it_begin = src_tmp.GlyphsSet.Storage.begin();
527 const ImU32* it_end = src_tmp.GlyphsSet.Storage.end();
528 for (const ImU32* it = it_begin; it < it_end; it++)
529 if (ImU32 entries_32 = *it)
530 for (ImU32 bit_n = 0; bit_n < 32; bit_n++)
531 if (entries_32 & ((ImU32)1 << bit_n))
532 {
533 ImFontBuildSrcGlyphFT src_glyph;
534 src_glyph.Codepoint = (ImWchar)(((it - it_begin) << 5) + bit_n);
535 //src_glyph.GlyphIndex = 0; // FIXME-OPT: We had this info in the previous step and lost it..
536 src_tmp.GlyphsList.push_back(src_glyph);
537 }
538 src_tmp.GlyphsSet.Clear();
539 IM_ASSERT(src_tmp.GlyphsList.Size == src_tmp.GlyphsCount);
540 }
541 for (int dst_i = 0; dst_i < dst_tmp_array.Size; dst_i++)
542 dst_tmp_array[dst_i].GlyphsSet.Clear();
543 dst_tmp_array.clear();
544
545 // Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0)
546 // (We technically don't need to zero-clear buf_rects, but let's do it for the sake of sanity)
547 ImVector<stbrp_rect> buf_rects;
548 buf_rects.resize(total_glyphs_count);
549 memset(buf_rects.Data, 0, (size_t)buf_rects.size_in_bytes());
550
551 // Allocate temporary rasterization data buffers.
552 // We could not find a way to retrieve accurate glyph size without rendering them.
553 // (e.g. slot->metrics->width not always matching bitmap->width, especially considering the Oblique transform)
554 // We allocate in chunks of 256 KB to not waste too much extra memory ahead. Hopefully users of FreeType won't mind the temporary allocations.
555 const int BITMAP_BUFFERS_CHUNK_SIZE = 256 * 1024;
556 int buf_bitmap_current_used_bytes = 0;
557 ImVector<unsigned char*> buf_bitmap_buffers;
558 buf_bitmap_buffers.push_back((unsigned char*)IM_ALLOC(BITMAP_BUFFERS_CHUNK_SIZE));
559
560 // 4. Gather glyphs sizes so we can pack them in our virtual canvas.
561 // 8. Render/rasterize font characters into the texture
562 int total_surface = 0;
563 int buf_rects_out_n = 0;
564 for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
565 {
566 ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
567 ImFontConfig& cfg = atlas->ConfigData[src_i];
568 if (src_tmp.GlyphsCount == 0)
569 continue;
570
571 src_tmp.Rects = &buf_rects[buf_rects_out_n];
572 buf_rects_out_n += src_tmp.GlyphsCount;
573
574 // Compute multiply table if requested
575 const bool multiply_enabled = (cfg.RasterizerMultiply != 1.0f);
576 unsigned char multiply_table[256];
577 if (multiply_enabled)
578 ImFontAtlasBuildMultiplyCalcLookupTable(multiply_table, cfg.RasterizerMultiply);
579
580 // Gather the sizes of all rectangles we will need to pack
581 const int padding = atlas->TexGlyphPadding;
582 for (int glyph_i = 0; glyph_i < src_tmp.GlyphsList.Size; glyph_i++)
583 {
584 ImFontBuildSrcGlyphFT& src_glyph = src_tmp.GlyphsList[glyph_i];
585
586 const FT_Glyph_Metrics* metrics = src_tmp.Font.LoadGlyph(src_glyph.Codepoint);
587 if (metrics == nullptr)
588 continue;
589
590 // Render glyph into a bitmap (currently held by FreeType)
591 const FT_Bitmap* ft_bitmap = src_tmp.Font.RenderGlyphAndGetInfo(&src_glyph.Info);
592 if (ft_bitmap == nullptr)
593 continue;
594
595 // Allocate new temporary chunk if needed
596 const int bitmap_size_in_bytes = src_glyph.Info.Width * src_glyph.Info.Height * 4;
597 if (buf_bitmap_current_used_bytes + bitmap_size_in_bytes > BITMAP_BUFFERS_CHUNK_SIZE)
598 {
599 buf_bitmap_current_used_bytes = 0;
600 buf_bitmap_buffers.push_back((unsigned char*)IM_ALLOC(BITMAP_BUFFERS_CHUNK_SIZE));
601 }
602 IM_ASSERT(buf_bitmap_current_used_bytes + bitmap_size_in_bytes <= BITMAP_BUFFERS_CHUNK_SIZE); // We could probably allocate custom-sized buffer instead.
603
604 // Blit rasterized pixels to our temporary buffer and keep a pointer to it.
605 src_glyph.BitmapData = (unsigned int*)(buf_bitmap_buffers.back() + buf_bitmap_current_used_bytes);
606 buf_bitmap_current_used_bytes += bitmap_size_in_bytes;
607 src_tmp.Font.BlitGlyph(ft_bitmap, src_glyph.BitmapData, src_glyph.Info.Width, multiply_enabled ? multiply_table : nullptr);
608
609 src_tmp.Rects[glyph_i].w = (stbrp_coord)(src_glyph.Info.Width + padding);
610 src_tmp.Rects[glyph_i].h = (stbrp_coord)(src_glyph.Info.Height + padding);
611 total_surface += src_tmp.Rects[glyph_i].w * src_tmp.Rects[glyph_i].h;
612 }
613 }
614
615 // We need a width for the skyline algorithm, any width!
616 // The exact width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height.
617 // User can override TexDesiredWidth and TexGlyphPadding if they wish, otherwise we use a simple heuristic to select the width based on expected surface.
618 const int surface_sqrt = (int)ImSqrt((float)total_surface) + 1;
619 atlas->TexHeight = 0;
620 if (atlas->TexDesiredWidth > 0)
621 atlas->TexWidth = atlas->TexDesiredWidth;
622 else
623 atlas->TexWidth = (surface_sqrt >= 4096 * 0.7f) ? 4096 : (surface_sqrt >= 2048 * 0.7f) ? 2048 : (surface_sqrt >= 1024 * 0.7f) ? 1024 : 512;
624
625 // 5. Start packing
626 // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values).
627 const int TEX_HEIGHT_MAX = 1024 * 32;
628 const int num_nodes_for_packing_algorithm = atlas->TexWidth - atlas->TexGlyphPadding;
629 ImVector<stbrp_node> pack_nodes;
630 pack_nodes.resize(num_nodes_for_packing_algorithm);
631 stbrp_context pack_context;
632 stbrp_init_target(&pack_context, atlas->TexWidth - atlas->TexGlyphPadding, TEX_HEIGHT_MAX - atlas->TexGlyphPadding, pack_nodes.Data, pack_nodes.Size);
633 ImFontAtlasBuildPackCustomRects(atlas, &pack_context);
634
635 // 6. Pack each source font. No rendering yet, we are working with rectangles in an infinitely tall texture at this point.
636 for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
637 {
638 ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
639 if (src_tmp.GlyphsCount == 0)
640 continue;
641
642 stbrp_pack_rects(&pack_context, src_tmp.Rects, src_tmp.GlyphsCount);
643
644 // Extend texture height and mark missing glyphs as non-packed so we won't render them.
645 // 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?)
646 for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
647 if (src_tmp.Rects[glyph_i].was_packed)
648 atlas->TexHeight = ImMax(atlas->TexHeight, src_tmp.Rects[glyph_i].y + src_tmp.Rects[glyph_i].h);
649 }
650
651 // 7. Allocate texture
652 atlas->TexHeight = (atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (atlas->TexHeight + 1) : ImUpperPowerOfTwo(atlas->TexHeight);
653 atlas->TexUvScale = ImVec2(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight);
654 if (src_load_color)
655 {
656 size_t tex_size = (size_t)atlas->TexWidth * atlas->TexHeight * 4;
657 atlas->TexPixelsRGBA32 = (unsigned int*)IM_ALLOC(tex_size);
658 memset(atlas->TexPixelsRGBA32, 0, tex_size);
659 }
660 else
661 {
662 size_t tex_size = (size_t)atlas->TexWidth * atlas->TexHeight * 1;
663 atlas->TexPixelsAlpha8 = (unsigned char*)IM_ALLOC(tex_size);
664 memset(atlas->TexPixelsAlpha8, 0, tex_size);
665 }
666
667 // 8. Copy rasterized font characters back into the main texture
668 // 9. Setup ImFont and glyphs for runtime
669 bool tex_use_colors = false;
670 for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
671 {
672 ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
673 if (src_tmp.GlyphsCount == 0)
674 continue;
675
676 // When merging fonts with MergeMode=true:
677 // - We can have multiple input fonts writing into a same destination font.
678 // - dst_font->ConfigData is != from cfg which is our source configuration.
679 ImFontConfig& cfg = atlas->ConfigData[src_i];
680 ImFont* dst_font = cfg.DstFont;
681
682 const float ascent = src_tmp.Font.Info.Ascender;
683 const float descent = src_tmp.Font.Info.Descender;
684 ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent);
685 const float font_off_x = cfg.GlyphOffset.x;
686 const float font_off_y = cfg.GlyphOffset.y + IM_ROUND(dst_font->Ascent);
687
688 const int padding = atlas->TexGlyphPadding;
689 for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
690 {
691 ImFontBuildSrcGlyphFT& src_glyph = src_tmp.GlyphsList[glyph_i];
692 stbrp_rect& pack_rect = src_tmp.Rects[glyph_i];
693 IM_ASSERT(pack_rect.was_packed);
694 if (pack_rect.w == 0 && pack_rect.h == 0)
695 continue;
696
697 GlyphInfo& info = src_glyph.Info;
698 IM_ASSERT(info.Width + padding <= pack_rect.w);
699 IM_ASSERT(info.Height + padding <= pack_rect.h);
700 const int tx = pack_rect.x + padding;
701 const int ty = pack_rect.y + padding;
702
703 // Register glyph
704 float x0 = info.OffsetX * src_tmp.Font.InvRasterizationDensity + font_off_x;
705 float y0 = info.OffsetY * src_tmp.Font.InvRasterizationDensity + font_off_y;
706 float x1 = x0 + info.Width * src_tmp.Font.InvRasterizationDensity;
707 float y1 = y0 + info.Height * src_tmp.Font.InvRasterizationDensity;
708 float u0 = (tx) / (float)atlas->TexWidth;
709 float v0 = (ty) / (float)atlas->TexHeight;
710 float u1 = (tx + info.Width) / (float)atlas->TexWidth;
711 float v1 = (ty + info.Height) / (float)atlas->TexHeight;
712 dst_font->AddGlyph(&cfg, (ImWchar)src_glyph.Codepoint, x0, y0, x1, y1, u0, v0, u1, v1, info.AdvanceX * src_tmp.Font.InvRasterizationDensity);
713
714 ImFontGlyph* dst_glyph = &dst_font->Glyphs.back();
715 IM_ASSERT(dst_glyph->Codepoint == src_glyph.Codepoint);
716 if (src_glyph.Info.IsColored)
717 dst_glyph->Colored = tex_use_colors = true;
718
719 // Blit from temporary buffer to final texture
720 size_t blit_src_stride = (size_t)src_glyph.Info.Width;
721 size_t blit_dst_stride = (size_t)atlas->TexWidth;
722 unsigned int* blit_src = src_glyph.BitmapData;
723 if (atlas->TexPixelsAlpha8 != nullptr)
724 {
725 unsigned char* blit_dst = atlas->TexPixelsAlpha8 + (ty * blit_dst_stride) + tx;
726 for (int y = 0; y < info.Height; y++, blit_dst += blit_dst_stride, blit_src += blit_src_stride)
727 for (int x = 0; x < info.Width; x++)
728 blit_dst[x] = (unsigned char)((blit_src[x] >> IM_COL32_A_SHIFT) & 0xFF);
729 }
730 else
731 {
732 unsigned int* blit_dst = atlas->TexPixelsRGBA32 + (ty * blit_dst_stride) + tx;
733 for (int y = 0; y < info.Height; y++, blit_dst += blit_dst_stride, blit_src += blit_src_stride)
734 for (int x = 0; x < info.Width; x++)
735 blit_dst[x] = blit_src[x];
736 }
737 }
738
739 src_tmp.Rects = nullptr;
740 }
741 atlas->TexPixelsUseColors = tex_use_colors;
742
743 // Cleanup
744 for (int buf_i = 0; buf_i < buf_bitmap_buffers.Size; buf_i++)
745 IM_FREE(buf_bitmap_buffers[buf_i]);
746 src_tmp_array.clear_destruct();
747
749
750 return true;
751}
752
753// FreeType memory allocation callbacks
754static void* FreeType_Alloc(FT_Memory /*memory*/, long size)
755{
756 return GImGuiFreeTypeAllocFunc((size_t)size, GImGuiFreeTypeAllocatorUserData);
757}
758
759static void FreeType_Free(FT_Memory /*memory*/, void* block)
760{
761 GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
762}
763
764static void* FreeType_Realloc(FT_Memory /*memory*/, long cur_size, long new_size, void* block)
765{
766 // Implement realloc() as we don't ask user to provide it.
767 if (block == nullptr)
768 return GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData);
769
770 if (new_size == 0)
771 {
772 GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
773 return nullptr;
774 }
775
776 if (new_size > cur_size)
777 {
778 void* new_block = GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData);
779 memcpy(new_block, block, (size_t)cur_size);
780 GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
781 return new_block;
782 }
783
784 return block;
785}
786
787static bool ImFontAtlasBuildWithFreeType(ImFontAtlas* atlas)
788{
789 // FreeType memory management: https://www.freetype.org/freetype2/docs/design/design-4.html
790 FT_MemoryRec_ memory_rec = {};
791 memory_rec.user = nullptr;
792 memory_rec.alloc = &FreeType_Alloc;
793 memory_rec.free = &FreeType_Free;
794 memory_rec.realloc = &FreeType_Realloc;
795
796 // https://www.freetype.org/freetype2/docs/reference/ft2-module_management.html#FT_New_Library
797 FT_Library ft_library;
798 FT_Error error = FT_New_Library(&memory_rec, &ft_library);
799 if (error != 0)
800 return false;
801
802 // If you don't call FT_Add_Default_Modules() the rest of code may work, but FreeType won't use our custom allocator.
803 FT_Add_Default_Modules(ft_library);
804
805#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
806 // Install svg hooks for FreeType
807 // https://freetype.org/freetype2/docs/reference/ft2-properties.html#svg-hooks
808 // https://freetype.org/freetype2/docs/reference/ft2-svg_fonts.html#svg_fonts
809 SVG_RendererHooks hooks = { ImGuiLunasvgPortInit, ImGuiLunasvgPortFree, ImGuiLunasvgPortRender, ImGuiLunasvgPortPresetSlot };
810 FT_Property_Set(ft_library, "ot-svg", "svg-hooks", &hooks);
811#endif // IMGUI_ENABLE_FREETYPE_LUNASVG
812
813 bool ret = ImFontAtlasBuildWithFreeTypeEx(ft_library, atlas, atlas->FontBuilderFlags);
814 FT_Done_Library(ft_library);
815
816 return ret;
817}
818
819const ImFontBuilderIO* ImGuiFreeType::GetBuilderForFreeType()
820{
821 static ImFontBuilderIO io;
822 io.FontBuilder_Build = ImFontAtlasBuildWithFreeType;
823 return &io;
824}
825
826void ImGuiFreeType::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data)
827{
828 GImGuiFreeTypeAllocFunc = alloc_func;
829 GImGuiFreeTypeFreeFunc = free_func;
830 GImGuiFreeTypeAllocatorUserData = user_data;
831}
832
833#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
834// For more details, see https://gitlab.freedesktop.org/freetype/freetype-demos/-/blob/master/src/rsvg-port.c
835// The original code from the demo is licensed under CeCILL-C Free Software License Agreement (https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/LICENSE.TXT)
836struct LunasvgPortState
837{
838 FT_Error err = FT_Err_Ok;
839 lunasvg::Matrix matrix;
840 std::unique_ptr<lunasvg::Document> svg = nullptr;
841};
842
843static FT_Error ImGuiLunasvgPortInit(FT_Pointer* _state)
844{
845 *_state = IM_NEW(LunasvgPortState)();
846 return FT_Err_Ok;
847}
848
849static void ImGuiLunasvgPortFree(FT_Pointer* _state)
850{
851 IM_DELETE(*(LunasvgPortState**)_state);
852}
853
854static FT_Error ImGuiLunasvgPortRender(FT_GlyphSlot slot, FT_Pointer* _state)
855{
856 LunasvgPortState* state = *(LunasvgPortState**)_state;
857
858 // If there was an error while loading the svg in ImGuiLunasvgPortPresetSlot(), the renderer hook still get called, so just returns the error.
859 if (state->err != FT_Err_Ok)
860 return state->err;
861
862 // rows is height, pitch (or stride) equals to width * sizeof(int32)
863 lunasvg::Bitmap bitmap((uint8_t*)slot->bitmap.buffer, slot->bitmap.width, slot->bitmap.rows, slot->bitmap.pitch);
864 state->svg->setMatrix(state->svg->matrix().identity()); // Reset the svg matrix to the default value
865 state->svg->render(bitmap, state->matrix); // state->matrix is already scaled and translated
866 state->err = FT_Err_Ok;
867 return state->err;
868}
869
870static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_Pointer* _state)
871{
872 FT_SVG_Document document = (FT_SVG_Document)slot->other;
873 LunasvgPortState* state = *(LunasvgPortState**)_state;
874 FT_Size_Metrics& metrics = document->metrics;
875
876 // This function is called twice, once in the FT_Load_Glyph() and another right before ImGuiLunasvgPortRender().
877 // If it's the latter, don't do anything because it's // already done in the former.
878 if (cache)
879 return state->err;
880
881 state->svg = lunasvg::Document::loadFromData((const char*)document->svg_document, document->svg_document_length);
882 if (state->svg == nullptr)
883 {
884 state->err = FT_Err_Invalid_SVG_Document;
885 return state->err;
886 }
887
888 lunasvg::Box box = state->svg->box();
889 double scale = std::min(metrics.x_ppem / box.w, metrics.y_ppem / box.h);
890 double xx = (double)document->transform.xx / (1 << 16);
891 double xy = -(double)document->transform.xy / (1 << 16);
892 double yx = -(double)document->transform.yx / (1 << 16);
893 double yy = (double)document->transform.yy / (1 << 16);
894 double x0 = (double)document->delta.x / 64 * box.w / metrics.x_ppem;
895 double y0 = -(double)document->delta.y / 64 * box.h / metrics.y_ppem;
896
897 // Scale and transform, we don't translate the svg yet
898 state->matrix.identity();
899 state->matrix.scale(scale, scale);
900 state->matrix.transform(xx, xy, yx, yy, x0, y0);
901 state->svg->setMatrix(state->matrix);
902
903 // Pre-translate the matrix for the rendering step
904 state->matrix.translate(-box.x, -box.y);
905
906 // Get the box again after the transformation
907 box = state->svg->box();
908
909 // Calculate the bitmap size
910 slot->bitmap_left = FT_Int(box.x);
911 slot->bitmap_top = FT_Int(-box.y);
912 slot->bitmap.rows = (unsigned int)(ImCeil((float)box.h));
913 slot->bitmap.width = (unsigned int)(ImCeil((float)box.w));
914 slot->bitmap.pitch = slot->bitmap.width * 4;
915 slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA;
916
917 // Compute all the bearings and set them correctly. The outline is scaled already, we just need to use the bounding box.
918 double metrics_width = box.w;
919 double metrics_height = box.h;
920 double horiBearingX = box.x;
921 double horiBearingY = -box.y;
922 double vertBearingX = slot->metrics.horiBearingX / 64.0 - slot->metrics.horiAdvance / 64.0 / 2.0;
923 double vertBearingY = (slot->metrics.vertAdvance / 64.0 - slot->metrics.height / 64.0) / 2.0;
924 slot->metrics.width = FT_Pos(IM_ROUND(metrics_width * 64.0)); // Using IM_ROUND() assume width and height are positive
925 slot->metrics.height = FT_Pos(IM_ROUND(metrics_height * 64.0));
926 slot->metrics.horiBearingX = FT_Pos(horiBearingX * 64);
927 slot->metrics.horiBearingY = FT_Pos(horiBearingY * 64);
928 slot->metrics.vertBearingX = FT_Pos(vertBearingX * 64);
929 slot->metrics.vertBearingY = FT_Pos(vertBearingY * 64);
930
931 if (slot->metrics.vertAdvance == 0)
932 slot->metrics.vertAdvance = FT_Pos(metrics_height * 1.2 * 64.0);
933
934 state->err = FT_Err_Ok;
935 return state->err;
936}
937
938#endif // #ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
939
940//-----------------------------------------------------------------------------
941
942#ifdef __GNUC__
943#pragma GCC diagnostic pop
944#endif
945
946#ifdef _MSC_VER
947#pragma warning (pop)
948#endif
949
950#endif // #ifndef IMGUI_DISABLE
int g
void ImFontAtlasBuildSetupFont(ImFontAtlas *atlas, ImFont *font, ImFontConfig *font_config, float ascent, float descent)
void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_brighten_factor)
void ImFontAtlasBuildInit(ImFontAtlas *atlas)
void ImFontAtlasBuildFinish(ImFontAtlas *atlas)
void ImFontAtlasBuildPackCustomRects(ImFontAtlas *atlas, void *stbrp_context_opaque)
#define FT_CEIL(X)
#define DE_MULTIPLY(color, alpha)
bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas *atlas, unsigned int extra_flags)
constexpr mat4 scale(const vec3 &xyz)
AmdFlash::SectorInfo Info
Definition RomManbow2.cc:18
size_t size(std::string_view utf8)
ImVector< ImFontBuildSrcGlyphFT > GlyphsList
const ImWchar * SrcRanges
unsigned int * BitmapData