I am writing a simple OpenGL/Freetype2 font engine for a project I am working on. I got it up and running so far, but there are a couple of things I hope someone can explain to me since I used some code I copied from the internet, and could not fully understand it.
I was unable to color my fonts using the original method (it was always black) and I don't understand how the new loading technique (gotten from NeHe tut #43) works to solve this. Moreover, why didn't the original method work? Could I still do it somehow by passing some other combination of GL enums to glTexImage2D? I am also not sure I understand the GL_* enums I pass. I would appreciate it if someone could explain it to me
Here's my new and current loading code which works:
bool Font::Load(const char* fontFilePath, int pixelSize)
{
// Error checking
if (fontFilePath == "")
{
std::cerr << "sl::Font: Empty file path passed as argument" << std::endl;
return false;
}
if (pixelSize == 0)
{
std::cerr << "sl::Font: Passed a zero character size" << std::endl;
return false;
}
if (!Font::library)
{
std::cerr << "sl::Font: FreeType2 library not initialized properly (" << ErrorString() << ")" << std::endl;
return false;
}
if (error = (FT_New_Face(Font::library, fontFilePath, 0, &face)))
{
std::cerr << "sl::Font: Unable to create new face (" << ErrorString() << ")" << std::endl;
return false;
}
source.assign(fontFilePath);
this->pixelSize = pixelSize;
// Warn the user if the font is not scalable (i.e. vector graphics/outline font)
if(!(face->face_flags & FT_FACE_FLAG_SCALABLE) || !(face->face_flags & FT_FACE_FLAG_HORIZONTAL))
std::cerr << "sl::Font warning: Font is not scalable (" << fontFilePath << ")" << std::endl;
FT_Set_Pixel_Sizes(face, pixelSize, pixelSize);
int textureWidth = 0;
int textureHeight = 0;
for(int c = 0; c < 256; ++c)
{
// Load the character into the face's glyph slot
FT_Load_Char(face, (char)c, FT_LOAD_RENDER);
// Save glyph stats
Glyph glyph;
glyph.Advance = face->glyph->advance.x >> 6;
glyph.Width = face->glyph->bitmap.width;
glyph.Height = face->glyph->bitmap.rows;
glyph.LeftBearing = face->glyph->bitmap_left;
glyph.TopBearing = face->glyph->bitmap_top;
textureWidth = NextPowerOf2(glyph.Width);
textureHeight = NextPowerOf2(glyph.Height);
// Adjust texture coordinate for glyph
glyph.texCoordX = (float)glyph.Width/(float)textureWidth;
glyph.texCoordY = (float)glyph.Height/(float)textureHeight;
// Array to hold pixel data
GLubyte* data = new GLubyte[2 * textureWidth * textureHeight];
// Pixel positions out of range (in padding area) are set to zero
for (int y = 0; y < textureHeight; ++y)
{
for (int x = 0; x < textureWidth; ++x)
{
//data[x + y * textureWidth] =
// (x >= glyph.Width || y >= glyph.Height) ?
// 0 :
// face->glyph->bitmap.buffer[x + y * glyph.Width];
data[2 * (x + y * textureWidth)] = data[2 * (x + y * textureWidth) + 1] =
(x >= glyph.Width || y >= glyph.Height) ?
0 : face->glyph->bitmap.buffer[x + glyph.Width * y];
}
}
if (!data)
{
std::cerr << "sl::Font: Font data is corrupt" << std::endl;
return false;
}
glGenTextures(1, &glyph.TextureID);
glBindTexture(GL_TEXTURE_2D, glyph.TextureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureWidth, textureHeight, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, data);
glyphList.push_back(glyph);
delete[] data;
}
std::cout << "Size: " << glyphList.size() << std::endl;
return true;
}
This is part of the original loading code (everything else is identical to the above):
// Same code here as above
// Array to hold pixel data
GLubyte* data = new GLubyte[textureWidth * textureHeight];
for (int y = 0; y < textureHeight; ++y)
{
for (int x = 0; x < textureWidth; ++x)
{
//data[x + y * textureWidth] =
// (x >= glyph.Width || y >= glyph.Height) ?
// 0 :
// face->glyph->bitmap.buffer[x + y * glyph.Width];
data[(x + y * textureWidth)] =
(x >= glyph.Width || y >= glyph.Height) ?
0 : face->glyph->bitmap.buffer[x + glyph.Width * y];
}
}
// Same code here, but different call to glTexImage2D
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureWidth, textureHeight, 0, GL_ALPHA, GL_UNSIGNED_BYTE, data);