Dark Bit Factory & Gravity
GENERAL => Projects => Topic started by: hellfire on December 22, 2009
-
As a friend of mine asked for a simple way to create bitmaps from true-type-fonts, I dug out an old tool of mine - maybe it's useful for someone else, too.
What it does is rendering each character into a bitmap and tries to place them all into one map.
It creates a list with information about
x-position
y-position,
width,
height,
offset from base-column (this is useful for serif-fonts where characters partially overlap),
offset from base-line
actual width required by the character (that's where the next character starts)
An extreme example of a single character stored in the map looks like this:
(http://hellfire.untergrund.net/misc/char-layout.gif)
You can specify font, size, bitmap, spacing, number of colours and color-encoding.
There are some additional options like creating a set of required characters from the actual text and exporting image-data as ready to include source-code.
A simple heuristic tries to avoid worst-case scenarios when assembling the different character-bitmaps into a single map - but it's yet far from optimal or even clever.
Download here (http://hellfire.untergrund.net/misc/fontmaker2.rar) (3.1mb due to Qt (http://doc.trolltech.com/4.6/index.html)).
Feature requests are welcome.
A very simple example how to use this with OpenGL is attached below.
Have fun.
-
Very useful stuff hellfire will definately be making some use of this in future
-
These kinds of tools always fill a need. Have some karma.
-
Looks like a really useful tool, thanks for sharing.
-
Great tool hellfire.
It has some nice features.... Generate chars list (from sentence), texture size, and the very useful char location array that goes with it.
karma++
-
I was able to get this working with a simple opengl texture example.
2 things though:
1) The "Save As Array" option in fontmaker doesn't add commas to the end of each line. I had to manually add these commas.
2) I had a little bit of trouble trying to figure out which OGL settings to use to import this data as a texture. I used the following:
glGenTextures( 1, &texture );
glBindTexture( GL_TEXTURE_2D, texture );
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 512, 512, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, fontdata);
-
I usually use
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_ALPHA,
sizeX,
sizeY,
0,
GL_ALPHA,
GL_UNSIGNED_BYTE,
data);
This way all information is inside the alpha-channel so you can use proper blending and specify the color using glcolor.
In your screenshot you need to take the "baseline" parameter into account (the 7th in each row).
Postive value means up, negative value means down.
See screenshot above.
-
ah that makes sense. I did get alpha working, but I did it an ugly way.
I created a temp buffer twice the size of the texture and...
for (i=0;i<512*512;i++) {
buffertmp[i*2]=buffer[i];
buffertmp[i*2+1]=buffer[i];
}
Then I used GL_RGBA and GL_LUMINANCE_AND_ALPHA (or something similar).
The method shown above(yours) is obviously cleaner. Thanks for pointing that out.
-
Well, the goal for the array-export is to keep the data as compact as posibble but still uncompressed (because thats what the exe-packer likes), so I thought it's enough to store a single channel.
If you like to store other formats I could add a selector for that.
I plan to support syntax for different languages, too.
-
Very nice - finally a feature-rich font editor without ads/fees/viruses. K++
-
ok, I got the alpha channel working as per hellfire's instructions. And the character positions also.
thanks again hellfire.
-
The "l" in "reload" isn't properly centered between the "e" and the "o".
So either you didn't take the offset from the base-column into account or I didn't calculate the character-width correctly (haven't tried any font in italics so that's quite possible).
I'll have a look at that later.
-
^^also the m.
Cool, will give this a whirl.
-
I hadn't included the column adjustments. I have now.
I'm not using italics, the letters are spinning. See the attached exe.
The source is a dog's breakfast at the moment. I'll clean it up a little and post it soon.
-
I'm not using italics, the letters are spinning. See the attached exe.
Cool :)
Noticed that I didn't reserve enough spare-pixels when rendering fonts with extreme amount of serifs, so I added an option to select it manually - cut regions are marked with a white line.
-
Great tool ,Hellfire....
:goodpost:
Possible to get output format for MASM???
-
Sure. I don't use MASM, though. So please post a syntax-example.
-
Added a distance field rendering option for completeness (updated download link is in post #1).
More about distance field font rendering can be found here (http://www.dbfinteractive.com/forum/index.php?topic=4945.0) and here (http://www.youtube.com/watch?v=CGZRHJvJYIg).
The distance is computed on a sub-pixel-level from the anti-aliased text, so the "supersampling" should be set to 1 (otherwise it takes literally ages to compute).
The interior threshold level is 128.
-
Interesting stuff Hellfire. I've come across this concept before, but this is the first time it's actually made sense to me.
Would it be possible to generate the distance fields at runtime for use in 64k intros or suchlike? I'm just curious as to whether the code to generate the data would be significantly smaller than a 256x256 or 512x512 texture.
-
Would it be possible to generate the distance fields at runtime for use in 64k intros or suchlike?
The code to generate the distance field is trivial and pretty compact.
It just does a spiraling search around each pixel to find the smallest distance to the neighbouring points.
To get some better precision I do a re-vectorization of the anti-aliased bitmap using marching squares.
Otherwise (when working with whole pixel distances only) you would need to increase the rendering size drastically (which makes the search incredibly slow).
But that's because the distance-search is just a naive add-on to the regular text-rendering function of this tool.
If you'd want to create a distance texture in a 64k-intro you'd probably store your font in some vectorized format anyway.
So you can simply test each pixels distance against the surrounding edges.
Since such vector-data is probably optimized (using as few points as possible to save space), the distance check should be pretty much faster than the pixel-based approach and certainly fits into a few hundred bytes of code.
-
Great. Thanks for the detailed info Hellfire, much appreciated.
-
Looks like a great little tool! :clap:
-
Nice one Hellfire, I will have a play later. :goodpost:
-
Nice work Hellfire, karma++
I've used this tool before, and I'll use it again.
To get some better precision I do a re-vectorization of the anti-aliased bitmap using marching squares.
Otherwise (when working with whole pixel distances only) you would need to increase the rendering size drastically (which makes the search incredibly slow).
Any chance I could get an explanation like I was 5? :)
Another question regarding the shader that would be used to draw these distance field fonts. It would look up a texel, and it would know how far from the font it is by this lookup.... but what to do with that info? Do I draw here or not?
-
To get some better precision I do a re-vectorization of the anti-aliased bitmap using marching squares.
Any chance I could get an explanation like I was 5? :)
Sure.
The marching squares reconstructs the subpixel edge (blue) from the surrounding 2x2 pixels (red):
(http://www.abload.de/img/vectorizem1cly.png)
This way the distance (orange) to the edge is much more precise; in contrast to using whole pixel positions only where the distance between two neighbouring pixels can naturally be only 1 or sqrt(2).
The only way to get a reasonable precision without subpixel-precision is to increase the resolution but that makes it extremely slow.
Another question regarding the shader that would be used to draw these distance field fonts. It would look up a texel, and it would know how far from the font it is by this lookup.... but what to do with that info? Do I draw here or not?
When fetching a texel from the distance-map a value >=192 is "inside" the character and <192 is "outside".
So in the easiest case you can just enable alpha-testing and drop all outside texels (that's compeltely un-antialiased, though).
Inside a shader you could do all sorts of color-ramping near the boundary to simulate anti-aliasing, glow, shadows, etc.
At the moment I'm not quite sure if the threshold of 192 makes much sense.
I just figured that font-characters are usually rather thin and there's probably more information outside than inside...
edit: the threshold value changed from 192 to 128 in the current version.
-
After reading that a couple (read 4) times, it clicked. \o/
So, for each pixel in char block, get distance from this pixel centre to the calculated subpixel surface. Adjust and clamp tween 0 and 1, and store. Right?
Thanks for the explanation hellfire.
Regarding all this effort though, couldn't you just blur the original 2 bit texture and use that? I'll attempt to answer that myself.
-
heh. Cause blur and distance are different functions. Distance affects the surrounding pixels but leaves the original font pixels untouched. Blur doesn't.
As you were people.
-
get distance from this pixel centre to the calculated subpixel surface. Adjust and clamp tween 0 and 1, and store. Right?
Pretty much, yes. The distance of an outside pixel is positive and the distance of an inside pixel is negative.
That's mapped to 0..255 where 128 represents a distance of 0.0, so it's right on the edge.
couldn't you just blur the original 2 bit texture and use that?
Yeah, when looking at the output of the distance-transform it just looks kinda blurred and actually it's not such a bad approximation.
It doesn't preserve hard corners, though - everything gets soft and round.
And, as you already noticed, it affects the threshold to the interior part.
-
Fixed a bug in the distance calculation, another one in the character positioning and moved the interior threshold to 128 (0.5) because it's nicer to handle.
Link (in post #1) updated.
-
Nice!
I'm going to test it later.