The simplest but not quite so fast way to display text in programs that use SDL is to use SDL_ttf which lets you use true type fonts. If your program has a lot of text in different sizes and colors then SDL_ttf is the best way to go. If you only need one or two sizes of text and want to display or animate it rapidly then blitting graphics is faster. That's what this article is about.
- Unsure About SDL? Read What is SDL?
To print out text as graphics, you need a font and the characters you use must be saved out in a graphics file in the correct size and color that you need. There are two choices of font, mono spaced or variable width. Monospaced are like courier, Consolas etc and each character occupies the same width so an I is the same width as an M. True type fonts are variable width which means you need to track how wide it is and where it starts in the graphic.
With fixed width you just multiply the width to get the offset, This assumes that all characters are stored in order on one row.. This example uses fixed width.
If your program doesn't use SDL_image, you will be restricted to BMP files. With SDL_image your program can use gif, png etc. In my case I wanted it to look nice so I used jpg. Gif is restricted to 256 colors and the image would have had banding.
The principle is that your program works out the code needed then calculates an offset into the graphic and blits that graphic onto the screen which is very fast.
Odometer Example
To show this in action, I've written a short program in C that simulates an Odometer (Analog trip meter in an automobile). This is six digits long, each 0-9 though it only uses 5 and the last digit scrolls for tenths of a mile. The font characters use a font I've made up of 3D metallic letters like you get on the outside of some cars. These 10 digits are 100 pixels high and 110 wide. You'll see those numbers in the program and the characters are in the file ododigits.gif.
To compile this you'll need a C compiler, I used Visual C++ 2010 Express and of course you must have SDL installed if you wish to build it. Here's links to the instructions for installing Visual C++ 2010 and setting up SDL
- How to Install Visual C++ 2010 Express (It's free!)
- Read how to install SDL with Visual C++ 2010 Express/Visual Studio
You can run the program as it is- I've supplied a compiled exe (built on a virus free PC). The following dlls and graphics need to be in the same folder as odo.exe.
- odo.jpg - the background image
- ododigits.gif - the font/li>
- SDL.dll - Main SDL dll
- SDL_image.dll - Needed for handling gif, jpg etc/li>
- jpeg.dll Needed for jpg.
- zlib1.dll - Does the decompression for jpg
That's it. I've added the source code file odometer.c (as a .txt file) to this and that's all the files in the odometer zip.
- Download all files (zipped) in odometer.zip
How It Works
InitData() does all the work for setting up. This initializes the random number generator seed, initializes SDL, setting up a 800 x 600 window with the caption Scrolling OdoMeter. The two files are loaded into memory in the LoadAllImages() function plus a few variables are then initialized. SDL uses surfaces and two are allocated for the background and font images as part of loading the images into memory; the function IMG_Load(file name) does this, loading and allocatimng memory and the third surface is for the screen itself (called screen) when the window is setup.
Back in the main function, the game loop uses the infinite loop while(1). You can close the window with the mouse or hit the escape key. There are three other keys that work. The f key toggles the frames per second (fps) which is displayed in the Windows caption along with the odometer delay. This is a value between 0 and 1 second in 100 ms intervals and the [ and ] keys slow it down and speed it up.
All keys are handled in the main loop. It does very little else except call UpdateOdo() and RenbderScreen(). This returns if sufficient time hasn't passed, and this depends upon the variable delay. It's how the scrolling digits speed is managed. If the time has passed, the variable tenth is incremented and when it reaches ten, the function rolldigit() increments tenthmiles and if it reaches ten, the variable miles is incremented.
Tenth is used to scroll the tenth of a mile digit (the rightmost one) by one tenth. So every ten scrolls the digit changes from 0 to 1. The value of that digit is stored in tenthmiles and when it rolls over from 9 to 0, the miles variable is incremented. It's left as an exercise for the reader to have the mile indicators scroll. In this version only then tenth of a mile indicator scrolls!
Displaying the OdoMeter
On my system which is quite fast, due to an incredible (as in incredibly poor piece of programming), I only get about 66 frames per second and this performance can definitely be improved but that's also left as an exercise. The reason it's so poor is that each time the display changes (ie when the tenth of a mile indicator scrolls), the entire background image is redrawn and then all the digits printed out. For best performance, only the digits that change should be redrawn.
The function RenderScreen() below redraws everything. The first line copies the background image, just blitting the surface into the screen. The second line calls DrawDigits() to print them out and the third flips the double buffer.
void RenderScreen() {
SDL_BlitSurface( planes[ODOMETER], NULL, screen, NULL ) ;
DrawDigits() ;
SDL_Flip( screen ) ;
}
Printing the digits
This is quite messy code due to the fact that from start to finish the whole program only took about 4 hours! There are two parts to it, printing the miles digits (the leftmost 4 digits), then the scrolling last digit. The print() function outputs a string at the specified point (x,y) on the screen. The partprintch() function is called twice to output the top and bottom parts of the scrolling digit.
The last parameter (0 or 1) is passed in to print() and partprintch() because the non scrolling and scrolling digits use the same printc()h function and this parameter is 1 for the lower part of the scrolling digit printing.
The principle behind this fast printing is to print each character one at a time. It finds the character block which is 110 wide by 100 high in the font graphic and blits it into place. The printch() function takes the char c, a target destination and the lower flag value (it has two values 0 and 1).
All character output happens in printch()
void printch(char c,SDL_Rect * target,int lower) {
SDL_Rect charblock;
int ok;
int start= (c-'0') ;
if (c!= ' ') {
charblock.h=(*target).h;
charblock.w=110;
charblock.x = start*110;
charblock.y = (charblock.h==100 || lower==1)?0:100-charblock.h;
ok=SDL_BlitSurface(planes[TEXT],&charblock,screen,target) ;
if (ok==-1)
{
printf("error %s",SDL_GetError()) ;
}
}
(*target).x+= 125;
}
Setting up charblock is the most complex piece. (h=height,w=width, x and y are where it starts from). For the miles digits it blits an entire 100 pixel character. For the scrolling digits, it's called twice, blitting the top part of the old digit that's scrolling up then the lower part of the next digit that is scrolling into view.
The lower flag indicates that it's blitting either the lower of the two scrolling digits (value 1) or the upper (value 0). The height of these two digits should always add to 100 and the upper digit is always blitted into the y=0 position. As the digits scroll the upper digits gets smaller while the lower digit gets taller and blitted progressively higher up the screen.
This confusing line manages most of that.
charblock.y = (charblock.h==100 || lower==1)?0:100-charblock.h;
What it means is, if the height of the blitted character is 100 (the non scrolling digits) or it's the lower of the scrolling digit then blit from the top (0th) row of the character. The top scrolling digit gradually vanishes so the blitting starts progressively further into the character.
Finally the line
(*target).x+= 125;
After outputting a character, it repositions the output block to the next digit in the odometer. Although the font chars are 110 pixels wide, in the odometer they are spaced 125 pixels apart.
Conclusion
That's it. A nice effect of scrolling digits that could with more effort, apply to every digit as it clocks over.


