Unravel Engine C++ Reference
Loading...
Searching...
No Matches
font_manager.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2013 Jeremie Roy. All rights reserved.
3 * License: https://github.com/bkaradzic/bgfx/blob/master/LICENSE
4 */
5
6#include <bx/bx.h>
7#define STB_TRUETYPE_IMPLEMENTATION
8#include "../common.h"
9#include <bgfx/bgfx.h>
10#include <stb/stb_truetype.h>
11
12BX_PRAGMA_DIAGNOSTIC_PUSH()
14 4244) // warning C4244: '=': conversion from 'double' to 'float', possible loss of data
15BX_PRAGMA_DIAGNOSTIC_IGNORED_MSVC(4701) // warning C4701: potentially uninitialized local variable 'pt' used
16#define SDF_IMPLEMENTATION
17#include "sdf.h"
18BX_PRAGMA_DIAGNOSTIC_POP()
19
20#include <wchar.h> // wcslen
21
22#include <tinystl/allocator.h>
23#include <tinystl/unordered_map.h>
24#include <tinystl/vector.h>
25namespace stl = tinystl;
26
27#include "../cube_atlas.h"
28#include "font_manager.h"
29#include "utf8.h"
30
31namespace gfx
32{
33
34#define MAX_FONT_BUFFER_SIZE (512 * 4 * 512 * 4 * 4)
35
37{
38 font_profile_scope(const char* func) : func_(func)
39 {
40 }
41
43 {
44 int64_t now = bx::getHPCounter();
45 int64_t frameTime = now - start;
46
47 double freq = double(bx::getHPFrequency());
48
49 // seconds:
50 float deltaTimeS = float(frameTime / freq);
51
52 // microseconds:
53 float deltaTimeUs = float(frameTime * 1e6 / freq);
54
55 bx::printf("--------------------------------------\n");
56 bx::printf("%s took %.2f us\n", func_, deltaTimeUs);
57 bx::printf("--------------------------------------\n");
58 }
59
60private:
61 int64_t start = bx::getHPCounter();
62 const char* func_{};
63};
64
66{
67public:
71 auto init(const uint8_t* buffer,
72 uint32_t buffer_size,
73 int32_t font_index,
74 uint32_t pixel_height,
75 int16_t width_padding,
76 int16_t height_padding) -> bool;
77
79 auto get_font_info() const -> font_info;
80
84 auto bake_glyph_alpha(code_point codepoint, glyph_info& out_info, uint8_t* out_buffer) -> bool;
85
89 auto bake_glyph_distance(code_point codepoint, glyph_info& out_info, uint8_t* out_buffer) -> bool;
90 auto bake_glyph_distance_fast(code_point codepoint, glyph_info& out_info, uint8_t* out_buffer) -> bool;
91 auto bake_glyph_distance_stb(code_point codepoint, glyph_info& out_info, uint8_t* out_buffer) -> bool;
92
93private:
94 friend class font_manager;
95
96 stbtt_fontinfo font_;
97 float scale_{};
98
99 int16_t width_padding_{};
100 int16_t height_padding_{};
101
102 int quality_{};
103
104 stl::vector<uint8_t> scratch_buffer_;
105 stl::vector<uint8_t> alpha_scratch_;
106};
107
108auto true_type_font::init(const uint8_t* buffer,
109 uint32_t buffer_size,
110 int32_t font_index,
111 uint32_t pixel_height,
112 int16_t width_padding,
113 int16_t height_padding) -> bool
114{
115 BX_WARN((buffer_size > 256 && buffer_size < 100000000),
116 "(FontIndex %d) TrueType buffer size is suspicious (%d)",
117 font_index,
118 buffer_size);
119 BX_WARN((pixel_height > 4 && pixel_height < 128),
120 "(FontIndex %d) TrueType pixel height is suspicious (%d)",
121 font_index,
122 pixel_height);
123 BX_UNUSED(buffer_size);
124
125 int fonts = stbtt_GetNumberOfFonts(buffer);
126
127 BX_WARN(fonts >= font_index, "(FontIndex %d) TrueType is not in range [0 - %d]", font_index, fonts);
128
129 int offset = stbtt_GetFontOffsetForIndex(buffer, font_index);
130
131 stbtt_InitFont(&font_, buffer, offset);
132
133 float rasterizer_density = 1.0f;
134 float pixel_height_f = float(pixel_height) * rasterizer_density;
135 scale_ = (pixel_height > 0) ? stbtt_ScaleForPixelHeight(&font_, pixel_height_f)
136 : stbtt_ScaleForMappingEmToPixels(&font_, -pixel_height_f);
137
138 width_padding_ = width_padding;
139 height_padding_ = height_padding;
140 return true;
141}
143{
144 int ascent{};
145 int descent{};
146 int lineGap{};
147 stbtt_GetFontVMetrics(&font_, &ascent, &descent, &lineGap);
148
149 float scale = scale_;
150
151 int x0{}, y0{}, x1{}, y1{};
152 stbtt_GetFontBoundingBox(&font_, &x0, &y0, &x1, &y1);
153
154 font_info info;
155 info.scale = 1.0f;
156 info.ascender = bx::round(float(ascent) * scale);
157 info.descender = bx::round(float(descent) * scale);
158 info.line_gap = bx::round(float(lineGap) * scale);
159 info.max_advance_width = bx::round(float(y1 - y0) * scale);
160
161 info.capline = float(ascent);
162
163 int caps[] = {'H', 'I'};
164 for(int codepoint : caps)
165 {
166 int x0{}, x1{}, y0{}, y1{};
167 if(stbtt_GetCodepointBox(&font_, codepoint, &x0, &y0, &x1, &y1))
168 {
169 info.capline = bx::floor(float(y1) * scale);
170 break;
171 }
172 }
173
174 info.xline = info.capline * 0.5f;
175
176 int xh[] = {'x', 'z'};
177 for(auto codepoint : xh)
178 {
179 int x0{}, x1{}, y0{}, y1{};
180 if(stbtt_GetCodepointBox(&font_, codepoint, &x0, &y0, &x1, &y1))
181 {
182 info.xline = bx::floor(float(y1) * scale);
183 break;
184 }
185 }
186
187 info.underline_position = float(x1 - x0) * scale - float(ascent);
188 info.underline_thickness = float(x1 - x0) * scale / 24.f;
189 return info;
190}
191
192auto true_type_font::bake_glyph_alpha(code_point codepoint, glyph_info& out_info, uint8_t* out_buffer) -> bool
193{
194 // 1) Get vertical font metrics.
195 int ascent{}, descent{}, line_gap{};
196 stbtt_GetFontVMetrics(&font_, &ascent, &descent, &line_gap);
197
198 // 2) Get horizontal metrics for this glyph.
199 int advance{}, lsb{};
200 stbtt_GetCodepointHMetrics(&font_, codepoint, &advance, &lsb);
201
202 // 3) Compute un-padded bitmap bounding box.
203 const float scale = scale_;
204 int x0{}, y0{}, x1{}, y1{};
205 stbtt_GetCodepointBitmapBox(&font_, codepoint, scale, scale, &x0, &y0, &x1, &y1);
206
207 const int ww = x1 - x0;
208 const int hh = y1 - y0;
209
210 // 4) Record un-padded glyph info.
211 out_info.offset_x = float(x0);
212 out_info.offset_y = float(y0);
213 out_info.width = float(ww);
214 out_info.height = float(hh);
215 out_info.advance_x = bx::round(float(advance) * scale);
216 out_info.advance_y = bx::round(float(ascent - descent + line_gap) * scale);
217
218 // 5) Render the grayscale bitmap into out_buffer.
219 // dst_pitch = bytes per row = glyph width.
220 int bpp = 1;
221 int dst_pitch = ww * bpp;
222 stbtt_MakeCodepointBitmap(&font_, out_buffer, ww, hh, dst_pitch, scale, scale, codepoint);
223
224 return ww * hh > 0;
225}
226
227auto true_type_font::bake_glyph_distance(code_point codepoint, glyph_info& out_info, uint8_t* out_buffer) -> bool
228{
229 if(quality_ == 0)
230 {
231 return bake_glyph_distance_fast(codepoint, out_info, out_buffer);
232 }
233
234 return bake_glyph_distance_stb(codepoint, out_info, out_buffer);
235}
236
237auto true_type_font::bake_glyph_distance_fast(code_point codepoint, glyph_info& out_info, uint8_t* out_buffer) -> bool
238{
239 // 1) Get vertical font metrics.
240 int ascent{}, descent{}, line_gap{};
241 stbtt_GetFontVMetrics(&font_, &ascent, &descent, &line_gap);
242
243 // 2) Get horizontal metrics for this glyph.
244 int advance{}, lsb{};
245 stbtt_GetCodepointHMetrics(&font_, codepoint, &advance, &lsb);
246
247 // 3) Compute un-padded bitmap bounding box.
248 const float scale = scale_;
249 int x0{}, y0{}, x1{}, y1{};
250 stbtt_GetCodepointBitmapBox(&font_, codepoint, scale, scale, &x0, &y0, &x1, &y1);
251
252 const int ww = x1 - x0;
253 const int hh = y1 - y0;
254
255 // 4) Record un-padded glyph info.
256 out_info.offset_x = float(x0);
257 out_info.offset_y = float(y0);
258 out_info.width = float(ww);
259 out_info.height = float(hh);
260 out_info.advance_x = bx::round(float(advance) * scale);
261 out_info.advance_y = bx::round(float(ascent - descent + line_gap) * scale);
262
263 // 5) Render the grayscale bitmap into out_buffer.
264 // dst_pitch = bytes per row = glyph width.
265 int bpp = 1;
266 int dst_pitch = ww * bpp;
267 stbtt_MakeCodepointBitmap(&font_, out_buffer, ww, hh, dst_pitch, scale, scale, codepoint);
268 // 6) If there's any pixel data, build the SDF:
269 if(ww > 0 && hh > 0)
270 {
271 // 6a) Compute padded dimensions.
272 const int dw = width_padding_;
273 const int dh = height_padding_;
274 const int nw = ww + 2 * dw;
275 const int nh = hh + 2 * dh;
276 BX_ASSERT(nw * nh < 128 * 128, "Glyph too big");
277
278 // 6b) Prepare (or resize) the alpha scratch buffer.
279 size_t alpha_bytes = size_t(nw) * size_t(nh);
280 if(alpha_scratch_.size() < alpha_bytes)
281 {
282 alpha_scratch_.resize(alpha_bytes);
283 }
284 uint8_t* alpha_img = alpha_scratch_.data();
285 bx::memSet(alpha_img, 0, alpha_bytes);
286
287 // 6c) Copy the rendered glyph into the center of the padded alpha image.
288 for(int y = 0; y < hh; ++y)
289 {
290 bx::memCopy(alpha_img + size_t(y + dh) * nw + dw, out_buffer + size_t(y) * ww, size_t(ww));
291 }
292
293 // 6d) Prepare (or resize) the SDF scratch buffer (floats + SDFpoint structs).
294 size_t temp_bytes = alpha_bytes * (sizeof(float) + sizeof(SDFpoint));
295 if(scratch_buffer_.size() < temp_bytes)
296 {
297 scratch_buffer_.resize(temp_bytes);
298 }
299 uint8_t* temp = scratch_buffer_.data();
300
301 // 6e) Run the no‐alloc distance‐transform, writing back into out_buffer.
302 sdfBuildDistanceFieldNoAlloc(out_buffer, // output buffer (one byte per pixel, row-major)
303 nw, // outstride = bytes per row
304 8.0f, // radius
305 alpha_img, // input alpha image
306 nw,
307 nh, // width, height of the padded image
308 nw, // stride = bytes per row of input
309 temp // scratch buffer (must be distinct from alpha_img)
310 );
311
312 // 6f) Update glyph info to reflect padding.
313 out_info.offset_x -= float(dw);
314 out_info.offset_y -= float(dh);
315 out_info.width = float(nw);
316 out_info.height = float(nh);
317 }
318
319 return true;
320}
321
322auto true_type_font::bake_glyph_distance_stb(code_point codepoint, glyph_info& out_info, uint8_t* out_buffer) -> bool
323{
324 // 1) Get vertical font metrics.
325 int ascent{}, descent{}, line_gap{};
326 stbtt_GetFontVMetrics(&font_, &ascent, &descent, &line_gap);
327
328 // 2) Get horizontal metrics for this glyph.
329 int advance{}, lsb{};
330 stbtt_GetCodepointHMetrics(&font_, codepoint, &advance, &lsb);
331
332 // 3) Decide on SDF parameters
333 float scale = scale_; // your font‐to‐pixel scale
334 int padding = width_padding_; // how many pixels to pad (radius)
335 unsigned char on_edge = 128; // “middle” of SDF range
336 float pixel_dist_scale = float(on_edge) / float(padding);
337
338 // 4) Call into stb to get a freshly‐malloc’d SDF bitmap
339 int w = 0, h = 0, xoff = 0, yoff = 0;
340 unsigned char* bitmap =
341 stbtt_GetCodepointSDF(&font_, scale, codepoint, padding, on_edge, pixel_dist_scale, &w, &h, &xoff, &yoff);
342
343 // 5) Fill out GlyphInfo
344 out_info.offset_x = float(xoff);
345 out_info.offset_y = float(yoff);
346 out_info.width = float(w);
347 out_info.height = float(h);
348 out_info.advance_x = bx::round(advance * scale);
349 out_info.advance_y = bx::round((ascent - descent + line_gap) * scale);
350
351 if(bitmap)
352 {
353 // 6) Copy row-by-row into the caller’s buffer
354 // (assumes out_buffer has at least w*h bytes allocated)
355 for(int y = 0; y < h; ++y)
356 {
357 bx::memCopy(out_buffer + size_t(y) * size_t(w), bitmap + size_t(y) * size_t(w), size_t(w));
358 }
359
360 // 7) Free the stb‐malloc’d bitmap
361 stbtt_FreeSDF(bitmap, nullptr);
362 }
363
364 return true;
365}
366
367using glyph_lut_t = stl::unordered_map<code_point, glyph_info>;
368
369// cache font data
371{
375 // an handle to a master font in case of sub distance field font
376 font_handle master_font_handle = BGFX_INVALID_HANDLE;
377 int16_t padding{};
378
381};
382
383auto add_white_glyph(Atlas* atlas, uint32_t size) -> glyph_info
384{
385 // Create filler rectangle
386 stl::vector<uint8_t> buffer(size * size * 4, 255);
387
388 glyph_info glyph{};
389 glyph.width = size;
390 glyph.height = size;
391
393 glyph.region_index = atlas->addRegion(size, size, buffer.data(), AtlasRegion::TYPE_GRAY, 1);
394 return glyph;
395}
396
397font_manager::font_manager(uint16_t texture_side_width) : atlas_size_(texture_side_width)
398{
399 init();
400}
401
402void font_manager::init()
403{
404 cached_files_ = new cached_file[MAX_OPENED_FILES];
405 cached_fonts_ = new cached_font[MAX_OPENED_FONT];
406 buffer_ = new uint8_t[MAX_FONT_BUFFER_SIZE];
407}
408
410{
411 auto font_handles = font_handles_;
412
413 for(uint16_t i = 0; i < font_handles.getNumHandles(); ++i)
414 {
415 if(font_handles.isValid(i))
416 {
417 font_handle handle{font_handles.getHandleAt(i)};
419 }
420 }
421
422 auto file_handles = file_handles_;
423
424 for(uint16_t i = 0; i < file_handles.getNumHandles(); ++i)
425 {
426 if(file_handles.isValid(i))
427 {
428 true_type_handle handle{file_handles.getHandleAt(i)};
430 }
431 }
432
433 BX_ASSERT(font_handles_.getNumHandles() == 0, "All the fonts must be destroyed before destroying the manager");
434 delete[] cached_fonts_;
435
436 BX_ASSERT(file_handles_.getNumHandles() == 0, "All the font files must be destroyed before destroying the manager");
437 delete[] cached_files_;
438
439 delete[] buffer_;
440}
441
442auto font_manager::get_atlas(font_handle handle) const -> Atlas*
443{
444 BX_ASSERT(isValid(handle), "Invalid handle used");
445 cached_font& font = cached_fonts_[handle.idx];
446 return font.atlas;
447}
448
449auto font_manager::create_ttf(const uint8_t* buffer, uint32_t size) -> true_type_handle
450{
451 uint16_t id = file_handles_.alloc();
452 BX_ASSERT(id != bx::kInvalidHandle, "Invalid handle used");
453 cached_files_[id].buffer = new uint8_t[size];
454 cached_files_[id].buffer_size = size;
455 bx::memCopy(cached_files_[id].buffer, buffer, size);
456
457 true_type_handle ret = {id};
458 return ret;
459}
460
461void font_manager::destroy_ttf(true_type_handle handle)
462{
463 BX_ASSERT(isValid(handle), "Invalid handle used");
464 delete[] cached_files_[handle.idx].buffer;
465 cached_files_[handle.idx].buffer_size = 0;
466 cached_files_[handle.idx].buffer = nullptr;
467 file_handles_.free(handle.idx);
468}
469
470auto font_manager::create_font_by_pixel_size(true_type_handle ttf_handle,
471 uint32_t typeface_index,
472 uint32_t pixel_size,
473 uint32_t font_type,
474 uint16_t glyph_width_padding,
475 uint16_t glyph_height_padding) -> font_handle
476{
477 BX_ASSERT(isValid(ttf_handle), "Invalid handle used");
478
479 true_type_font* ttf = new true_type_font();
480 if(!ttf->init(cached_files_[ttf_handle.idx].buffer,
481 cached_files_[ttf_handle.idx].buffer_size,
482 typeface_index,
483 pixel_size,
484 glyph_width_padding,
485 glyph_height_padding))
486 {
487 delete ttf;
488 font_handle invalid = {bx::kInvalidHandle};
489 return invalid;
490 }
491
492 uint16_t font_idx = font_handles_.alloc();
493 BX_ASSERT(font_idx != bx::kInvalidHandle, "Invalid handle used");
494
495 cached_font& font = cached_fonts_[font_idx];
496 font.font = ttf;
497 font.info = ttf->get_font_info();
498 font.info.font_type = int16_t(font_type);
499 font.info.pixel_size = uint16_t(pixel_size);
500 font.cached_glyphs.clear();
501 font.master_font_handle.idx = bx::kInvalidHandle;
502 font.atlas = new Atlas(atlas_size_);
503 font.white_glyph = add_white_glyph(font.atlas, 3);
504 font_handle handle = {font_idx};
505 return handle;
506}
507
508auto font_manager::create_scaled_font_to_pixel_size(font_handle base_font_handle, uint32_t pixel_size) -> font_handle
509{
510 BX_ASSERT(isValid(base_font_handle), "Invalid handle used");
511 cached_font& base_font = cached_fonts_[base_font_handle.idx];
512 font_info& info = base_font.info;
513
514 font_info new_font_info = info;
515 new_font_info.pixel_size = uint16_t(pixel_size);
516 new_font_info.scale = (float)pixel_size / (float)info.pixel_size;
517 new_font_info.ascender = (new_font_info.ascender * new_font_info.scale);
518 new_font_info.descender = (new_font_info.descender * new_font_info.scale);
519 new_font_info.capline = (new_font_info.capline * new_font_info.scale);
520 new_font_info.xline = (new_font_info.xline * new_font_info.scale);
521 new_font_info.line_gap = (new_font_info.line_gap * new_font_info.scale);
522 new_font_info.max_advance_width = (new_font_info.max_advance_width * new_font_info.scale);
523 new_font_info.underline_thickness = (new_font_info.underline_thickness * new_font_info.scale);
524 new_font_info.underline_position = (new_font_info.underline_position * new_font_info.scale);
525
526 uint16_t font_idx = font_handles_.alloc();
527 BX_ASSERT(font_idx != bx::kInvalidHandle, "Invalid handle used");
528
529 cached_font& font = cached_fonts_[font_idx];
530 font.cached_glyphs.clear();
531 font.info = new_font_info;
532 font.font = nullptr;
533 font.atlas = base_font.atlas;
534 font.white_glyph = base_font.white_glyph;
535 font.master_font_handle = base_font_handle;
536
537 font_handle handle = {font_idx};
538 return handle;
539}
540
542{
543 BX_ASSERT(isValid(handle), "Invalid handle used");
544
545 cached_font& font = cached_fonts_[handle.idx];
546
547 if(font.font != nullptr)
548 {
549 delete font.font;
550 font.font = nullptr;
551
552 if(font.atlas != nullptr)
553 {
554 delete font.atlas;
555 font.atlas = nullptr;
556 }
557 }
558
559 font.cached_glyphs.clear();
560 font_handles_.free(handle.idx);
561}
562
563auto font_manager::preload_glyph_ranges(font_handle handle, const code_point* ranges) -> bool
564{
565 BX_ASSERT(isValid(handle), "Invalid handle used");
566 cached_font& font = cached_fonts_[handle.idx];
567 if(font.font == nullptr)
568 {
569 return false;
570 }
571
572 // font_profile_scope profile(__func__);
573 // ranges is like { start0, end0, start1, end1, …, 0 }
574 // Walk it two at a time until the 0 sentinel
575 for(const code_point* p = ranges; *p != 0; p += 2)
576 {
577 code_point start = p[0];
578 code_point end = p[1];
579 // guard against malformed data
580 BX_ASSERT(end >= start, "Range end before start");
581
582 for(code_point cp = start; cp <= end; ++cp)
583 {
584 if(!preload_glyph(handle, cp))
585 {
586 return false;
587 }
588 }
589 }
590
591 return true;
592}
593
594auto font_manager::preload_glyph(font_handle handle, const char* str, const char* end) -> bool
595{
596 if(end == nullptr)
597 {
598 end = str + strlen(str);
599 }
600 BX_ASSERT(end >= str, "");
601
602 BX_ASSERT(isValid(handle), "Invalid handle used");
603 cached_font& font = cached_fonts_[handle.idx];
604
605 if(nullptr == font.font)
606 {
607 return false;
608 }
609
610 uint32_t state = 0;
611
612 for(; *str && str < end; ++str)
613 {
614 code_point codepoint = 0;
615 if(utf8_decode(&state, &codepoint, *str) == UTF8_ACCEPT)
616 {
617 if(!preload_glyph(handle, codepoint))
618 {
619 return false;
620 }
621 }
622 }
623
624 BX_ASSERT(state == UTF8_ACCEPT, "The string is not well-formed");
625
626 return true;
627}
628
629auto font_manager::preload_glyph(font_handle handle, const wchar_t* str, const wchar_t* end) -> bool
630{
631 if(end == nullptr)
632 {
633 end = str + wcslen(str);
634 }
635 BX_ASSERT(end >= str, "");
636
637 BX_ASSERT(isValid(handle), "Invalid handle used");
638 cached_font& font = cached_fonts_[handle.idx];
639
640 if(nullptr == font.font)
641 {
642 return false;
643 }
644
645 for(const wchar_t* current = str; current < end; ++current)
646 {
647 code_point codepoint = *current;
648 if(!preload_glyph(handle, codepoint))
649 {
650 return false;
651 }
652 }
653
654 return true;
655}
656
657auto font_manager::preload_glyph(font_handle handle, code_point codepoint) -> bool
658{
659 BX_ASSERT(isValid(handle), "Invalid handle used");
660 cached_font& font = cached_fonts_[handle.idx];
661 font_info& info = font.info;
662
663 glyph_lut_t::iterator iter = font.cached_glyphs.find(codepoint);
664 if(iter != font.cached_glyphs.end())
665 {
666 return true;
667 }
668
669 if(nullptr != font.font)
670 {
671 glyph_info glinfo;
672
673 switch(font.info.font_type)
674 {
675 case FONT_TYPE_ALPHA:
676 font.font->bake_glyph_alpha(codepoint, glinfo, buffer_);
677 break;
678
680 font.font->bake_glyph_distance(codepoint, glinfo, buffer_);
681 break;
682
684 font.font->bake_glyph_distance(codepoint, glinfo, buffer_);
685 break;
686
692 font.font->bake_glyph_distance(codepoint, glinfo, buffer_);
693 break;
694
695 default:
696 BX_ASSERT(false, "TextureType not supported yet");
697 }
698
699 if(!add_bitmap(font.atlas, glinfo, buffer_))
700 {
701 return false;
702 }
703
704 glinfo.advance_x = (glinfo.advance_x * info.scale);
705 glinfo.advance_y = (glinfo.advance_y * info.scale);
706 glinfo.offset_x = (glinfo.offset_x * info.scale);
707 glinfo.offset_y = (glinfo.offset_y * info.scale);
708 glinfo.height = (glinfo.height * info.scale);
709 glinfo.width = (glinfo.width * info.scale);
710
711 font.cached_glyphs[codepoint] = glinfo;
712 return true;
713 }
714
715 if(isValid(font.master_font_handle) && preload_glyph(font.master_font_handle, codepoint))
716 {
717 const glyph_info* glyph = get_glyph_info(font.master_font_handle, codepoint);
718
719 glyph_info glinfo = *glyph;
720 glinfo.advance_x = (glinfo.advance_x * info.scale);
721 glinfo.advance_y = (glinfo.advance_y * info.scale);
722 glinfo.offset_x = (glinfo.offset_x * info.scale);
723 glinfo.offset_y = (glinfo.offset_y * info.scale);
724 glinfo.height = (glinfo.height * info.scale);
725 glinfo.width = (glinfo.width * info.scale);
726
727 font.cached_glyphs[codepoint] = glinfo;
728 return true;
729 }
730
731 return false;
732}
733
735 code_point codepoint,
736 uint16_t width,
737 uint16_t height,
738 uint16_t pitch,
739 float extra_scale,
740 const uint8_t* bitmap_buffer,
741 float glyph_offset_x,
742 float glyph_offset_y) -> bool
743{
744 BX_ASSERT(isValid(handle), "Invalid handle used");
745 cached_font& font = cached_fonts_[handle.idx];
746
747 glyph_lut_t::iterator iter = font.cached_glyphs.find(codepoint);
748 if(iter != font.cached_glyphs.end())
749 {
750 return true;
751 }
752
753 glyph_info glinfo;
754
755 float glyph_scale = extra_scale;
756 glinfo.offset_x = glyph_offset_x * glyph_scale;
757 glinfo.offset_y = glyph_offset_y * glyph_scale;
758 glinfo.width = (float)width;
759 glinfo.height = (float)height;
760 glinfo.advance_x = (float)width * glyph_scale;
761 glinfo.advance_y = (float)height * glyph_scale;
762 glinfo.bitmap_scale = glyph_scale;
763
764 uint32_t dst_pitch = width * 4;
765
766 uint8_t* dst = buffer_;
767 const uint8_t* src = bitmap_buffer;
768 uint32_t src_pitch = pitch;
769
770 for(int32_t ii = 0; ii < height; ++ii)
771 {
772 bx::memCopy(dst, src, dst_pitch);
773
774 dst += dst_pitch;
775 src += src_pitch;
776 }
777
778 auto atlas = get_atlas(handle);
779 glinfo.region_index = atlas->addRegion((uint16_t)bx::ceil(glinfo.width),
780 (uint16_t)bx::ceil(glinfo.height),
781 buffer_,
783
784 font.cached_glyphs[codepoint] = glinfo;
785 return true;
786}
787
788auto font_manager::get_font_info(font_handle handle) const -> const font_info&
789{
790 BX_ASSERT(isValid(handle), "Invalid handle used");
791 return cached_fonts_[handle.idx].info;
792}
793
794auto font_manager::get_white_glyph(font_handle handle) const -> const glyph_info&
795{
796 BX_ASSERT(isValid(handle), "Invalid handle used");
797 return cached_fonts_[handle.idx].white_glyph;
798}
799
800auto font_manager::get_kerning(font_handle handle, code_point prev_codepoint, code_point codepoint) -> float
801{
802 const cached_font& cfont = cached_fonts_[handle.idx];
803 if(isValid(cfont.master_font_handle))
804 {
805 cached_font& master_font = cached_fonts_[cfont.master_font_handle.idx];
806 return master_font.font->scale_ *
807 stbtt_GetCodepointKernAdvance(&master_font.font->font_, prev_codepoint, codepoint) *
808 cfont.info.scale;
809 }
810
811 return cfont.font->scale_ * stbtt_GetCodepointKernAdvance(&cfont.font->font_, prev_codepoint, codepoint);
812}
813
814auto font_manager::get_glyph_info(font_handle handle, code_point codepoint) -> const glyph_info*
815{
816 const glyph_lut_t& cached_glyphs = cached_fonts_[handle.idx].cached_glyphs;
817 glyph_lut_t::const_iterator it = cached_glyphs.find(codepoint);
818
819 if(it == cached_glyphs.end())
820 {
821 if(!preload_glyph(handle, codepoint))
822 {
823 return nullptr;
824 }
825
826 it = cached_glyphs.find(codepoint);
827 }
828
829 BX_ASSERT(it != cached_glyphs.end(), "Failed to preload glyph.");
830 return &it->second;
831}
832
833auto font_manager::add_bitmap(Atlas* atlas, glyph_info& glinfo, const uint8_t* data) -> bool
834{
835 glinfo.region_index = atlas->addRegion((uint16_t)bx::ceil(glinfo.width),
836 (uint16_t)bx::ceil(glinfo.height),
837 data,
839 return true;
840}
841
842} // namespace gfx
auto create_ttf(const uint8_t *buffer, uint32_t size) -> true_type_handle
auto get_glyph_info(font_handle handle, code_point code_point) -> const glyph_info *
auto get_white_glyph(font_handle handle) const -> const glyph_info &
auto preload_glyph_ranges(font_handle handle, const code_point *ranges) -> bool
auto get_atlas(font_handle handle) const -> Atlas *
Retrieve the atlas used by the font manager (e.g. to add stuff to it)
auto get_kerning(font_handle handle, code_point prev_code_point, code_point code_point) -> float
auto create_font_by_pixel_size(true_type_handle handle, uint32_t typeface_index, uint32_t pixel_size, uint32_t font_type=FONT_TYPE_ALPHA, uint16_t glyph_width_padding=8, uint16_t glyph_height_padding=8) -> font_handle
Return a font whose height is a fixed pixel size.
auto preload_glyph(font_handle handle, const wchar_t *string, const wchar_t *end=nullptr) -> bool
auto create_scaled_font_to_pixel_size(font_handle base_font_handle, uint32_t pixel_size) -> font_handle
Return a scaled child font whose height is a fixed pixel size.
void destroy_font(font_handle handle)
destroy a font (truetype or baked)
void destroy_ttf(true_type_handle handle)
Unload a TrueType font (free font memory) but keep loaded glyphs.
auto get_font_info(font_handle handle) const -> const font_info &
auto add_glyph_bitmap(font_handle handle, code_point character, uint16_t width, uint16_t height, uint16_t pitch, float extra_scale, const uint8_t *bitmap_buffer, float glyph_offset_x, float glyph_offset_y) -> bool
font_manager(uint16_t texture_side_width=512)
linear filtering.
auto bake_glyph_alpha(code_point codepoint, glyph_info &out_info, uint8_t *out_buffer) -> bool
auto bake_glyph_distance(code_point codepoint, glyph_info &out_info, uint8_t *out_buffer) -> bool
auto init(const uint8_t *buffer, uint32_t buffer_size, int32_t font_index, uint32_t pixel_height, int16_t width_padding, int16_t height_padding) -> bool
auto get_font_info() const -> font_info
return the font descriptor of the current font
auto bake_glyph_distance_stb(code_point codepoint, glyph_info &out_info, uint8_t *out_buffer) -> bool
auto bake_glyph_distance_fast(code_point codepoint, glyph_info &out_info, uint8_t *out_buffer) -> bool
#define MAX_FONT_BUFFER_SIZE
BX_PRAGMA_DIAGNOSTIC_IGNORED_MSVC(4244) namespace stl
#define FONT_TYPE_DISTANCE_OUTLINE_DROP_SHADOW_IMAGE
#define FONT_TYPE_ALPHA
#define MAX_OPENED_FONT
#define FONT_TYPE_DISTANCE_DROP_SHADOW
#define FONT_TYPE_DISTANCE_OUTLINE_IMAGE
#define FONT_TYPE_DISTANCE
#define MAX_OPENED_FILES
#define FONT_TYPE_DISTANCE_DROP_SHADOW_IMAGE
#define FONT_TYPE_DISTANCE_OUTLINE
#define FONT_TYPE_DISTANCE_SUBPIXEL
#define UTF8_ACCEPT
Definition utf8.h:9
float scale
Definition hub.cpp:25
ImGui::Font::Enum font
Definition hub.cpp:24
auto add_white_glyph(Atlas *atlas, uint32_t size) -> glyph_info
uint32_t code_point
Unicode value of a character.
stl::unordered_map< code_point, glyph_info > glyph_lut_t
bgfx::Caps caps
Definition graphics.h:29
void end(encoder *_encoder)
Definition graphics.cpp:270
float y
void sdfBuildDistanceFieldNoAlloc(unsigned char *out, int outstride, float radius, const unsigned char *img, int width, int height, int stride, unsigned char *temp)
float line_gap
The spacing in pixels between one row's descent and the next row's ascent.
float xline
The extends above the baseline representing of the small letters.
float underline_thickness
The thickness of the under/hover/strike-trough line in pixels.
float underline_position
The position of the underline relatively to the baseline.
float ascender
The pixel extents above the baseline in pixels (typically positive).
float scale
Scale to apply to glyph data.
float descender
The extents below the baseline in pixels (typically negative).
uint16_t pixel_size
The font height in pixel.
float max_advance_width
This field gives the maximum horizontal cursor advance for all glyphs in the font.
float capline
The extends above the baseline representing of the capital letters.
font_profile_scope(const char *func)
A structure that describe a glyph.
float bitmap_scale
Amount to scale a bitmap image glyph.
float width
Glyph's width in pixels.
float offset_x
Glyph's left offset in pixels.
uint16_t region_index
Region index in the atlas storing textures.
float height
Glyph's height in pixels.
T height
Definition basetypes.hpp:56
gfx::uniform_handle handle
Definition uniform.cpp:9
uint32_t utf8_decode(uint32_t *_state, uint32_t *_codep, uint8_t _ch)
Definition utf8.cpp:46
bool isValid(SpriteHandle _handle)
Definition debugdraw.h:34