#include "PreCompile.h"
#include "FontAtlas.h"
#include <ft2build.h>
#include <freetype/freetype.h>
#include <freetype/ftglyph.h>
#include <freetype/ftoutln.h>
#include <freetype/fttrigon.h>
#include "FTBitmapChar.h"
#include "TreeNode.h"
#include <algorithm>
#include "ResManager.h"
#include "FTBitmapFont.h"
FontAtlas::FontAtlas()
{
if (FT_Init_FreeType( &m_pLibrary ))
throw RacException("FT_Init_FreeType failed");
}
FontAtlas::~FontAtlas()
{
int n;
for (n = 0; n < (int)m_listBitmapChar.size(); n++)
{
delete m_listBitmapChar[n];
}
for (n = 0; n < (int)m_listBitmapFont.size(); n++)
{
delete m_listBitmapFont[n];
}
if (m_pShowAtlas) delete m_pShowAtlas;
Release();
FT_Done_FreeType(m_pLibrary);
}
void FontAtlas::AddFont(const string strFileName, int size, const char* szLetters)
{
// The Object In Which FreeType Holds Information On A Given
// Font Is Called A "face".
FT_Face face;
string file = g_resManager.GetDirResources()+strFileName;
// This Is Where We Load In The Font Information From The File.
if (FT_New_Face(m_pLibrary, file.c_str(), 0, &face ))
throw RacException("no font: ", file);
// FreeType Measures Font Size In Terms Of 1/64ths Of Pixels.
FT_Set_Char_Size(face, size*64, size*64, 72, 72);
int len = strlen(szLetters);
int n;
FTBitmapChar* pFTBitmapChar;
FT_Glyph pGlyph;
FTBitmapFont* pFTBitmapFont = new FTBitmapFont(this);
pFTBitmapFont->SetLineHeight(face->size->metrics.height>>6);
pFTBitmapFont->SetFTFace(face);
m_listBitmapFont.push_back(pFTBitmapFont);
char c;
int height;
int yOffset;
for (n = 0; n < len; n++)
{
c = szLetters[n];
// Load The Glyph For Our Character.
if(FT_Load_Glyph( face, FT_Get_Char_Index(face, szLetters[n]), FT_LOAD_DEFAULT))
throw RacException("FT_Load_Glyph failed");
// Move The Face's Glyph Into A Glyph Object.
if (FT_Get_Glyph(face->glyph, &pGlyph ))
throw RacException("FT_Get_Glyph failed");
pFTBitmapChar = new FTBitmapChar((unsigned char)szLetters[n]);
// all metrics dimensions are multiplied by 64, so we have to divide by 64
height = face->glyph->metrics.height >> 6;
yOffset = -(face->glyph->metrics.horiBearingY >> 6) ;
pFTBitmapChar->SetOffsets(face->glyph->metrics.horiBearingX >> 6,
yOffset);
pFTBitmapChar->SetSize(face->glyph->metrics.width >> 6, height);
pFTBitmapChar->SetXAdvance(face->glyph->metrics.horiAdvance >> 6);
pFTBitmapChar->SetGlyph(pGlyph);
m_listBitmapChar.push_back(pFTBitmapChar);
pFTBitmapFont->AddChar(szLetters[n], pFTBitmapChar);
}
}
void GetNextTextureSize(int &texWidth, int &texHeight, int ixSize)
{
if (ixSize % 2)
texHeight *= 2;
else
texWidth *= 2;
if (texWidth > 1024 || texHeight > 1024)
{
throw RacException("to many images to fit in one texture");
}
}
bool GreaterSize(FTBitmapChar* pFTBitmapChar1, FTBitmapChar* pFTBitmapChar2)
{
return pFTBitmapChar1->GetNumPixels() > pFTBitmapChar2->GetNumPixels();
}
void FontAtlas::CreateAtlas()
{
int n;
int totalPixels = 0;
for (n = 0; n < (int)m_listBitmapChar.size(); n++)
{
totalPixels += m_listBitmapChar[n]->GetNumPixels();
}
int ixSize = 0;
int texWidth = 32;
int texHeight = 32;
while (true)
{
if (totalPixels <= texWidth*texHeight)
{
break;
}
GetNextTextureSize(texWidth, texHeight, ixSize);
ixSize++;
}
sort(m_listBitmapChar.begin(), m_listBitmapChar.end(), GreaterSize);
while (!BinPack(texWidth, texHeight))
{
GetNextTextureSize(texWidth, texHeight, ixSize);
ixSize++;
}
unsigned char* pData = new unsigned char[texWidth * texHeight];
memset(pData, 0, texWidth * texHeight);
for (n = 0; n < (int)m_listBitmapChar.size(); n++)
{
m_listBitmapChar[n]->DrawToBitmap(pData, texWidth, texHeight);
m_listBitmapChar[n]->ReleaseGlyph();
}
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &m_textureID);
glBindTexture( GL_TEXTURE_2D, m_textureID);
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texWidth, texHeight,
0, GL_ALPHA, GL_UNSIGNED_BYTE, pData);
int err = glGetError();
if (err != GL_NO_ERROR)
{
throw RacException("Error in glTexImage2D: ", err);
}
// clean up memory
delete [] pData;
m_pShowAtlas = new FTBitmapChar();
m_pShowAtlas->SetXY(0, 0);
m_pShowAtlas->SetSize(texWidth, texHeight);
m_pShowAtlas->InitTexCoords(texWidth, texHeight);
}
void FontAtlas::RenderAtlas(int x, int y)
{
g_resManager.GetBatcher().SetAttribs(
m_textureID, 0, 1.0f, true, false);
m_pShowAtlas->Render(x, y);
}
bool FontAtlas::BinPack(int texWidth, int texHeight)
{
// to do sort bitmaps by size
TreeNode treeNode(0, 0, texWidth, texHeight);
for (int n = 0; n < (int)m_listBitmapChar.size(); n++)
{
if (!treeNode.Add(m_listBitmapChar[n]))
{
return false;
}
}
return true;
}
void FontAtlas::Release()
{
// note: make sure you do this before shutting down opengl
if (m_textureID != 0)
{
glDeleteTextures(1, &m_textureID);
m_textureID = 0;
}
}