This chapter explains in detail how FreeWPC fonts and graphics work.
FreeWPC uses simple bitmaps to represent fonts, icons, and full-screen graphics. A bitmap is an array of rectangular pixel data. The data bits are prefixed by a short header which contains the width and height information. The image is stored one row at a time, starting from the top. Within a row, the bits are stored in little-endian format, with the least significant bit representing the leftmost pixel.
A font is a set of bitmaps mapped to a contiguous sequence of characters. The font header identifies the ASCII code for the first bitmap that is represented; this saves space by not needing to encode the low-valued ASCII control characters. The nominal height of the characters is also defined, which allows for descenders.
A frame is a full-screen bitmap, 128x32. Since these are fairly common, special APIs are used to draw them that are more optimal, and the width/height is implied and not actually stored in the bitmap. A frame can have 4 colors per pixel (3 shades plus black) so the frame is actually stored as two consecutive bit planes.
Each bit plane of a frame can optionally be compressed using run-length
encoding (RLE) or zero suppression to save space. An uncompressed
frame requires 1KB, which limits the total number of frames in a ROM
significantly. Every frame header includes
a flags field, which indicates which if any compression method was used.
The frame decoders are written in assembly language in platform/wpc/dmd.s
.
Compression is a balance between ROM size and processor speed. The best compression methods would take far too long for the 6809 to decode. The methods used strike a compromise. In particular, bitwise operations are avoided in the decoders because of the 6809's inability to do bitlevel operations quickly.
Useful for images that contain long sequences of the same byte value. Each long run is replaced by a 3-byte code: the first byte is the code 0xA8, which signals an escape sequence, the second byte is the number of bytes divided by 2 (so the length must be even), and the third byte is the repeated value. A special two-byte code also indicate end of frame, so that the decoder does not need to count the number of decoded bytes.
The sparse format encodes images that contain mostly zeroes, or which have large transparent sections that do not require writing to the display. The image is encoded as a series of (length, data, move) triples, where the move value says how many bytes forward to move the cursor.
The script fontgen2 converts TrueType font files (.ttf files) into WPC font files (.fon). Not all characters are supported; in particular, lowercase characters are not included by default in order to save space. Trying to display any characters not included in the font will generally cause a system crash, as there is not much error checking.
The font_render_string
family of APIs is used to draw text to
the display. The arguments are a font object, the x and y coordinates
where the string should be placed, and a string. The string can be
a constant string literal or the global format buffer (see below).
There are three variants which justify the text differently: centered, left-justified, and right-justified. Centering is done both vertically and horizontally; the others only justify left-to-right, and the y coordinate always specifies the top of the print area.
Text printing is CPU intensive. Display effects should take care not to print text more than needed. It is often more efficient to print strings to an overlay buffer, and then copy them to the main display page, if the same text needs to be printed over and over again.
FreeWPC contains a printf
-like function for formatting text
strings with variable data, however, it is not quite the same.
The sprintf()
function formats a string into a unique,
global buffer named sprintf_buffer
. It is like the
actual C function of the same name, but the first argument is implied.
The format specifiers are also slightly different. Here is a list of the valid ones:
%b
%8b
would print a 4-byte BCD
string containing 8 digits. Also, this format will insert commas
(or periods) between digits as necessary.
%c
%d
U8
).
%E
%ld
U16
).
%lx
U16
).
%p
%s
%w
U32
).
%x
U8
).
Like in C, you can insert a number in front of the format letter to limit the output to a particular width. If the length begins with '0', then it will be padded with leading zeroes if necessary.
The formatter does not support signed numbers, and will print them as if they were declared unsigned.
The formatter is not particularly efficient for printing large decimal values, as the 6809 is not very good at long division.
The list of all frames compiled in the ROM is defined in an image map. This is a machine-specific file that says which images to copy into the final ROM image.
There can be multiple frame list files. The common code provides a frame list of standard images, like the FreeWPC logo, which go into every build.
Each entry in the image map gives an image to be imported, such as a PGM graphics file, plus an optional frame ID. The frame ID becomes a C #define that refers to the image from the source code. The image linker writes a file build/imagemap.h which contains a list of all the frame IDs. Frame IDs are optional for the internal frames in a sequence (a for loop would only need to name the starting and ending frame).
It is the job of the image linker to decide what compression techniques to perform. The linker is told the maximum amount of space that can be used for images, which is the total size of the ROM minus any sections reserved for source code. If all images fit without compression, then all is well.
Otherwise, the linker will perform as much as compression as necessary. At present, images are compressed in the order that they were declared. A future enhancement would be to start with those images that can be compressed the best without requiring much more CPU power to decode them.
In some rare cases, trying to compress an image fails to produce a smaller buffer. The linker notices this and leaves such images uncompressed.