PS2 Linux Programming
This tutorial will describe the use of a bitmap font class for rendering text to the screen. The text can be used for debug purposes and for in-game information display.
The bitmap font class loads a bitmap image into GSmem then display a chain of 2D sprites, each one being a font character. The bitmap file, font.bmp, that is included with the sample code is filled with a grid of letters. There are 16 letters per row and 16 rows. Each character in the bitmap is 16x16 pixels in size. The ASCII character 0 starts at the top left, and moving to the right increases the ASCII character number. Moving to a new line increases the ASCII characters number by 16. The ASCII character number can be thought of as being the “frame” or character number that is to be rendered.
Starting with an ASCII character number, the row and column that the letter is on can be determined with the following code:
int tx = cLetter % LETTERSPERROW;
int ty = cLetter / LETTERSPERROW;
If tx and ty are multiplied by 16, the result is the UV coordinates of the top left corner of the required letter, which can then be used for rendering.
So why is the font texture red? - When loading a font bitmap where RGB(0,0,0) is transparent, often and very annoyingly, there is an “almost black” colours that appears round the edges of the characters of the font. So while the bitmap looks great in Photoshop smoothed against a black background layer, it looks horrible against a light background on the PS2, since there is still the blackish outline around the edges of the font which is not transparent. This would be unacceptable for a font class since the colour of the background onto which the text is to be rendered in not generally constant (or even known). The best way to solve this problem would be to have an alpha channel included in the texture so that the edges were smoothed by having low alpha around the edges. Then the outsides would be blended with whatever colour the sprite was rendered onto. Unfortunately, BMP files don’t support an alpha channel, so instead of completely scrapping the BMP loading code for a more complex graphics format a workaround is to load the red channel of the bitmap as the alpha channel.
The texture class has a bool parameter (flag) bRedAsAlpha which determines whether the red channel is to be loaded as the alpha channel. If this flag is true, the loading process makes the texture pure white, but with a varying alpha channel. This means that the colour of the font can be changed by changing the vertex colour, since white multiplied by the vertex colour equals the vertex colour.
The font.bmp file supplied with the code is actually Times New Roman, which is not a fixed width font. In order to render the letters to the correct width, the character widths are loaded from the file font.dat. This file simply contains 256 8-bit numbers containing the width of the characters in pixels.
Creating a fonts is quite easy. All that is needed is “Bitmap Font Builder” from http://www.lmnopc.com/bitmapfontbuilder/ and follow these steps:
· Load up Bitmap Font Builder
· Go to the “Character Set” menu and select Full ASCII (0 – 255)
· Choose the required settings for Font 1
· Set the texture size to 256 x 256
· Texture colours should be: background = black, foreground = red
· Make sure “grid” is unchecked
· Go to the “Font Smoothing” menu and make sure it is enabled
· Next go to File -> Save BMP (this will save as a 24 bit bmp, but it may be better (in terms of size) to convert it to 8 bit).
· Finally File -> Save Font Widths (Byte Format)
The font class is initialise at the start of the program using the following code:
// Load the font bitmap and data
g_bLoop = false;
printf("Can't load font.dat\n");
if(!FontTex.LoadBitmap("font.bmp", false, true))
printf("Can't load font.bmp\n");
g_bLoop = false;
Font.Load() loads the character widths file and initialises the height of the characters. If the second parameter is true, the characters will be rendered at twice their normal height. This is good when rendering in PAL or NTCS mode to a television as the smaller characters can be difficult to see. If the second parameter is missed out, or is false, normal height characters will be rendered.
Rendering of the text is accomplished within the main render loop as follows:
Font.RenderC( -115, -200, "White Text", 127, 127, 127 );
Font.RenderL( -115, -160, "Red Text", 127, 0, 0 );
Font.RenderR( -115, -120, "Blue Text", 0, 0, 127 );
Font.printfC( 180, -240, 127, 127, 127, 127, "Frame %d", Frame);
Font.printfL( 180, -200, 127, 127, 127, 127, "Frame %d", Frame);
Font.printfR( 180, -160, 127, 127, 127, 127, "Frame %d", Frame++);
Firstly the font texture is loaded into GSmem (the font texture can be kept permanently in one buffer to prevent uploading every frame if desired) then selected for rendering. There are six rendering functions. In the Font.RenderX() methods, the text can be centre, left or right justified as requested. The first two parameters are the position of the text on screen, the third parameter is the text to be rendered and the final three parameters is the RGB colour for the text. The Font.printfX() methods similarly produce left, right and centre justified text about the position on screen which is specified by the first two parameters. The next three parameters are the RGBA colour for the text and the next parameters are the standard text formatting string and variables in normal printf() notation. The Font.printfX() functions act exactly like the standard printf() function so that values of variables etc. can be rendered to the screen without having to go through the trouble of manually putting variable values into a string.
In this tutorial the use of a font class has been presented to allow text to be rendered to the screen.
Dr Henry S Fortuna
University of Abertay Dundee