Font rendering with the FP10 drawing API

After all the vector/font rendering library is done – the HaXe sources and SWC files for ActionScript 3.0 are available on the polygonal google code project page. As a reminder – the project started as an experiment if it’s possible to render fonts using the FP10 drawing API without loading or embedding any additional assets.

For obvious reasons, I can only include free fonts. At the moment the de.polygonal.graphics.text.fonts package contains Microsoft’s TrueType core fonts hosted on sourceforge, the Bitstream Vera fonts as well as the famous bitmap04 pixel fonts.

The pros and cons

Pros

  • No font embedding required :) Import a font class and you are ready to go
  • Provides high quality font rendering; best used for extra smooth text animation
  • Seriously fast!
  • Seamless integration into the FP 10 drawing API

Cons

  • An ASCII set of printable characters adds about 20kb-30kb to the swf file – I’ll try to reduce this in a future release.
  • Not very readable at small font sizes (except pixel fonts) because it does not include any hinting information for improving the quality – text remains legible down to about 12 points (viewed at 100%)
  • You need a copy of Fontographer 4.1 to convert ttf files
  • No text field functionality yet
  • Only supports ASCII character (latin character set like ISO-8859 is planned)

Examples

Here are the ‘MS core fonts for the web’ rendered with the font library:

This also works amazingly well for pixel fonts (which I didn’t expect at all):

How it works

First a .ttf file is loaded into Fontographer and exported as a postscript file (I tried other methods but I stuck with this approach because postscript files are easy to understand). A parser reads this file and generates a HaXe class that contains the glyph data. The result is something like this: Arial.hx.

If using HaXe, the font_inline compiler flag gives you control over compilation time vs runtime performance. If omitted, compilation is fast so it’s best suited for frequent testing and debugging and the compiler-based auto-completion remains responsive. If compiled with -D font_inline, compilation is slow but results in the best performance.

The actual rendering is done by a class named de.polygonal.graphics.VectorRenderer. It uses the FP 10 drawing API in conjunction with ‘alchemy memory’ as a temporary buffer to gain some extra speed. At first all drawing commands are written into a chunk of memory, then copied into a vector and finally sent to the screen via graphics.drawGraphicsData(…). Depending on the CPU this is roughly 1.5-4x faster than using only vector. Note that this only accelerates the process of preparing the data, not the rendering itself (everything beyond drawGraphicsData())

ActionScript 3.0 usage

Grab the SWC file, add it to the library and the following code should (hopefully) compile fine:

package 
{
  import de.polygonal.ds.mem.MemoryManager;
  import de.polygonal.graphics.text.fonts.coreweb.Arial;
  import de.polygonal.graphics.VectorRenderer;
  import flash.display.MovieClip;
  import flash.Boot;
  
  public class Main extends MovieClip
  {
    public function Main():void 
    {
      new Boot(this);
      MemoryManager.allocate(4096);
     
      var vr:VectorRenderer = new VectorRenderer(512);
      vr.setLineStyle(0, 1, 0);
      
      var font:Arial = new Arial();
      font.bezierThreshold = 0.001;
      font.setPointSize(100);
      font.setRenderer(vr);
      font.write("Hello World!", 0, 100, false);
      
      vr.flush(graphics);
    }
  }
}

The source code reads like this:

  • initialize HaXe specific things
  • allocate 4 megs of alchemy memory to be on the safe side
  • create a vector renderer using a buffer size of 512kb
  • assign a line style (rgb, alpha, thickness)
  • create a font object
  • define curve smoothness, the smaller, the better (0=linear approx. using 2 segments/curve)
  • set the font size: 100 equals 72pt or one inch.
  • assign a renderer so the font can send drawing commmands to it
  • draw “Hello, World!” at the coordinates 0,100 (x,y), if the last parameter is true, the text will be centered around (x,y)
  • flush the buffer which draws everything to the screen

Glyph and text bounds

You can compute axis aligned bounding boxes for the whole text block or individual characters using the getBounds() and getIndividualBounds() methods prior to drawing the text:

Creating font classes

Converting fonts can be done using de.polygonal.gl.text.util.EPSConvert.

Goodbye, TextField!

Working with text in Flash can be a painstaking procedure, especially if you just want to quickly draw and animate some simple characters on screen using ActionScript. It usually takes many lines of codes to setup and adjust the appearance and alignment of text fields and you need to take care of other little annoying details like the whole font embedding procedure.

Lately I was working on a new interactive testbed for the motor physics engine where I’m solely using the new flash player 10 vector drawing API, but I needed to add some text. I was curious about how hard it would be to add text rendering capabilities to the drawing API and after some investigation I found an easy way using an old copy of Fontographer to extract the necessary data from a true type font file (much easier than trying to parse a .ttf file directly):

  1. Export the font data as an EPS postscript file.
  2. Write a parser to transform each glyph into a bunch of lineTo, moveTo and curveTo commands.
  3. Export and parse the spacing and kerning table (proportional fonts only).
  4. Write a method for drawing cubic b├ęzier curves.

Following these steps I ended up with a simple text rendering engine. Here my result:

Monospace/Proportional font rendering (Consolas/Arial)

As you see small font sizes won’t be that sharp and readable as a TextField set to “Anti-alias for readability” but on the other hand the results are very smooth and perfect for animation. Placing and aligning text blocks is also much easier since it’s straightforward to compute bounding boxes for glyphs and text blocks.

So now drawing a text is a matter of writing:

...
graphics.beginFill(0, 1);
new Arial(10.0).print("Hello World", 100, 100);
graphics.endFill();
...

This will draw “Hello World” at x,y=100,100 and a font size of 10 points. KISS!
An exciting thing is that the FP10 drawing API is actually very fast; the following demo scrolls all ASCII characters back and forth. Press space to toggle between regular text field and vector rendering. In the first case a TextField object is created once and then moved by adjusting its position, whereas in the second case the whole text is redrawn in every frame at a new position:

TextField vs. Graphics

If (hopefully) more people besides me find this useful I would invest some extra time to polish the code and publish it as open source. It made my coding life simpler :-)