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