| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | |
| 10 | |
| 11 | |
| 12 | |
| 13 | |
| 14 | |
| 15 | |
| 16 | |
| 17 | |
| 18 | |
| 19 | |
| 20 | |
| 21 | |
| 22 | |
| 23 | |
| 24 | |
| 25 | |
| 26 | |
| 27 | |
| 28 | |
| 29 | |
| 30 | |
| 31 | |
| 32 | |
| 33 | |
| 34 | |
| 35 | |
| 36 | |
| 37 | |
| 38 | |
| 39 | |
| 40 | #include "imgui.h" |
| 41 | #ifndef IMGUI_DISABLE |
| 42 | #include "imgui_freetype.h" |
| 43 | #include "imgui_internal.h" |
| 44 | #include <stdint.h> |
| 45 | #include <ft2build.h> |
| 46 | #include FT_FREETYPE_H |
| 47 | #include FT_MODULE_H |
| 48 | #include FT_GLYPH_H |
| 49 | #include FT_SIZES_H |
| 50 | #include FT_SYNTHESIS_H |
| 51 | |
| 52 | |
| 53 | #if defined(IMGUI_ENABLE_FREETYPE_LUNASVG) && defined(IMGUI_ENABLE_FREETYPE_PLUTOSVG) |
| 54 | #error "Cannot enable both IMGUI_ENABLE_FREETYPE_LUNASVG and IMGUI_ENABLE_FREETYPE_PLUTOSVG" |
| 55 | #endif |
| 56 | #ifdef IMGUI_ENABLE_FREETYPE_LUNASVG |
| 57 | #include FT_OTSVG_H |
| 58 | #include FT_BBOX_H |
| 59 | #include <lunasvg.h> |
| 60 | #endif |
| 61 | #ifdef IMGUI_ENABLE_FREETYPE_PLUTOSVG |
| 62 | #include <plutosvg.h> |
| 63 | #endif |
| 64 | #if defined(IMGUI_ENABLE_FREETYPE_LUNASVG) || defined (IMGUI_ENABLE_FREETYPE_PLUTOSVG) |
| 65 | #if !((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12)) |
| 66 | #error IMGUI_ENABLE_FREETYPE_PLUTOSVG or IMGUI_ENABLE_FREETYPE_LUNASVG requires FreeType version >= 2.12 |
| 67 | #endif |
| 68 | #endif |
| 69 | |
| 70 | #ifdef _MSC_VER |
| 71 | #pragma warning (push) |
| 72 | #pragma warning (disable: 4505) |
| 73 | #pragma warning (disable: 26812) |
| 74 | #endif |
| 75 | |
| 76 | #ifdef __GNUC__ |
| 77 | #pragma GCC diagnostic push |
| 78 | #pragma GCC diagnostic ignored "-Wpragmas" |
| 79 | #pragma GCC diagnostic ignored "-Wunused-function" |
| 80 | #ifndef __clang__ |
| 81 | #pragma GCC diagnostic ignored "-Wsubobject-linkage" |
| 82 | #endif |
| 83 | #endif |
| 84 | |
| 85 | |
| 86 | |
| 87 | |
| 88 | |
| 89 | |
| 90 | static void* ImGuiFreeTypeDefaultAllocFunc(size_t size, void* user_data) { IM_UNUSED(user_data); return IM_ALLOC(size); } |
| 91 | static void ImGuiFreeTypeDefaultFreeFunc(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_FREE(ptr); } |
| 92 | |
| 93 | |
| 94 | static void* (*GImGuiFreeTypeAllocFunc)(size_t size, void* user_data) = ImGuiFreeTypeDefaultAllocFunc; |
| 95 | static void (*GImGuiFreeTypeFreeFunc)(void* ptr, void* user_data) = ImGuiFreeTypeDefaultFreeFunc; |
| 96 | static void* GImGuiFreeTypeAllocatorUserData = nullptr; |
| 97 | |
| 98 | |
| 99 | static ImFontLoader GImGuiFreeTypeLoader; |
| 100 | static char GImGuiFreeTypeLoaderName[48] = "FreeType"; |
| 101 | |
| 102 | |
| 103 | #ifdef IMGUI_ENABLE_FREETYPE_LUNASVG |
| 104 | static FT_Error ImGuiLunasvgPortInit(FT_Pointer* state); |
| 105 | static void ImGuiLunasvgPortFree(FT_Pointer* state); |
| 106 | static FT_Error ImGuiLunasvgPortRender(FT_GlyphSlot slot, FT_Pointer* _state); |
| 107 | static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_Pointer* _state); |
| 108 | #endif |
| 109 | |
| 110 | |
| 111 | |
| 112 | |
| 113 | |
| 114 | #define FT_CEIL(X) (((X + 63) & -64) / 64) |
| 115 | #define FT_SCALEFACTOR 64.0f |
| 116 | |
| 117 | |
| 118 | |
| 119 | |
| 120 | |
| 121 | |
| 122 | |
| 123 | |
| 124 | |
| 125 | |
| 126 | |
| 127 | |
| 128 | |
| 129 | |
| 130 | |
| 131 | |
| 132 | |
| 133 | |
| 134 | |
| 135 | |
| 136 | |
| 137 | |
| 138 | |
| 139 | |
| 140 | |
| 141 | |
| 142 | |
| 143 | |
| 144 | |
| 145 | |
| 146 | |
| 147 | |
| 148 | |
| 149 | struct ImGui_ImplFreeType_Data |
| 150 | { |
| 151 | FT_Library Library; |
| 152 | FT_MemoryRec_ MemoryManager; |
| 153 | char BackendName[48]; |
| 154 | ImGui_ImplFreeType_Data() { memset((void*)this, 0, sizeof(*this)); } |
| 155 | }; |
| 156 | |
| 157 | |
| 158 | struct ImGui_ImplFreeType_FontSrcData |
| 159 | { |
| 160 | |
| 161 | bool InitFont(FT_Library ft_library, const ImFontConfig* src, ImGuiFreeTypeLoaderFlags extra_user_flags); |
| 162 | void CloseFont(); |
| 163 | ImGui_ImplFreeType_FontSrcData() { memset((void*)this, 0, sizeof(*this)); } |
| 164 | ~ImGui_ImplFreeType_FontSrcData() { CloseFont(); } |
| 165 | |
| 166 | |
| 167 | FT_Face FtFace; |
| 168 | ImGuiFreeTypeLoaderFlags UserFlags; |
| 169 | FT_Int32 LoadFlags; |
| 170 | ImFontBaked* BakedLastActivated; |
| 171 | }; |
| 172 | |
| 173 | |
| 174 | struct ImGui_ImplFreeType_FontSrcBakedData |
| 175 | { |
| 176 | FT_Size FtSize; |
| 177 | ImGui_ImplFreeType_FontSrcBakedData() { memset((void*)this, 0, sizeof(*this)); } |
| 178 | }; |
| 179 | |
| 180 | bool ImGui_ImplFreeType_FontSrcData::InitFont(FT_Library ft_library, const ImFontConfig* src, ImGuiFreeTypeLoaderFlags extra_font_loader_flags) |
| 181 | { |
| 182 | FT_Error error = FT_New_Memory_Face(ft_library, (const FT_Byte*)src->FontData, (FT_Long)src->FontDataSize, (FT_Long)src->FontNo, &FtFace); |
| 183 | if (error != 0) |
| 184 | return false; |
| 185 | error = FT_Select_Charmap(FtFace, FT_ENCODING_UNICODE); |
| 186 | if (error != 0) |
| 187 | return false; |
| 188 | |
| 189 | |
| 190 | UserFlags = (ImGuiFreeTypeLoaderFlags)(src->FontLoaderFlags | extra_font_loader_flags); |
| 191 | |
| 192 | LoadFlags = 0; |
| 193 | if ((UserFlags & ImGuiFreeTypeLoaderFlags_Bitmap) == 0) |
| 194 | LoadFlags |= FT_LOAD_NO_BITMAP; |
| 195 | if (UserFlags & ImGuiFreeTypeLoaderFlags_NoHinting) |
| 196 | LoadFlags |= FT_LOAD_NO_HINTING; |
| 197 | if (UserFlags & ImGuiFreeTypeLoaderFlags_NoAutoHint) |
| 198 | LoadFlags |= FT_LOAD_NO_AUTOHINT; |
| 199 | if (UserFlags & ImGuiFreeTypeLoaderFlags_ForceAutoHint) |
| 200 | LoadFlags |= FT_LOAD_FORCE_AUTOHINT; |
| 201 | if (UserFlags & ImGuiFreeTypeLoaderFlags_LightHinting) |
| 202 | LoadFlags |= FT_LOAD_TARGET_LIGHT; |
| 203 | else if (UserFlags & ImGuiFreeTypeLoaderFlags_MonoHinting) |
| 204 | LoadFlags |= FT_LOAD_TARGET_MONO; |
| 205 | else |
| 206 | LoadFlags |= FT_LOAD_TARGET_NORMAL; |
| 207 | |
| 208 | if (UserFlags & ImGuiFreeTypeLoaderFlags_LoadColor) |
| 209 | LoadFlags |= FT_LOAD_COLOR; |
| 210 | |
| 211 | return true; |
| 212 | } |
| 213 | |
| 214 | void ImGui_ImplFreeType_FontSrcData::CloseFont() |
| 215 | { |
| 216 | if (FtFace) |
| 217 | { |
| 218 | FT_Done_Face(FtFace); |
| 219 | FtFace = nullptr; |
| 220 | } |
| 221 | } |
| 222 | |
| 223 | static const FT_Glyph_Metrics* ImGui_ImplFreeType_LoadGlyph(ImGui_ImplFreeType_FontSrcData* src_data, uint32_t codepoint) |
| 224 | { |
| 225 | uint32_t glyph_index = FT_Get_Char_Index(src_data->FtFace, codepoint); |
| 226 | if (glyph_index == 0) |
| 227 | return nullptr; |
| 228 | |
| 229 | |
| 230 | |
| 231 | |
| 232 | |
| 233 | |
| 234 | FT_Error error = FT_Load_Glyph(src_data->FtFace, glyph_index, src_data->LoadFlags); |
| 235 | if (error) |
| 236 | return nullptr; |
| 237 | |
| 238 | |
| 239 | FT_GlyphSlot slot = src_data->FtFace->glyph; |
| 240 | #if defined(IMGUI_ENABLE_FREETYPE_LUNASVG) || defined(IMGUI_ENABLE_FREETYPE_PLUTOSVG) |
| 241 | IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP || slot->format == FT_GLYPH_FORMAT_SVG); |
| 242 | #else |
| 243 | #if ((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12)) |
| 244 | IM_ASSERT(slot->format != FT_GLYPH_FORMAT_SVG && "The font contains SVG glyphs, you'll need to enable IMGUI_ENABLE_FREETYPE_PLUTOSVG or IMGUI_ENABLE_FREETYPE_LUNASVG in imconfig.h and install required libraries in order to use this font"); |
| 245 | #endif |
| 246 | IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP); |
| 247 | #endif |
| 248 | |
| 249 | |
| 250 | if (src_data->UserFlags & ImGuiFreeTypeLoaderFlags_Bold) |
| 251 | FT_GlyphSlot_Embolden(slot); |
| 252 | if (src_data->UserFlags & ImGuiFreeTypeLoaderFlags_Oblique) |
| 253 | { |
| 254 | FT_GlyphSlot_Oblique(slot); |
| 255 | |
| 256 | |
| 257 | |
| 258 | |
| 259 | } |
| 260 | |
| 261 | return &slot->metrics; |
| 262 | } |
| 263 | |
| 264 | static void ImGui_ImplFreeType_BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch) |
| 265 | { |
| 266 | IM_ASSERT(ft_bitmap != nullptr); |
| 267 | const uint32_t w = ft_bitmap->width; |
| 268 | const uint32_t h = ft_bitmap->rows; |
| 269 | const uint8_t* src = ft_bitmap->buffer; |
| 270 | const uint32_t src_pitch = ft_bitmap->pitch; |
| 271 | |
| 272 | switch (ft_bitmap->pixel_mode) |
| 273 | { |
| 274 | case FT_PIXEL_MODE_GRAY: |
| 275 | { |
| 276 | for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch) |
| 277 | for (uint32_t x = 0; x < w; x++) |
| 278 | dst[x] = IM_COL32(255, 255, 255, src[x]); |
| 279 | break; |
| 280 | } |
| 281 | case FT_PIXEL_MODE_MONO: |
| 282 | { |
| 283 | for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch) |
| 284 | { |
| 285 | uint8_t bits = 0; |
| 286 | const uint8_t* bits_ptr = src; |
| 287 | for (uint32_t x = 0; x < w; x++, bits <<= 1) |
| 288 | { |
| 289 | if ((x & 7) == 0) |
| 290 | bits = *bits_ptr++; |
| 291 | dst[x] = IM_COL32(255, 255, 255, (bits & 0x80) ? 255 : 0); |
| 292 | } |
| 293 | } |
| 294 | break; |
| 295 | } |
| 296 | case FT_PIXEL_MODE_BGRA: |
| 297 | { |
| 298 | |
| 299 | #define DE_MULTIPLY(color, alpha) ImMin((ImU32)(255.0f * (float)color / (float)(alpha + FLT_MIN) + 0.5f), 255u) |
| 300 | for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch) |
| 301 | for (uint32_t x = 0; x < w; x++) |
| 302 | { |
| 303 | uint8_t r = src[x * 4 + 2], g = src[x * 4 + 1], b = src[x * 4], a = src[x * 4 + 3]; |
| 304 | dst[x] = IM_COL32(DE_MULTIPLY(r, a), DE_MULTIPLY(g, a), DE_MULTIPLY(b, a), a); |
| 305 | } |
| 306 | #undef DE_MULTIPLY |
| 307 | break; |
| 308 | } |
| 309 | default: |
| 310 | IM_ASSERT(0 && "FreeTypeFont::BlitGlyph(): Unknown bitmap pixel mode!"); |
| 311 | } |
| 312 | } |
| 313 | |
| 314 | |
| 315 | static void* FreeType_Alloc(FT_Memory , long size) |
| 316 | { |
| 317 | return GImGuiFreeTypeAllocFunc((size_t)size, GImGuiFreeTypeAllocatorUserData); |
| 318 | } |
| 319 | |
| 320 | static void FreeType_Free(FT_Memory , void* block) |
| 321 | { |
| 322 | GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData); |
| 323 | } |
| 324 | |
| 325 | static void* FreeType_Realloc(FT_Memory , long cur_size, long new_size, void* block) |
| 326 | { |
| 327 | |
| 328 | if (block == nullptr) |
| 329 | return GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData); |
| 330 | |
| 331 | if (new_size == 0) |
| 332 | { |
| 333 | GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData); |
| 334 | return nullptr; |
| 335 | } |
| 336 | |
| 337 | if (new_size > cur_size) |
| 338 | { |
| 339 | void* new_block = GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData); |
| 340 | memcpy(new_block, block, (size_t)cur_size); |
| 341 | GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData); |
| 342 | return new_block; |
| 343 | } |
| 344 | |
| 345 | return block; |
| 346 | } |
| 347 | |
| 348 | static bool ImGui_ImplFreeType_LoaderInit(ImFontAtlas* atlas) |
| 349 | { |
| 350 | IM_ASSERT(atlas->FontLoaderData == nullptr); |
| 351 | ImGui_ImplFreeType_Data* bd = IM_NEW(ImGui_ImplFreeType_Data)(); |
| 352 | |
| 353 | |
| 354 | bd->MemoryManager.user = nullptr; |
| 355 | bd->MemoryManager.alloc = &FreeType_Alloc; |
| 356 | bd->MemoryManager.free = &FreeType_Free; |
| 357 | bd->MemoryManager.realloc = &FreeType_Realloc; |
| 358 | |
| 359 | |
| 360 | FT_Error error = FT_New_Library(&bd->MemoryManager, &bd->Library); |
| 361 | if (error != 0) |
| 362 | { |
| 363 | IM_DELETE(bd); |
| 364 | return false; |
| 365 | } |
| 366 | |
| 367 | |
| 368 | FT_Int ver_linked_major, ver_linked_minor, ver_linked_patch; |
| 369 | FT_Library_Version(bd->Library, &ver_linked_major, &ver_linked_minor, &ver_linked_patch); |
| 370 | snprintf(GImGuiFreeTypeLoaderName, sizeof(GImGuiFreeTypeLoaderName), "FreeType (%d.%d.%d; %d.%d.%d)", FREETYPE_MAJOR, FREETYPE_MINOR, FREETYPE_PATCH, ver_linked_major, ver_linked_minor, ver_linked_patch); |
| 371 | |
| 372 | |
| 373 | FT_Add_Default_Modules(bd->Library); |
| 374 | |
| 375 | #ifdef IMGUI_ENABLE_FREETYPE_LUNASVG |
| 376 | |
| 377 | |
| 378 | |
| 379 | SVG_RendererHooks hooks = { ImGuiLunasvgPortInit, ImGuiLunasvgPortFree, ImGuiLunasvgPortRender, ImGuiLunasvgPortPresetSlot }; |
| 380 | FT_Property_Set(bd->Library, "ot-svg", "svg-hooks", &hooks); |
| 381 | #endif |
| 382 | #ifdef IMGUI_ENABLE_FREETYPE_PLUTOSVG |
| 383 | |
| 384 | FT_Property_Set(bd->Library, "ot-svg", "svg-hooks", plutosvg_ft_svg_hooks()); |
| 385 | #endif |
| 386 | |
| 387 | |
| 388 | atlas->FontLoaderData = (void*)bd; |
| 389 | |
| 390 | return true; |
| 391 | } |
| 392 | |
| 393 | static void ImGui_ImplFreeType_LoaderShutdown(ImFontAtlas* atlas) |
| 394 | { |
| 395 | ImGui_ImplFreeType_Data* bd = (ImGui_ImplFreeType_Data*)atlas->FontLoaderData; |
| 396 | IM_ASSERT(bd != nullptr); |
| 397 | FT_Done_Library(bd->Library); |
| 398 | IM_DELETE(bd); |
| 399 | atlas->FontLoaderData = nullptr; |
| 400 | } |
| 401 | |
| 402 | static bool ImGui_ImplFreeType_FontSrcInit(ImFontAtlas* atlas, ImFontConfig* src) |
| 403 | { |
| 404 | ImGui_ImplFreeType_Data* bd = (ImGui_ImplFreeType_Data*)atlas->FontLoaderData; |
| 405 | ImGui_ImplFreeType_FontSrcData* bd_font_data = IM_NEW(ImGui_ImplFreeType_FontSrcData); |
| 406 | IM_ASSERT(src->FontLoaderData == nullptr); |
| 407 | src->FontLoaderData = bd_font_data; |
| 408 | |
| 409 | if (!bd_font_data->InitFont(bd->Library, src, (ImGuiFreeTypeLoaderFlags)atlas->FontLoaderFlags)) |
| 410 | { |
| 411 | IM_DELETE(bd_font_data); |
| 412 | src->FontLoaderData = nullptr; |
| 413 | return false; |
| 414 | } |
| 415 | |
| 416 | return true; |
| 417 | } |
| 418 | |
| 419 | static void ImGui_ImplFreeType_FontSrcDestroy(ImFontAtlas* atlas, ImFontConfig* src) |
| 420 | { |
| 421 | IM_UNUSED(atlas); |
| 422 | ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData; |
| 423 | IM_DELETE(bd_font_data); |
| 424 | src->FontLoaderData = nullptr; |
| 425 | } |
| 426 | |
| 427 | static bool ImGui_ImplFreeType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src) |
| 428 | { |
| 429 | IM_UNUSED(atlas); |
| 430 | float size = baked->Size; |
| 431 | const float ref_size = baked->OwnerFont->Sources[0]->SizePixels; |
| 432 | if (src->MergeMode && src->SizePixels != 0.0f) |
| 433 | size *= (src->SizePixels / ref_size); |
| 434 | size *= src->ExtraSizeScale; |
| 435 | |
| 436 | ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData; |
| 437 | bd_font_data->BakedLastActivated = baked; |
| 438 | |
| 439 | |
| 440 | ImGui_ImplFreeType_FontSrcBakedData* bd_baked_data = (ImGui_ImplFreeType_FontSrcBakedData*)loader_data_for_baked_src; |
| 441 | IM_ASSERT(bd_baked_data != nullptr); |
| 442 | IM_PLACEMENT_NEW(bd_baked_data) ImGui_ImplFreeType_FontSrcBakedData(); |
| 443 | |
| 444 | FT_New_Size(bd_font_data->FtFace, &bd_baked_data->FtSize); |
| 445 | FT_Activate_Size(bd_baked_data->FtSize); |
| 446 | |
| 447 | |
| 448 | |
| 449 | |
| 450 | |
| 451 | const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity; |
| 452 | FT_Size_RequestRec req; |
| 453 | req.type = (bd_font_data->UserFlags & ImGuiFreeTypeLoaderFlags_Bitmap) ? FT_SIZE_REQUEST_TYPE_NOMINAL : FT_SIZE_REQUEST_TYPE_REAL_DIM; |
| 454 | req.width = 0; |
| 455 | req.height = (uint32_t)(size * 64 * rasterizer_density); |
| 456 | req.horiResolution = 0; |
| 457 | req.vertResolution = 0; |
| 458 | FT_Request_Size(bd_font_data->FtFace, &req); |
| 459 | |
| 460 | |
| 461 | if (src->MergeMode == false) |
| 462 | { |
| 463 | |
| 464 | FT_Size_Metrics metrics = bd_baked_data->FtSize->metrics; |
| 465 | const float scale = 1.0f / (rasterizer_density * src->ExtraSizeScale); |
| 466 | baked->Ascent = (float)FT_CEIL(metrics.ascender) * scale; |
| 467 | baked->Descent = (float)FT_CEIL(metrics.descender) * scale; |
| 468 | |
| 469 | |
| 470 | |
| 471 | } |
| 472 | return true; |
| 473 | } |
| 474 | |
| 475 | static void ImGui_ImplFreeType_FontBakedDestroy(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src) |
| 476 | { |
| 477 | IM_UNUSED(atlas); |
| 478 | IM_UNUSED(baked); |
| 479 | IM_UNUSED(src); |
| 480 | ImGui_ImplFreeType_FontSrcBakedData* bd_baked_data = (ImGui_ImplFreeType_FontSrcBakedData*)loader_data_for_baked_src; |
| 481 | IM_ASSERT(bd_baked_data != nullptr); |
| 482 | FT_Done_Size(bd_baked_data->FtSize); |
| 483 | bd_baked_data->~ImGui_ImplFreeType_FontSrcBakedData(); |
| 484 | } |
| 485 | |
| 486 | static bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph, float* out_advance_x) |
| 487 | { |
| 488 | ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData; |
| 489 | uint32_t glyph_index = FT_Get_Char_Index(bd_font_data->FtFace, codepoint); |
| 490 | if (glyph_index == 0) |
| 491 | return false; |
| 492 | |
| 493 | if (bd_font_data->BakedLastActivated != baked) |
| 494 | { |
| 495 | |
| 496 | ImGui_ImplFreeType_FontSrcBakedData* bd_baked_data = (ImGui_ImplFreeType_FontSrcBakedData*)loader_data_for_baked_src; |
| 497 | FT_Activate_Size(bd_baked_data->FtSize); |
| 498 | bd_font_data->BakedLastActivated = baked; |
| 499 | } |
| 500 | |
| 501 | const FT_Glyph_Metrics* metrics = ImGui_ImplFreeType_LoadGlyph(bd_font_data, codepoint); |
| 502 | if (metrics == nullptr) |
| 503 | return false; |
| 504 | |
| 505 | FT_Face face = bd_font_data->FtFace; |
| 506 | FT_GlyphSlot slot = face->glyph; |
| 507 | const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity; |
| 508 | |
| 509 | |
| 510 | const float advance_x = (slot->advance.x / FT_SCALEFACTOR) / rasterizer_density; |
| 511 | if (out_advance_x != NULL) |
| 512 | { |
| 513 | IM_ASSERT(out_glyph == NULL); |
| 514 | *out_advance_x = advance_x; |
| 515 | return true; |
| 516 | } |
| 517 | |
| 518 | |
| 519 | FT_Render_Mode render_mode = (bd_font_data->UserFlags & ImGuiFreeTypeLoaderFlags_Monochrome) ? FT_RENDER_MODE_MONO : FT_RENDER_MODE_NORMAL; |
| 520 | FT_Error error = FT_Render_Glyph(slot, render_mode); |
| 521 | const FT_Bitmap* ft_bitmap = &slot->bitmap; |
| 522 | if (error != 0 || ft_bitmap == nullptr) |
| 523 | return false; |
| 524 | |
| 525 | const int w = (int)ft_bitmap->width; |
| 526 | const int h = (int)ft_bitmap->rows; |
| 527 | const bool is_visible = (w != 0 && h != 0); |
| 528 | |
| 529 | |
| 530 | out_glyph->Codepoint = codepoint; |
| 531 | out_glyph->AdvanceX = advance_x; |
| 532 | |
| 533 | |
| 534 | if (is_visible) |
| 535 | { |
| 536 | ImFontAtlasRectId pack_id = ImFontAtlasPackAddRect(atlas, w, h); |
| 537 | if (pack_id == ImFontAtlasRectId_Invalid) |
| 538 | { |
| 539 | |
| 540 | IM_ASSERT(pack_id != ImFontAtlasRectId_Invalid && "Out of texture memory."); |
| 541 | return false; |
| 542 | } |
| 543 | ImTextureRect* r = ImFontAtlasPackGetRect(atlas, pack_id); |
| 544 | |
| 545 | |
| 546 | atlas->Builder->TempBuffer.resize(w * h * 4); |
| 547 | uint32_t* temp_buffer = (uint32_t*)atlas->Builder->TempBuffer.Data; |
| 548 | ImGui_ImplFreeType_BlitGlyph(ft_bitmap, temp_buffer, w); |
| 549 | |
| 550 | const float ref_size = baked->OwnerFont->Sources[0]->SizePixels; |
| 551 | const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f; |
| 552 | float font_off_x = ImFloor(src->GlyphOffset.x * offsets_scale + 0.5f); |
| 553 | float font_off_y = ImFloor(src->GlyphOffset.y * offsets_scale + 0.5f) + baked->Ascent; |
| 554 | float recip_h = 1.0f / rasterizer_density; |
| 555 | float recip_v = 1.0f / rasterizer_density; |
| 556 | |
| 557 | |
| 558 | float glyph_off_x = (float)face->glyph->bitmap_left; |
| 559 | float glyph_off_y = (float)-face->glyph->bitmap_top; |
| 560 | out_glyph->X0 = glyph_off_x * recip_h + font_off_x; |
| 561 | out_glyph->Y0 = glyph_off_y * recip_v + font_off_y; |
| 562 | out_glyph->X1 = (glyph_off_x + w) * recip_h + font_off_x; |
| 563 | out_glyph->Y1 = (glyph_off_y + h) * recip_v + font_off_y; |
| 564 | out_glyph->Visible = true; |
| 565 | out_glyph->Colored = (ft_bitmap->pixel_mode == FT_PIXEL_MODE_BGRA); |
| 566 | out_glyph->PackId = pack_id; |
| 567 | ImFontAtlasBakedSetFontGlyphBitmap(atlas, baked, src, out_glyph, r, (const unsigned char*)temp_buffer, ImTextureFormat_RGBA32, w * 4); |
| 568 | } |
| 569 | |
| 570 | return true; |
| 571 | } |
| 572 | |
| 573 | static bool ImGui_ImplFreetype_FontSrcContainsGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImWchar codepoint) |
| 574 | { |
| 575 | IM_UNUSED(atlas); |
| 576 | ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData; |
| 577 | int glyph_index = FT_Get_Char_Index(bd_font_data->FtFace, codepoint); |
| 578 | return glyph_index != 0; |
| 579 | } |
| 580 | |
| 581 | const ImFontLoader* ImGuiFreeType::GetFontLoader() |
| 582 | { |
| 583 | ImFontLoader* loader = &GImGuiFreeTypeLoader; |
| 584 | loader->Name = GImGuiFreeTypeLoaderName; |
| 585 | loader->LoaderInit = ImGui_ImplFreeType_LoaderInit; |
| 586 | loader->LoaderShutdown = ImGui_ImplFreeType_LoaderShutdown; |
| 587 | loader->FontSrcInit = ImGui_ImplFreeType_FontSrcInit; |
| 588 | loader->FontSrcDestroy = ImGui_ImplFreeType_FontSrcDestroy; |
| 589 | loader->FontSrcContainsGlyph = ImGui_ImplFreetype_FontSrcContainsGlyph; |
| 590 | loader->FontBakedInit = ImGui_ImplFreeType_FontBakedInit; |
| 591 | loader->FontBakedDestroy = ImGui_ImplFreeType_FontBakedDestroy; |
| 592 | loader->FontBakedLoadGlyph = ImGui_ImplFreeType_FontBakedLoadGlyph; |
| 593 | loader->FontBakedSrcLoaderDataSize = sizeof(ImGui_ImplFreeType_FontSrcBakedData); |
| 594 | return loader; |
| 595 | } |
| 596 | |
| 597 | void ImGuiFreeType::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data) |
| 598 | { |
| 599 | GImGuiFreeTypeAllocFunc = alloc_func; |
| 600 | GImGuiFreeTypeFreeFunc = free_func; |
| 601 | GImGuiFreeTypeAllocatorUserData = user_data; |
| 602 | } |
| 603 | |
| 604 | bool ImGuiFreeType::DebugEditFontLoaderFlags(unsigned int* p_font_loader_flags) |
| 605 | { |
| 606 | bool edited = false; |
| 607 | edited |= ImGui::CheckboxFlags("NoHinting", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_NoHinting); |
| 608 | edited |= ImGui::CheckboxFlags("NoAutoHint", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_NoAutoHint); |
| 609 | edited |= ImGui::CheckboxFlags("ForceAutoHint",p_font_loader_flags, ImGuiFreeTypeLoaderFlags_ForceAutoHint); |
| 610 | edited |= ImGui::CheckboxFlags("LightHinting", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_LightHinting); |
| 611 | edited |= ImGui::CheckboxFlags("MonoHinting", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_MonoHinting); |
| 612 | edited |= ImGui::CheckboxFlags("Bold", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_Bold); |
| 613 | edited |= ImGui::CheckboxFlags("Oblique", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_Oblique); |
| 614 | edited |= ImGui::CheckboxFlags("Monochrome", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_Monochrome); |
| 615 | edited |= ImGui::CheckboxFlags("LoadColor", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_LoadColor); |
| 616 | edited |= ImGui::CheckboxFlags("Bitmap", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_Bitmap); |
| 617 | return edited; |
| 618 | } |
| 619 | |
| 620 | #ifdef IMGUI_ENABLE_FREETYPE_LUNASVG |
| 621 | |
| 622 | |
| 623 | struct LunasvgPortState |
| 624 | { |
| 625 | FT_Error err = FT_Err_Ok; |
| 626 | lunasvg::Matrix matrix; |
| 627 | std::unique_ptr<lunasvg::Document> svg = nullptr; |
| 628 | }; |
| 629 | |
| 630 | static FT_Error ImGuiLunasvgPortInit(FT_Pointer* _state) |
| 631 | { |
| 632 | *_state = IM_NEW(LunasvgPortState)(); |
| 633 | return FT_Err_Ok; |
| 634 | } |
| 635 | |
| 636 | static void ImGuiLunasvgPortFree(FT_Pointer* _state) |
| 637 | { |
| 638 | IM_DELETE(*(LunasvgPortState**)_state); |
| 639 | } |
| 640 | |
| 641 | static FT_Error ImGuiLunasvgPortRender(FT_GlyphSlot slot, FT_Pointer* _state) |
| 642 | { |
| 643 | LunasvgPortState* state = *(LunasvgPortState**)_state; |
| 644 | |
| 645 | |
| 646 | if (state->err != FT_Err_Ok) |
| 647 | return state->err; |
| 648 | |
| 649 | |
| 650 | lunasvg::Bitmap bitmap((uint8_t*)slot->bitmap.buffer, slot->bitmap.width, slot->bitmap.rows, slot->bitmap.pitch); |
| 651 | #if LUNASVG_VERSION_MAJOR >= 3 |
| 652 | state->svg->render(bitmap, state->matrix); |
| 653 | #else |
| 654 | state->svg->setMatrix(state->svg->matrix().identity()); |
| 655 | state->svg->render(bitmap, state->matrix); |
| 656 | #endif |
| 657 | state->err = FT_Err_Ok; |
| 658 | return state->err; |
| 659 | } |
| 660 | |
| 661 | static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_Pointer* _state) |
| 662 | { |
| 663 | FT_SVG_Document document = (FT_SVG_Document)slot->other; |
| 664 | LunasvgPortState* state = *(LunasvgPortState**)_state; |
| 665 | FT_Size_Metrics& metrics = document->metrics; |
| 666 | |
| 667 | |
| 668 | |
| 669 | if (cache) |
| 670 | return state->err; |
| 671 | |
| 672 | state->svg = lunasvg::Document::loadFromData((const char*)document->svg_document, document->svg_document_length); |
| 673 | if (state->svg == nullptr) |
| 674 | { |
| 675 | state->err = FT_Err_Invalid_SVG_Document; |
| 676 | return state->err; |
| 677 | } |
| 678 | |
| 679 | #if LUNASVG_VERSION_MAJOR >= 3 |
| 680 | lunasvg::Box box = state->svg->boundingBox(); |
| 681 | #else |
| 682 | lunasvg::Box box = state->svg->box(); |
| 683 | #endif |
| 684 | double scale = std::min(metrics.x_ppem / box.w, metrics.y_ppem / box.h); |
| 685 | double xx = (double)document->transform.xx / (1 << 16); |
| 686 | double xy = -(double)document->transform.xy / (1 << 16); |
| 687 | double yx = -(double)document->transform.yx / (1 << 16); |
| 688 | double yy = (double)document->transform.yy / (1 << 16); |
| 689 | double x0 = (double)document->delta.x / 64 * box.w / metrics.x_ppem; |
| 690 | double y0 = -(double)document->delta.y / 64 * box.h / metrics.y_ppem; |
| 691 | |
| 692 | #if LUNASVG_VERSION_MAJOR >= 3 |
| 693 | |
| 694 | state->matrix = lunasvg::Matrix::translated(-box.x, -box.y); |
| 695 | state->matrix.multiply(lunasvg::Matrix(xx, xy, yx, yy, x0, y0)); |
| 696 | state->matrix.scale(scale, scale); |
| 697 | |
| 698 | |
| 699 | box.transform(state->matrix); |
| 700 | #else |
| 701 | |
| 702 | state->matrix.identity(); |
| 703 | state->matrix.scale(scale, scale); |
| 704 | state->matrix.transform(xx, xy, yx, yy, x0, y0); |
| 705 | state->svg->setMatrix(state->matrix); |
| 706 | |
| 707 | |
| 708 | state->matrix.translate(-box.x, -box.y); |
| 709 | |
| 710 | |
| 711 | box = state->svg->box(); |
| 712 | #endif |
| 713 | |
| 714 | |
| 715 | slot->bitmap_left = FT_Int(box.x); |
| 716 | slot->bitmap_top = FT_Int(-box.y); |
| 717 | slot->bitmap.rows = (unsigned int)(ImCeil((float)box.h)); |
| 718 | slot->bitmap.width = (unsigned int)(ImCeil((float)box.w)); |
| 719 | slot->bitmap.pitch = slot->bitmap.width * 4; |
| 720 | slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA; |
| 721 | |
| 722 | |
| 723 | double metrics_width = box.w; |
| 724 | double metrics_height = box.h; |
| 725 | double horiBearingX = box.x; |
| 726 | double horiBearingY = -box.y; |
| 727 | double vertBearingX = slot->metrics.horiBearingX / 64.0 - slot->metrics.horiAdvance / 64.0 / 2.0; |
| 728 | double vertBearingY = (slot->metrics.vertAdvance / 64.0 - slot->metrics.height / 64.0) / 2.0; |
| 729 | slot->metrics.width = FT_Pos(IM_ROUND(metrics_width * 64.0)); |
| 730 | slot->metrics.height = FT_Pos(IM_ROUND(metrics_height * 64.0)); |
| 731 | slot->metrics.horiBearingX = FT_Pos(horiBearingX * 64); |
| 732 | slot->metrics.horiBearingY = FT_Pos(horiBearingY * 64); |
| 733 | slot->metrics.vertBearingX = FT_Pos(vertBearingX * 64); |
| 734 | slot->metrics.vertBearingY = FT_Pos(vertBearingY * 64); |
| 735 | |
| 736 | if (slot->metrics.vertAdvance == 0) |
| 737 | slot->metrics.vertAdvance = FT_Pos(metrics_height * 1.2 * 64.0); |
| 738 | |
| 739 | state->err = FT_Err_Ok; |
| 740 | return state->err; |
| 741 | } |
| 742 | |
| 743 | #endif |
| 744 | |
| 745 | |
| 746 | |
| 747 | #ifdef __GNUC__ |
| 748 | #pragma GCC diagnostic pop |
| 749 | #endif |
| 750 | |
| 751 | #ifdef _MSC_VER |
| 752 | #pragma warning (pop) |
| 753 | #endif |
| 754 | |
| 755 | #endif |