#include "TextRasterizer.h" #include #include #include TextRasterizer::TextRasterizer(std::string fontPath, int size, int resolution) { FT_Init_FreeType(&lib); face = std::make_unique(); if (FT_New_Face(lib, fontPath.c_str(), 0, face.get())) { std::cout << "Could not open font" << std::endl; return; } if (!isUnicodeBMP(*face)) { std::cout << "Font is not Unicode BMP" << std::endl; return; } if (FT_Set_Char_Size(*face, 0, size * 64, resolution, resolution)) { std::cout << "Could not set font size" << std::endl; return; } font = hb_ft_font_create(*face, NULL); buffer = hb_buffer_create(); if (!hb_buffer_allocation_successful(buffer)) { std::cout << "Could not create HarfBuzz buffer" << std::endl; return; } } TextRasterizer::~TextRasterizer() { hb_buffer_destroy(buffer); hb_font_destroy(font); FT_Done_FreeType(lib); } std::unique_ptr TextRasterizer::rasterize(std::string text, int x, int y, unsigned int &glyphCount) { hb_buffer_reset(buffer); hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); hb_buffer_set_language(buffer, hb_language_from_string("en", 2)); hb_buffer_add_utf8(buffer, "Hello, World!", text.length(), 0, text.length()); hb_feature_t KerningOn = { HB_TAG('k', 'e', 'r', 'n'), 1, 0, std::numeric_limits::max() }; hb_shape(font, buffer, &KerningOn, 1); hb_glyph_info_t *glyphInfo = hb_buffer_get_glyph_infos(buffer, &glyphCount); hb_glyph_position_t *glyphPos = hb_buffer_get_glyph_positions(buffer, &glyphCount); std::unique_ptr glyphs = std::unique_ptr(new Glyph[glyphCount]); for (int i = 0; i < glyphCount; i++) { if (FT_Load_Glyph(*face, glyphInfo[i].codepoint, FT_LOAD_DEFAULT)) { std::cout << "Could not load glyph" << std::endl; return nullptr; } FT_GlyphSlot slot = (*face)->glyph; if (FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL)) { std::cout << "Could not render glyph" << std::endl; return nullptr; } FT_Bitmap ftBitmap = slot->bitmap; glyphs[i].textureWidth = pow(2, ceil(log(ftBitmap.width) / log(2))); glyphs[i].textureHeight = pow(2, ceil(log(ftBitmap.rows) / log(2))); glyphs[i].textureData = std::unique_ptr(new unsigned char[glyphs[i].textureWidth * glyphs[i].textureHeight]); for (int iy = 0; iy < ftBitmap.rows; iy++) { memcpy(glyphs[i].textureData.get() + iy * glyphs[i].textureWidth, ftBitmap.buffer + iy * ftBitmap.width, ftBitmap.width); } float xa = (float) glyphPos[i].x_advance / 64; float ya = (float) glyphPos[i].y_advance / 64; float xo = (float) glyphPos[i].x_offset / 64; float yo = (float) glyphPos[i].y_offset / 64; glyphs[i].x0 = x + xo + slot->bitmap_left; glyphs[i].y0 = floor(y + yo + slot->bitmap_top); glyphs[i].x1 = glyphs[i].x0 + ftBitmap.width; glyphs[i].y1 = floor(glyphs[i].y0 - ftBitmap.rows); glyphs[i].s0 = 0.0f; glyphs[i].t0 = 0.0f; glyphs[i].s1 = (float) ftBitmap.width / glyphs[i].textureWidth; glyphs[i].t1 = (float) ftBitmap.rows / glyphs[i].textureHeight; x += xa; y += ya; } return glyphs; } bool TextRasterizer::isUnicodeBMP(FT_Face face) { for (int i = 0; i < face->num_charmaps; i++) { if (((face->charmaps[i]->platform_id == 0) && (face->charmaps[i]->encoding_id == 3)) || ((face->charmaps[i]->platform_id == 3) && (face->charmaps[i]->encoding_id == 1))) { FT_Set_Charmap(face, face->charmaps[i]); return true; } } return false; }