Click here to Skip to main content
15,896,379 members
Articles / Mobile Apps / iPhone

FreeType on OpenGL ES (iPhone)

Rate me:
Please Sign up or sign in to vote.
4.75/5 (10 votes)
24 Nov 2010CPOL4 min read 87K   3K   27  
Faster, smarter and better looking fonts rendered with OpenGL ES
#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;
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Astronautz
Spain Spain
After working in the software industry for many years, I've started my own games company that specialises in strategy games for mobile platforms.

Comments and Discussions