Log in

View Full Version : Yes! YOU can have proportionally-spaced fonts!


Larry Hastings
01-17-2004, 10:31 PM
Like most everybody else, I have my own font engine. But I only supported monospaced fonts. I figured it would be too big a pain to support proportionally spaced fonts--I'd had enough trouble just making the monospaced font texture, so making one for a proportionally spaced font sounded like a real pain. And I didn't look forward to generating the character width information!

Well, yesterday I finally got sick of looking at my monospaced font, and of being envious of other games' proportionally-spaced fonts. So I dug out a tool I downloaded most of a year ago, and lookee there! it was actually pretty easy. Less than a day of work later and I had 'em working--and I'm here to share with you the two stumbling blocks I tripped over.



The tool I downloaded a year ago was MudgeFont (http://sourceforge.net/projects/mudgefont/). I first read about it when it was Flipcode's Code Of The Day for May 19th, 2003 (http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-FontGenerationTool&forum=cotd&id=-1). I suspect font generator tools are a dime a dozen, but this one was nice because of its liberal license. The author wrote:This tool is quite similar to "all" (I only found 2) those other font generation tools, but it comes with full source-code, and you are free to use, modify, and redistribute it, also for commercial use.. no GPL or anything... just completely free.You specify what font/size you want, and how big you want the bitmap to be, and it generates two files: a .TGA file with the font encoded in the alpha channel, and a .XML file describing the coordinates and width/height for each character in the font. You can also save your preferences, in a (poorly-named) .FNT file, though the reloading later is a skosh wonky.

Generating the font I wanted was a snap, and I had the TGA and XML file in five minutes. My next step, and the first stumbling block, was getting that XML into a format I actually wanted to use. As ostensibly "easy" a format as XML is, I don't want to add another file format to my game. I have my own deadly simple hierarchical data format, which in a whimsical moment I named "Twiddle". It looks like this:
a = b
x = y
subkey name
{
this=that
those other things = whatever
nested dude
{
oops = I did it again
}
}
You get the idea. Anyway, I wanted to get the font metrics as a Twiddle file instead of as XML. I considered just hacking the MudgeFont sources to output Twiddle, but I didn't want to maintain those diffs. I also thought about a brute-force converter, ignoring the XML and just picking out the pieces I needed, but that too seemed inflexible and subject to breakage if MudgeFont ever changed.

So I looked into using Python's XML support. After a couple of false starts, I figured out how to suck the XML file in and work it over with Python's xml.dom.minidom library. My final Python script is attached to this posting; feel free to download it and adapt it to your funny-little hierarachical file format. Just specify the .xml files on the command-line, and it'll generate matching .txt files. I'm glad I settled on that solution, as it's definitely the best of the three solutions, and it wasn't really any harder or more time-consuming.



So, that was problem one. Problem two came later. First I had a long debugging session, until I realized I had inverted the vertices for the font (my Y coordinates start at the lower left and increase going up the screen, but my font had the bottoms above the tops, which flipped them from CW to CCW...). Once I had the font rendering to the screen, I discovered the second problem: sometimes I was getting little thin lines between characters, or sometimes above or below characters.

I'd had a problem with that before on my monospaced font, where the texel coordinates were just bordering on the next character over, and with the occasional rounding error I'd sometimes pick up some pixels from the neighboring character. But I knew that MudgeFont generates a one-pixel (or wider) border around each character, so that couldn't be it... could it?

It turned out that it was, sorta. I wasn't picking up the next character. Rather, the characters were surrounded with boxes of opaque pixels! When MudgeFont generated the font, it first filled in the bitmap with bright-cyan pixels. It would then clear the rectangle for each character as it drew it, but it would leave the surrounding bright cyan pixels alone. Those wound up in the final texture. (Seeing that in the final texture was a pain, as Photoshop would draw those TGA files as all opaque white. I only saw it when I tried the texture in the DirectX Texture Tool.)

My fix for that was a change to MudgeFont. Unfortunately, the email address for MudgeFont's author doesn't work anymore, so I don't know if this was the right thing to do. But it works for me. Just download the MudgeFont sources, then change the first m_Dib.SetPixel() call
in cMudFontDlg::UpdateTextureSize() from this:m_Dib.SetPixel(x,y, RGB(255,0,255));to this:m_Dib.SetPixel(x,y, RGB(0,0,0));Since I wasn't sure what the other m_Dib.SetPixel() calls were for, I took a guess, and changed the one that set pixels black to setting them to bright cyan. (It didn't seem to hurt anything.) With that change in place, MudgeFont generated .TGA files that were completly transparent except where the characters should be. And now my game has sharp-looking proportionally-spaced fonts!

If your game has only monospaced fonts, I hope this article inspires you to trade up to proportionally-spaced fonts. It's not that much harder to do, and you'll be very pleased with the results. I know I am.

Cheers ,

CoLSoN
01-18-2004, 03:01 AM
While parsing XML docs with python I found xmltramp (http://www.aaronsw.com/2002/xmltramp/) to be REALLY easy to work with. It uses SAX but you don't have to care about it.

ggambett
01-19-2004, 04:17 AM
Even this sounds too complicated... if you have all the fonts in a texture and draw quads, each character is stored as the texture coordinates... why not just "horizontally-crop" the rectangle? Works for me...

Raptisoft
01-19-2004, 05:19 AM
Ah, such a pity... if MudFont would make SEVERAL textures if you specify a big font, then it would work for me. But as it is, it just clips them.

Llyod
01-19-2004, 07:54 AM
http://www.lmnopc.com/bitmapfontbuilder/

Creates a font texture from a Truetype font, and adds an .INI file with the char widths. Can't be much simpler?

Larry Hastings
01-20-2004, 02:00 AM
A minor update to the original posting:
In my next-to-last paragraph, I wondered what the "other m_Dib.SetPixel() calls" did. I figured it out: they draw the crosshatched background of the texture window. Set 'em to any color you like, you won't hurt anything.

Raptisoft:
But that would mean a font that straddled multiple textures, and that would be a big pain when it came time to render, no? If you really are willing to deal with multiple textures, you can still use MudFont; just divide your character set between the textures. (You were gonna have to do that anyway, yeah?)

llyod:
I didn't know about BitmapFontBuilder until you mentioned it, but I still like MudFont better. First and foremost, Mudfont packs the characters. Bitmap Font Builder stores the characters in a strict grid. (And I mean strict! If you make the font larger, the wide characters will start overlapping! Now that's just silly!) Compare that to the MudFont screenshot I attached to this reply. The skinny characters take up less space than the wide characters--compare, say, i with W. Better packing means you can render with a larger font. And that means a better-looking font.

Also, BitmapFontBuilder lets you choose from two possible character sets--one full ASCII, and one (the scattershot "NeHe Character Set") where you actually squeeze two fonts onto one texture. MudFont lets you specify the character set, through an admittedly awkward interface. So I don't have to have a permillage (or a double-back-quote or an S with a circumflex) if I don't need it (and I don't!). My MudFont-generated fonts have only the characters I need.

Using these two features together, I have packed a deliciously hi-res 48-point Futura Bold into a 512x512 texture. It has exactly the character set I want (ASCII '!' through '~'), and the characters themselves have enough pixels to look smooth even when large. My results from using BitmapFontBuilder would not look as nice.

ggambett:
How do you find each character in your texture? If your answer is "it's stored in a grid", that means you're not packing 'em in like I am with MudFont. (See above.)

Anyway, surely you realize how easy it is to support proportional rendering. Just subclass your monospaced font renderer, and add an array of 256 structs containing x, y, width, and height. Suck the data in any way you like. In your subclass, overload the stream() function and change "compute the x&y based on the character, use precomputed width&height" to "look up the x&y&width&height". And zippity-doo-dah, you're done!

But I have learned not to argue with success. The fonts in Betty's Beer Bar look fine, and I'm not going to tell you that you'd be better off doing it differently.


And finally, a question for y'all:
I realized that all I really care about in my font texture is the alpha layer. I'm not using the color on the font; I render my fonts with D3DTSS_COLOROP pointed at D3DTA_DIFFUSE. But MudgeFont renders my fonts as 32-bit TGA files. When DX8 reads the file in, it becomes a D3DFMT_A8R8G8B8 texture. Which means my 512x512 font takes up 1MB of memory! Eek!

Is there a lighter-weight D3DFORMAT that is widely supported and would suit my needs? D3DFMT_A8 seems perfect for the job, but it's only supported on relatively modern cards. D3DFMT_A1R5G5B5 is right out, as I render with alphablending sometimes. I saw one clever suggestion to use an 8-bit paletted format (D3DFMT_P8?), but I think card support is shaky. I could live with 16 shades of alpha; is D3DFMT_A4R4G4B4 widely supported? For what it's worth, my goal is to support 16MB cards.

What do you suggest, gentle reader?

ggambett
01-20-2004, 05:12 AM
How do you find each character in your texture? If your answer is "it's stored in a grid", that means you're not packing 'em in like I am with MudFont.
Not a grid. I have one image with all the characters separated by an arbitrary space. Every line starts with an A, which I use as a reference character to compute the vertical offset of each glyph. The strip of characters is read, and each character is separated by an arbitrary number of completely transparent vertical lines.

Sure, individual positions could be computed at compile time and not at run time as I'm doing, but it's so fast I really don't care.

princec
01-20-2004, 06:27 AM
I bet it doesn't kern though, like my magic font rendery thing ;)

Cas :)

Beaker
01-20-2004, 04:56 PM
Hi. There is another tool that packs the fonts, creates a range of effects, and spans multiple textures (although differently to most methods):
FONText bitmap font tool (http://www.playerfactory.co.uk)

Just for completions sake. :)

Dan MacDonald
01-21-2004, 08:07 AM
There's always patrox's font tool...

http://www.phelios.net/font.html

Coyote
01-28-2004, 02:04 PM
Hey - I wanted to thank everyone that contributed to this thread. This made my life MUCH easier.

Larry Hastings
01-28-2004, 02:59 PM
Maybe this is a dumb question, but... both the previous times I posted messages to this thread, I also "attached a file". But I don't see the file attachments. Are they there, and the all-knowing vBB simply chooses not to show them to me, the original poster? Or did I screw up posting them (as is more likely)?

damocles
02-14-2004, 06:00 AM
Time to revive a dying thread :)

I decided to switch from fixed-width to variable-wdith fonts and have started remaking my font engine. I'm using mudFont (it does a good job and the output is simple enough to parse - the only thing I don't like is having to fill the opaque areas in photoshop, but that's not a serious issue).

Anyway, my font engine is working great except for one thing - characters that are designed to be extra wide and overlap the next character create big spaces in my engine because I've just simply been adding the width of the character. Now I want to update it to use the proper font sizing and spacing, but I'm not sure what the three spacing attributes mean in the xml file. They are simply listed as a,b,c. As near as I can figure c is the offset for the next character. I'm guessing a and b might be to do with the offset for the current character, but I'm not certain.

If anyone else already knows what they stand for, please let me know, otherwise I'm going to have to spend ages trying to figure it out by trial and error.

Larry Hastings
02-14-2004, 08:43 AM
Not to overly chide you, but to answer your question I did what you should have done--I went and looked at the mudFont source.

The three spacing values a, b, and c were generated by calling GetCharABCWidths(). The documentation for that function says:
The TrueType rasterizer provides ABC character spacing after a specific point size has been selected. A spacing is the distance added to the current position before placing the glyph. B spacing is the width of the black part of the glyph. C spacing is the distance added to the current position to provide white space to the right of the glyph. The total advanced width is specified by A+B+C.So theoretically you should move right by A, then draw the character, then move right by B+C.

In my engine, I ignored the whole mess. I just add a constant kerning value between each pair of characters. But I don't have any designed-to-overlap characters as you do.

damocles
02-14-2004, 09:52 AM
D'Oh I never thought about the source. I only downloaded the mudFont binary and that was a while ago.

Thanks for the info.

Larry Hastings
02-19-2004, 08:39 PM
I've made some more changes to "MudFont". Enough that it's worth a new zip file. While I was at it, I went ahead and made a web page and everything.

You can read more about it here:
MudgeFont home (http://www.midwinter.com/~lch/programming/mudgefont/).

The big change since my original posting in this thread: now MudgeFont supports negative numbers for border padding on the top/bottom/left/right. This means you can strip off extraneous padding on fonts, letting you squeeze more detail into the same bitmap. Lots of other touch-ups too, see the web page.

Enjoy!

Larry Hastings
02-29-2004, 07:39 AM
Try to contain your excitement, but I've posted a slightly updated version of MudgeFont to my page:

MudgeFont home (http://www.midwinter.com/~lch/programming/mudgefont/)

The big change is that I added "Force Monospaced", which makes it possible to render any font as monospaced. (I had some fonts that were almost monospaced, and... well, anyway.) I also diddled the colors a little. And, and! I added some screenshots to the web page, so now you can see what it looks like before you download it, use it, and send me gifts and karma as a result.

Enjoy!

damocles
02-29-2004, 08:08 AM
Nice work.

Oodles of Karma coming your way. (You can have gifts when I'm a successful indie).

I must admit, I do liek MudgeFont. Most other font makers are rather limited or clearly just designed for use in one person's engine. MF has allowed me to have proportionally spaced fonts - hey, the thread title came true! :)