Putting fonts on a texture

Started by
4 comments, last by deffer 18 years, 8 months ago
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
Advertisement
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.

Edit:
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;	glyphs.resize(chars.size());	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);			continue;		}		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;	}	glyphs.resize(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;		else			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;	m_font->m_image->clear();//	Draw glyphs	std::vector<u8> data;	data.resize(maxArea);	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)			continue;		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)					continue;			//	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;		m_font->m_glyphs.insert(g);		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.
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 :-)
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? ;-)
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
~zix~
---------------------------------------------------Game Programming Resources, Tutorials, and Multimedia | Free Skyboxes
Quote:Original post by zix99
Did you try Bitmap font builder.


Wow, I just looked at their site and it looks like it's just what I dreamed about.
Thanks!

This topic is closed to new replies.

Advertisement