#include #include #include #include #include #include FT_FREETYPE_H #include FT_STROKER_H #define round_up_26_6(val) (((val) + 63) >> 6u) typedef struct { int32_t line_height; int32_t baseline_offset; FT_Face ft_face; } dft_font_t; typedef struct { uint8_t * bitmap; int32_t width; int32_t height; int32_t left; int32_t top; int32_t advance; FT_Glyph ft_glyph; } dft_glyph_t; static FT_Library ft_library_handle; static bool ft_initialized; static bool initialize_freetype() { if (ft_initialized) { return true; } if (FT_Init_FreeType(&ft_library_handle) != 0) { return false; } ft_initialized = true; return true; } dft_glyph_t * dft_glyph_new(dft_font_t * dft_font, uint32_t char_code, int32_t outline_size) { FT_Glyph glyph; FT_BitmapGlyph bitmap_glyph; FT_UInt glyph_index = FT_Get_Char_Index(dft_font->ft_face, char_code); /* Load the glyph to the face object. */ FT_Load_Glyph(dft_font->ft_face, glyph_index, FT_LOAD_NO_BITMAP); FT_Get_Glyph(dft_font->ft_face->glyph, &glyph); if (outline_size > 0) { FT_Stroker stroker; FT_Stroker_New(ft_library_handle, &stroker); FT_Stroker_Set(stroker, outline_size, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); FT_Glyph_StrokeBorder(&glyph, stroker, false, true); } FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, NULL, true); bitmap_glyph = (FT_BitmapGlyph)glyph; int32_t width = bitmap_glyph->bitmap.width; int32_t height = bitmap_glyph->bitmap.rows; dft_glyph_t * dft_glyph = (dft_glyph_t *)malloc(sizeof(dft_glyph_t)); dft_glyph->ft_glyph = glyph; dft_glyph->width = width; dft_glyph->height = height; dft_glyph->left = bitmap_glyph->left; dft_glyph->top = bitmap_glyph->top; dft_glyph->advance = round_up_26_6(dft_font->ft_face->glyph->advance.x); size_t n_bytes = (size_t)(width * height); dft_glyph->bitmap = (uint8_t *)malloc(n_bytes); memcpy(dft_glyph->bitmap, bitmap_glyph->bitmap.buffer, n_bytes); return dft_glyph; } void dft_glyph_free(dft_glyph_t * dft_glyph) { FT_Done_Glyph(dft_glyph->ft_glyph); free(dft_glyph->bitmap); free(dft_glyph); } static inline void build_font_metrics(dft_font_t * dft_font) { static const char load_chars[] = "Mg_^"; int max_top = INT_MIN; int min_bottom = INT_MAX; for (size_t i = 0u; i < (sizeof(load_chars) - 1u); i++) { dft_glyph_t * dft_glyph = dft_glyph_new(dft_font, load_chars[i], 0); if (dft_glyph->top > max_top) { max_top = dft_glyph->top; } int bottom = dft_glyph->top - dft_glyph->height; if (bottom < min_bottom) { min_bottom = bottom; } } dft_font->line_height = round_up_26_6(dft_font->ft_face->size->metrics.height); dft_font->baseline_offset = (dft_font->line_height - (max_top - min_bottom)) / 2 - min_bottom; } dft_font_t * dft_font_new(const uint8_t * file_data, uint32_t file_size, int font_size) { if (!initialize_freetype()) { return NULL; } FT_Face ft_face; if (FT_New_Memory_Face(ft_library_handle, file_data, file_size, 0, &ft_face) != 0) { return NULL; } dft_font_t * dft_font = (dft_font_t *)malloc(sizeof(dft_font_t)); dft_font->ft_face = ft_face; FT_Set_Pixel_Sizes(ft_face, 0, font_size); build_font_metrics(dft_font); return dft_font; } void dft_font_free(dft_font_t * dft_font) { FT_Done_Face(dft_font->ft_face); free(dft_font); }