7#define STB_TRUETYPE_IMPLEMENTATION
10#include <stb/stb_truetype.h>
12BX_PRAGMA_DIAGNOSTIC_PUSH()
16#define SDF_IMPLEMENTATION
18BX_PRAGMA_DIAGNOSTIC_POP()
22#include <tinystl/allocator.h>
23#include <tinystl/unordered_map.h>
24#include <tinystl/vector.h>
25namespace stl = tinystl;
34#define MAX_FONT_BUFFER_SIZE (512 * 4 * 512 * 4 * 4)
44 int64_t now = bx::getHPCounter();
45 int64_t frameTime = now - start;
47 double freq = double(bx::getHPFrequency());
50 float deltaTimeS = float(frameTime / freq);
53 float deltaTimeUs = float(frameTime * 1e6 / freq);
55 bx::printf(
"--------------------------------------\n");
56 bx::printf(
"%s took %.2f us\n", func_, deltaTimeUs);
57 bx::printf(
"--------------------------------------\n");
61 int64_t start = bx::getHPCounter();
71 auto init(
const uint8_t* buffer,
74 uint32_t pixel_height,
75 int16_t width_padding,
76 int16_t height_padding) -> bool;
99 int16_t width_padding_{};
100 int16_t height_padding_{};
104 stl::vector<uint8_t> scratch_buffer_;
105 stl::vector<uint8_t> alpha_scratch_;
109 uint32_t buffer_size,
111 uint32_t pixel_height,
112 int16_t width_padding,
113 int16_t height_padding) ->
bool
115 BX_WARN((buffer_size > 256 && buffer_size < 100000000),
116 "(FontIndex %d) TrueType buffer size is suspicious (%d)",
119 BX_WARN((pixel_height > 4 && pixel_height < 128),
120 "(FontIndex %d) TrueType pixel height is suspicious (%d)",
123 BX_UNUSED(buffer_size);
125 int fonts = stbtt_GetNumberOfFonts(buffer);
127 BX_WARN(fonts >= font_index,
"(FontIndex %d) TrueType is not in range [0 - %d]", font_index, fonts);
129 int offset = stbtt_GetFontOffsetForIndex(buffer, font_index);
131 stbtt_InitFont(&font_, buffer, offset);
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);
138 width_padding_ = width_padding;
139 height_padding_ = height_padding;
147 stbtt_GetFontVMetrics(&font_, &ascent, &descent, &lineGap);
149 float scale = scale_;
151 int x0{}, y0{}, x1{}, y1{};
152 stbtt_GetFontBoundingBox(&font_, &x0, &y0, &x1, &y1);
163 int caps[] = {
'H',
'I'};
164 for(
int codepoint :
caps)
166 int x0{}, x1{}, y0{}, y1{};
167 if(stbtt_GetCodepointBox(&font_, codepoint, &x0, &y0, &x1, &y1))
176 int xh[] = {
'x',
'z'};
177 for(
auto codepoint : xh)
179 int x0{}, x1{}, y0{}, y1{};
180 if(stbtt_GetCodepointBox(&font_, codepoint, &x0, &y0, &x1, &y1))
195 int ascent{}, descent{}, line_gap{};
196 stbtt_GetFontVMetrics(&font_, &ascent, &descent, &line_gap);
199 int advance{}, lsb{};
200 stbtt_GetCodepointHMetrics(&font_, codepoint, &advance, &lsb);
203 const float scale = scale_;
204 int x0{}, y0{}, x1{}, y1{};
205 stbtt_GetCodepointBitmapBox(&font_, codepoint,
scale,
scale, &x0, &y0, &x1, &y1);
207 const int ww = x1 - x0;
208 const int hh = y1 - y0;
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);
221 int dst_pitch = ww * bpp;
222 stbtt_MakeCodepointBitmap(&font_, out_buffer, ww, hh, dst_pitch,
scale,
scale, codepoint);
231 return bake_glyph_distance_fast(codepoint, out_info, out_buffer);
234 return bake_glyph_distance_stb(codepoint, out_info, out_buffer);
240 int ascent{}, descent{}, line_gap{};
241 stbtt_GetFontVMetrics(&font_, &ascent, &descent, &line_gap);
244 int advance{}, lsb{};
245 stbtt_GetCodepointHMetrics(&font_, codepoint, &advance, &lsb);
248 const float scale = scale_;
249 int x0{}, y0{}, x1{}, y1{};
250 stbtt_GetCodepointBitmapBox(&font_, codepoint,
scale,
scale, &x0, &y0, &x1, &y1);
252 const int ww = x1 - x0;
253 const int hh = y1 - y0;
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);
266 int dst_pitch = ww * bpp;
267 stbtt_MakeCodepointBitmap(&font_, out_buffer, ww, hh, dst_pitch,
scale,
scale, codepoint);
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");
279 size_t alpha_bytes = size_t(nw) * size_t(nh);
280 if(alpha_scratch_.size() < alpha_bytes)
282 alpha_scratch_.resize(alpha_bytes);
284 uint8_t* alpha_img = alpha_scratch_.data();
285 bx::memSet(alpha_img, 0, alpha_bytes);
288 for(
int y = 0;
y < hh; ++
y)
290 bx::memCopy(alpha_img +
size_t(
y + dh) * nw + dw, out_buffer +
size_t(
y) * ww,
size_t(ww));
294 size_t temp_bytes = alpha_bytes * (
sizeof(float) +
sizeof(SDFpoint));
295 if(scratch_buffer_.size() < temp_bytes)
297 scratch_buffer_.resize(temp_bytes);
299 uint8_t* temp = scratch_buffer_.data();
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);
325 int ascent{}, descent{}, line_gap{};
326 stbtt_GetFontVMetrics(&font_, &ascent, &descent, &line_gap);
329 int advance{}, lsb{};
330 stbtt_GetCodepointHMetrics(&font_, codepoint, &advance, &lsb);
333 float scale = scale_;
334 int padding = width_padding_;
335 unsigned char on_edge = 128;
336 float pixel_dist_scale = float(on_edge) / float(padding);
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);
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);
355 for(
int y = 0;
y < h; ++
y)
357 bx::memCopy(out_buffer +
size_t(
y) *
size_t(w), bitmap +
size_t(
y) *
size_t(w),
size_t(w));
361 stbtt_FreeSDF(bitmap,
nullptr);
386 stl::vector<uint8_t> buffer(
size *
size * 4, 255);
402void font_manager::init()
411 auto font_handles = font_handles_;
413 for(uint16_t i = 0; i < font_handles.getNumHandles(); ++i)
415 if(font_handles.isValid(i))
417 font_handle
handle{font_handles.getHandleAt(i)};
422 auto file_handles = file_handles_;
424 for(uint16_t i = 0; i < file_handles.getNumHandles(); ++i)
426 if(file_handles.isValid(i))
428 true_type_handle
handle{file_handles.getHandleAt(i)};
433 BX_ASSERT(font_handles_.getNumHandles() == 0,
"All the fonts must be destroyed before destroying the manager");
434 delete[] cached_fonts_;
436 BX_ASSERT(file_handles_.getNumHandles() == 0,
"All the font files must be destroyed before destroying the manager");
437 delete[] cached_files_;
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);
457 true_type_handle ret = {
id};
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);
471 uint32_t typeface_index,
474 uint16_t glyph_width_padding,
475 uint16_t glyph_height_padding) -> font_handle
477 BX_ASSERT(
isValid(ttf_handle),
"Invalid handle used");
480 if(!ttf->
init(cached_files_[ttf_handle.idx].buffer,
481 cached_files_[ttf_handle.idx].buffer_size,
485 glyph_height_padding))
488 font_handle invalid = {bx::kInvalidHandle};
492 uint16_t font_idx = font_handles_.alloc();
493 BX_ASSERT(font_idx != bx::kInvalidHandle,
"Invalid handle used");
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;
504 font_handle
handle = {font_idx};
510 BX_ASSERT(
isValid(base_font_handle),
"Invalid handle used");
511 cached_font& base_font = cached_fonts_[base_font_handle.idx];
515 new_font_info.
pixel_size = uint16_t(pixel_size);
526 uint16_t font_idx = font_handles_.alloc();
527 BX_ASSERT(font_idx != bx::kInvalidHandle,
"Invalid handle used");
531 font.info = new_font_info;
535 font.master_font_handle = base_font_handle;
537 font_handle
handle = {font_idx};
547 if(
font.font !=
nullptr)
552 if(
font.atlas !=
nullptr)
555 font.atlas =
nullptr;
560 font_handles_.free(
handle.idx);
567 if(
font.font ==
nullptr)
575 for(
const code_point* p = ranges; *p != 0; p += 2)
580 BX_ASSERT(
end >= start,
"Range end before start");
584 if(!preload_glyph(
handle, cp))
598 end = str + strlen(str);
600 BX_ASSERT(
end >= str,
"");
605 if(
nullptr ==
font.font)
612 for(; *str && str <
end; ++str)
617 if(!preload_glyph(
handle, codepoint))
624 BX_ASSERT(state ==
UTF8_ACCEPT,
"The string is not well-formed");
633 end = str + wcslen(str);
635 BX_ASSERT(
end >= str,
"");
640 if(
nullptr ==
font.font)
645 for(
const wchar_t* current = str; current <
end; ++current)
648 if(!preload_glyph(
handle, codepoint))
663 glyph_lut_t::iterator iter =
font.cached_glyphs.find(codepoint);
664 if(iter !=
font.cached_glyphs.end())
669 if(
nullptr !=
font.font)
673 switch(
font.info.font_type)
676 font.font->bake_glyph_alpha(codepoint, glinfo, buffer_);
680 font.font->bake_glyph_distance(codepoint, glinfo, buffer_);
684 font.font->bake_glyph_distance(codepoint, glinfo, buffer_);
692 font.font->bake_glyph_distance(codepoint, glinfo, buffer_);
696 BX_ASSERT(
false,
"TextureType not supported yet");
699 if(!add_bitmap(
font.atlas, glinfo, buffer_))
711 font.cached_glyphs[codepoint] = glinfo;
715 if(
isValid(
font.master_font_handle) && preload_glyph(
font.master_font_handle, codepoint))
717 const glyph_info* glyph = get_glyph_info(
font.master_font_handle, codepoint);
727 font.cached_glyphs[codepoint] = glinfo;
740 const uint8_t* bitmap_buffer,
741 float glyph_offset_x,
742 float glyph_offset_y) ->
bool
748 if(iter !=
font.cached_glyphs.end())
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;
764 uint32_t dst_pitch = width * 4;
766 uint8_t* dst = buffer_;
767 const uint8_t* src = bitmap_buffer;
768 uint32_t src_pitch = pitch;
770 for(int32_t ii = 0; ii < height; ++ii)
772 bx::memCopy(dst, src, dst_pitch);
778 auto atlas = get_atlas(
handle);
780 (uint16_t)bx::ceil(glinfo.
height),
784 font.cached_glyphs[codepoint] = glinfo;
791 return cached_fonts_[
handle.idx].info;
797 return cached_fonts_[
handle.idx].white_glyph;
806 return master_font.
font->scale_ *
807 stbtt_GetCodepointKernAdvance(&master_font.
font->font_, prev_codepoint, codepoint) *
811 return cfont.
font->scale_ * stbtt_GetCodepointKernAdvance(&cfont.
font->font_, prev_codepoint, codepoint);
817 glyph_lut_t::const_iterator it = cached_glyphs.find(codepoint);
819 if(it == cached_glyphs.end())
821 if(!preload_glyph(
handle, codepoint))
826 it = cached_glyphs.find(codepoint);
829 BX_ASSERT(it != cached_glyphs.end(),
"Failed to preload glyph.");
833auto font_manager::add_bitmap(
Atlas* atlas,
glyph_info& glinfo,
const uint8_t* data) ->
bool
835 glinfo.region_index = atlas->addRegion((uint16_t)bx::ceil(glinfo.width),
836 (uint16_t)bx::ceil(glinfo.height),
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_DISTANCE_DROP_SHADOW
#define FONT_TYPE_DISTANCE_OUTLINE_IMAGE
#define FONT_TYPE_DISTANCE
#define FONT_TYPE_DISTANCE_DROP_SHADOW_IMAGE
#define FONT_TYPE_DISTANCE_OUTLINE
#define FONT_TYPE_DISTANCE_SUBPIXEL
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
void end(encoder *_encoder)
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_handle master_font_handle
glyph_lut_t cached_glyphs
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.
uint32_t utf8_decode(uint32_t *_state, uint32_t *_codep, uint8_t _ch)
bool isValid(SpriteHandle _handle)