Still Learning SDL

I was rereading Programming Linux Games the other day. I love rereading books because there will always be something that you didn’t notice the first few times through. This time, I learned two ways to render sprites much more efficiently than I was currently doing. I had heard about these two techniques, especially from my first readings, but I forgot about them.

Understanding colorkeys

In Chapter 4, Mastering SDL, John Hall talked about setting colorkeys to simulate transparency when drawing sprites. If you’ve ever seen the image files in many games and tutorials, you’ll notice that they aren’t transparent images. See Aaron Cox’s sprite tutorial for an example. Usually the sprite being rendered will be surrounded by a certain color, sometimes hot pink, but no matter what, there is a color instead of complete transparency. The reason why the sprites render as if they are transparent is because that color is set as the colorkey, and SDL knows not to render it.

Why not just use alpha values in an image? You get the same effect, right? While the end result might look the same, the amount of processing power you need is not the same. To render alpha values properly, there are calculations needed for each pixel. The surrounding pixels are taken into account, and the final value is determined before the color is set. With colorkeys, the pixel is either rendered or not rendered.

If you have a game in which it is possible for many sprites to overlap, calculating alpha blending for otherwise blank areas of multiple sprites will slow down rendering time. Maybe faster computers won’t even blink, but you might be increasing your game’s minimum hardware requirements needlessly.

Understanding Display Format

SDL allows you to load images from a file into an SDL_Surface. To render them to your screen, which is also a surface, you simply blit them together using SDL_BlitSurface(). Hall also warns that doing so with slightly incompatible surfaces can slow down rendering. If your sprite is in one format, and your screen is in another, SDL must convert the sprite to the screen’s format before rendering. It does this transparently so you don’t need to specify anything, but do you really need to do these calculations every time it renders?

No. SDL_DisplayFormat() will take a surface and return a surface that is in a format that is optimal for fast blitting to your screen.

Here’s what threw me off. My code was organized like so:


if (!isSDLVersionUpToDate() ||
!initializeSDL() ||
!initializeWindow()) ||
{

My code first checks to make sure you have an up-to-date SDL version on your system. Assuming that’s cool, it initializes SDL using SDL_Init() and gets my program ready. Then it creates the window and sets up my main screen surface for rendering.

I then changed the sprite loading code from:


m_sprite = IMG_Load("foo.png");
if (NULL == m_sprite)
{
// output error
}

to


SDL_Surface * tempSprite = IMG_Load("foo.png");
if (NULL == tempSprite)
{
// output error
}
m_sprite = SDL_DisplayFormat(tempSprite);
if (NULL == m_sprite)
{
// output error
}
SDL_FreeSurface(tempSprite);

All fine and good, except my sprites were getting loaded in initializeSDL(). (NOTE: Yes, this is bad. The name of the function didn’t indicate that it was also loading sprites. I was the one who wrote this code, and I was the one getting burned by it). When I tried to change my sprite loading code, SDL_DisplayFormat() kept returning NULL, which indicates failure. Why was it failing, though?

After asking on IRC and doing research online, I found out why. SDL_DisplayFormat() converts your surface into an optimal format for fast blitting to the screen. Well, until you create a screen, how does it know what that format should be? In my code, SDL_SetVideoMode() gets called in initializeWindow(), which was after the sprites got loaded, so of course SDL_DisplayFormat() would fail. It didn’t know what the optimal format would be because we hadn’t created the format target! Well, that’s an easy fix. I just moved the sprite loading code to its own function, which I called AFTER initializeWindow(). Now SDL_DisplayFormat() returns a valid surface.

Of course, I hadn’t set the colorkey yet, and I wasn’t ready to make the changes to the graphics as it was getting late, so to make sure my game otherwise looked the same, I changed all calls to SDL_DisplayFormat() to SDL_DisplayFormatAlpha(). Now it converts the surface to allow for faster blitting, and it handles alpha transparency correctly. It isn’t the most efficient, but it is definitely an improvement over what I had before. I still intend to convert my transparent images to opaque ones that get rendered properly using colorkeys since there is still a performance gain there.

Hopefully, my efforts helped someone out there, if only by allowing them to play games without needing to spend the money on upgrading hardware.


3 comments to Still Learning SDL

  • Nice. I don’t know SDL at all, but since I’m complete noob when it comes to graphics, bookmarking this for later consumption. Just with the mucking around I’ve done to get bad programmer art on screen, I’m sure my usage of alpha could be a heck of a lot slimmer.

  • I figure that the above info is fairly common knowledge for people who know what they are doing, and you will probably find these things done in sprite engines built on SDL. Still, I had trouble finding an answer, so I thought I would write it up myself in case someone ends up in my position in the future.

  • Hi,
    I don’t know how useful this will be to you, as my interests are typically focused on things other than programming, but I found this tutorial a while back:

    http://lazyfoo.net/SDL_tutorials/index.php

    Perhaps you know everything here, but maybe someone else who reads these comments will find it useful. It looked like a good tutorial for basic SDL to me. :p