Thursday, July 19, 2012

Lesson 3: SDL Extension Libraries

Deprecation Notice:

I've been updating the lessons now that SDL2 is officially released, please visit my new site on Github that has updated lessons and improved code.

You may have been wondering throughout the previous tutorials if it is possible to load images that aren't BMPs. Although SDL does not provide functionality for loading non-BMP images itself, there exists a large amount of robust and powerful libraries that extend SDL's functionalities to include loading many image formats via SDL_image, rendering true-type fonts with SDL_ttf, support for more audio formats with SDL_mixer and even networking from SDL_net.

By using these libraries we can add much more functionality to our programs and do many things much easier. For now we will only be using SDL_image, but will cover the other extensions in later lessons.

In Lesson 0, when you cloned from the Mercurial and built the libraries you should have also downloaded and built the source for SDL_image, SDL_ttf and SDL_mixer, although I was unable to get SDL_mixer to compile with Visual Studio at the time of writing. This part of the tutorial will be updated when SDL 2.0 is officially released to simply contain links to download the libraries.

If you have not done this, head over to the SDL Mercurial and clone and build the extension libraries available there.

To include the headers and library files on Windows simply paste them in the include and lib directories of your SDL 2.0 folder and add SDL_image.lib and etc. to your linker settings. On Linux following the configure, make, make install pattern will spit out a location where the libraries were installed that you can link against.

In addition to setting up the includes and libraries you will also need to put the dlls that are located in the externals folder along with the dll that was created when building the project. Some dlls are dynamically loaded and as such, only needed if you intend to use their functionality, you may consult the extension library page for the specifics on which dlls behave this way.

In addition to exploring the SDL_image library in this lesson we will also begin to learn about throwing and catching exceptions by adding some error handling to our LoadImage function from last time, instead of checking if the returned pointer was still null. This will allow us to get more information about the exact error that happened and help us track down the cause of the error faster.

For this lesson we will simply be adding some extra stuff to the code we wrote in Lesson 2, so let's get that open. IDE users will want to add SDL_image to their linker settings as described above, and for those of you compiling with g++ add -lSDL_image or -lSDL2_image depending on how your 2.0 libraries are named.

We'll need to add the extra includes for the new things we want to use, SDL_image and stdexcept.
From here we setup as before our screen parameters and create a global window and renderer. If you're feeling ill from the use of global objects you may want to take a moment.
Now let's rework our LoadImage function to use SDL_image's IMG_LoadTexture to directly load a texture from an image. SDL_image supports many image formats: BMP, GIF, JPEG, LBM, PCX, PNG, PNM, TGA, TIFF, WEBP, XCF, XPM, XV and as such is a very powerful and useful library, as we are no longer limited to the BMP format

Reworking our function is surprisingly simple, we just take out the SDL_Surface business and load the texture directly. Note that we now use IMG_GetError() instead of SDL_GetError(), this is because we're now using the SDL_image library to load the image, and as such the appropriate error information would be stored there instead of in SDL.
But we've got one more new thing here. Since we've decided to be smart and add more informative error checking if our image fails to load we will throw a runtime_error which will tell us what happened and more helpfully, which image caused the error. However if you leave this error uncaught when you call the function later in your program and it occurs, it will crash the program. Depending on the situation this may be desirable, but at the moment we'll want it to tell us what happened as opposed to just crashing.

We're almost ready to draw our images, for this lesson we'll be using this image as our background:
and our foreground image will be:

If you look at our foreground image you'll notice that it has a transparent background, I've set it like this to demonstrate that IMG_LoadTexture will respect alpha channels and that when the texture is drawn the transparent properties will be retained.

Now that we've got our images we can write our loading code, but recall that we'll need to add a try/catch structure to handle the error if one is thrown. Also, don't forget to set the file paths to match the image locations on your system.
In our catch statement we tell the program to print out e.what(), which will display the string that we put into the runtime_error's parentheses. Note that depending on how you've configured your project to build you may not get stdout displayed, if that is the case you can simply write the information to a file instead.

The rest of the code is not shown as it is identical to what was written for Lesson 2, so you can just drop in this new functionality into the old program. If you want to mess with the image positions, feel free to play around. When running the program with the same position settings as Lesson 2 you should see this:

Note that the transparency of the foreground image was preserved. This is a very useful effect as it means we won't have to use color keying to cut out the background of our images but instead can use simple transparency. However if you do wish to use color keying, it will be covered in later lessons.

End of Lesson 3: SDL Extension Libraries

If you have issues compiling or running the code make sure that you've added the include paths and linker settings correctly, along with putting the dlls from SDL_image and its dependencies, located in the externals folder of the source code, into the same folder as your executable.

I'll see you again soon in Lesson 4: Event Driven Programming!


  1. Hey it's me again, I got a couple errors when trying to build SDL_image extension:
    1>c:\users\paulo\sdl_image\img_bmp.c(387): error C2039: 'a' : is not a member of 'SDL_Color'
    1>c:\users\paulo\sdl_image\img_bmp.c(394): warning C4022: 'function through pointer' : pointer mismatch for actual parameter 2
    1>c:\users\paulo\sdl_image\img_bmp.c(394): error C2198: 'function through pointer' : too few arguments for call

    Got multiple errors about SDL_Color, as you probably noticed I'm getting the latest files today; sorry for inconvenience, but since it's SDL's source I don't feel encouraged to touch it, can you help me?

    1. Goddamn forget it, I re-downloaded everything and re-compiled everything (SDL, SDLmain, and SDL_image) and it's working now.
      Dude really, computers...

  2. I am building on a command line on a mac. I tried to load the png file and I get "Unsupported Image Format". I brew installed libpng and linked to it on the cmd line but I still get the same error. How do I make SDL_image aware of libpng? Do I need to recompile SDL_image?

  3. Nevermind I figured it out. Disregard my post. The image I was trying to load wasn't an image.

  4. Hey! What am I doing wrong?
    My code compiles and runs without problems, returning 0. However, I only get a black screen. And if I try to change the filenames to something that doesn't exist, it still returns 0 and shows no errors. It doesn't even try to load the files, it seems.
    And btw, I hope you see how I did the extra challenge ;)

    1. Whoops, looks like you've got assignment where you meant comparison:

      if (tex = nullptr)

      should be

      if (tex == nullptr)

      Within the LoadImage function.

      The extra challenge looks pretty good! I think for loops would be a bit more concise, but the idea is the same. Neat!

    2. Oh damn. How could I forget that :D I thought I made some basic mistake with C++, as I only have real experience from Java, but no. Thank you, I'll check the code right away.

  5. Hey, I'm liking the tutorials so far but I've run into an error that I can't figure out
    When I run it says: "Failed to load image: background.png Invalid renderer"

    1. The invalid renderer error makes me think there's something wrong with the renderer you're using, is it creating ok? Also it's probably best to double check your filepaths and such.

  6. "If you have not done this, head over to the SDL Mercurial and clone and build the extension libraries available there.

    To include the headers and library files on Windows simply paste them in the include and lib directories of your SDL 2.0 folder and add SDL_image.lib and etc. to your linker settings"

    I tried doing this but it doesn't even make any sense to me. SDL_image did not even get created on my pc. Can you make this a little more clearer??

    1. So now that SDL2 is officially released you don't need to build it or the extensions from source. An updated version of this lesson (with an updated setup guide) can be found here:

    2. Ok, I figured out how to do that I think, but it keeps saying "Error, cannot open file 'SDL_image.lib'.

    3. Now i get a "cannot open file 'libjpeg-9'" error. Why does this have to be so confusing and complicated just to put some images on a screen? As intelligent as programers seem to be, they should surely be able to make it easier than this. It's like they purposefully make it hard, confusing, and convoluted for beginners who are trying to learn.

    4. I went to the updated page, I tried to follow the instructions but it still came up with the "cannot open file 'libjpeg-9'" error. It may be because I don't exactly understand what to do when you said this on that page after we're supposed to merge the extension's libraries with our current SDL directory:

      "To use the libraries you’ll need to update your includes and build system to link against the new extension library."

    5. Ok, I got it now, I needed to remove the extra lib files that this page says to add to the linker dependencies since the updated page omits them.