Bitmap Fonts
Bitmap Fonts
com
Bitmap Fonts
1
.com
Contents
Introduction .................................................................................................................................................. 3
Writing Code to Write Code.......................................................................................................................... 4
Measuring Your Grid ..................................................................................................................................... 5
Converting an Image with PHP ..................................................................................................................... 6
Step 1: Load the Image ............................................................................................................................. 6
Step 2: Scan the Image.............................................................................................................................. 7
Step 3: Save the Header File ..................................................................................................................... 8
The 1602 Character Set............................................................................................................................... 10
The 1602 Character Map ............................................................................................................................ 11
Converting the Image to Code .................................................................................................................... 12
Conclusion ................................................................................................................................................... 15
Rendering the Characters ........................................................................................................................... 16
Rendering Complete Strings ....................................................................................................................... 17
2
.com
Introduction
Before we had True Type fonts, we had Bitmap Fonts. Bitmap Fonts are exactly what they sound like:
fonts made from images. True Type fonts are made of math which is why they can scale so cleanly to
any size. Bitmap fonts are made of discrete pixels and as such, don’t scale well.
Early computers like the Apple ][, Commodore, and IBM PC contained a special bit of memory that held
the character set that was used to display information to the user. The original IBM PC had a character
set called Code Page 437
https://en.wikipedia.org/wiki/Code_page_437
This set of characters includes a number that are useful for games and drawing box graphics. Before
there were graphical user interfaces, interfaces were drawn entirely with text. In total there are 256
characters that are 9x16 pixels in size. However, the last pixel is always blank and simply adds space
between characters. The real width of the actual images are 8 pixels which fit into a byte. Since the
Arduino is equally limited, this character set makes a very good choice for a font with a lot of versatile
uses.
http://www.vintagecomputing.com/index.php/archives/790/the-ibm-smiley-character-turns-30
The above link contains a conversation with one of the developers of the ASCII table used with the IBM
PC and why they chose the characters they did.
256 characters times 1 byte wide times 2 bytes wide is 512 bytes of space that will be used to store this
font or 4096 bits. Obviously, we do not want to have to hand code all this data in. Fortunately, we don’t
have to.
3
.com
While this could be done in about any language, we’re going to use PHP because it is very similar to C (it
is basically scripted C) and includes very easy to use graphics functions.
Codepage437.h is generated by the php file and Codepage-437.png is a slightly modified version of the
file found on Wikipedia. The Wikipedia image contains a black border around the characters which I’ve
removed in my own version.
The most important feature of this image is that it is in a regular grid pattern. This makes the math to
convert the bitmap into binary for the Arduino much easier.
Each character is in a 9x16 area and we only need the 8x16 image in each of those areas.
The basic logic is to read each pixel of the image and store either a “0” or a “1” in a string that will then
go into the h file. We’re converting numeric pixel values into the string equivalent of on and off. This
image conveniently uses pure black as the background color so we will just check for black and if the
pixel is black, we store a “0” otherwise we store a “1.” We don’t care what color the text is, just that it
isn’t black.
4
.com
This is where your code will start looking for images in your code. For our Codepage347 image, we have
cropped the image out X starts at 0 and our Y starts at 0.
• The number of pixels horizontally from the start of one tile to the start of the next
• The number of pixels vertically from the start of one tile to the start of the next
This is the boundary of each tile. Note that the boundary may not match the size of the title. We will
see an example of that when we load the character set from the 1602 LCD. The Code Page 437 image
has a boundary size of 9x16
This is the size of each tile. The Code Page 437 image has a tile size of 8x16.
As you will see in the 1602 Character set image, each pixel of the character is represented by a 5x5
square. The Code Page 437 image is actual size, so each pixel is 1x1. When the image is magnified to
have bigger pixels, it is a good idea to calculate the general midpoint as well, so you are scanning the
middle of each pixel. Use the floor function to round down to a whole pixel.
You may have noticed already, that this technique is also useful for sprites. There are many resources
on the internet with tile sets from various video games.
While you may not use them in a commercial product or profit from them in any way, you may use them
as a starting point for developing your own sprites.
Note that “profiting” from copyrighted works also includes notoriety. For example, if you have a popular
independent game that uses the graphics from Mega Man and distribute it on a web-site for free but sell
other products or services on that web-site, you are risking legal problems because you are using other
people’s work to attract attention to your own work for profit.
Absolutely use tile sets that other people have developed to practice programing and game design with.
But eventually, you will need to find your own graphics that you are legally allowed to use and
distribute. A game development team generally starts with a programmer and an artist.
5
.com
PHP includes a simple function called “imagecreatefrompng” which loads the file into a format in
memory that PHP can work with. There are functions to load many different types of image files but
we’re using a PNG.
Once the image is loaded, we get the width and height of it in pixels. “$binary” is where we will be
storing our generated code. Finally, we do the calculations as described in “Measuring Your Grid.” From
these measurements we can now scan the image to convert the image into binary strings.
For this particular image, we can get the number of columns from the width of the image and the width
of each character. Other images may require specifying the number of rows and columns explicitly as it
may be easier than trying to come up with a formula.
6
.com
Step 2 is to iterate over the image reading every pixel into a format that will be useful for the Arduino.
Notice there are two sets of X, Y loops. The first set iterates over the characters, the second set iterates
over the pixels in each character.
This logic could be implanted in a number of different ways. You may notice that the pattern
Y=X*C+B
C is the scaling factor and B is the offset. You may have first seen this function in the form
Y = mX + b
At this point the $binary variable contains the font information in the following format:
7
.com
Each character is in the first dimension of the array and each line of the character is in the second
dimension.
Step 3 starts with going through the $binary array and compressing the second dimension into a single
string with commas after each line. We’re also putting the character number in a comment after each
line so it’s easy to find what you are looking for later.
After the 2nd dimension is rolled up, then we can compress the 1st dimension into a single string which is
suitable to be used as source code for the Arduino.
And finally, we write that string to the .h file we will load into the Arduino.
8
.com
9
.com
The 1602 LCD Character display is a 2-line, 16-column display with a built-in character set that is 5x8
pixels per character. This will give us 8 lines of text and 25 columns of text on our screen. The limitation
of this character set is that it doesn’t have as many useful special characters.
If you’re looked at the Dinosaur project you will see how special characters were stored on the 1602 to
create the sprites used in the game.
You can see that we’re listing 5 bits horizontally and 8 bytes vertically. Which isn’t really possible.
We’re actually storing 8 bits wide and 8 bytes high. The missing zeros are implied. Which is 3 bytes of
wasted memory that we aren’t using per sprite.
There is nothing we can do about this as this is simply how the 1602 character display works. But we’re
not using the 1602 LCD Character display. We just want the character map. So, we can store the
characters however we want. Since the characters are 8 bits high and 5 bits tall, we’re going to rotate
our character map so each row in our code represents one column of the character. We can then store
each character in 5 bytes instead of the 8 bytes required by the actual LCD Character display.
10
.com
Fortunately for us, someone has already gone to the trouble of documenting all the characters in an
image that is of fixed width and fixed height.
Unfortunately, there seems to be 3 missing columns. The first 32 characters of the LCD controller are
empty. Also missing are columns 8 and 9. Those correspond to rows 1 and 5 Code Page 437. A full 64
characters just don’t exist. But we must leave memory for them. This leaves a lot of space in this
character set for custom characters/sprites for your project.
11
.com
As before, we use the imagecreatefrompng function to load the PNG of the character map. Then we get
the size and set the number of expected rows and columns. Note however that we don’t have 16
columns. But our final code needs 16 columns.
Instead of using math and logic to figure out what column of the image each column of fonts
corresponds to, we’re just using a precalculated look up table. The numbers correspond the column in
the image that each column of characters starts at in the image.
Two things to keep in mind: the 1602 set is stored vertically and we’re rotating the font, so it is stored in
memory rotated 90 degrees.
While before we processed the columns and then rows, for this character set we must process rows and
then columns. The image is also the inverse with black representing a 1 and anything else representing
a zero.
12
.com
Again, the Y = mX + B formula is repeated many times. Each pixel of the character in the image is 5
pixels wide and hide. So we have to adjust for that. There is also a pixel of padding on the left and right
side of each character, so we are excluding that. And finally, we add 2 pixels to look in the center of
each square of the image to see if the cell is black or not black.
We always start at pixel 46 for the Y starting position because that is the vertical location each column
starts.
If you remove the zeros of the output and replace them with spaces, you can see how the characters are
stored easier.
13
.com
Notice that we preface the name of the variable with LCD instead of just calling it 1602. This is because
variables in C cannot start with a number.
The logic for generating the source code from our array is the same as before.
14
.com
Conclusion
Both character sets are converted from images to source code in much the same fashion. The
difference is the math and how we store the data in strings representing binary values. This technique
can be used in a lot of different contexts. While in the Dinosaur game we used graphing paper and hand
coded our sprites, we can now use PHP or some other scripting language to read sprite data we can
create in any drawing program.
15
.com
We now need a couple more function in our Display128x64.h file so that we can render the 8x16
characters and the 5x8 characters. The 8x16 character is stored in the normal fashion where the order
of bits matches the visual display. We grab one byte per vertical slice of the character and then render
the 8 bits.
For the 5x8 sprites, we must account for the rotation. Rather than reading the front top to bottom, we
are reading from left to right. We also remove the “7 –“ because the bits are in the correct order
relative to the display. Normally, while the text of the binary strings looks visually correct, it is actually
mirrored relative to how it’s read since the lest significant bit is on the right side and we start rendering
on the left side.
For the 5x8 we are rendering columns where for the 8x16 we are rendering rows.
Rendering Bitmap fonts is a great exercise for encoding and decoding data.
16
.com
And finally, we create a couple functions so we can easily render complete strings to the display.
A string is simply an array of bytes and a zero-value byte indicates the end of the string. For safety, we
are exiting the loop if we get to the edge of the screen.
I’ll leave it as an exercise for the reader at this point to implement line breaks and word wrapping.
When using strings, the compiler automatically adds the null character. Just because you can’t see it,
doesn’t mean it’s not there.
Where you run into trouble is when you dynamically create strings using your own custom functions and
forget the add it.
17