GFX with SDL. Lesson 2: Loading and displaying images

Author: Marius Andra. Link to original: http://cone3d.gamedev.net/cgi-bin/index.pl?page=tutorials/gfxsdl/tut2 (English).
Tags: c++, gamedev, SDL, программирование Submitted by algor_1 13.07.2009. Public material.
I've had some request for graphics tutorials with C/C++. So I started this tutorial series. We will be using SDL as the main GFX library here. I will assume that you have a good knowledege of C/C++. If you need to freshen up on the C/C++ part, try reading the Grounds Up! tutorials.

Translations of this material:

into Russian: Графика с использованием SDL. Урок 2. Загрузка и отображение изображений. Translated in draft, editing and proof-reading required. Completed: 9%.
Submitted for translation by algor_1 13.07.2009 Published 9 years, 1 month ago.

Text

Hello and welcome to this neat little tutorial, whose version number is one unit bigger than the last tutorial's! In this tutorial you will learn how to load and display images inside your SDL progams/games/demos/etc. In this tutorial I will go through the source code (almost) line-by-line, trying to explain what each part of the code does as we go along. The program we will make today is really simple: we draw a nice background and then a cube on it that you can move with the keyboard.

We start by including 3 header files: stdio.h, stdlib.h and SDL.h. (stdlib.h is later used by the atexit() function)

#include <stdio.h>

#include <stdlib.h>

#include <SDL/SDL.h>

After that we have 3 global SDL_Surface's. Global means that they can be used by all the functions in this source code file. To delcare something global it has to be at the top of the source code file, before the functions. And after the SDL_Surface's come 2 integers: xpos and ypos. They are used for the location of the box.

SDL_Surface *back;

SDL_Surface *image;

SDL_Surface *screen;

int xpos=0,ypos=0;

The InitImages function is used to load in all the images from the bitmap files (.bmp's) into SDL_Surfaces. InitImages() is later called from main(). Inside InitImages we use the SDL_LoadBMP function. SDL_LoadBMP takes a filename as it's parameter and returns the image data from the filename as a pointer to type SDL_Surface. This time we load 2 images: bg.bmp into the global SDL_Surface back that we will later use to draw the background. And image.bmp to the SDL_Surface image that we will later use to draw the box wannabe.

int InitImages()

{

back = SDL_LoadBMP("bg.bmp");

image = SDL_LoadBMP("image.bmp");

return 0;

}

Next come 2 functions that we use to blit the image onto the screen. Both functions are called DrawIMG. The first DrawIMG function takes the SDL_Surface to blit and the x, y of where to blit as it's parameters. We use the function SDL_BlitSurface() to blit the image onto the screen surface. Taken from the SDL docs the prototype for SDL_BlitSurface() is

int SDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect,

SDL_Surface *dst, SDL_Rect *dstrect);

where src is the surface to blit and dst is the surface to blit to. SDL_BlitSurface() also takes 2 more parameters of type SDL_Rect: srcrect and dstrect. SDL_Rect is a structure that contains 4 16 bit integers: x, y, w (width) and h (height). srcrect specifies what part of the source surface to blit and dstrect specifies where to blit it. If you type in NULL as the second parameter (srcrect), then the entire source image is blitted. The x and y of dstrect specify where to blit the SDL_Surface src. With dstrect the width and the heigth are ignored. Here's the first DrawIMG function:

void DrawIMG(SDL_Surface *img, int x, int y)

{

SDL_Rect dest;

dest.x = x;

dest.y = y;

SDL_BlitSurface(img, NULL, screen, &dest);

}

The second DrawIMG function uses srcrect. The x and y of srcrect tell from where to start blitting and the w and h tell how much to blit. Look at this image:

NOTE: The image is enlarged 2 times.

Suppose the darker area of the image is the rectangle you want to pass as srcrect. The darker area starts at the point (20x25) and it's width is 61 pixels and height 70 pixels. If that image (the entire image, not just the darket part) would be a SDL_Surface to be blitted and the x, y, w and h of srcrect would be 20, 25 61 and 70 then you would be blitting the darker area (on the image) of that surface.

The second DrawIMG function is used when you want to blit some part of the source surface onto the screen. Look at it and try to understand what is happening.

void DrawIMG(SDL_Surface *img, int x, int y,

int w, int h, int x2, int y2)

{

SDL_Rect dest;

dest.x = x;

dest.y = y;

SDL_Rect dest2;

dest2.x = x2;

dest2.y = y2;

dest2.w = w;

dest2.h = h;

SDL_BlitSurface(img, &dest2, screen, &dest);

}

Now we are reaching the function DrawBG(). DrawBG is called from main before the main loop starts so that we have a nice background to draw on. NOTE that to blit images onto the screen we don't lock the screen. This is so because we need to lock the screen ONLY if we want to manipulate pixels directly (draw pixels on the screen). We then blit the background surface on the screen. Note that we have no screen updating function (like SDL_Flip()) here. That's because this function is used just so that we will have a nice background to draw on. We don't want the user to see a background without anything on at the very start of the progrgam.

NOTE: you can blit a SDL_Surface onto an other SDL_Surface, not just the screen surface. You can modify the DrawIMG functions to have them blit onto other surfaces as well.

void DrawBG()

{

DrawIMG(back, 0, 0);

}

Next comes the actual drawing function. First we draw the part of the background that we are currently on. If we wouldn't do that then the "box" that we move on the screen would leave a trail behind it. We don't draw the entire background since that would be too slow. We only draw the part of the background that needs to be drawn. We draw the background as a box that starts 2 pixels before and above the (movable blue) box and ends 2 pixels after and below the box. Since the (real (blue)) box can only move 1 pixel at some direction at a time we know that drawing the background box 2 pixels larger than the moving box at each direction would clear the box's trail. Since the box itself is 128x128 pixels in size then the Background replacement thingy should be (128+2+2)x(128+2+2) = 132x132 pixels in size. Comment out the first (background) DrawIMG function to see the trail of the box. The second DrawIMG function simply draws the box at the x and y locations. We then use SDL_Flip to flip the buffers (aka update the screen).

void DrawScene()

{

DrawIMG(back, xpos-2, ypos-2, 132, 132, xpos-2, ypos-2);

DrawIMG(image, xpos, ypos);

SDL_Flip(screen);

}

And all that remains is main(). We first create a pointer to an "unsigned 8 bit integer" type. We will call the pointer keys. keys will be later used to get the state of the keyboard keys at a given moment.

int main(int argc, char *argv[])

{

Uint8* keys;

We then do the standard initalization stuff that you read about in the last lesson.

if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0 )

{

printf("Unable to init SDL: %s\n", SDL_GetError());

exit(1);

}

atexit(SDL_Quit);

screen=SDL_SetVideoMode(640,480,32,SDL_HWSURFACE|SDL_DOUBLEBUF);

if ( screen == NULL )

{

printf("Unable to set 640x480 video: %s\n", SDL_GetError());

exit(1);

}

We then init the images (load them into the SDL_Surfaces). Then we draw the background. Drawing the background won't update the (monitor) screen, but it will make the SDL_Surface screen equal a nice picture that we will use to draw stuff on.

InitImages();

DrawBG();

We now start the main loop and check for the quit event or the escape button being pressed.

int done=0;

while(done == 0)

{

SDL_Event event;

while ( SDL_PollEvent(&event) )

{

if ( event.type == SDL_QUIT ) { done = 1; }

if ( event.type == SDL_KEYDOWN )

{

if ( event.key.keysym.sym == SDLK_ESCAPE ) { done = 1; }

}

}

Now we get the state of the keyboard keys. SDL_GetKeyState() returns a pointer to an array of type Uint8. Each element of the array has information about a specific key (whether it is held down or not). The reason why we don't check for the keys in the event loop is because in the event loop events are generated when a key has been pressed down, not if it is being held down. We check if the left, right, up or down keys are pressed. And if so then we move the box one unit left, right, up or down. Since many keys can be pressed down at once we don't use "else if"'s this time.

keys = SDL_GetKeyState(NULL);

if ( keys[SDLK_UP] ) { ypos -= 1; }

if ( keys[SDLK_DOWN] ) { ypos += 1; }

if ( keys[SDLK_LEFT] ) { xpos -= 1; }

if ( keys[SDLK_RIGHT] ) { xpos += 1; }

And now we draw the screen.

DrawScene();

}

Pages: ← previous Ctrl next
1 2