jes-ruby/src/gui/Font.cc

114 lines
3.3 KiB
C++

#include "Font.h"
#include <iostream>
#include <stdint.h>
#define round_up_26_6(val) (((val) + 63) >> 6u)
namespace jes
{
Font::Font()
{
m_loaded = false;
}
Font::~Font()
{
if (m_loaded)
{
FT_Done_Face(m_face);
}
}
bool Font::load(FT_Library ft, const char * fname, size_t size)
{
if (FT_New_Face(ft, fname, 0, &m_face) != 0)
{
std::cerr << "Could not load font " << fname << std::endl;
return false;
}
FT_Set_Pixel_Sizes(m_face, 0, size);
GlyphRef x_glyph = load_glyph('x');
if (x_glyph == NULL)
{
std::cerr << "Could not load 'x' glyph from font" << fname << std::endl;
return false;
}
m_glyphs['x'] = x_glyph;
m_advance = x_glyph->get_advance();
m_line_height = round_up_26_6(m_face->size->metrics.height);
m_baseline_offset = round_up_26_6((m_face->size->metrics.height - (m_face->size->metrics.ascender - m_face->size->metrics.descender)) / 2 - m_face->size->metrics.descender);
m_loaded = true;
return true;
}
Font::GlyphRef Font::load_glyph(FT_ULong char_code)
{
GlyphRef glyph = new Glyph();
if (glyph->load(m_face, char_code))
return glyph;
return NULL;
}
Font::Glyph::Glyph()
{
m_loaded = false;
}
Font::Glyph::~Glyph()
{
if (m_loaded)
{
glDeleteTextures(1, &m_texture_id);
}
}
bool Font::Glyph::load(FT_Face face, FT_ULong char_code)
{
if (FT_Load_Char(face, char_code, FT_LOAD_RENDER) != 0)
return false;
int width = face->glyph->bitmap.width;
int height = face->glyph->bitmap.rows;
int left = face->glyph->bitmap_left;
int top = face->glyph->bitmap_top;
m_advance = round_up_26_6(face->glyph->advance.x);
uint8_t * texture = new uint8_t[width * height];
for (int i = 0; i < height; i++)
{
memcpy(&texture[width * i],
&face->glyph->bitmap.buffer[width * (height - i - 1)],
width);
}
glGenTextures(1, &m_texture_id);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_texture_id);
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);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, texture);
delete[] texture;
float s = 1.0 / (width * 2);
float t = 1.0 / (height * 2);
GLfloat box[4][4] = {
{(GLfloat)left, (GLfloat)(top - height), s, t},
{(GLfloat)(left + width - 1), (GLfloat)(top - height), 1 - s, t},
{(GLfloat)left, (GLfloat)(top - 1), s, 1 - t},
{(GLfloat)(left + width - 1), (GLfloat)(top - 1), 1 - s, 1 - t},
};
glGenBuffers(1, &m_vbo_id);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id);
glBufferData(GL_ARRAY_BUFFER, sizeof(box), box, GL_STATIC_DRAW);
m_loaded = true;
return true;
}
}