Friday, September 21, 2018

Text on a Graphics Display

Whenever possible I like to write my own code instead of using a library (at first). It's a challenging puzzle and often times I learn how to do something useful as well. Most recently I got a 128x64px LCD to use with an Arduino micro-controller, the display could show graphics but its built in text mode was very limited, so after getting the graphics mode running I set about building a way to turn strings into text on my display.

My basic plan was to store a bitmap of each letter and its width in an array so that I could look it up and read it back as x/y pixel locations. In order to keep my program as small as possible I had to choose the type of data I used to store the letters so that there would be very little wasted space while also being easy to maintain and update, eventually I settled on storing each letter as a 32bit integer.

The letters would have a maximum size of 5x5px, this would use 25bits, leaving 7bits for storing extra data about the letter. Initially, I used the first 4bits to store the letter's width, the next 25bits would hold the letter data and finally there would be 3 zeros at the end. The letter "A" for example would look like this:

01010111010001111111000110001000

Or for better readability:

0101    <-Character is 5 bits wide
01110   <+Bitmap of character
10001    |
11111    |
10001    |
10001   <+
000     <-Leftover bits

Unfortunately for me, C/C++ doesn't allow integers to be expressed in binary format if they are longer than 8 bits so I had to write a small program using Processing.org to scrape all the strings of ones ans zeros from a text file and then convert them into integers so my final value for storing the letter "A" ended up being 464332680.

To read back each letter and get it on screen I first extract the letter width by bit-shifting the variable 28 times, this leaves only the 4 most significant bits, and in the case of "A" this is a value of 5 "0101." Next I read back the 25 bitmap bits by shifting the variable from 27 to 4 times and masking all but the least significant bit (with the & 1):

int letterWidth = bitMap >> 28;   <-Extract the width of the letter
int i = 27;                      <-Starting at bit 27
for(int cy = 0; cy < 5; cy++){   <-Starting on row 0 and increasing to row 4
  for(int cx = 0; cx < 5; cx++){   <-Starting on column 0 and increasing to column 4
    if(((bitMap >> i) & 1) == 1){   <-If bit "i" is equal to 1 then draw a point at x, y
      point(x, y,);
    }
    i--;    <-Decrease "i" because we are moving from bit 27 back to bit 4
  }
}

Afterwards I pass the letter's width back to the string handling function so that I leave the appropriate amount of space between the letter I just printed and the one that comes next. Once this was working I used one of the three trailing zeros to indicate if the letter was a lowercase that needed to descend below the normally lowest pixel. The results are satisfactory given the small size of the display.



No comments:

Post a Comment