Up until now we’ve only been using BMP images as they’re the only type supported by the base SDL library, but being restricted to using BMP images isn’t that great. Fortunately there are a set of SDL extension libraries that add useful features to SDL, such as support for a wide variety of image types through SDL_image. The other available libraries are SDL_ttf which provides TTF rendering support, SDL_net which provides low level networking and SDL_mixer which provides multi-channel audio playback.
In this lesson we’ll just need SDL_image but the installation process for all the extension libraries is the same. Download the development library for your system from the project page and follow the instructions below to get SDL_image set up on your system.
Windows (MinGW or Visual Studio): Merge the extension library’s files into your existing SDL2 directory. You’ll also need to copy the SDL2_image, zlib and any image format dlls (such as libpng) over to your executable directory so that they’re available at runtime
Linux: Install from your package manager or by downloading the source and building with CMake.
Mac: Download the .dmg from the site and follow the Readme.
To use the libraries you’ll need to update your includes and build system to link against the new extension library.
Grab the appropriate module from the repo
and add it to your CMake modules directory (
cmake/). You can then look up the new extension library using the same
find_package method used to find SDL2 and include
SDL_image.h in your source file.
SDL_image.h and add
SDL2_image.lib to your linker dependencies.
SDL2/SDL_image.h and add
-lSDL2_image to the
SDLLIB variable in your makefile.
SDL2_image/SDL_image.h and add
-framework SDL2_image to the
SDL variable in your makefile.
In this lesson we’ll see how to load images with SDL_image along with looking into scaling our textures when they’re drawn and come up with a method to place tiles based on the window size and the tile size. To do this we’ll need to define a tile size constant, which we’ll add below our window constants.
SDL_image lets us load multiple types of images along with allowing us to load them directly to an SDL_Texture
IMG_LoadTexture. With this function almost all of our
loadTexture code can be replaced and now
IMG_LoadTexture to load the texture, check for errors and return.
We can still use
logSDLError to log errors from the SDL_image library as the
IMG_GetError function is just a define of
With SDL2 we’re able to scale textures when they’re rendered by specifying a width and height for the
destination rectangle that differ from the texture’s width and height. However it’s also common to
draw the textures without any scaling applied and it’d be a real pain if we had to specify the width
and height each time we wanted to draw without scaling.
To get around this we’ll create two versions of
One will take the width and height along with the other parameters while another version will mirror our original
and will use the texture’s width and height for the destination.
To set the texture’s width and height for drawing we simply write the width and height we want to the destination rect’s width and height fields instead of getting them from the texture.
We’ll also create a function that provides the old functionality of drawing the texture without any scaling. This
function will just get the width and height from the texture then call our new
When loading an image for the first time SDL_image will automatically initialize the necessary image loading
subsystem, however this will cause some delay in loading the image since SDL_image will have to perform
its initialization setup first. If you’d like to initialize SDL_image earlier
to avoid the delay when loading an image type for the first time you can do so with
IMG_Init will return
a bitmask of all the currently initialized image loaders, so we’ll perform an and with the flags we passed
to see if initialization was successful. Here we only initialize the PNG loader since that’s all we’ll need for this lesson. This initialization should be done after initializing SDL.
We then open a window and renderer the same as we did in lesson 2.
Since this lesson is to demonstrate loading PNGs we’ll be using new images for our background and foreground. We’ll also demonstrate that PNG transparency is preserved by using a foreground image with a transparent background drawn on top of a tiled background. Grab them both below.
The images are loaded exactly the same with our
loadTexture function. Be sure to update the file paths to match
your project structure.
Since our tiles are much smaller now we’ll need a lot more than 4 to cover the entire screen and typing their positions out by hand would be a real pain. Instead let’s generate the tile draw positions to fill the screen by using some math!
We can determine how many tiles each row will need by dividing the
SCREEN_WIDTH by the
TILE_SIZE. To determine
the number of tiles per column we can do the same thing but for the
SCREEN_HEIGHT. Since we’ll be filling a square
area of tiles the total number of tiles will be
tiles_per_row * tiles_per_col. We could use a single for
loop to run through all the tiles, or nested for loops to fill each tile in a row for each row. I’ve chosen to go with a single loop.
In the loop we compute the x and y indices of the tile to determine where it should be placed. Since we’re drawing
row by row in this method the x index will repeat each row, while the y index will increment after each row is filled
and we move down to the next row.
Thus we can calculate the x index using the absolute tile index modded with the number of tiles per row:
x = tile_idx % tiles_per_row.
For example, if we were drawing a 2x2 grid of tiles we’d expect tile 0 to have the same x index as tile 2, which with this method it will:
0 % 2 == 0 and
2 % 2 == 0.
The y index should increase after an entire row of tiles has been placed, so every
tiles_per_row tiles. Since we’re using integers
we can take advantage of integer truncation and compute this as
y = tile_idx / tiles_per_row. So on our 2x2
grid example: row 0 will have tiles 0 and 1:
0/2 == 0 and
1/2 == 0,
and row 1 will have tiles 2 and 3:
2/2 == 1 and
3/2 == 1 giving the correct y indices.
All that’s left to do is convert the indices into the pixel coordinates of the tile, which is done by multiplying the x and y indices by the tile size, and our tiling loop is done!
Note: All of this rendering code will be placed within our main loop, similar to lesson 1.
Our foreground image is drawn the same as before, centered in the screen.
We’ll then present the renderer and wait a few seconds before exiting just as we did in Lesson 2.
Clean up is the same as in lesson 2 with one added line to quit SDL_image by calling
If everything went well you should see this draw to your window.
If you have any issues check your error log and check back through the lesson. Feel free to post any questions you may have below.
I’ll see you again soon in Lesson 4: Handling Events!