Skip to content

Instantly share code, notes, and snippets.

@matjam
Last active June 15, 2020 06:43
Show Gist options
  • Save matjam/0281990eb80bda5547a53f0972d7fffb to your computer and use it in GitHub Desktop.
Save matjam/0281990eb80bda5547a53f0972d7fffb to your computer and use it in GitHub Desktop.
inline bool
glyphBit(const FT_GlyphSlot &glyph, const int x, const int y)
{
int pitch = abs(glyph->bitmap.pitch);
unsigned char *row = &glyph->bitmap.buffer[pitch * y];
char cValue = row[x >> 3];
return (cValue & (128 >> (x & 7))) != 0;
}
// When update() is called, we scan through each character and look in the m_glyph_images map
// for the image that holds the glyph. If we don't find one, we construct it from the sf::Font
// that we loaded on start. This is slow for the first time we render that character, but is
// cached after that so should be fast.
//
// These images are then rendered directly to the texture backing the m_console_sprite.
void
ConsoleScreen::update()
{
sf::Clock timer;
sf::Image glyph;
glyph.create(m_character_width, m_character_height, m_palette_colors[m_current_bg]);
for (uint32_t y = 0; y < m_height; y++) {
for (uint32_t x = 0; x < m_width; x++) {
if (!m_console_dirty[x + y * m_width])
continue; // skip this character as it doesn't need to be rendered.
auto &[character, fg, bg] = peek(sf::Vector2i(x, y));
auto &bitmap = m_glyph_bitmap[character];
auto fg_color = m_palette_colors[fg];
auto bg_color = m_palette_colors[bg];
if (bitmap.size() == 0) {
cache_misses++;
bitmap.resize(m_character_width * m_character_height, 0);
// no bitmap exists for this, we need to build the bitmap from the FreeType font
if (FT_Load_Char(m_face,
character,
FT_LOAD_RENDER | FT_LOAD_MONOCHROME | FT_LOAD_TARGET_MONO) !=
FT_Err_Ok) {
continue;
}
FT_GlyphSlot glyph = m_face->glyph;
int pitch = abs(glyph->bitmap.pitch);
for (size_t y = 0; y < glyph->bitmap.rows; ++y) {
for (size_t x = 0; x < glyph->bitmap.width; ++x) {
int imageIndex = (glyph->bitmap.width * y) + x;
if (glyphBit(glyph, x, y)) {
bitmap[imageIndex] = 255;
} else {
bitmap[imageIndex] = 0;
}
}
}
} else {
cache_hits++;
}
for (uint32_t offset_y = 0; offset_y < m_character_height; offset_y++) {
for (uint32_t offset_x = 0; offset_x < m_character_width; offset_x++) {
uint32_t pixel_offset = 4 * offset_x + offset_y * m_character_width;
if (bitmap[offset_x + offset_y * m_character_width] == 255) {
glyph.setPixel(offset_x, offset_y, fg_color);
} else {
glyph.setPixel(offset_x, offset_y, bg_color);
}
}
}
m_console_texture.update(glyph, x * m_character_width, y * m_character_height);
m_console_dirty[x + y * m_width] = false; // it's clean now!
}
}
update_time = timer.getElapsedTime().asMilliseconds();
}
void
ConsoleScreen::loadFont(const std::string font_file, uint32_t pixel_size)
{
auto font_data = file_cache->Get(font_file);
auto error = FT_Init_FreeType(&m_library);
if (error != FT_Err_Ok) {
SPDLOG_ERROR("unable to initialize freetype");
return;
}
const auto font_data_bytes = (FT_Byte *)font_data->data();
error = FT_New_Memory_Face(m_library, font_data_bytes, font_data->size(), 0, &m_face);
if (error == FT_Err_Unknown_File_Format) {
SPDLOG_ERROR("unable to load unknown font data format");
return;
} else if (error != FT_Err_Ok) {
SPDLOG_ERROR("unable to load font data, unknown error");
return;
}
if (FT_Set_Pixel_Sizes(m_face, 0, pixel_size) != FT_Err_Ok) {
SPDLOG_ERROR("unable to set pixel size");
return;
}
SPDLOG_INFO("loaded font {}", m_face->family_name);
return;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment