SDL:Tutorials:Displaying a Bitmap

From GPWiki
Jump to: navigation, search

Unless you're interested in making an old school text based game, you're probably going to want to display an image here or there. This tutorial will show you how to set up SDL video and display a bitmap to the screen.

Initializing SDL video is fairly easy:

if (SDL_Init(SDL_INIT_VIDEO) != 0) {
	printf("Unable to initialize SDL: %s\n", SDL_GetError());
	return 1;
}
 
atexit(SDL_Quit);

The SDL_Init function is passed the SDL_INIT_VIDEO constant to indicate that we'd like to initialize SDL video. SDL_Init returns zero on success, so the code above checks for failure, and outputs a message if needed. The SDL_GetError function returns a string describing the most recent SDL error, and can be quite handy for determining what went wrong with your program. atexit is then used to guarantee that the SDL_Quit function is called when the application exits; this is very important, otherwise SDL won't clean itself up, and resources may not be released!

Now that SDL video has been initialized, we need to set the video mode:

SDL_Surface *screen;
 
screen = SDL_SetVideoMode(640, 480, 16, SDL_DOUBLEBUF | SDL_FULLSCREEN);
if (screen == NULL) {
	printf("Unable to set video mode: %s\n", SDL_GetError());
	return 1;
}

The SDL_SetVideoMode function's first three parameters indicate screen resolution and colour depth. The above code will set the video mode to 640pixels by 480pixels, with 16bit colour. The fourth parameter is the flags parameter, and will accept the following values:

SDL_ANYFORMAT 
If the colour depth requested in the SDL_SetVideoMode is not available, passing the SDL_ANYFORMAT constant will instruct SDL not to emulate the colour depth, and simply use whatever format the hardware can accomodate.
SDL_ASYNCBLIT 
Allow the display to be updated asynchronously. This may give a speed boost on some systems, but could also result in visual artifacts.
SDL_DOUBLEBUF 
Double buffering sets up a second screen-sized surface in memory (called a back buffer) enables fast pointer flips between the back buffer and the primary buffer when SDL_Flip is called. This prevents problems with tearing, but requires a little more memory and CPU time. If your program doesn't need to update the entire screen every frame, you might be better off using a "dirty rectangles" method of redrawing the screen.
SDL_FULLSCREEN 
If possible, SDL will attempt to change to the specified resolution in a full-screen mode. If SDL fails to achieve this, it will attempt to emulate the effect.
SDL_HWPALETTE 
You must pass SDL_HWPALETTE if you intend to use SDL_SetColors or SDL_SetPalette, or you may get unexpected results.
SDL_HWSURFACE 
Use hardware to create the video surface; this creates the surface in video memory.
SDL_NOFRAME 
If the underlying windowing system will allow it, SDL will create a window with no title bar or frame.
SDL_OPENGL 
Sets up the display to act as an OpenGL context. This prevents this tutorial from working!
SDL_RESIZABLE 
Passing this constant creates a resizable window. If the user resizes the window, a SDL_VIDEORESIZE event is triggered, and the program can make a new call to SDL_SetVideoMode to accomodate the size change.
SDL_RLEACCEL 
Uses Run Length Encoding to compress video data. This may have the effect of increasing display speed.
SDL_SWSURFACE 
Use software to create the video surface; this creates the surface in system memory.

In the code above we have passed SDL_DOUBLEBUF | SDL_FULLSCREEN which will combine the flags to give us a double-buffered full-screen display. If successful, SDL_SetVideoMode returns a pointer to an SDL_Surface. On failure, it returns NULL. The code above demonstrates how to check for failure, and output a meaningful description of the problem.

Now that the video mode has been set, we can load our chosen bitmap into a surface:

SDL_Surface *image;
SDL_Surface *temp;
 
temp = SDL_LoadBMP("image.bmp");
if (temp == NULL) {
	printf("Unable to load bitmap: %s\n", SDL_GetError());
	return 1;
}
 
image = SDL_DisplayFormat(temp);
SDL_FreeSurface(temp);

The SDL_LoadBMP function accepts a path string, pointing to the bitmap we wish to load. On success, the function will return a pointer to an SDL_Surface, which we will store in a temporary variable, temp. On failure, SDL_LoadBMP returns NULL. Again, the code shows how to handle and report on failure.

Once the temp variable has been assigned, we call SDL_DisplayFormat to convert the surface data to a format which is compatible with the video mode that we selected with SDL_SetVideoMode. The result is referenced by our pointer image. SDL_FreeSurface is then called to release the temporary variable, as the surface it references is no longer needed.

Now that our bitmap has been loaded into a surface, we can display it to the screen:

SDL_Rect src, dest;
 
src.x = 0;
src.y = 0;
src.w = image->w;
src.h = image->h;
 
dest.x = 100;
dest.y = 100;
dest.w = image->w;
dest.h = image->h;
 
SDL_BlitSurface(image, &src, screen, &dest);

We first define two SDL_Rect structures, and fill them with data. The src rectangle will describe the portion of the surface we wish to display. Since we want to display the entire image, we set the x and y values to zero, and the w (width) and h (height) values to the w and h values of the image. If you're a lazy coder, you can pass NULL as an argument, since you want to display the entire image.

The dest rectangle describes the portion of the screen to display the image upon. As above, we want to display the entire image, so we set the width and height values to equal the values contained within the image. The x and y values of the dest indicate where the top-left corner of the image will be displayed, and I've set the values to 100, in order place the image slightly away from the top-left corner of the screen.

Once our two SDL_Rect structures are ready, we can call SDL_BlitSurface, passing the source surface, the source rectangle, the destination surface, and the destination rectangle. SDL_BlitSurface takes this information and performs a Bit-Block Transfer, copying the image data from the source surface onto the destination surface, within the rectangles defined. The result of the above code is that our image is copied on to the back buffer, ready to be flipped to the primary buffer.

SDL_Flip(screen);
 
SDL_Delay(2500);

SDL_Flip swaps the primary buffer pointer to reference the back buffer, thereby displaying our image on the screen. The call to SDL_Delay simply pauses execution for 2500ms (2.5 seconds), in order to let us see the results on screen.

Only clean-up remains to be handled:

SDL_FreeSurface(image);
 
return 0;

SDL_FreeSurface is called, releasing the resources associated with our image. That's all we have to do! When the program exits, our previous atexit(SDL_Quit) call will ensure that SDL has a chance to clean up.

Source code

  • click here to download this tutorial's sample source code, including a sample bitmap file