Using the first tutorial as our base, we'll delve more into the world of
SDL surfaces. As I attempted to explain in the last lesson, SDL Surfaces are basically images stored in memory. Imagine we have a blank 320x240 pixel surface. Illustrating the
SDL coordinate system, we have something like this:

This coordinate system is quite different than the normal one you are familiar with. Notice how the Y coordinate increases going down, and the X coordinate increases going right. Understanding the SDL coordinate system is important in order to properly draw images on the screen.
Since we already have the main surface (Surf_Display) setup and ready, we are going to need a way to draw images on it. This process is called Blitting, where we basically transfer one image onto another. But before we can do that, we have to have a way to load these images into memory. SDL offers a simple function to do this called
SDL_LoadBMP. Some pseudocode might look like this:
SDL_Surface* Surf_Temp;
if((Surf_Temp = SDL_LoadBMP("mypicture.bmp")) == NULL) {
//Error!
}
It's rather simple,
SDL_LoadBMP takes a single argument, the file you want to load, and returns a surface. If the functions returns NULL, either the file is not found, corrupted, or some other error. Unfortunately, for the sake of effeciency, this method is not enough. Often the image loaded will be in a different pixel format than the display. Thus, when we draw an image to the display we can incurr performance loss, lose image colors, and the like. But, thanks to SDL, it offers a quick solution around this,
SDL_DisplayFormat. This function takes a surface already loaded, and returns a new surface using the same format as the display.
Let's take this process and throw it into a reusable class. Use
SDL Tutorial 1 as the basis for you code, and add the following two new files: CSurface.h, CSurface.cpp. Open up CSurface.h and add the following:
#ifndef _CSURFACE_H_
#define _CSURFACE_H_
#include <SDL.h>
class CSurface {
public:
CSurface();
public:
static SDL_Surface* OnLoad(char* File);
};
#endif
We've created a simple static function, OnLoad, that will load a surface for us. Now open CSurface.cpp:
#include "CSurface.h"
CSurface::CSurface() {
}
SDL_Surface* CSurface::OnLoad(char* File) {
SDL_Surface* Surf_Temp = NULL;
SDL_Surface* Surf_Return = NULL;
if((Surf_Temp = SDL_LoadBMP(File)) == NULL) {
return NULL;
}
Surf_Return = SDL_DisplayFormat(Surf_Temp);
SDL_FreeSurface(Surf_Temp);
return Surf_Return;
}
There are a couple of important things to note here. Firstly, always remember that when you make a pointer to set it to NULL, or 0. Many problems can come along later if you fail to do this. Secondly, notice how
SDL_DisplayFormat returns a new Surface, and doesn't overwrite the original. This important to remember because since it creates a new surface, we have to free the old one. Otherwise, we have a surface floating around in memory.
Now that we have a way of loading surfaces into memory, we are also going to need a way to draw them onto other surfaces. Just like
SDL offers a function to load images, it also offers a function to draw (blit) images:
SDL_BlitSurface. Unfortunately, this function is not as easy to use as the
SDL_LoadBMP, but nonetheless, it's simple enough. Open back up CSurface.h and add the following function prototype:
#ifndef _CSURFACE_H_
#define _CSURFACE_H_
#include <SDL.h>
class CSurface {
public:
CSurface();
public:
static SDL_Surface* OnLoad(char* File);
static bool OnDraw(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src, int X, int Y);
};
#endif
Now, open back up CSurface.cpp, and add the following:
#include "CSurface.h"
CSurface::CSurface() {
}
SDL_Surface* CSurface::OnLoad(char* File) {
SDL_Surface* Surf_Temp = NULL;
SDL_Surface* Surf_Return = NULL;
if((Surf_Temp = SDL_LoadBMP(File)) == NULL) {
return NULL;
}
Surf_Return = SDL_DisplayFormat(Surf_Temp);
SDL_FreeSurface(Surf_Temp);
return Surf_Return;
}
bool CSurface::OnDraw(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src, int X, int Y) {
if(Surf_Dest == NULL || Surf_Src == NULL) {
return false;
}
SDL_Rect DestR;
DestR.x = X;
DestR.y = Y;
SDL_BlitSurface(Surf_Src, NULL, Surf_Dest, &DestR);
return true;
}
First of all, look at the arguments being passed to the OnDraw function. We have two surfaces, and two int variables. The first surface is the destination surface, or the surface we are going to draw on. The second surface is the source surface, or the surface we going to use to draw on another surface. Basically, we are putting Surf_Src on top of Surf_Dest. The X, Y variables is the position on the Surf_Dest where we are drawing this surface to.
The start of the function makes sure we have valid surfaces, if we don't, return false. Next, we find
SDL_Rect. This is a
SDL structure that basically has four members: x, y, w, h. This, of course, creates the dimensions for a rectangle. We are only worried about where we are drawing to, not the size. So we assign X, Y coordinates to the destination surface. If you are wondering what NULL is within
SDL_BlitSurface, it's another parameter for a
SDL_Rect. We'll get to this later on in this lesson.
Lastly, we actualy call the function to draw the image, and then return true.
Now, to make sure all of this works, let's create a test surface. Open up CApp.h, and create a new surface, and include the new created CSurface.h:
#ifndef _CAPP_H_
#define _CAPP_H_
#include <SDL.h>
#include "CSurface.h"
class CApp {
private:
bool Running;
SDL_Surface* Surf_Display;
SDL_Surface* Surf_Test;
public:
CApp();
int OnExecute();
public:
bool OnInit();
void OnEvent(SDL_Event* Event);
void OnLoop();
void OnRender();
void OnCleanup();
};
#endif
Also, initialize the surface to NULL in the constructor:
CApp::CApp() {
Surf_Test = NULL;
Surf_Display = NULL;
Running = true;
}
And, remember to cleanup:
#include "CApp.h"
void CApp::OnCleanup() {
SDL_FreeSurface(Surf_Test);
SDL_FreeSurface(Surf_Display);
SDL_Quit();
}
Now, lets actually load an image. Open up CApp_OnInit.cpp, and add the code to load a surface:
#include "CApp.h"
bool CApp::OnInit() {
if(SDL_Init(SDL_INIT_EVERYTHING) < 0) {
return false;
}
if((Surf_Display = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE | SDL_DOUBLEBUF)) == NULL) {
return false;
}
if((Surf_Test = CSurface::OnLoad("myimage.bmp")) == NULL) {
return false;
}
return true;
}
Be sure to replace "myimage.bmp" with an actual bitmap image you have. If you don't have one, open mspaint and draw something real quick, and save it in the same folder where your executable goes.
Now that we have the image loaded, lets actually draw it. Open up CApp_OnRender.cpp and add the following:
#include "CApp.h"
void CApp::OnRender() {
CSurface::OnDraw(Surf_Display, Surf_Test, 0, 0);
SDL_Flip(Surf_Display);
}
Notice a new function here
SDL_Flip. This basically refreshes the buffer and displays Surf_Display onto the screen. This is called double buffering. It's the process of drawing everything into memory, and then finally drawing everything to the screen. If we didn't do this, we would have images flickering on the screen. Remember the
SDL_DOUBLEBUF flag? This is what turns double buffering on.
Compile your code, and make sure everything works correctly. You should see your image on the top left hand corner of the screen. If so, congratulations, you are one step closer to an actual game. If not, make sure you have the myimage.bmp in the same folder as your executable. Also insure it's a valid bitmap file.

Now lets take this process a little bit further. While it's nice and all to draw images to the screen, often we'll need to draw only parts of an image. Take, for example, a tileset:

Though this is one single image, we only want to draw a part of it. Open back up CSurface.h, and add the following code:
#ifndef _CSURFACE_H_
#define _CSURFACE_H_
#include <SDL.h>
class CSurface {
public:
CSurface();
public:
static SDL_Surface* OnLoad(char* File);
static bool OnDraw(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src, int X, int Y);
static bool OnDraw(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src, int X, int Y, int X2, int Y2, int W, int H);
};
#endif
Open back up CSurface.cpp, and
add the following function
(Important, we are adding a second OnDraw function, we are not replacing the first one):
bool CSurface::OnDraw(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src, int X, int Y, int X2, int Y2, int W, int H) {
if(Surf_Dest == NULL || Surf_Src == NULL) {
return false;
}
SDL_Rect DestR;
DestR.x = X;
DestR.y = Y;
SDL_Rect SrcR;
SrcR.x = X2;
SrcR.y = Y2;
SrcR.w = W;
SrcR.h = H;
SDL_BlitSurface(Surf_Src, &SrcR, Surf_Dest, &DestR);
return true;
}
Notice that it's basically the same function as our first one, except we've added another
SDL_Rect. This source rect allows use to specify what pixels from the source to copy over to the destination. If we specified 0, 0, 50, 50 as parameters for X2...H, it would only draw upper left part of the surface (a 50x50 square).

Lets also test this new function out, open back up CApp_OnRender.cpp, and add the following:
#include "CApp.h"
void CApp::OnRender() {
CSurface::OnDraw(Surf_Display, Surf_Test, 0, 0);
CSurface::OnDraw(Surf_Display, Surf_Test, 100, 100, 0, 0, 50, 50);
SDL_Flip(Surf_Display);
}
You should notice that your image is drawn at 100, 100 and only part of it is being displayed. You should take special care understanding how these functions work, and how the
SDL coordinate system is setup, you will be using this quite a bit.
Jump on over to the next
SDL tutorial, where we'll look more at
SDL events, and how to make that process a whole lot simpler.
I can for the life of me make the transparency work. I'm using .png with SDL_image instead of bmp but I no matter what I do in CSurface I can't get the pink to become transparent. I've read a few tutorials now and still can't get it to work. Any Ideas?
Email (required, not published):
Website:
http://www.mediafire.com/?mmnnppm1qyl
Thanks!
Email (required, not published):
Website:
SDL_LoadBMP only accepts 24bit BMPs, anything else, and SDL_LoadBMP will return NULL. So, even if it was finding the image, it was still returning NULL (and thus, returning false on our OnInit function), which was closing your program.
FYI - SDL_LoadBMP is only temporarily used within these tutorials. You'll notice an SDL_image side tutorial on this site, which is meant to show you how to replace SDL_LoadBMP with a better library. I don't recommend BMPs for any software purposes, especially games.
Glad that I could help, don't hesitate to ask again. :)
Email (required, not published):
Website:
I wasn't able to add SDLMain to the linkers, but SDL was okay.
Hehe I thought int main() was supposed to stay argument free lol. But that didn't change it. And the .cpp's are all checked under targets.
The BMP...I tried re-saving it again with GIMP as a 24-bit BMP and OMG IT WORKED!!!
Thanks a lot for looking at my code lol, I appreciate it.
Do you know why it did what it did with an apparently non-24bit BMP? Why is that important?
Email (required, not published):
Website:
Next, I noticed you had your BMP with the source files, and not with the exe. So, I moved it into the same folder. I tried running it, it closed. So, I went ahead and opened the image in a paint editor (mspaint), and resaved as a 24bit BMP (important). It then worked.
Hopefully this all helps. :)
Email (required, not published):
Website:
printf(â€Image not found!â€);
to CApp_OnInit.cpp, where you told me, but it still just runs and closes again automatically. I uploaded the files I made to this site, can you download them and see if you can find the problem?
http://www.mediafire.com/?9gdyojbmxun
Thanks again for helping. I love your tutorials too btw! (If only they'd work hehe).
- Arseniy
Email (required, not published):
Website:
if((Surf_Test = CSurface::OnLoad("myimage.bmp")) == NULL) {
printf("Image not found!");
return false;
}
The error will print to a stderr.txt file. Let me know if that helps.
Mackyman, I'm glad you like my tutorials. I hope I can keep the coming tutorials just as good. :)
Email (required, not published):
Website:
I really hope all the rest tutorials is this high standard ^^ Then I will have no need to fear to not learn SDL ^^
Email (required, not published):
Website:
Email (required, not published):
Website:
Email (required, not published):
Website:
Email (required, not published):
Website: