commit 2c725d3cdd02a7bf80fd25c2ca8c5b588a438144 Author: Josh Holtrop Date: Wed Dec 30 22:56:52 2020 -0500 Add initial sources. diff --git a/dft.c b/dft.c new file mode 100644 index 0000000..6132e28 --- /dev/null +++ b/dft.c @@ -0,0 +1,145 @@ +#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); +} diff --git a/package.d b/package.d new file mode 100644 index 0000000..3afdf59 --- /dev/null +++ b/package.d @@ -0,0 +1,96 @@ +module dft; + +import std.file; + +private struct dft_font_t +{ + int line_height; + int baseline_offset; +}; + +private struct dft_glyph_t +{ + ubyte * bitmap; + int width; + int height; + int left; + int top; + int advance; +}; + +private extern(C) dft_glyph_t * dft_glyph_new(dft_font_t * dft_font, uint char_code, int outline_size); +private extern(C) void dft_glyph_free(dft_glyph_t * dft_glyph); +private extern(C) dft_font_t * dft_font_new(const ubyte * file_data, uint file_size, int font_size); +private extern(C) void dft_font_free(dft_font_t * dft_font); + +class Font +{ + class Glyph + { + dft_glyph_t * m_dft_glyph; + + private this(Font font, uint char_code, int outline_size) + { + m_dft_glyph = dft_glyph_new(font.m_dft_font, char_code, outline_size); + } + + ~this() + { + dft_glyph_free(m_dft_glyph); + } + + @property int width() + { + return m_dft_glyph.width; + } + + @property int height() + { + return m_dft_glyph.height; + } + + @property int left() + { + return m_dft_glyph.left; + } + + @property int top() + { + return m_dft_glyph.top; + } + + @property const(ubyte)[] bitmap() + { + int n_bytes = width * height; + return m_dft_glyph.bitmap[0..n_bytes]; + } + } + + dft_font_t * m_dft_font; + + this(const(ubyte)[] file_data, int font_size) + { + m_dft_font = dft_font_new(file_data.ptr, cast(uint)file_data.length, font_size); + } + + this(string file_name, int font_size) + { + const(ubyte)[] file_data = cast(const(ubyte)[])std.file.read(file_name); + this(file_data, font_size); + } + + ~this() + { + dft_font_free(m_dft_font); + } + + @property int line_height() + { + return m_dft_font.line_height; + } + + @property int baseline_offset() + { + return m_dft_font.baseline_offset; + } +}