graphics: Fonts and strings

In this section, you will learn about:

Just like with colors, you can find a large list of fonts that the LCD screen can use under the grlib.h file. This file can be accessed by holding Ctrl and clicking on the <ti/grlib/grlib.h> part of your include statement. Below is a snippet of some of the fonts you can find:

Defined as macros, these fonts are actually files containing bitmaps for each ASCII character in the font. Each font follows the format g_sFont[Type][Size][Bold/Italics]. The types of fonts available are as follows:

Each font comes in sizes from 12pt to 48pt, in 2pt increments. A 'b' appended to the font name means that the characters are boldfaced, whereas an 'i' indicates italics. If you'd like to see what some of these fonts look like on the MSP432, try running the following demo on your board:

https://github.com/ECE2564-VT/LCD_fontDemo

Be wary that some characters may not appear correctly with some fonts. This should not be an issue for alphanumeric characters, but characters like '_', '>', or '|' may look completely different in some fonts such as Cm14.

To set a font, use the following function:

Where context is a pointer to your graphics context and font is a pointer to one of the Graphics_Font constants located in the graphics library. If you want to set the font to computer modern teletype with a font size of 24, for example, you would do something like this:

Graphics_Context g_sContext;

Graphics_setFont(&g_sContext, &g_sFontCmtt24);

To draw text on the screen, use the following function:

Where context is (once again) a pointer to your graphics context.

string is, as you may surmise, a string. However, strings work slightly differently in C than they do in C++. This quirk will be elaborated upon later.

lLength determines how many characters in the string are drawn. That is, for a number n, every character in the string up to the nth character will be drawn (so n = 1 would print only the first character, n = 2 would print only the first and second, etc.). Usually, you will want to set lLength to -1 to print the entire string.

x and y are the horizontal and vertical coordinates, respectively, of where the start of the string will be printed.

Finally, opaque determines whether the background color is printed along with the string. true means that the text will be drawn with a background, as if you drew a rectangle with the background color first and then drew text on top of it, whereas false means only the text is drawn. By setting opaque to true, you can effectively erase the old text by drawing over it with the new text. Otherwise, if opaque was set to false, the new text would simply overlap with the old text, making it nearly impossible to read.

If we wanted to print a simple "Hello!" message, here's what the function call may look like:

Graphics_Context g_sContext;

Graphics_drawString(&g_sContext, "Hello!", -1, 5, 20, true);

Now, let's talk about strings in C. A string is simply an array of characters (or char data types). In C++, you may be used to declaring a string like so:

std::string myString = "Hello!"; // DO NOT USE THIS!!

This will NOT work in C. A string in C assumes the char* data type. This is because strings in C are actually pointers to the first element of the character array. So a declaration like this would work:

char* myString = "Hello!";

Alternatively, since strings are arrays of characters, you could also opt for the following declaration:

char myString[7] = "Hello!";

Did you notice that the string "Hello!" contains six characters, but we've allocated the array for seven characters? That's because the last character is the NULL character (which has an ASCII value of 0). This null termination character marks the end of the string. If you tried to draw the following string:

char myString[5] = {'T','e',0,'s','t'};

Only "Te" would be drawn, since 0 indicates the end of the string. If you wanted to print these string variables to the LCD screen, it is as simple as passing that variable as a parameter. Below shows how to do so for all three declaration methods:

Graphics_Context g_sContext;


char* myString1 = "myString1";

Graphics_drawString(&g_sContext, (int8_t*) myString1, -1, 5, 20, false);


char myString2[10] = "myString2";

Graphics_drawString(&g_sContext, (int8_t*) myString2, -1, 5, 50, false);


char myString3[10] = {'m','y','S','t','r','i','n','g','3',0};

Graphics_drawString(&g_sContext, (int8_t*) myString3, -1, 5, 80, false);

Now, how do we print variables containing numbers? I guarantee you that passing the numerical variable directly into the text drawing function will give you garbage. This is because of ASCII (see this page) representation. In short, every character you're familiar with seeing in a string actually has a corresponding numerical value. For example, the character 'Z' has an ASCII value of 90, the character ':' has a value of 58, and the NULL character has a value of 0. So if you pass in a variable with the number 107, for instance, you'll probably get the character 'k' printed instead of 107 (or worse, considering Graphics_drawString asks for a pointer rather than a value).

Knowing this, how can we deal with printing numbers if every character is really just another number? There are two ways:

For the first method, we know that the characters representing the numbers 0 through 9 (in other words, '0' through '9') are represented by the ASCII codes 48 through 57. Take note that the character '0' (as opposed to the number 0 -- note the apostrophes) is the same as 48. Notice that '1' is the same as 49, or 48 + 1. Similarly, '2' is the same as 50, which is 48 + 2. Notice a pattern? Every number's equivalent in ASCII is that number plus 48 or '0'. So if we need to convert a number into ASCII, we simply need to separate the individual digits, then add 48 or '0' to each one.

Suppose our number is 123. We need some way to convert that number into the string {'1', '2', '3'}. The simplest digit to obtain is the ones digit, 3. By taking the modulus of 10 of the number, we can easily get the ones digit.

int myNumber = 123;

char myNumString[4] = "";


myNumString[2] = (myNumber % 10) + '0';

The tens digit, 2, is a bit more challenging. If we could right-shift the entire number by 1, we could apply the mod-10 and extract the digit. We know how to do a binary shift, but how would we do a shift in decimal? Keep in mind that a right-shift in binary is the same as dividing by 2 since binary is a base-two number system. Applying that to a base-10 system means that dividing by 10 would be the same as right-shifting. However, division can lead to fractions that aren't whole numbers. To resolve this, we can take advantage of the fact that int data types truncate fractional components. For instance,

int someNumber = 5/2; // this will be 2 instead of 2.5

By combining this "right-shift" principle with a mod-10, we can get the tens digit.

myNumString[1] = ((myNumber / 10) % 10) + '0';

Now, if we want the hundreds digit, 1, we simply right-shift twice. In binary, this would mean dividing by 4, or 2^2. Thus, in decimal, we would divide by 100, or 10^2.

myNumString[0] = ((myNumber / 100) % 10) + '0';

Now, myNumString contains the value {'1', '2', '3'}. If we passed myNumString into Graphics_drawString, we would correctly see "123" drawn on the LCD.

The second method makes use of a function in the stdio C library called snprintf. If you're familiar with printf or fprintf, snprintf works similarly. These formatted print functions all allow you to swap out parts of a string known as tokens for variables, oftentimes numbers. The difference lies in their outputs. printf prints to an output stream, fprintf prints to a file, and snprintf prints to a character array. This property of snprintf is what we'll be using to convert numbers into strings.

To start, we'll need to initialize a string to use as a buffer.

#define BUFFER_SIZE 10


char buffer[BUFFER_SIZE];

Next, we'll use snprintf to insert our variable into the string. Here, %03d is a token meaning "three-digit signed decimal integer." There are other tokens you can use, which can be viewed here: https://www.tutorialspoint.com/c_standard_library/c_function_printf.htm 

int myNum = 100; // number to print

snprintf(buffer, BUFFER_SIZE, "Hello! %03d", myNum);

buffer now contains the string "Hello! 100". All that is left to do is print this to the LCD.

Graphics_Context g_sContext;

Graphics_drawString(&g_sContext, (int8_t*) buffer, -1, 5, 20, true);