diff --git a/core/src/rendering/font.cpp b/core/src/rendering/font.cpp index 0161913..ef87423 100644 --- a/core/src/rendering/font.cpp +++ b/core/src/rendering/font.cpp @@ -10,6 +10,7 @@ #include #include FT_FREETYPE_H +#include FT_STROKER_H // https://learnopengl.com/In-Practice/Text-Rendering @@ -52,6 +53,34 @@ void fontFinish() { } } +static void loadCharTexture(FT_Face& face, FT_BitmapGlyph& glyph_bitmap, Character& character) { + // Generate texture + unsigned int texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RED, + glyph_bitmap->bitmap.width, + glyph_bitmap->bitmap.rows, + 0, + GL_RED, + GL_UNSIGNED_BYTE, + glyph_bitmap->bitmap.buffer + ); + // set texture options + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + // now store character for later use + character.ID = texture; + character.size = glm::ivec2(glyph_bitmap->bitmap.width, glyph_bitmap->bitmap.rows); + character.bearing = glm::ivec2(glyph_bitmap->left, glyph_bitmap->top); + character.advance = (unsigned int)face->glyph->advance.x; +} + std::shared_ptr loadFont(std::string fontName) { std::string fontPath = "assets/font/" + fontName; @@ -66,50 +95,53 @@ std::shared_ptr loadFont(std::string fontName) { FT_Set_Pixel_Sizes(face, 0, 16); font->height = face->size->metrics.y_ppem; + FT_Stroker stroker; + FT_Stroker_New(freetype, &stroker); + FT_Stroker_Set(stroker, 2 * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); + // Load each glyph glPixelStorei(GL_UNPACK_ALIGNMENT, 1); for (unsigned char c = 0; c < 128; c++) { // load character glyph - if (FT_Error err = FT_Load_Char(face, c, FT_LOAD_RENDER)) { + if (FT_Error err = FT_Load_Char(face, c, FT_LOAD_DEFAULT)) { Logger::errorf("Failed to load glyph %d in font '%s': [%d]", c, fontName.c_str(), err); continue; } - // Generate texture - unsigned int texture; - glGenTextures(1, &texture); - glBindTexture(GL_TEXTURE_2D, texture); - glTexImage2D( - GL_TEXTURE_2D, - 0, - GL_RED, - face->glyph->bitmap.width, - face->glyph->bitmap.rows, - 0, - GL_RED, - GL_UNSIGNED_BYTE, - face->glyph->bitmap.buffer - ); - // set texture options - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - // now store character for later use + FT_Glyph glyph; + FT_BitmapGlyph glyph_bitmap; + Character character; - character.ID = texture; - character.size = glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows); - character.bearing = glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top); - character.advance = (unsigned int)face->glyph->advance.x; - font->characters[c] = character; - } + // Render base + FT_Get_Glyph(face->glyph, &glyph); + FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, nullptr, true); + glyph_bitmap = (FT_BitmapGlyph)glyph; + + loadCharTexture(face, glyph_bitmap, character); + font->characters[c] = character; + FT_Done_Glyph(glyph); + // TODO: Find out how to clear FT_BitmapGlyph... I cant import FT_Bitmap_Done for some reason + + // Render stroked + // https://stackoverflow.com/a/28078293/16255372 + FT_Get_Glyph(face->glyph, &glyph); + FT_Glyph_StrokeBorder(&glyph, stroker, false, true); + FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, nullptr, true); + glyph_bitmap = reinterpret_cast(glyph); + + loadCharTexture(face, glyph_bitmap, character); + font->strokeCharacters[c] = character; + FT_Done_Glyph(glyph); + } + + FT_Stroker_Done(stroker); FT_Done_Face(face); return font; } -void drawText(std::shared_ptr font, std::string text, float x, float y, float scale, glm::vec3 color) { +void drawText(std::shared_ptr font, std::string text, float x, float y, float scale, glm::vec3 color, bool drawStroke) { // activate corresponding render state glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); @@ -129,7 +161,7 @@ void drawText(std::shared_ptr font, std::string text, float x, float y, fl // iterate through all characters for (size_t i = 0; i < text.size(); i++) { unsigned char c = text[i]; - Character ch = font->characters[c]; + Character ch = drawStroke ? font->strokeCharacters[c] : font->characters[c]; float xpos = x + ch.bearing.x * scale; float ypos = viewportHeight - y - font->height - (ch.size.y - ch.bearing.y) * scale; diff --git a/core/src/rendering/font.h b/core/src/rendering/font.h index 2b732ca..e2ca9ca 100644 --- a/core/src/rendering/font.h +++ b/core/src/rendering/font.h @@ -16,10 +16,11 @@ struct Character { struct Font { unsigned int height; Character characters[128]; + Character strokeCharacters[128]; }; void fontInit(); void fontFinish(); std::shared_ptr loadFont(std::string fontName); -void drawText(std::shared_ptr font, std::string text, float x, float y, float scale=1.f, glm::vec3 color = glm::vec3(1,1,1)); +void drawText(std::shared_ptr font, std::string text, float x, float y, float scale=1.f, glm::vec3 color = glm::vec3(1,1,1), bool drawStroke = false); float calcTextWidth(std::shared_ptr font, std::string text, float scale = 1.f); \ No newline at end of file