Advertisement Jump to content
Sign in to follow this  

Putting fonts on a texture

This topic is 4906 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi. I am in need to render some fonts, actually single letters, on objects. To do this I plan on putting all fonts of a single style on one big texture, and give objects being rendered proper texture coordinates, according to which letter it wants to use. To do this properly, I need the letters to create some kind of regular grid on the texture. Well, I've seen those kind of textures a few times, but I'm in need to do this myself, since I need high resolution for this. Is there some way to do this fast and smooth? In photoshop maybe? Or are there any automatic ways of doing this. I don't want to do this by hand, since there will bea lot of tweaking requiring changing fonts for the best fit, so I would have to do this maaany times. Cheers. /def

Share this post

Link to post
Share on other sites
Write a program to render the chars (glyphs) that you wan't into an image, then convert it to a texture?

Look up GetGlyphOutline and CreateFont to start.

It's not that hard, I wrote my converter in a few hours.

Here's the source for my converter, it's not going to compile out of the box, but all the relevant win32 bits are in there.

// Create the font
m_gdiFont = CreateFont(height, width, angle, angle, weight, ((flags & flagItalic) != 0) ? 1 : 0, ((flags & flagUnderline) != 0) ? 1 : 0, ((flags & flagStrikeOut) != 0) ? 1 : 0, charSet, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, DEFAULT_PITCH | FF_DONTCARE, faceName.c_str());
if (!m_gdiFont){
errorSet("Couldn't create font '%s' with the specified parameters", faceName.c_str());
return false;
// Create DC
m_dc = CreateDC("DISPLAY", NULL, NULL, NULL);
if (!m_dc){
errorSet("Couldn't create DC");
return false;
SelectObject(m_dc, m_gdiFont);
// Get glyph sizes
std::vector<Glyph> glyphs;
MAT2 mat;
Mem::set(&mat, 0, sizeof(mat));
mat.eM11.value = 1;
mat.eM22.value = 1;
uint out = 0;
uint i;
for (i = 0; i < chars.size(); ++ i){
glyphs[out].m_glyph = chars;
glyphs[out].m_size = GetGlyphOutline(m_dc, glyphs[out].m_glyph, GGO_METRICS, &glyphs[out].m_metrics, 0, NULL, &mat);
if (glyphs[out].m_size == GDI_ERROR){
logWarning("Couldn't get glyph: 0x%08x", glyphs[out].m_glyph);
glyphs[out].m_size = GetGlyphOutline(m_dc, glyphs[out].m_glyph, GGO_GRAY8_BITMAP, &glyphs[out].m_metrics, 0, NULL, &mat);
glyphs[out].m_metrics.gmBlackBoxX = glyphs[out].m_size / glyphs[out].m_metrics.gmBlackBoxY;
++ out;
// Calculate the total area of the glyphs and get the greatest area
uint size = glyphs.size();
uint maxArea = 0;
uint area = 0;
for (i = 0; i < size; ++ i){
uint a = (glyphs.m_metrics.gmBlackBoxX + spacing) * (glyphs.m_metrics.gmBlackBoxY + spacing);
area += a;
a = glyphs.m_size;
if (a > maxArea)
maxArea = a;
// Calculate initial image size
uint w = IntMath::sqrti(area);
if (!Bit::isPow2(w))
w = Bit::nextPow2(w);
uint h = (area + w - 1) / w;
if (!Bit::isPow2(h))
h = Bit::nextPow2(h);
PHAT_ASSERT(w * h >= area);
// Sort glyph in increasing size order
std::sort(glyphs.begin(), glyphs.end());
// Try to fit glyphs
while (!fit(w, h, spacing, glyphs)){
// Increase image size if they didn't fit
if (h < w)
h += h;
w += w;
// Create the font-image
m_font->m_image = Image::create(w, h, 0, 0, 0, 8);
if (!m_font->m_image)
return false;
// Draw glyphs
std::vector<u8> data;
for (i = 0; i < glyphs.size(); ++ i){
uint size = GetGlyphOutline(m_dc, glyphs.m_glyph, GGO_GRAY8_BITMAP, &glyphs.m_metrics, maxArea, &data[0], &mat);
if (size == GDI_ERROR)
uint index = 0;
uint y;
uint sx = size / glyphs.m_metrics.gmBlackBoxY;
for (y = 0; y < glyphs.m_metrics.gmBlackBoxY; ++ y){
uint x;
for (x = 0; x < sx; ++ x){
uint c = data[index ++];
if (c == 0)
// Rescale to [0, 255]
c = (c << 8) - c;
c >>= 6;
m_font->m_image->setIndex(glyphs.m_x + x, glyphs.m_y + y, c);
// Output glyph data
m_font->m_height = 0;
for (i = 0; i < glyphs.size(); ++ i){
Glyph g;
g.m_glyph = glyphs.m_glyph;
g.m_tx0 = glyphs.m_x;
g.m_ty0 = glyphs.m_y;
g.m_tx1 = g.m_tx0 + glyphs.m_metrics.gmBlackBoxX;
g.m_ty1 = g.m_ty0 + glyphs.m_metrics.gmBlackBoxY;
g.m_px = glyphs.m_metrics.gmptGlyphOrigin.x;
g.m_py = glyphs.m_metrics.gmptGlyphOrigin.y;
g.m_sx = glyphs.m_metrics.gmCellIncX;
g.m_sy = (glyphs.m_metrics.gmCellIncY * stepHeightScale) / 100;
int h = g.m_py + (g.m_ty1 - g.m_ty0);
if (h > int(m_font->m_height))
m_font->m_height = uint(h);

It doesn't output on a regular grid, rather it tries to fit all glyphs into the smallest possible image (pow2 x pow2).
Then I've got a map from glyph=>texture coords that I use when rendering.

Share this post

Link to post
Share on other sites
Hi :-)

If you're using OpenGL with SDL and SDL_TTF, you may want to look at this thread, where I've posted code of my font engine. If you're using different API, I suggest to visit it anyway :-)

About that code: basically, it loads .ttf font, and if detects whether letters that you want to render, were used. If not, it creates single texture (for every single letter) and renders it letter on that texture. If yes, then just glBindTexture() and do what you want.

That way, you aren't wasting memory for letters you aren't using, and it allows for very high font sizes. Also, you don't do it by hand since letters are generated at runtime. Disadvantage: high number of small textures may fragment memory. The choice is yours :-)

Share this post

Link to post
Share on other sites
Thanks, guys... but... OMG! A lot of code! A lot of work to be done...
All right, all right, guess I will look at it during the weekend. Try construct something handy. This is for offline only, so speed and memory consumption is not an issue here.

Until then, maybe someone will save me(from inevitably large amount of work) with a simple solution? ;-)

Share this post

Link to post
Share on other sites
Did you try Bitmap font builder. It allows textures up to the size of 2048x2048, and can explort in the bitmap format, targa format, and even exports the size of each letter (so it doesn't look blocky)

Hopefully this will help out a bit so it's less work for you

Share this post

Link to post
Share on other sites
Sign in to follow this  

  • Advertisement

Important Information

By using, you agree to our community Guidelines, Terms of Use, and Privacy Policy. is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!