Skip to content

Instantly share code, notes, and snippets.

@matjam
Last active June 16, 2020 23:08
Show Gist options
  • Save matjam/fd6596de18c62affe0c32cc73c58ff25 to your computer and use it in GitHub Desktop.
Save matjam/fd6596de18c62affe0c32cc73c58ff25 to your computer and use it in GitHub Desktop.
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;
std::vector<uint8_t> bitmap;
for (uint32_t y = 0; y < m_height; y++) {
for (uint32_t x = 0; x < m_width; x++) {
uint32_t atlas_offset = 0;
auto &[character, fg, bg] = peek(sf::Vector2i(x, y));
auto fg_color = m_palette_colors[fg];
auto bg_color = m_palette_colors[bg];
auto atlas_offset_it = m_console_atlas_offset.find(character);
if (atlas_offset_it == m_console_atlas_offset.end()) {
// We need to generate a new image for this glyph and write it to the atlas.
// I think this is larger than what we need
bitmap.resize(m_character_width * m_character_height, 0);
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);
// the glyphs coming out of FreeType are bitmap mode so each bit is a pixel.
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;
}
}
}
// we can probably do both of this in a single operation but I've had a horrible
// time doing that. so above, we're wasting a whole byte to represent a bit..
sf::Image glyph_image;
glyph_image.create(m_character_width, m_character_height, sf::Color::Transparent);
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 the pixel is set then write the color as white. We will apply
// color to the vertex.
if (bitmap[offset_x + offset_y * m_character_width] == 255) {
glyph_image.setPixel(offset_x, offset_y, sf::Color::White);
}
}
}
// ok so now we have an image with the rendered glyph in white with a transparent bg.
atlas_offset = setAtlasGlyph(character, glyph_image);
} else {
atlas_offset = atlas_offset_it->second;
}
setQuadColorForScreenLocation(m_console_bg_vertices, sf::Vector2i(x, y), bg_color);
setQuadColorForScreenLocation(m_console_fg_vertices, sf::Vector2i(x, y), fg_color);
setTexCoordsForScreenLocation(m_console_fg_vertices,
sf::Vector2i(x, y),
getAtlasCoordsForOffset(atlas_offset));
m_console_dirty[x + y * m_width] = false; // it's clean now!
}
}
assert(m_console_bg_vertex_buffer.update(m_console_bg_vertices.data(), m_width * m_height * 4, 0));
assert(m_console_fg_vertex_buffer.update(m_console_fg_vertices.data(), m_width * m_height * 4, 0));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment