Putting fonts on a texture
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
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.
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.
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 :-)
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? ;-)
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~
Hopefully this will help out a bit so it's less work for you
~zix~
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
Popular Topics
Advertisement