Created
January 7, 2022 01:35
-
-
Save natanaeljr/af96deedcae48a9793c753433cc94ad6 to your computer and use it in GitHub Desktop.
Loading fonts and baking bitmaps with FreeType for rendering in opengl (partial implementation)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
auto load_font_texture(gsl::span<const uint8_t> data, size_t width, size_t height) -> GLTexture | |
{ | |
GLuint texture; | |
glGenTextures(1, &texture); | |
glBindTexture(GL_TEXTURE_2D, texture); | |
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | |
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); | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, data.data()); | |
glGenerateMipmap(GL_TEXTURE_2D); | |
return { texture }; | |
} | |
struct Glyph { | |
glm::ivec2 size; | |
glm::ivec2 offset; | |
unsigned int advance; | |
}; | |
struct GLFont { | |
GLTexture texture; | |
std::unordered_map<char, Glyph> glyphs; | |
// Movable but not Copyable | |
GLFont(GLFont&&) = default; | |
GLFont(const GLFont&) = delete; | |
GLFont& operator=(GLFont&&) = default; | |
GLFont& operator=(const GLFont&) = delete; | |
}; | |
/// Read font file and upload generated bitmap texture to GPU memory | |
auto load_font(FT_Library& freetype, const std::string& fontname) -> std::optional<GLFont> | |
{ | |
const std::string filepath = SPACESHIP_ASSETS_PATH + "/fonts/"s + fontname; | |
FT_Face face; | |
int err = FT_New_Face(freetype, filepath.data(), 0, &face); | |
if (err) { | |
ERROR("Failed to load font ({}), error: {}", filepath, err); | |
return std::nullopt; | |
} | |
const size_t kPixelHeight = 48; | |
const size_t kPixelWidth = kPixelHeight * (2.f / 3.f); | |
const size_t kPixelSize = kPixelHeight * kPixelWidth; | |
FT_Set_Pixel_Sizes(face, kPixelWidth, kPixelHeight); | |
std::vector<uint8_t> data; | |
data.reserve(kPixelSize * 128); | |
size_t data_height = 0; | |
std::unordered_map<char, Glyph> glyphs(128); | |
// Load only ASCII characters | |
for (unsigned c = 65; c < 128; c++) { | |
err = FT_Load_Char(face, c, FT_LOAD_RENDER); | |
if (err) { | |
WARN("Failed to load char '{}' from font ({}), error: {}", c, fontname, err); | |
continue; | |
} | |
FT_GlyphSlot glyph = face->glyph; | |
FT_Bitmap& bitmap = glyph->bitmap; | |
char ch = (c > 32 && c < 128) ? c : ' '; | |
TRACE("Font ({}) char {} '{}' row {} width {} left {} top {} adx {} ady {}", fontname, c, ch, bitmap.rows, bitmap.width, glyph->bitmap_left, glyph->bitmap_top, glyph->advance.x >> 6, glyph->advance.y >> 6); | |
if(bitmap.rows > kPixelHeight || bitmap.width > kPixelWidth) { | |
WARN("No support for oversized glyph '{}' ({},{})", glyph->glyph_index, bitmap.rows, bitmap.width); | |
continue; | |
} | |
size_t pad_width = kPixelWidth - bitmap.width; | |
for (int row = bitmap.rows - 1; row >= 0; row--) { | |
std::copy_n(&bitmap.buffer[bitmap.width * row], bitmap.width, std::back_inserter(data)); | |
std::fill_n(std::back_inserter(data), pad_width, 0); | |
} | |
glyphs.emplace(c, Glyph{ | |
.size = { bitmap.width, bitmap.rows }, | |
.offset = { glyph->bitmap_left, glyph->bitmap_top }, | |
.advance = static_cast<unsigned int>(glyph->advance.x >> 6), // pt to pixel | |
}); | |
data_height += bitmap.rows; | |
} | |
FT_Done_Face(face); | |
GLTexture texture = load_font_texture(data, kPixelWidth, data_height); | |
return GLFont{ std::move(texture), std::move(glyphs) }; | |
} | |
struct Fonts { | |
GLFont main; | |
}; | |
Fonts load_fonts() | |
{ | |
FT_Library freetype; | |
ASSERT(!FT_Init_FreeType(&freetype)); | |
auto main_font = load_font(freetype, "Menlo-Regular.ttf"); | |
ASSERT(main_font); | |
ASSERT(!FT_Done_FreeType(freetype)); | |
return { | |
.main = std::move(*main_font), | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment