imgui_freetype.cpp (37549B)
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 // 2021/03/05: added ImGuiFreeTypeBuilderFlags_Bitmap to load bitmap glyphs. 10 // 2021/03/02: set 'atlas->TexPixelsUseColors = true' to help some backends with deciding of a prefered texture format. 11 // 2021/01/28: added support for color-layered glyphs via ImGuiFreeTypeBuilderFlags_LoadColor (require Freetype 2.10+). 12 // 2021/01/26: simplified integration by using '#define IMGUI_ENABLE_FREETYPE'. 13 // renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API. removed ImGuiFreeType::BuildFontAtlas(). 14 // 2020/06/04: fix for rare case where FT_Get_Char_Index() succeed but FT_Load_Glyph() fails. 15 // 2019/02/09: added RasterizerFlags::Monochrome flag to disable font anti-aliasing (combine with ::MonoHinting for best results!) 16 // 2019/01/15: added support for imgui allocators + added FreeType only override function SetAllocatorFunctions(). 17 // 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. 18 // 2018/06/08: added support for ImFontConfig::GlyphMinAdvanceX, GlyphMaxAdvanceX. 19 // 2018/02/04: moved to main imgui repository (away from http://www.github.com/ocornut/imgui_club) 20 // 2018/01/22: fix for addition of ImFontAtlas::TexUvscale member. 21 // 2017/10/22: minor inconsequential change to match change in master (removed an unnecessary statement). 22 // 2017/09/26: fixes for imgui internal changes. 23 // 2017/08/26: cleanup, optimizations, support for ImFontConfig::RasterizerFlags, ImFontConfig::RasterizerMultiply. 24 // 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. 25 26 // About Gamma Correct Blending: 27 // - FreeType assumes blending in linear space rather than gamma space. 28 // - See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Render_Glyph 29 // - For correct results you need to be using sRGB and convert to linear space in the pixel shader output. 30 // - The default dear imgui styles will be impacted by this change (alpha values will need tweaking). 31 32 // FIXME: cfg.OversampleH, OversampleV are not supported (but perhaps not so necessary with this rasterizer). 33 34 #include "imgui_freetype.h" 35 #include "imgui_internal.h" // ImMin,ImMax,ImFontAtlasBuild*, 36 #include <stdint.h> 37 #include <ft2build.h> 38 #include FT_FREETYPE_H // <freetype/freetype.h> 39 #include FT_MODULE_H // <freetype/ftmodapi.h> 40 #include FT_GLYPH_H // <freetype/ftglyph.h> 41 #include FT_SYNTHESIS_H // <freetype/ftsynth.h> 42 43 #ifdef _MSC_VER 44 #pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) 45 #endif 46 47 #if defined(__GNUC__) 48 #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind 49 #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used 50 #endif 51 52 //------------------------------------------------------------------------- 53 // Data 54 //------------------------------------------------------------------------- 55 56 // Default memory allocators 57 static void* ImGuiFreeTypeDefaultAllocFunc(size_t size, void* user_data) { IM_UNUSED(user_data); return IM_ALLOC(size); } 58 static void ImGuiFreeTypeDefaultFreeFunc(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_FREE(ptr); } 59 60 // Current memory allocators 61 static void* (*GImGuiFreeTypeAllocFunc)(size_t size, void* user_data) = ImGuiFreeTypeDefaultAllocFunc; 62 static void (*GImGuiFreeTypeFreeFunc)(void* ptr, void* user_data) = ImGuiFreeTypeDefaultFreeFunc; 63 static void* GImGuiFreeTypeAllocatorUserData = NULL; 64 65 //------------------------------------------------------------------------- 66 // Code 67 //------------------------------------------------------------------------- 68 69 namespace 70 { 71 // Glyph metrics: 72 // -------------- 73 // 74 // xmin xmax 75 // | | 76 // |<-------- width -------->| 77 // | | 78 // | +-------------------------+----------------- ymax 79 // | | ggggggggg ggggg | ^ ^ 80 // | | g:::::::::ggg::::g | | | 81 // | | g:::::::::::::::::g | | | 82 // | | g::::::ggggg::::::gg | | | 83 // | | g:::::g g:::::g | | | 84 // offsetX -|-------->| g:::::g g:::::g | offsetY | 85 // | | g:::::g g:::::g | | | 86 // | | g::::::g g:::::g | | | 87 // | | g:::::::ggggg:::::g | | | 88 // | | g::::::::::::::::g | | height 89 // | | gg::::::::::::::g | | | 90 // baseline ---*---------|---- gggggggg::::::g-----*-------- | 91 // / | | g:::::g | | 92 // origin | | gggggg g:::::g | | 93 // | | g:::::gg gg:::::g | | 94 // | | g::::::ggg:::::::g | | 95 // | | gg:::::::::::::g | | 96 // | | ggg::::::ggg | | 97 // | | gggggg | v 98 // | +-------------------------+----------------- ymin 99 // | | 100 // |------------- advanceX ----------->| 101 102 // A structure that describe a glyph. 103 struct GlyphInfo 104 { 105 int Width; // Glyph's width in pixels. 106 int Height; // Glyph's height in pixels. 107 FT_Int OffsetX; // The distance from the origin ("pen position") to the left of the glyph. 108 FT_Int OffsetY; // The distance from the origin to the top of the glyph. This is usually a value < 0. 109 float AdvanceX; // The distance from the origin to the origin of the next glyph. This is usually a value > 0. 110 bool IsColored; // The glyph is colored 111 }; 112 113 // Font parameters and metrics. 114 struct FontInfo 115 { 116 uint32_t PixelHeight; // Size this font was generated with. 117 float Ascender; // The pixel extents above the baseline in pixels (typically positive). 118 float Descender; // The extents below the baseline in pixels (typically negative). 119 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. 120 float LineGap; // The spacing in pixels between one row's descent and the next row's ascent. 121 float MaxAdvanceWidth; // This field gives the maximum horizontal cursor advance for all glyphs in the font. 122 }; 123 124 // FreeType glyph rasterizer. 125 // NB: No ctor/dtor, explicitly call Init()/Shutdown() 126 struct FreeTypeFont 127 { 128 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. 129 void CloseFont(); 130 void SetPixelHeight(int pixel_height); // Change font pixel size. All following calls to RasterizeGlyph() will use this size 131 const FT_Glyph_Metrics* LoadGlyph(uint32_t in_codepoint); 132 const FT_Bitmap* RenderGlyphAndGetInfo(GlyphInfo* out_glyph_info); 133 void BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch, unsigned char* multiply_table = NULL); 134 ~FreeTypeFont() { CloseFont(); } 135 136 // [Internals] 137 FontInfo Info; // Font descriptor of the current font. 138 FT_Face Face; 139 unsigned int UserFlags; // = ImFontConfig::RasterizerFlags 140 FT_Int32 LoadFlags; 141 FT_Render_Mode RenderMode; 142 }; 143 144 // From SDL_ttf: Handy routines for converting from fixed point 145 #define FT_CEIL(X) (((X + 63) & -64) / 64) 146 147 bool FreeTypeFont::InitFont(FT_Library ft_library, const ImFontConfig& cfg, unsigned int extra_font_builder_flags) 148 { 149 FT_Error error = FT_New_Memory_Face(ft_library, (uint8_t*)cfg.FontData, (uint32_t)cfg.FontDataSize, (uint32_t)cfg.FontNo, &Face); 150 if (error != 0) 151 return false; 152 error = FT_Select_Charmap(Face, FT_ENCODING_UNICODE); 153 if (error != 0) 154 return false; 155 156 // Convert to FreeType flags (NB: Bold and Oblique are processed separately) 157 UserFlags = cfg.FontBuilderFlags | extra_font_builder_flags; 158 159 LoadFlags = 0; 160 if ((UserFlags & ImGuiFreeTypeBuilderFlags_Bitmap) == 0) 161 LoadFlags |= FT_LOAD_NO_BITMAP; 162 163 if (UserFlags & ImGuiFreeTypeBuilderFlags_NoHinting) 164 LoadFlags |= FT_LOAD_NO_HINTING; 165 if (UserFlags & ImGuiFreeTypeBuilderFlags_NoAutoHint) 166 LoadFlags |= FT_LOAD_NO_AUTOHINT; 167 if (UserFlags & ImGuiFreeTypeBuilderFlags_ForceAutoHint) 168 LoadFlags |= FT_LOAD_FORCE_AUTOHINT; 169 if (UserFlags & ImGuiFreeTypeBuilderFlags_LightHinting) 170 LoadFlags |= FT_LOAD_TARGET_LIGHT; 171 else if (UserFlags & ImGuiFreeTypeBuilderFlags_MonoHinting) 172 LoadFlags |= FT_LOAD_TARGET_MONO; 173 else 174 LoadFlags |= FT_LOAD_TARGET_NORMAL; 175 176 if (UserFlags & ImGuiFreeTypeBuilderFlags_Monochrome) 177 RenderMode = FT_RENDER_MODE_MONO; 178 else 179 RenderMode = FT_RENDER_MODE_NORMAL; 180 181 if (UserFlags & ImGuiFreeTypeBuilderFlags_LoadColor) 182 LoadFlags |= FT_LOAD_COLOR; 183 184 memset(&Info, 0, sizeof(Info)); 185 SetPixelHeight((uint32_t)cfg.SizePixels); 186 187 return true; 188 } 189 190 void FreeTypeFont::CloseFont() 191 { 192 if (Face) 193 { 194 FT_Done_Face(Face); 195 Face = NULL; 196 } 197 } 198 199 void FreeTypeFont::SetPixelHeight(int pixel_height) 200 { 201 // Vuhdo: I'm not sure how to deal with font sizes properly. As far as I understand, currently ImGui assumes that the 'pixel_height' 202 // 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. 203 // NB: FT_Set_Pixel_Sizes() doesn't seem to get us the same result. 204 FT_Size_RequestRec req; 205 req.type = (UserFlags & ImGuiFreeTypeBuilderFlags_Bitmap) ? FT_SIZE_REQUEST_TYPE_NOMINAL : FT_SIZE_REQUEST_TYPE_REAL_DIM; 206 req.width = 0; 207 req.height = (uint32_t)pixel_height * 64; 208 req.horiResolution = 0; 209 req.vertResolution = 0; 210 FT_Request_Size(Face, &req); 211 212 // Update font info 213 FT_Size_Metrics metrics = Face->size->metrics; 214 Info.PixelHeight = (uint32_t)pixel_height; 215 Info.Ascender = (float)FT_CEIL(metrics.ascender); 216 Info.Descender = (float)FT_CEIL(metrics.descender); 217 Info.LineSpacing = (float)FT_CEIL(metrics.height); 218 Info.LineGap = (float)FT_CEIL(metrics.height - metrics.ascender + metrics.descender); 219 Info.MaxAdvanceWidth = (float)FT_CEIL(metrics.max_advance); 220 } 221 222 const FT_Glyph_Metrics* FreeTypeFont::LoadGlyph(uint32_t codepoint) 223 { 224 uint32_t glyph_index = FT_Get_Char_Index(Face, codepoint); 225 if (glyph_index == 0) 226 return NULL; 227 FT_Error error = FT_Load_Glyph(Face, glyph_index, LoadFlags); 228 if (error) 229 return NULL; 230 231 // Need an outline for this to work 232 FT_GlyphSlot slot = Face->glyph; 233 IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP); 234 235 // Apply convenience transform (this is not picking from real "Bold"/"Italic" fonts! Merely applying FreeType helper transform. Oblique == Slanting) 236 if (UserFlags & ImGuiFreeTypeBuilderFlags_Bold) 237 FT_GlyphSlot_Embolden(slot); 238 if (UserFlags & ImGuiFreeTypeBuilderFlags_Oblique) 239 { 240 FT_GlyphSlot_Oblique(slot); 241 //FT_BBox bbox; 242 //FT_Outline_Get_BBox(&slot->outline, &bbox); 243 //slot->metrics.width = bbox.xMax - bbox.xMin; 244 //slot->metrics.height = bbox.yMax - bbox.yMin; 245 } 246 247 return &slot->metrics; 248 } 249 250 const FT_Bitmap* FreeTypeFont::RenderGlyphAndGetInfo(GlyphInfo* out_glyph_info) 251 { 252 FT_GlyphSlot slot = Face->glyph; 253 FT_Error error = FT_Render_Glyph(slot, RenderMode); 254 if (error != 0) 255 return NULL; 256 257 FT_Bitmap* ft_bitmap = &Face->glyph->bitmap; 258 out_glyph_info->Width = (int)ft_bitmap->width; 259 out_glyph_info->Height = (int)ft_bitmap->rows; 260 out_glyph_info->OffsetX = Face->glyph->bitmap_left; 261 out_glyph_info->OffsetY = -Face->glyph->bitmap_top; 262 out_glyph_info->AdvanceX = (float)FT_CEIL(slot->advance.x); 263 out_glyph_info->IsColored = (ft_bitmap->pixel_mode == FT_PIXEL_MODE_BGRA); 264 265 return ft_bitmap; 266 } 267 268 void FreeTypeFont::BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch, unsigned char* multiply_table) 269 { 270 IM_ASSERT(ft_bitmap != NULL); 271 const uint32_t w = ft_bitmap->width; 272 const uint32_t h = ft_bitmap->rows; 273 const uint8_t* src = ft_bitmap->buffer; 274 const uint32_t src_pitch = ft_bitmap->pitch; 275 276 switch (ft_bitmap->pixel_mode) 277 { 278 case FT_PIXEL_MODE_GRAY: // Grayscale image, 1 byte per pixel. 279 { 280 if (multiply_table == NULL) 281 { 282 for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch) 283 for (uint32_t x = 0; x < w; x++) 284 dst[x] = IM_COL32(255, 255, 255, src[x]); 285 } 286 else 287 { 288 for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch) 289 for (uint32_t x = 0; x < w; x++) 290 dst[x] = IM_COL32(255, 255, 255, multiply_table[src[x]]); 291 } 292 break; 293 } 294 case FT_PIXEL_MODE_MONO: // Monochrome image, 1 bit per pixel. The bits in each byte are ordered from MSB to LSB. 295 { 296 uint8_t color0 = multiply_table ? multiply_table[0] : 0; 297 uint8_t color1 = multiply_table ? multiply_table[255] : 255; 298 for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch) 299 { 300 uint8_t bits = 0; 301 const uint8_t* bits_ptr = src; 302 for (uint32_t x = 0; x < w; x++, bits <<= 1) 303 { 304 if ((x & 7) == 0) 305 bits = *bits_ptr++; 306 dst[x] = IM_COL32(255, 255, 255, (bits & 0x80) ? color1 : color0); 307 } 308 } 309 break; 310 } 311 case FT_PIXEL_MODE_BGRA: 312 { 313 // FIXME: Converting pre-multiplied alpha to straight. Doesn't smell good. 314 #define DE_MULTIPLY(color, alpha) (ImU32)(255.0f * (float)color / (float)alpha + 0.5f) 315 if (multiply_table == NULL) 316 { 317 for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch) 318 for (uint32_t x = 0; x < w; x++) 319 { 320 uint8_t r = src[x * 4 + 2], g = src[x * 4 + 1], b = src[x * 4], a = src[x * 4 + 3]; 321 dst[x] = IM_COL32(DE_MULTIPLY(r, a), DE_MULTIPLY(g, a), DE_MULTIPLY(b, a), a); 322 } 323 } 324 else 325 { 326 for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch) 327 { 328 for (uint32_t x = 0; x < w; x++) 329 { 330 uint8_t r = src[x * 4 + 2], g = src[x * 4 + 1], b = src[x * 4], a = src[x * 4 + 3]; 331 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]); 332 } 333 } 334 } 335 #undef DE_MULTIPLY 336 break; 337 } 338 default: 339 IM_ASSERT(0 && "FreeTypeFont::BlitGlyph(): Unknown bitmap pixel mode!"); 340 } 341 } 342 } 343 344 #ifndef STB_RECT_PACK_IMPLEMENTATION // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds) 345 #ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION 346 #define STBRP_ASSERT(x) do { IM_ASSERT(x); } while (0) 347 #define STBRP_STATIC 348 #define STB_RECT_PACK_IMPLEMENTATION 349 #endif 350 #ifdef IMGUI_STB_RECT_PACK_FILENAME 351 #include IMGUI_STB_RECT_PACK_FILENAME 352 #else 353 #include "imstb_rectpack.h" 354 #endif 355 #endif 356 357 struct ImFontBuildSrcGlyphFT 358 { 359 GlyphInfo Info; 360 uint32_t Codepoint; 361 unsigned int* BitmapData; // Point within one of the dst_tmp_bitmap_buffers[] array 362 363 ImFontBuildSrcGlyphFT() { memset(this, 0, sizeof(*this)); } 364 }; 365 366 struct ImFontBuildSrcDataFT 367 { 368 FreeTypeFont Font; 369 stbrp_rect* Rects; // Rectangle to pack. We first fill in their size and the packer will give us their position. 370 const ImWchar* SrcRanges; // Ranges as requested by user (user is allowed to request too much, e.g. 0x0020..0xFFFF) 371 int DstIndex; // Index into atlas->Fonts[] and dst_tmp_array[] 372 int GlyphsHighest; // Highest requested codepoint 373 int GlyphsCount; // Glyph count (excluding missing glyphs and glyphs already set by an earlier source font) 374 ImBitVector GlyphsSet; // Glyph bit map (random access, 1-bit per codepoint. This will be a maximum of 8KB) 375 ImVector<ImFontBuildSrcGlyphFT> GlyphsList; 376 }; 377 378 // Temporary data for one destination ImFont* (multiple source fonts can be merged into one destination ImFont) 379 struct ImFontBuildDstDataFT 380 { 381 int SrcCount; // Number of source fonts targeting this destination font. 382 int GlyphsHighest; 383 int GlyphsCount; 384 ImBitVector GlyphsSet; // This is used to resolve collision when multiple sources are merged into a same destination font. 385 }; 386 387 bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, unsigned int extra_flags) 388 { 389 IM_ASSERT(atlas->ConfigData.Size > 0); 390 391 ImFontAtlasBuildInit(atlas); 392 393 // Clear atlas 394 atlas->TexID = (ImTextureID)NULL; 395 atlas->TexWidth = atlas->TexHeight = 0; 396 atlas->TexUvScale = ImVec2(0.0f, 0.0f); 397 atlas->TexUvWhitePixel = ImVec2(0.0f, 0.0f); 398 atlas->ClearTexData(); 399 400 // Temporary storage for building 401 bool src_load_color = false; 402 ImVector<ImFontBuildSrcDataFT> src_tmp_array; 403 ImVector<ImFontBuildDstDataFT> dst_tmp_array; 404 src_tmp_array.resize(atlas->ConfigData.Size); 405 dst_tmp_array.resize(atlas->Fonts.Size); 406 memset((void*)src_tmp_array.Data, 0, (size_t)src_tmp_array.size_in_bytes()); 407 memset((void*)dst_tmp_array.Data, 0, (size_t)dst_tmp_array.size_in_bytes()); 408 409 // 1. Initialize font loading structure, check font data validity 410 for (int src_i = 0; src_i < atlas->ConfigData.Size; src_i++) 411 { 412 ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i]; 413 ImFontConfig& cfg = atlas->ConfigData[src_i]; 414 FreeTypeFont& font_face = src_tmp.Font; 415 IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == atlas)); 416 417 // Find index from cfg.DstFont (we allow the user to set cfg.DstFont. Also it makes casual debugging nicer than when storing indices) 418 src_tmp.DstIndex = -1; 419 for (int output_i = 0; output_i < atlas->Fonts.Size && src_tmp.DstIndex == -1; output_i++) 420 if (cfg.DstFont == atlas->Fonts[output_i]) 421 src_tmp.DstIndex = output_i; 422 IM_ASSERT(src_tmp.DstIndex != -1); // cfg.DstFont not pointing within atlas->Fonts[] array? 423 if (src_tmp.DstIndex == -1) 424 return false; 425 426 // Load font 427 if (!font_face.InitFont(ft_library, cfg, extra_flags)) 428 return false; 429 430 // Measure highest codepoints 431 src_load_color |= (cfg.FontBuilderFlags & ImGuiFreeTypeBuilderFlags_LoadColor) != 0; 432 ImFontBuildDstDataFT& dst_tmp = dst_tmp_array[src_tmp.DstIndex]; 433 src_tmp.SrcRanges = cfg.GlyphRanges ? cfg.GlyphRanges : atlas->GetGlyphRangesDefault(); 434 for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2) 435 src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]); 436 dst_tmp.SrcCount++; 437 dst_tmp.GlyphsHighest = ImMax(dst_tmp.GlyphsHighest, src_tmp.GlyphsHighest); 438 } 439 440 // 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. 441 int total_glyphs_count = 0; 442 for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) 443 { 444 ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i]; 445 ImFontBuildDstDataFT& dst_tmp = dst_tmp_array[src_tmp.DstIndex]; 446 src_tmp.GlyphsSet.Create(src_tmp.GlyphsHighest + 1); 447 if (dst_tmp.GlyphsSet.Storage.empty()) 448 dst_tmp.GlyphsSet.Create(dst_tmp.GlyphsHighest + 1); 449 450 for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2) 451 for (int codepoint = src_range[0]; codepoint <= (int)src_range[1]; codepoint++) 452 { 453 if (dst_tmp.GlyphsSet.TestBit(codepoint)) // Don't overwrite existing glyphs. We could make this an option (e.g. MergeOverwrite) 454 continue; 455 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..) 456 if (glyph_index == 0) 457 continue; 458 459 // Add to avail set/counters 460 src_tmp.GlyphsCount++; 461 dst_tmp.GlyphsCount++; 462 src_tmp.GlyphsSet.SetBit(codepoint); 463 dst_tmp.GlyphsSet.SetBit(codepoint); 464 total_glyphs_count++; 465 } 466 } 467 468 // 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) 469 for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) 470 { 471 ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i]; 472 src_tmp.GlyphsList.reserve(src_tmp.GlyphsCount); 473 474 IM_ASSERT(sizeof(src_tmp.GlyphsSet.Storage.Data[0]) == sizeof(ImU32)); 475 const ImU32* it_begin = src_tmp.GlyphsSet.Storage.begin(); 476 const ImU32* it_end = src_tmp.GlyphsSet.Storage.end(); 477 for (const ImU32* it = it_begin; it < it_end; it++) 478 if (ImU32 entries_32 = *it) 479 for (ImU32 bit_n = 0; bit_n < 32; bit_n++) 480 if (entries_32 & ((ImU32)1 << bit_n)) 481 { 482 ImFontBuildSrcGlyphFT src_glyph; 483 src_glyph.Codepoint = (ImWchar)(((it - it_begin) << 5) + bit_n); 484 //src_glyph.GlyphIndex = 0; // FIXME-OPT: We had this info in the previous step and lost it.. 485 src_tmp.GlyphsList.push_back(src_glyph); 486 } 487 src_tmp.GlyphsSet.Clear(); 488 IM_ASSERT(src_tmp.GlyphsList.Size == src_tmp.GlyphsCount); 489 } 490 for (int dst_i = 0; dst_i < dst_tmp_array.Size; dst_i++) 491 dst_tmp_array[dst_i].GlyphsSet.Clear(); 492 dst_tmp_array.clear(); 493 494 // Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0) 495 // (We technically don't need to zero-clear buf_rects, but let's do it for the sake of sanity) 496 ImVector<stbrp_rect> buf_rects; 497 buf_rects.resize(total_glyphs_count); 498 memset(buf_rects.Data, 0, (size_t)buf_rects.size_in_bytes()); 499 500 // Allocate temporary rasterization data buffers. 501 // We could not find a way to retrieve accurate glyph size without rendering them. 502 // (e.g. slot->metrics->width not always matching bitmap->width, especially considering the Oblique transform) 503 // 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. 504 const int BITMAP_BUFFERS_CHUNK_SIZE = 256 * 1024; 505 int buf_bitmap_current_used_bytes = 0; 506 ImVector<unsigned char*> buf_bitmap_buffers; 507 buf_bitmap_buffers.push_back((unsigned char*)IM_ALLOC(BITMAP_BUFFERS_CHUNK_SIZE)); 508 509 // 4. Gather glyphs sizes so we can pack them in our virtual canvas. 510 // 8. Render/rasterize font characters into the texture 511 int total_surface = 0; 512 int buf_rects_out_n = 0; 513 for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) 514 { 515 ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i]; 516 ImFontConfig& cfg = atlas->ConfigData[src_i]; 517 if (src_tmp.GlyphsCount == 0) 518 continue; 519 520 src_tmp.Rects = &buf_rects[buf_rects_out_n]; 521 buf_rects_out_n += src_tmp.GlyphsCount; 522 523 // Compute multiply table if requested 524 const bool multiply_enabled = (cfg.RasterizerMultiply != 1.0f); 525 unsigned char multiply_table[256]; 526 if (multiply_enabled) 527 ImFontAtlasBuildMultiplyCalcLookupTable(multiply_table, cfg.RasterizerMultiply); 528 529 // Gather the sizes of all rectangles we will need to pack 530 const int padding = atlas->TexGlyphPadding; 531 for (int glyph_i = 0; glyph_i < src_tmp.GlyphsList.Size; glyph_i++) 532 { 533 ImFontBuildSrcGlyphFT& src_glyph = src_tmp.GlyphsList[glyph_i]; 534 535 const FT_Glyph_Metrics* metrics = src_tmp.Font.LoadGlyph(src_glyph.Codepoint); 536 if (metrics == NULL) 537 continue; 538 539 // Render glyph into a bitmap (currently held by FreeType) 540 const FT_Bitmap* ft_bitmap = src_tmp.Font.RenderGlyphAndGetInfo(&src_glyph.Info); 541 IM_ASSERT(ft_bitmap); 542 543 // Allocate new temporary chunk if needed 544 const int bitmap_size_in_bytes = src_glyph.Info.Width * src_glyph.Info.Height * 4; 545 if (buf_bitmap_current_used_bytes + bitmap_size_in_bytes > BITMAP_BUFFERS_CHUNK_SIZE) 546 { 547 buf_bitmap_current_used_bytes = 0; 548 buf_bitmap_buffers.push_back((unsigned char*)IM_ALLOC(BITMAP_BUFFERS_CHUNK_SIZE)); 549 } 550 551 // Blit rasterized pixels to our temporary buffer and keep a pointer to it. 552 src_glyph.BitmapData = (unsigned int*)(buf_bitmap_buffers.back() + buf_bitmap_current_used_bytes); 553 buf_bitmap_current_used_bytes += bitmap_size_in_bytes; 554 src_tmp.Font.BlitGlyph(ft_bitmap, src_glyph.BitmapData, src_glyph.Info.Width, multiply_enabled ? multiply_table : NULL); 555 556 src_tmp.Rects[glyph_i].w = (stbrp_coord)(src_glyph.Info.Width + padding); 557 src_tmp.Rects[glyph_i].h = (stbrp_coord)(src_glyph.Info.Height + padding); 558 total_surface += src_tmp.Rects[glyph_i].w * src_tmp.Rects[glyph_i].h; 559 } 560 } 561 562 // We need a width for the skyline algorithm, any width! 563 // The exact width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height. 564 // User can override TexDesiredWidth and TexGlyphPadding if they wish, otherwise we use a simple heuristic to select the width based on expected surface. 565 const int surface_sqrt = (int)ImSqrt((float)total_surface) + 1; 566 atlas->TexHeight = 0; 567 if (atlas->TexDesiredWidth > 0) 568 atlas->TexWidth = atlas->TexDesiredWidth; 569 else 570 atlas->TexWidth = (surface_sqrt >= 4096 * 0.7f) ? 4096 : (surface_sqrt >= 2048 * 0.7f) ? 2048 : (surface_sqrt >= 1024 * 0.7f) ? 1024 : 512; 571 572 // 5. Start packing 573 // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values). 574 const int TEX_HEIGHT_MAX = 1024 * 32; 575 const int num_nodes_for_packing_algorithm = atlas->TexWidth - atlas->TexGlyphPadding; 576 ImVector<stbrp_node> pack_nodes; 577 pack_nodes.resize(num_nodes_for_packing_algorithm); 578 stbrp_context pack_context; 579 stbrp_init_target(&pack_context, atlas->TexWidth, TEX_HEIGHT_MAX, pack_nodes.Data, pack_nodes.Size); 580 ImFontAtlasBuildPackCustomRects(atlas, &pack_context); 581 582 // 6. Pack each source font. No rendering yet, we are working with rectangles in an infinitely tall texture at this point. 583 for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) 584 { 585 ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i]; 586 if (src_tmp.GlyphsCount == 0) 587 continue; 588 589 stbrp_pack_rects(&pack_context, src_tmp.Rects, src_tmp.GlyphsCount); 590 591 // Extend texture height and mark missing glyphs as non-packed so we won't render them. 592 // 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?) 593 for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++) 594 if (src_tmp.Rects[glyph_i].was_packed) 595 atlas->TexHeight = ImMax(atlas->TexHeight, src_tmp.Rects[glyph_i].y + src_tmp.Rects[glyph_i].h); 596 } 597 598 // 7. Allocate texture 599 atlas->TexHeight = (atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (atlas->TexHeight + 1) : ImUpperPowerOfTwo(atlas->TexHeight); 600 atlas->TexUvScale = ImVec2(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight); 601 if (src_load_color) 602 { 603 atlas->TexPixelsRGBA32 = (unsigned int*)IM_ALLOC(atlas->TexWidth * atlas->TexHeight * 4); 604 memset(atlas->TexPixelsRGBA32, 0, atlas->TexWidth * atlas->TexHeight * 4); 605 } 606 else 607 { 608 atlas->TexPixelsAlpha8 = (unsigned char*)IM_ALLOC(atlas->TexWidth * atlas->TexHeight); 609 memset(atlas->TexPixelsAlpha8, 0, atlas->TexWidth * atlas->TexHeight); 610 } 611 612 // 8. Copy rasterized font characters back into the main texture 613 // 9. Setup ImFont and glyphs for runtime 614 bool tex_use_colors = false; 615 for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) 616 { 617 ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i]; 618 if (src_tmp.GlyphsCount == 0) 619 continue; 620 621 // When merging fonts with MergeMode=true: 622 // - We can have multiple input fonts writing into a same destination font. 623 // - dst_font->ConfigData is != from cfg which is our source configuration. 624 ImFontConfig& cfg = atlas->ConfigData[src_i]; 625 ImFont* dst_font = cfg.DstFont; 626 627 const float ascent = src_tmp.Font.Info.Ascender; 628 const float descent = src_tmp.Font.Info.Descender; 629 ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent); 630 const float font_off_x = cfg.GlyphOffset.x; 631 const float font_off_y = cfg.GlyphOffset.y + IM_ROUND(dst_font->Ascent); 632 633 const int padding = atlas->TexGlyphPadding; 634 for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++) 635 { 636 ImFontBuildSrcGlyphFT& src_glyph = src_tmp.GlyphsList[glyph_i]; 637 stbrp_rect& pack_rect = src_tmp.Rects[glyph_i]; 638 IM_ASSERT(pack_rect.was_packed); 639 if (pack_rect.w == 0 && pack_rect.h == 0) 640 continue; 641 642 GlyphInfo& info = src_glyph.Info; 643 IM_ASSERT(info.Width + padding <= pack_rect.w); 644 IM_ASSERT(info.Height + padding <= pack_rect.h); 645 const int tx = pack_rect.x + padding; 646 const int ty = pack_rect.y + padding; 647 648 // Blit from temporary buffer to final texture 649 size_t blit_src_stride = (size_t)src_glyph.Info.Width; 650 size_t blit_dst_stride = (size_t)atlas->TexWidth; 651 unsigned int* blit_src = src_glyph.BitmapData; 652 if (atlas->TexPixelsAlpha8 != NULL) 653 { 654 unsigned char* blit_dst = atlas->TexPixelsAlpha8 + (ty * blit_dst_stride) + tx; 655 for (int y = 0; y < info.Height; y++, blit_dst += blit_dst_stride, blit_src += blit_src_stride) 656 for (int x = 0; x < info.Width; x++) 657 blit_dst[x] = (unsigned char)((blit_src[x] >> IM_COL32_A_SHIFT) & 0xFF); 658 } 659 else 660 { 661 unsigned int* blit_dst = atlas->TexPixelsRGBA32 + (ty * blit_dst_stride) + tx; 662 for (int y = 0; y < info.Height; y++, blit_dst += blit_dst_stride, blit_src += blit_src_stride) 663 for (int x = 0; x < info.Width; x++) 664 blit_dst[x] = blit_src[x]; 665 } 666 667 // Register glyph 668 float x0 = info.OffsetX + font_off_x; 669 float y0 = info.OffsetY + font_off_y; 670 float x1 = x0 + info.Width; 671 float y1 = y0 + info.Height; 672 float u0 = (tx) / (float)atlas->TexWidth; 673 float v0 = (ty) / (float)atlas->TexHeight; 674 float u1 = (tx + info.Width) / (float)atlas->TexWidth; 675 float v1 = (ty + info.Height) / (float)atlas->TexHeight; 676 dst_font->AddGlyph(&cfg, (ImWchar)src_glyph.Codepoint, x0, y0, x1, y1, u0, v0, u1, v1, info.AdvanceX); 677 678 ImFontGlyph* dst_glyph = &dst_font->Glyphs.back(); 679 IM_ASSERT(dst_glyph->Codepoint == src_glyph.Codepoint); 680 if (src_glyph.Info.IsColored) 681 dst_glyph->Colored = tex_use_colors = true; 682 } 683 684 src_tmp.Rects = NULL; 685 } 686 atlas->TexPixelsUseColors = tex_use_colors; 687 688 // Cleanup 689 for (int buf_i = 0; buf_i < buf_bitmap_buffers.Size; buf_i++) 690 IM_FREE(buf_bitmap_buffers[buf_i]); 691 for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) 692 src_tmp_array[src_i].~ImFontBuildSrcDataFT(); 693 694 ImFontAtlasBuildFinish(atlas); 695 696 return true; 697 } 698 699 // FreeType memory allocation callbacks 700 static void* FreeType_Alloc(FT_Memory /*memory*/, long size) 701 { 702 return GImGuiFreeTypeAllocFunc((size_t)size, GImGuiFreeTypeAllocatorUserData); 703 } 704 705 static void FreeType_Free(FT_Memory /*memory*/, void* block) 706 { 707 GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData); 708 } 709 710 static void* FreeType_Realloc(FT_Memory /*memory*/, long cur_size, long new_size, void* block) 711 { 712 // Implement realloc() as we don't ask user to provide it. 713 if (block == NULL) 714 return GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData); 715 716 if (new_size == 0) 717 { 718 GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData); 719 return NULL; 720 } 721 722 if (new_size > cur_size) 723 { 724 void* new_block = GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData); 725 memcpy(new_block, block, (size_t)cur_size); 726 GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData); 727 return new_block; 728 } 729 730 return block; 731 } 732 733 static bool ImFontAtlasBuildWithFreeType(ImFontAtlas* atlas) 734 { 735 // FreeType memory management: https://www.freetype.org/freetype2/docs/design/design-4.html 736 FT_MemoryRec_ memory_rec = {}; 737 memory_rec.user = NULL; 738 memory_rec.alloc = &FreeType_Alloc; 739 memory_rec.free = &FreeType_Free; 740 memory_rec.realloc = &FreeType_Realloc; 741 742 // https://www.freetype.org/freetype2/docs/reference/ft2-module_management.html#FT_New_Library 743 FT_Library ft_library; 744 FT_Error error = FT_New_Library(&memory_rec, &ft_library); 745 if (error != 0) 746 return false; 747 748 // If you don't call FT_Add_Default_Modules() the rest of code may work, but FreeType won't use our custom allocator. 749 FT_Add_Default_Modules(ft_library); 750 751 bool ret = ImFontAtlasBuildWithFreeTypeEx(ft_library, atlas, atlas->FontBuilderFlags); 752 FT_Done_Library(ft_library); 753 754 return ret; 755 } 756 757 const ImFontBuilderIO* ImGuiFreeType::GetBuilderForFreeType() 758 { 759 static ImFontBuilderIO io; 760 io.FontBuilder_Build = ImFontAtlasBuildWithFreeType; 761 return &io; 762 } 763 764 void ImGuiFreeType::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data) 765 { 766 GImGuiFreeTypeAllocFunc = alloc_func; 767 GImGuiFreeTypeFreeFunc = free_func; 768 GImGuiFreeTypeAllocatorUserData = user_data; 769 }