2012-08-29 18:53:53 +00:00
|
|
|
#include "Font.h"
|
|
|
|
#include <iostream>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <vector>
|
|
|
|
#include "Renderer.h"
|
2012-11-20 01:57:34 +00:00
|
|
|
#include <boost/filesystem.hpp>
|
2013-01-04 23:31:51 +00:00
|
|
|
#include "Log.h"
|
2013-06-14 12:34:12 +00:00
|
|
|
#include "Vector2.h"
|
2012-08-29 18:53:53 +00:00
|
|
|
|
|
|
|
FT_Library Font::sLibrary;
|
2012-10-24 15:28:37 +00:00
|
|
|
bool Font::libraryInitialized = false;
|
2012-08-29 18:53:53 +00:00
|
|
|
|
2012-10-24 15:28:37 +00:00
|
|
|
int Font::getDpiX() { return 96; }
|
|
|
|
int Font::getDpiY() { return 96; }
|
2012-08-29 18:53:53 +00:00
|
|
|
|
2012-10-31 14:46:06 +00:00
|
|
|
int Font::getSize() { return mSize; }
|
|
|
|
|
|
|
|
std::string Font::getDefaultPath()
|
|
|
|
{
|
2013-05-13 19:53:28 +00:00
|
|
|
const int fontCount = 4;
|
2013-05-14 19:01:08 +00:00
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
std::string fonts[] = {"DejaVuSerif.ttf",
|
|
|
|
"Arial.ttf",
|
|
|
|
"Verdana.ttf",
|
|
|
|
"Tahoma.ttf" };
|
|
|
|
|
|
|
|
//build full font path
|
|
|
|
TCHAR winDir[MAX_PATH];
|
|
|
|
GetWindowsDirectory(winDir, MAX_PATH);
|
|
|
|
#ifdef UNICODE
|
|
|
|
char winDirChar[MAX_PATH*2];
|
|
|
|
char DefChar = ' ';
|
|
|
|
WideCharToMultiByte(CP_ACP, 0, winDir, -1, winDirChar, MAX_PATH, &DefChar, NULL);
|
|
|
|
std::string fontPath(winDirChar);
|
|
|
|
#else
|
|
|
|
std::string fontPath(winDir);
|
|
|
|
#endif
|
|
|
|
fontPath += "\\Fonts\\";
|
|
|
|
//prepend to font file names
|
|
|
|
for(int i = 0; i < fontCount; i++)
|
|
|
|
{
|
|
|
|
fonts[i] = fontPath + fonts[i];
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
std::string fonts[] = {"/usr/share/fonts/truetype/ttf-dejavu/DejaVuSerif.ttf",
|
2012-12-30 16:40:07 +00:00
|
|
|
"/usr/share/fonts/TTF/DejaVuSerif.ttf",
|
2013-05-13 19:53:28 +00:00
|
|
|
"/usr/share/fonts/dejavu/DejaVuSerif.ttf",
|
|
|
|
"font.ttf" };
|
2013-05-14 19:01:08 +00:00
|
|
|
#endif
|
2012-11-20 01:57:34 +00:00
|
|
|
|
|
|
|
for(int i = 0; i < fontCount; i++)
|
|
|
|
{
|
|
|
|
if(boost::filesystem::exists(fonts[i]))
|
|
|
|
return fonts[i];
|
|
|
|
}
|
|
|
|
|
2013-01-04 23:31:51 +00:00
|
|
|
LOG(LogError) << "Error - could not find a font!";
|
2012-11-20 01:57:34 +00:00
|
|
|
|
|
|
|
return "";
|
2012-10-31 14:46:06 +00:00
|
|
|
}
|
|
|
|
|
2012-08-29 18:53:53 +00:00
|
|
|
void Font::initLibrary()
|
|
|
|
{
|
|
|
|
if(FT_Init_FreeType(&sLibrary))
|
|
|
|
{
|
2013-01-04 23:31:51 +00:00
|
|
|
LOG(LogError) << "Error initializing FreeType!";
|
2012-10-24 15:28:37 +00:00
|
|
|
}else{
|
|
|
|
libraryInitialized = true;
|
2012-08-29 18:53:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Font::Font(std::string path, int size)
|
2013-06-12 12:42:09 +00:00
|
|
|
: fontScale(1.0f)
|
2012-08-29 18:53:53 +00:00
|
|
|
{
|
2012-10-24 15:28:37 +00:00
|
|
|
mPath = path;
|
|
|
|
mSize = size;
|
|
|
|
|
2013-04-10 17:29:07 +00:00
|
|
|
init();
|
2012-10-24 15:28:37 +00:00
|
|
|
}
|
|
|
|
|
2013-04-10 17:29:07 +00:00
|
|
|
void Font::init()
|
2012-10-24 15:28:37 +00:00
|
|
|
{
|
|
|
|
if(!libraryInitialized)
|
|
|
|
initLibrary();
|
|
|
|
|
2012-08-29 21:52:25 +00:00
|
|
|
mMaxGlyphHeight = 0;
|
|
|
|
|
2012-08-29 18:53:53 +00:00
|
|
|
buildAtlas();
|
2012-10-24 15:28:37 +00:00
|
|
|
}
|
|
|
|
|
2013-04-10 17:29:07 +00:00
|
|
|
void Font::deinit()
|
2012-10-24 15:28:37 +00:00
|
|
|
{
|
|
|
|
if(textureID)
|
|
|
|
glDeleteTextures(1, &textureID);
|
2012-08-29 18:53:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Font::buildAtlas()
|
|
|
|
{
|
2013-06-12 12:42:09 +00:00
|
|
|
if(FT_New_Face(sLibrary, mPath.c_str(), 0, &face))
|
|
|
|
{
|
|
|
|
LOG(LogError) << "Error creating font face! (path: " << mPath.c_str();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//FT_Set_Char_Size(face, 0, size * 64, getDpiX(), getDpiY());
|
|
|
|
FT_Set_Pixel_Sizes(face, 0, mSize);
|
|
|
|
|
2012-08-29 18:53:53 +00:00
|
|
|
//find the size we should use
|
|
|
|
FT_GlyphSlot g = face->glyph;
|
|
|
|
int w = 0;
|
|
|
|
int h = 0;
|
|
|
|
|
|
|
|
/*for(int i = 32; i < 128; i++)
|
|
|
|
{
|
|
|
|
if(FT_Load_Char(face, i, FT_LOAD_RENDER))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Loading character %c failed!\n", i);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
w += g->bitmap.width;
|
|
|
|
h = std::max(h, g->bitmap.rows);
|
|
|
|
}*/
|
|
|
|
|
2012-08-29 21:52:25 +00:00
|
|
|
//the max size (GL_MAX_TEXTURE_SIZE) is like 3300
|
2012-08-29 18:53:53 +00:00
|
|
|
w = 2048;
|
2012-08-29 21:52:25 +00:00
|
|
|
h = 512;
|
2012-08-29 18:53:53 +00:00
|
|
|
|
|
|
|
textureWidth = w;
|
|
|
|
textureHeight = h;
|
|
|
|
|
|
|
|
//create the texture
|
|
|
|
glGenTextures(1, &textureID);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, textureID);
|
|
|
|
|
|
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
|
|
|
|
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
|
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
|
|
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, NULL);
|
|
|
|
|
|
|
|
//copy the glyphs into the texture
|
|
|
|
int x = 0;
|
|
|
|
int y = 0;
|
|
|
|
int maxHeight = 0;
|
|
|
|
for(int i = 32; i < 128; i++)
|
|
|
|
{
|
|
|
|
if(FT_Load_Char(face, i, FT_LOAD_RENDER))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
//prints rendered texture to the console
|
|
|
|
/*std::cout << "uploading at x: " << x << ", w: " << g->bitmap.width << " h: " << g->bitmap.rows << "\n";
|
|
|
|
|
|
|
|
for(int k = 0; k < g->bitmap.rows; k++)
|
|
|
|
{
|
|
|
|
for(int j = 0; j < g->bitmap.width; j++)
|
|
|
|
{
|
|
|
|
if(g->bitmap.buffer[g->bitmap.width * k + j])
|
|
|
|
std::cout << ".";
|
|
|
|
else
|
|
|
|
std::cout << " ";
|
|
|
|
}
|
|
|
|
std::cout << "\n";
|
|
|
|
}*/
|
|
|
|
|
|
|
|
if(x + g->bitmap.width >= textureWidth)
|
|
|
|
{
|
|
|
|
x = 0;
|
2013-06-12 12:42:09 +00:00
|
|
|
y += maxHeight + 1; //leave one pixel of space between glyphs
|
2012-08-29 18:53:53 +00:00
|
|
|
maxHeight = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(g->bitmap.rows > maxHeight)
|
|
|
|
maxHeight = g->bitmap.rows;
|
|
|
|
|
2012-08-29 21:52:25 +00:00
|
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, g->bitmap.width, g->bitmap.rows, GL_ALPHA, GL_UNSIGNED_BYTE, g->bitmap.buffer);
|
2012-08-29 18:53:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
charData[i].texX = x;
|
2012-08-29 21:52:25 +00:00
|
|
|
charData[i].texY = y;
|
2012-08-29 18:53:53 +00:00
|
|
|
charData[i].texW = g->bitmap.width;
|
|
|
|
charData[i].texH = g->bitmap.rows;
|
2013-06-27 10:30:04 +00:00
|
|
|
charData[i].advX = (float)g->metrics.horiAdvance / 64.0f;
|
|
|
|
charData[i].advY = (float)g->metrics.vertAdvance / 64.0f;
|
|
|
|
charData[i].bearingX = (float)g->metrics.horiBearingX / 64.0f;
|
|
|
|
charData[i].bearingY = (float)g->metrics.horiBearingY / 64.0f;
|
2012-08-29 18:53:53 +00:00
|
|
|
|
2012-08-29 21:52:25 +00:00
|
|
|
if(charData[i].texH > mMaxGlyphHeight)
|
|
|
|
mMaxGlyphHeight = charData[i].texH;
|
|
|
|
|
2013-06-12 12:42:09 +00:00
|
|
|
x += g->bitmap.width + 1; //leave one pixel of space between glyphs
|
2012-08-29 18:53:53 +00:00
|
|
|
}
|
|
|
|
|
2013-06-12 12:42:09 +00:00
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
|
|
|
|
FT_Done_Face(face);
|
|
|
|
|
|
|
|
if((y + maxHeight) >= textureHeight)
|
2012-10-31 14:46:06 +00:00
|
|
|
{
|
2013-06-12 12:42:09 +00:00
|
|
|
//failed to create a proper font texture
|
2013-06-14 12:34:12 +00:00
|
|
|
LOG(LogWarning) << "Font with size " << mSize << " exceeded max texture size! Trying again...";
|
2013-06-12 12:42:09 +00:00
|
|
|
//try a 3/4th smaller size and redo initialization
|
|
|
|
fontScale *= 1.25f;
|
|
|
|
mSize = (int)(mSize * (1.0f / fontScale));
|
|
|
|
deinit();
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
else {
|
2013-06-14 12:34:12 +00:00
|
|
|
LOG(LogInfo) << "Created font with size " << mSize << ".";
|
2012-10-31 14:46:06 +00:00
|
|
|
}
|
2012-08-29 18:53:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Font::~Font()
|
|
|
|
{
|
2012-10-24 15:28:37 +00:00
|
|
|
if(textureID)
|
|
|
|
glDeleteTextures(1, &textureID);
|
2012-08-29 18:53:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-14 12:34:12 +00:00
|
|
|
struct Vertex
|
|
|
|
{
|
|
|
|
Vector2<GLfloat> pos;
|
|
|
|
Vector2<GLfloat> tex;
|
2012-08-29 18:53:53 +00:00
|
|
|
};
|
|
|
|
|
2012-10-17 18:21:56 +00:00
|
|
|
void Font::drawText(std::string text, int startx, int starty, int color)
|
2012-08-29 18:53:53 +00:00
|
|
|
{
|
2012-10-24 15:28:37 +00:00
|
|
|
if(!textureID)
|
|
|
|
{
|
2013-01-04 23:31:51 +00:00
|
|
|
LOG(LogError) << "Error - tried to draw with Font that has no texture loaded!";
|
2012-10-24 15:28:37 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-14 12:34:12 +00:00
|
|
|
const int triCount = text.length() * 2;
|
|
|
|
Vertex* vert = new Vertex[triCount * 3];
|
|
|
|
GLubyte* colors = new GLubyte[triCount * 3 * 4];
|
2012-08-29 18:53:53 +00:00
|
|
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, textureID);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
|
|
|
|
//texture atlas width/height
|
|
|
|
float tw = (float)textureWidth;
|
|
|
|
float th = (float)textureHeight;
|
|
|
|
|
2013-05-13 19:53:28 +00:00
|
|
|
float x = (float)startx;
|
2013-06-12 12:42:09 +00:00
|
|
|
float y = starty + mMaxGlyphHeight * 1.1f * fontScale; //padding (another 0.5% is added to the bottom through the sizeText function)
|
|
|
|
|
2013-06-14 12:34:12 +00:00
|
|
|
int charNum = 0;
|
|
|
|
for(int i = 0; i < triCount * 3; i += 6, charNum++)
|
2012-08-29 18:53:53 +00:00
|
|
|
{
|
2013-06-14 12:34:12 +00:00
|
|
|
unsigned char letter = text[charNum];
|
2012-08-29 18:53:53 +00:00
|
|
|
|
|
|
|
if(letter < 32 || letter >= 128)
|
2012-09-16 19:18:11 +00:00
|
|
|
letter = 127; //print [X] if character is not standard ASCII
|
2012-08-29 18:53:53 +00:00
|
|
|
|
2013-06-27 10:30:04 +00:00
|
|
|
//the glyph might not start at the cursor position, but needs to be shifted a bit
|
|
|
|
const float glyphStartX = x + charData[letter].bearingX * fontScale;
|
2013-06-14 12:34:12 +00:00
|
|
|
//order is bottom left, top right, top left
|
2013-06-27 10:30:04 +00:00
|
|
|
vert[i + 0].pos = Vector2<GLfloat>(glyphStartX, y + (charData[letter].texH - charData[letter].bearingY) * fontScale);
|
|
|
|
vert[i + 1].pos = Vector2<GLfloat>(glyphStartX + charData[letter].texW * fontScale, y - charData[letter].bearingY * fontScale);
|
|
|
|
vert[i + 2].pos = Vector2<GLfloat>(glyphStartX, vert[i + 1].pos.y);
|
2013-06-14 12:34:12 +00:00
|
|
|
|
|
|
|
Vector2<int> charTexCoord(charData[letter].texX, charData[letter].texY);
|
|
|
|
Vector2<int> charTexSize(charData[letter].texW, charData[letter].texH);
|
|
|
|
|
|
|
|
vert[i + 0].tex = Vector2<GLfloat>(charTexCoord.x / tw, (charTexCoord.y + charTexSize.y) / th);
|
|
|
|
vert[i + 1].tex = Vector2<GLfloat>((charTexCoord.x + charTexSize.x) / tw, charTexCoord.y / th);
|
|
|
|
vert[i + 2].tex = Vector2<GLfloat>(vert[i + 0].tex.x, vert[i + 1].tex.y);
|
|
|
|
|
|
|
|
//next triangle (second half of the quad)
|
|
|
|
vert[i + 3].pos = vert[i + 0].pos;
|
|
|
|
vert[i + 4].pos = vert[i + 1].pos;
|
|
|
|
vert[i + 5].pos.x = vert[i + 1].pos.x;
|
|
|
|
vert[i + 5].pos.y = vert[i + 0].pos.y;
|
|
|
|
|
|
|
|
vert[i + 3].tex = vert[i + 0].tex;
|
|
|
|
vert[i + 4].tex = vert[i + 1].tex;
|
|
|
|
vert[i + 5].tex.x = vert[i + 1].tex.x;
|
|
|
|
vert[i + 5].tex.y = vert[i + 0].tex.y;
|
2013-06-12 12:42:09 +00:00
|
|
|
|
|
|
|
x += charData[letter].advX * fontScale;
|
2012-08-29 18:53:53 +00:00
|
|
|
}
|
|
|
|
|
2013-06-14 12:34:12 +00:00
|
|
|
Renderer::buildGLColorArray(colors, color, triCount * 3);
|
2012-08-29 18:53:53 +00:00
|
|
|
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glEnableClientState(GL_COLOR_ARRAY);
|
|
|
|
|
2013-06-14 12:34:12 +00:00
|
|
|
glVertexPointer(2, GL_FLOAT, sizeof(Vertex), &vert[0].pos);
|
|
|
|
glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &vert[0].tex);
|
2012-08-29 18:53:53 +00:00
|
|
|
glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors);
|
|
|
|
|
2013-06-14 12:34:12 +00:00
|
|
|
glDrawArrays(GL_TRIANGLES, 0, triCount * 3);
|
2012-08-29 18:53:53 +00:00
|
|
|
|
|
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
|
|
|
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
|
2013-06-14 12:34:12 +00:00
|
|
|
delete[] vert;
|
2012-08-29 19:22:05 +00:00
|
|
|
delete[] colors;
|
2012-08-29 18:53:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Font::sizeText(std::string text, int* w, int* h)
|
|
|
|
{
|
2013-06-14 11:26:04 +00:00
|
|
|
float cwidth = 0.0f;
|
2012-08-29 21:52:25 +00:00
|
|
|
for(unsigned int i = 0; i < text.length(); i++)
|
2012-08-29 18:53:53 +00:00
|
|
|
{
|
2012-09-04 16:45:16 +00:00
|
|
|
unsigned char letter = text[i];
|
|
|
|
if(letter < 32 || letter >= 128)
|
2012-10-24 15:28:37 +00:00
|
|
|
letter = 127;
|
2012-09-04 16:45:16 +00:00
|
|
|
|
2013-06-12 12:42:09 +00:00
|
|
|
cwidth += charData[letter].advX * fontScale;
|
2012-08-29 21:52:25 +00:00
|
|
|
}
|
2012-08-29 18:53:53 +00:00
|
|
|
|
2012-08-29 21:52:25 +00:00
|
|
|
if(w != NULL)
|
2013-06-14 11:26:04 +00:00
|
|
|
*w = (int)cwidth;
|
2012-08-29 18:53:53 +00:00
|
|
|
|
|
|
|
if(h != NULL)
|
2013-06-14 11:26:04 +00:00
|
|
|
*h = getHeight();
|
2012-08-29 18:53:53 +00:00
|
|
|
}
|
2012-10-24 15:28:37 +00:00
|
|
|
|
|
|
|
int Font::getHeight()
|
|
|
|
{
|
2013-06-12 12:42:09 +00:00
|
|
|
return (int)(mMaxGlyphHeight * 1.5f * fontScale);
|
2012-10-24 15:28:37 +00:00
|
|
|
}
|