Signed Distance Bitmap Font Tool

Started by
12 comments, last by wagenheimer 15 years, 6 months ago
Hi, All. OK, there is a cool paper from Valve here, on using a signed distance field in the alpha channel of a texture to recover a nice vector-like representation of a shape using a simple alpha threshold, or looking a bit better in a shader with a smoothstep function. Then I saw this journal, where OrangyTang was using it to render text. So I thought it would be cool to have a tool to build these bitmap font textures from the raw TTF font files. And it would be really cool if it had similar features to the awesome AngelCode Bitmap Font Generator! So I'm writing one, and here is the initial trial (it contains the exe and 3 very nice free fonts for testing purposes). How to use: * drag font files onto the exe * the app asks for a texture size (256 will yield a 256x256 texture for each input font file) * the app will render all valid glyphs in the ANSI range [0..255] * the app will automatically search for the largest rendering size (integer pixels only, so hinting works with a greater range of TrueType fonts) where it can pack the output into the texture you specified earlier * the app packs the rendered Signed Distance Field glyphs into a single texture * look at the output PNG and TXT file (in the same directory as the font file) Credits: * FreeType2 for font rendering * LodePNG for saving the resultant file * BinPacker code from GameDev's own jyk! Please try it out and let me know what you think. Here are my ideas for where I'm going after this (please give any feedback or feature requests): * I want to output a text file compliant with AngelCode's BMFont spec so existing loaders can use this output without changing anything other than the alpha threshold * I want an XML file for configuration so you can specify specific unicode characters (I'm thinking <range>A B</range> and <chars>A B C...ZY ZZ</chars>) * I want to allow an image for input, where a Signed Distance Field would then be computed from that into a lower resolution texture So, what do you think? The source will be released (MIT) when I'm a bit closer to done. (Note, the RGB and A channels all share the same signed distance data, so there is a bit of a border to each glyph...I could just use all white for the color, but I think this looks pretty good.)
Advertisement
Very nice tool lonesock, you sure do create alot of useful stuff, much appreciated
Looking good! I certainly think adding ranges for unicode characters is a good thing, not many games can get away with using only ascii characters these days.
Make mine another vote for Unicode, though I wonder whether it would just be a lot simpler to just have a little UTF-8 data file specifying the characters you want, in the order you want them?
I've updated the zip (same link). The changes:

* after dragging fonts onto the exe, you specify texture size and the highest Unicode character number (only characters with glyphs will be rendered).

* the output text file now has the offset and advance information, so you can finally render text properly (I hope!) I appended "page=0 chnl=0" to the end of each line to mirror the AngelCode BMFont tool. Note that this info is not integer data, and the offsets may be negative (both functions of the same thing...the actual glyph has border texels to help define the Signed Distance Field)

* There is a bit more text describing the requested inputs

* there is much better error checking (e.g. dragging non-font files onto the exe)

* the zip now includes all my source code, ugly as it is (it also has the LodePNG and BinPacker code...you'll need to get and compile FreeType2 yourself)


@AndyPandyV2: thanks for the kind words, you're welcome for the app [8^)

@OrangyTang & Kylotan: thanks for the feedback/input. As a simple hack you can now input the range of characters to be rendered. I think I like the XML approach as it would be slightly easier for me to implement than the UTF-8 file with all needed characters for 3 reasons: easier to parse as I already use an XML lib, I could see the UTF-8 file getting huge, and there is no guarantee of the glyph order after the packing (though the attendant text file would be in the specified order). Would the XML file be an OK solution for your needs? Or even the current incarnation? (note that rendering the entire Microsoft Arial font into a 4096x4096 took > 20 minutes, so I could see the use of limited sub-ranges! [8^)

Here is some sample output, as I forgot to include this last time:

and the text file:
info face="Automatica BRK"chars count=97char id=32    x=208   y=249   width=4     height=4     xoffset=-1.500    yoffset=1.500     xadvance=15.625      page=0  chnl=0char id=33    x=247   y=205   width=9     height=22    xoffset=-0.750    yoffset=19.625    xadvance=6.875       page=0  chnl=0char id=34    x=232   y=88    width=17    height=9     xoffset=-0.750    yoffset=19.625    xadvance=14.500      page=0  chnl=0char id=35    x=198   y=105   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=36    x=32    y=105   width=24    height=27    xoffset=-0.750    yoffset=22.125    xadvance=22.438      page=0  chnl=0char id=37    x=0     y=22    width=32    height=22    xoffset=-0.750    yoffset=19.625    xadvance=30.375      page=0  chnl=0char id=38    x=56    y=105   width=24    height=27    xoffset=-0.750    yoffset=22.125    xadvance=22.375      page=0  chnl=0char id=39    x=18    y=220   width=9     height=9     xoffset=-0.750    yoffset=19.625    xadvance=6.625       page=0  chnl=0char id=40    x=80    y=176   width=17    height=22    xoffset=-0.750    yoffset=19.625    xadvance=14.625      page=0  chnl=0char id=41    x=80    y=220   width=17    height=22    xoffset=-0.750    yoffset=19.625    xadvance=14.563      page=0  chnl=0char id=42    x=216   y=88    width=16    height=11    xoffset=-0.750    yoffset=19.625    xadvance=14.125      page=0  chnl=0char id=43    x=72    y=88    width=24    height=17    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=44    x=18    y=229   width=9     height=9     xoffset=-0.750    yoffset=4.750     xadvance=6.625       page=0  chnl=0char id=45    x=223   y=249   width=24    height=7     xoffset=-0.750    yoffset=14.688    xadvance=22.375      page=0  chnl=0char id=46    x=199   y=249   width=9     height=7     xoffset=-0.750    yoffset=4.875     xadvance=6.875       page=0  chnl=0char id=47    x=222   y=105   width=24    height=22    xoffset=-0.750    yoffset=19.563    xadvance=22.375      page=0  chnl=0char id=48    x=56    y=132   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=49    x=246   y=105   width=9     height=22    xoffset=-0.750    yoffset=19.625    xadvance=6.625       page=0  chnl=0char id=50    x=56    y=154   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=51    x=101   y=105   width=25    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.500      page=0  chnl=0char id=52    x=56    y=176   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=53    x=56    y=198   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=54    x=56    y=220   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=55    x=32    y=132   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=56    x=32    y=154   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=57    x=32    y=176   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=58    x=9     y=220   width=9     height=22    xoffset=-0.750    yoffset=19.688    xadvance=6.875       page=0  chnl=0char id=59    x=0     y=220   width=9     height=24    xoffset=-0.750    yoffset=19.688    xadvance=6.875       page=0  chnl=0char id=60    x=233   y=166   width=17    height=12    xoffset=-0.750    yoffset=17.125    xadvance=14.500      page=0  chnl=0char id=61    x=56    y=242   width=24    height=12    xoffset=-0.750    yoffset=17.125    xadvance=22.438      page=0  chnl=0char id=62    x=80    y=242   width=17    height=12    xoffset=-0.750    yoffset=17.125    xadvance=14.500      page=0  chnl=0char id=63    x=101   y=127   width=25    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.500      page=0  chnl=0char id=64    x=0     y=44    width=32    height=22    xoffset=-0.750    yoffset=19.625    xadvance=30.375      page=0  chnl=0char id=65    x=0     y=110   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=66    x=0     y=132   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=67    x=0     y=154   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.375      page=0  chnl=0char id=68    x=0     y=176   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=69    x=126   y=149   width=25    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.500      page=0  chnl=0char id=70    x=32    y=220   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.375      page=0  chnl=0char id=71    x=32    y=198   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=72    x=151   y=205   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=73    x=175   y=227   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.375      page=0  chnl=0char id=74    x=175   y=205   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=75    x=223   y=183   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=76    x=199   y=227   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.375      page=0  chnl=0char id=77    x=0     y=66    width=32    height=22    xoffset=-0.750    yoffset=19.625    xadvance=30.313      page=0  chnl=0char id=78    x=223   y=227   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=79    x=175   y=183   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=80    x=151   y=183   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.375      page=0  chnl=0char id=81    x=199   y=183   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=82    x=199   y=205   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=83    x=223   y=205   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.375      page=0  chnl=0char id=84    x=151   y=227   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=85    x=126   y=171   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=86    x=126   y=193   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.375      page=0  chnl=0char id=87    x=0     y=88    width=32    height=22    xoffset=-0.750    yoffset=19.625    xadvance=30.313      page=0  chnl=0char id=88    x=126   y=215   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=89    x=151   y=127   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=90    x=175   y=127   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.375      page=0  chnl=0char id=91    x=80    y=198   width=17    height=22    xoffset=-0.750    yoffset=19.625    xadvance=14.500      page=0  chnl=0char id=92    x=126   y=127   width=25    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.500      page=0  chnl=0char id=93    x=0     y=198   width=17    height=22    xoffset=-0.750    yoffset=19.625    xadvance=14.688      page=0  chnl=0char id=94    x=57    y=66    width=16    height=9     xoffset=-0.750    yoffset=19.625    xadvance=14.500      page=0  chnl=0char id=95    x=32    y=66    width=25    height=7     xoffset=-0.750    yoffset=-0.063    xadvance=22.500      page=0  chnl=0char id=96    x=18    y=238   width=9     height=9     xoffset=-0.750    yoffset=19.625    xadvance=6.625       page=0  chnl=0char id=97    x=192   y=88    width=24    height=17    xoffset=-0.750    yoffset=14.688    xadvance=22.438      page=0  chnl=0char id=98    x=199   y=127   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=99    x=183   y=149   width=25    height=17    xoffset=-0.750    yoffset=14.688    xadvance=22.500      page=0  chnl=0char id=100   x=223   y=127   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=101   x=208   y=166   width=25    height=17    xoffset=-0.750    yoffset=14.625    xadvance=22.500      page=0  chnl=0char id=102   x=101   y=149   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.375      page=0  chnl=0char id=103   x=101   y=171   width=24    height=22    xoffset=-0.750    yoffset=14.688    xadvance=22.438      page=0  chnl=0char id=104   x=101   y=193   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.438      page=0  chnl=0char id=105   x=247   y=127   width=9     height=22    xoffset=-0.750    yoffset=19.625    xadvance=6.750       page=0  chnl=0char id=106   x=80    y=105   width=21    height=27    xoffset=-0.750    yoffset=19.688    xadvance=18.500      page=0  chnl=0char id=107   x=101   y=215   width=24    height=22    xoffset=-0.750    yoffset=19.625    xadvance=22.375      page=0  chnl=0char id=108   x=247   y=183   width=9     height=22    xoffset=-0.750    yoffset=19.625    xadvance=6.625       page=0  chnl=0char id=109   x=151   y=149   width=32    height=17    xoffset=-0.750    yoffset=14.688    xadvance=30.313      page=0  chnl=0char id=110   x=232   y=149   width=24    height=17    xoffset=-0.750    yoffset=14.688    xadvance=22.438      page=0  chnl=0char id=111   x=208   y=149   width=24    height=17    xoffset=-0.750    yoffset=14.688    xadvance=22.438      page=0  chnl=0char id=112   x=126   y=105   width=24    height=22    xoffset=-0.750    yoffset=14.688    xadvance=22.375      page=0  chnl=0char id=113   x=150   y=105   width=24    height=22    xoffset=-0.750    yoffset=14.688    xadvance=22.438      page=0  chnl=0char id=114   x=183   y=166   width=25    height=17    xoffset=-0.750    yoffset=14.688    xadvance=22.500      page=0  chnl=0char id=115   x=168   y=88    width=24    height=17    xoffset=-0.750    yoffset=14.688    xadvance=22.438      page=0  chnl=0char id=116   x=126   y=237   width=24    height=19    xoffset=-0.750    yoffset=17.125    xadvance=22.438      page=0  chnl=0char id=117   x=101   y=237   width=24    height=17    xoffset=-0.750    yoffset=14.688    xadvance=22.500      page=0  chnl=0char id=118   x=96    y=88    width=24    height=17    xoffset=-0.750    yoffset=14.688    xadvance=22.438      page=0  chnl=0char id=119   x=151   y=166   width=32    height=17    xoffset=-0.750    yoffset=14.688    xadvance=30.313      page=0  chnl=0char id=120   x=120   y=88    width=24    height=17    xoffset=-0.750    yoffset=14.688    xadvance=22.438      page=0  chnl=0char id=121   x=174   y=105   width=24    height=22    xoffset=-0.750    yoffset=14.688    xadvance=22.500      page=0  chnl=0char id=122   x=144   y=88    width=24    height=17    xoffset=-0.750    yoffset=14.688    xadvance=22.438      page=0  chnl=0char id=123   x=80    y=132   width=21    height=22    xoffset=-0.750    yoffset=19.625    xadvance=18.500      page=0  chnl=0char id=124   x=247   y=227   width=9     height=27    xoffset=-0.750    yoffset=19.625    xadvance=6.625       page=0  chnl=0char id=125   x=80    y=154   width=20    height=22    xoffset=-0.750    yoffset=19.625    xadvance=18.438      page=0  chnl=0char id=126   x=32    y=242   width=24    height=12    xoffset=-0.813    yoffset=17.125    xadvance=22.375      page=0  chnl=0char id=198   x=0     y=0     width=40    height=22    xoffset=-0.750    yoffset=19.625    xadvance=38.188      page=0  chnl=0char id=230   x=32    y=88    width=40    height=17    xoffset=-0.750    yoffset=14.688    xadvance=38.313      page=0  chnl=0
Quote:Original post by lonesock
@OrangyTang & Kylotan: thanks for the feedback/input. As a simple hack you can now input the range of characters to be rendered. I think I like the XML approach as it would be slightly easier for me to implement than the UTF-8 file with all needed characters for 3 reasons: easier to parse as I already use an XML lib, I could see the UTF-8 file getting huge, and there is no guarantee of the glyph order after the packing


By definition the UTF-8 file would almost always be smaller than the XML version. However, you've not made it clear what metadata would go in the XML - not that I can see an immediate need for any. Not a big deal to me either way though.
Awesome work :)

I finally (after some confusion) got the output of your SDFont rendering using OrangyTang's shader and it looks great!

Between the two of you you've given the community a really cool couple of examples of a nice tech to work with!

Andy

"Ars longa, vita brevis, occasio praeceps, experimentum periculosum, iudicium difficile"

"Life is short, [the] craft long, opportunity fleeting, experiment treacherous, judgement difficult."

Also I did it using your first version and as soon as I've got my rendering running you've already released a _new_ version! Outstanding!

Andy

"Ars longa, vita brevis, occasio praeceps, experimentum periculosum, iudicium difficile"

"Life is short, [the] craft long, opportunity fleeting, experiment treacherous, judgement difficult."

Quote:Original post by Kylotan
...you've not made it clear what metadata would go in the XML...

I'm sorry, you are correct. Here is an example of what I was thinking:
<?xml version="1.0"?><SDFont>  <range name="Basic Latin">0000 007F</range>  <range name="Extended Latin">00A0 02FF</range>  <range name="Greek">0300 03FF</range>  <chars name="Just Because">20A3 20A4 2500 2663</chars></SDFont>


@NineYearCycle: Thank you, I'm glad you can use it!
I've put a version that I knocked up over a couple of lunchtimes here (.zip & VC2005 .sln) there's no prizes for guessing where I grabbed the basecode from :D

It just uses the shader that OrangyTang gave out on his journal and some basic setup stuff that I grabbed from other OSS sites.

The effect is really cool and I'm impressed just how little effort and data it requires to get something like this going!

I'll try and pull out some of the shader constants later on and get it to give some options about which font it loads and uses.

At the moment there's some graphical issues relating to texture wrap, or maybe I've calculated the S & T values wrong for some of the characters. Haven't had chance to take a closer look yet.

Good work! I updated it to use your new version as well.

Andy

"Ars longa, vita brevis, occasio praeceps, experimentum periculosum, iudicium difficile"

"Life is short, [the] craft long, opportunity fleeting, experiment treacherous, judgement difficult."

This topic is closed to new replies.

Advertisement