Article
MIDP Terminal Emulation, Part 3: Custom Fonts for MIDP
 

by Michael Powers
June 2004

Read Part 1: A Terminal Emulator for MIDP
Read Part 2: Advanced Terminal Emulation for MIDP
Download the project code

In Part 1 of this series we explored MIDP's potential for networked applications by building a terminal emulator. Part 2 focused on building a proper user interface to make MIDTerm a decently interactive application. In Part 3, we'll add some polish -- and some badly needed control over appearance -- by taking advantage of MIDP's capabilities for rendering custom fonts.

First we'll examine the advantages and constraints of the built-in support for fonts, and determine what kinds of applications might require custom fonts. Then I'll show you a technique for implementing your own fonts, and we'll put it to good use in the terminal emulator MIDlet.

Built-In Fonts

You can draw text with MIDP's Graphics class by calling its setFont() to specify a font, then calling one of that font's drawChar() or drawString() methods to render your text onto a canvas or onto an off-screen image. MIDP affords you only a limited set of font options, however: The font type can be monospaced or proportional, the size can be small, medium, or large, and the style can be plain or any combination of bold, italic, and underlined. The runtime implementation may not have a font that meets your criteria, and it's free to return any font it determines to be a close enough approximation.

Most applications can live with these limitations. Their developers want them to blend in with the look and feel of the host platform anyway. To make doing so easier, MIDP 2.0 provides a new getFont() method that can give you the font commonly used for drawing static text, like titles and labels, and the one used for drawing characters the user enters in text fields. You can be confident these fonts exist and that they are appropriately sized and consistent with the rest of the application. As long as you measure the height, width, and baseline of the fonts you are given and adjust your user interface appropriately at runtime, your application should run well on many different types of devices.

Some applications, though, need more control over how they appear. For example, most games want a typeface that is consistent in theme with the rest of their graphics. Some platforms may present specific problems: Requesting a monospaced font might return a proportional font, the smallest font may be too big, and some fonts may be unattractive or even illegible. These differences multiply the difficulties in creating and maintaining cross-platform software.

MIDTerm is just such a case. We need to be sure that its font is monospaced, small enough to maximize the number of rows and columns on the screen, and easily legible, but at this point the canvas that displays the terminal content is at the mercy of the device implementation. If the given font is too large the terminal becomes unusable, and if it isn't monospaced the application doesn't work at all. At this point in our project, the font that the MIDP emulator returns is monospaced and appropriately small, but it isn't particularly easy to read.

Implementing a custom font will give us the control we need to have confidence in MIDterm's usability. Equally important, its performance will be more predictable across all MIDP devices because it will no longer depend on each platform's native font implementation.

When it's done, you'll be able to see the improvement for yourself:

Before: Built-in Font
After: Custom Font

Creating A Custom Font

Fonts come in two varieties:

  • Outline fonts contain the instructions needed to draw each of the characters. These instructions are precise, lengthy, and complex, but they allow the operating system to render the font at any size and in any style.
  • Bitmap fonts tell the operating system only which pixels are on and which are off for each character. They're simple and concise, but they need a separate set of instructions for each supported size and style of the font.

Mobile devices have such limited processing power that outline fonts are out of the question. Natively, these devices use bitmap fonts, and that's what we'll use as well. The operating system won't render our implementation as quickly as a native font, but that is an acceptable sacrifice to gain absolute control over the appearance and layout of our font.

The drawing instructions for a bitmap font are literally a mapping of bits to a location in a graphics context. Each character in a bitmap font corresponds to an arrangement of pixels that is copied from memory to the screen. Said another way, each character is represented by an off-screen image that gets copied to the screen. Looking at the font this way immediately suggests that the font definition -- the file that contains the instructions for rendering the fonts -- can itself be an image file in a compressed format like PNG or JPEG. We can read this image into memory, then copy portions of it to screen whenever we want to draw characters in our font.

The first step then is to create this image. On your desktop computer, use your favorite image editor to create a new document. This document should be as tall as you want your font to be, and wide enough to hold each of the characters in the character set you want to render. How wide is that?

Java technology generally uses the Unicode Character Standard. The Unicode character set has over 95,000 characters -- more than we can afford, and far more than we need! -- so MIDTerm's implementation of the Telnet protocol is based on the 7-bit ASCII character set, which contains only 128 characters. Because MIDTerm needs a monospaced font, the width of the document will be 128 times the width of one character. For instance, if each character is 12 pixels high and 10 pixels wide, to hold all 128 characters you'll need an image that's 12 high and 1,280 wide.

You now have 128 places to put characters that correspond to ASCII codes 0 through 127. To find out which characters to place in which slots, you may need an ASCII chart. Note that the first 31 characters are "control characters" and are not meant to be seen, so you can just leave them blank.

You can plot each of the characters into its slot by hand, pixel-by-pixel, but it's much easier to use characters from one of the fonts on your desktop computer. Just make sure it's a font you either own or have a license to use. I used a font called Anonymous, which has a very legible 6-point rendition and is free to use. You can download it from Mark Simonson Studios.

Using Anonymous, my font image file looks like this:

By definition a bitmap font uses only two colors because a bit has only two states: on and off. You should therefore use one color to define the pixels that are "on," and a different color for all the rest. Because MIDP 2.0 devices are required to support images with transparency, you should have your image editor mark the "off" color as transparent. Transparent pixels are invisible, allowing whatever pixels are already on your application's canvas to show through.

You'll want to save the image file in PNG format. A one-bit image with many contiguous regions of uniform color is exactly the kind of image for which the basic PNG compression algorithm was designed. Your font image file will be very compact: the PNG file for Anonymous is only 803 bytes in size.

The Custom Font Class

Now we need to write some software that can read the image file and render portions of it on screen, in a new class named CustomFont.

To make CustomFont easy to learn and use, it should look and feel as much like the standard MIDP Font class as possible. We can't subclass Font, because it has been declared final, to allow implementers to optimize using native code. We can, however, mimic its public interface. Every method of Font is also present in CustomFont, and only the static factory method getFont() has a different signature, taking the name of a file instead of a preferred font type:

public static CustomFont getFont( 
    String inName, int inStyle, int inSize );

In many MIDP applications we'd pass a font to the Graphics object by calling its setFont() method, then invoke that object's drawChars() and drawString() methods to render the text. We can't follow that pattern here, however. Because CustomFont isn't a subclass of Font, we cannot pass it to Graphics.setFont(). Instead, CustomFont provides public methods for drawing characters that mimic methods of the Graphics class, each taking the current Graphics instance as its first argument.

public void drawChar( 
    Graphics g, char character, 
    int x, int y, int anchor );

public void drawChars( 
    Graphics g, char[] data, 
    int offset, int length, 
    int x, int y, int anchor );

public void drawString(
    Graphics g, String str, 
    int x, int y, int anchor );

public void drawSubstring( 
    Graphics g, String str, 
    int offset, int length, 
    int x, int y, int anchor );

Applications that use CustomFont will need to modify their drawing code to call these methods of the custom font class, rather than the corresponding methods of the Graphics object.

Now let's take a look at how the character bitmaps get to the screen. There are two approaches to consider.

The obvious approach is to take the source image and break it up into 128 images, one for each character, as shown here:

...
images = new Image[ 128 ]; 
for ( int i = 0; i < 128; i++ )
{
    images[i] = Image.createImage( 
       image, i*width, 0, width, height, 0 );
}
...

Images created from an external resource are considered immutable. Because the pixels cannot change, the runtime can optimize memory allocation. Images we create from an immutable image with the createImage() method are immutable too. So, while we have 128 small images and one large image, we can hope that the runtime will be smart enough to notice that none of them can be modified, and keep only one copy of our bitmap in memory. Even if it does, however, the sheer overhead of 128 image instances may prove too much for a small device to handle.

An alternate approach is to have only one large image and use it like an IBM Selectric typewriter's typeball. Revolutionary in its day, the typeball replaced individual typebars for each letter with raised characters arranged on a spherical surface. The ball swiveled and tilted for each keystroke, and enabled the Selectric to achieve then-dazzling speed (almost 15 characters a second!) The typeball may be long obsolete, but we can borrow the idea behind it to make MIDTerm more efficient. To draw a character, we'll clip the graphics context, and shift the image left or right so that only the character we want is visible in the clipped area, then draw the image. Every Graphics instance maintains a clip region at all times, and only commands that affect the clip region are actually executed. In this case, we can count on the runtime to draw only those pixels that fit into the clip region. This approach is demonstrated here:

public void drawChar( 
    Graphics g, char character, int x, int y, int anchor )
{
    int clipX = g.getClipX();
    int clipY = g.getClipY();
    int clipW = g.getClipWidth();
    int clipH = g.getClipHeight();
            
    g.setClip( x, y, width, height );
    g.drawImage( 
        image, x - width*character, y, anchor );
    
    g.setClip( clipX, clipY, clipW, clipH );
}

The downside to this approach is that we have to remember the existing clip region before we change it, so we can return the Graphics instance to the state in which we found it. The clip region must be preserved in each call to one of the draw methods, at a significant cost: We must make four method calls and allocate four ints each time.

We're stuck with a classic time-space tradeoff: To save memory CustomFont consumes more processor time, using clipping instead of allocating individual image instances for each character. On devices with little memory, it's better to run slowly than not at all.

One little chore CustomFont must do when initializing is to calculate the font's baseline. Along with its height and the widths of its characters, a font's baseline is another important determinant in font rendering because it defines where a font's characters "sit" on a line of text. If you have different fonts on the same line of text, you need to align their baselines to make them appear at the same level.

We could change CustomFont.getFont() to require the caller to specify the baseline for its chosen font, but there's a better way. MIDP 2.0 makes it possible to detect the baseline dynamically. The getRGB() method gives us direct access to the individual pixels of an image, and a simple heuristic can determine which row of pixels is the baseline.

...
// determine background color: assume it's at 0, 0
image.getRGB( row, 0, 1, 0, 0, 1, 1 );
background = row[0];

// here's the heuristic: find the row on the bottom
// half of the image with the most non-background pixels
for ( int y = height/2; y < height; y++ )
{
    total = 0;
    image.getRGB( row, 0, imageWidth, 0, y, imageWidth, 1 );
    for ( int x = 0; x < imageWidth; x++ )
    {
        if ( row[x] != background ) total++;
    }
    if ( total > max ) 
    {
        max = total;
        result = y;
    }
}
...

CustomFont assumes the top-left pixel in the image is in the background color. It counts the non-background pixels in each row in the bottom half of the image, and judges the row with the most foreground pixels to be the baseline. Determining the baseline at runtime gives you one less thing to worry about when creating custom font bitmaps.

Dynamic Styling

The signatures of Font.getFont() and CustomFont.getFont() differ in their first argument -- the standard method takes a font type, and ours takes the name of an image file -- but both accept size and style arguments. CustomFont ignores the requested font size, because having a separate image in memory for each font size would be too expensive, and attempting to scale a bitmap larger or smaller at runtime would produce results that were less than legible. Our custom font class does try to respect the requested style, however, by employing some simple but effective techniques that alter how it draws the character images at runtime.

Easiest is underlining. We just draw a simple line under the character, two pixels below the baseline.

...
if ( ( style & Font.STYLE_UNDERLINED ) != 0 )
{
    g.drawLine( 
        x, y + baseline + 2, x + width, y + baseline + 2 );
}
...

Bolding is only slightly more complicated. To make a character bold, CustomFont draws the character an extra time, one pixel to the right. Because the background is transparent, the two sets of pixels overlap to make the character appear darker.

...
if ( ( style & Font.STYLE_BOLD ) != 0 )
{
    // draw an additional time, one pixel to the right
    g.drawImage( 
        image, x - width*character + 1, y, anchor );
} 
...

To make a character italic, CustomFont uses clipping to draw the top half of the character shifted one pixel to the right. The result is surprisingly good.

...
if ( ( style & Font.STYLE_ITALIC ) != 0 )
{
    g.setClip( x + 1, y, width, height/2 );
    g.drawImage( 
        image, x - width*character + 1, y, anchor );
    g.setClip( x, y+height/2, width, height/2 );
    g.drawImage( 
        image, x - width*character, y, anchor );
}
...

Here's how these styles look in practice:

STYLE_PLAIN
STYLE_UNDERLINED
STYLE_BOLD
STYLE_ITALIC

CustomFont has a more difficult time drawing characters in specific colors. While the Graphics class renders a font in the current color, CustomFont can only copy the pixels from the full font image, so the characters appear in whatever non-transparent colors were used in the original image.

MIDP 2.0 does provide a way to create a new image from the pixels returned by getRGB(). You can copy the pixels for a character, scan through and change the color of every non-background pixel, create a new image, and paint that image to the screen -- but this tactic is processor-intensive and perceptibly slow. If you know in advance what color you'll need, you should use that color in the foreground when creating your font. MIDTerm uses a black background, so the full font image contains white characters against a transparent background.

Putting the Custom Font to Work

Because CustomFont conforms so closely to MIDP's Font class, MIDTerm's TelnetCanvas class needs very few changes to reap the advantages of a custom font.

Using MIDP Fonts Using Custom Font
font = Font.getFont( 
    Font.FACE_MONOSPACE, 
    Font.STYLE_SMALL, 
    Font.SIZE_PLAIN );
font = CustomFont.getFont( 
    "/mono.png", // the font bitmap file
    Font.SIZE_SMALL, // ignored
    Font.STYLE_PLAIN ); // no styling
g.setFont( font );
g.drawChar( (char) b, 
    insetX + x*fontWidth, 
    insetY + y*fontHeight, 
    g.TOP | g.LEFT );
 
font.drawChar( g, (char) b, 
    insetX + x*fontWidth, 
    insetY + y*fontHeight, 
    g.TOP | g.LEFT );

With these few changes, the application runs as before, only better: more legibly, if slightly more slowly, and more predictably on varying MIDP implementations. You can use CustomFont likewise in other applications with little or no modification.

Summary

We've employed a simple but effective technique to give MIDP applications absolute control over the drawing of text. You can create your own fonts with a simple image editor and, using the graphics and imaging capabilities of MIDP 2.0, you can render your font on screen with acceptable performance. You can style your font however you wish, even dynamically at runtime. For MIDTerm, a custom font improves legibility and usability and reduces the effort needed to ensure that the application runs smoothly on multiple platforms.

Reference

The links in this article are listed again here.