SDL Programming in Linux: Playing with Graphics
Posted On July 5, 2007 by Anush filed under Miscellaneous
Introduction
Graphics manipulation is the core of any game-programming library. Without graphics manipulation, games can never realize the actual potential of interactivity and environmental feedback – two facets of any game that make it engrossing. SDL provides for this through its graphics subsystem. In this part, we will deal with the graphics subsystem and get to know the various functionalities it offers.
Though the routines to work with graphics are present in the corresponding subsystem, the graphics sub-subsystem itself is a part of the video subsystem. Hence, all graphics related routines are present in the Video subsystem. The first section covers the basics, starting with initializing the video for best resolution. The second section will look in detail about loading a bitmap on to the screen.
Working with Video
When working with gaming libraries, one has to drop into system specific APIs (Win SDK on Windows and Xlib et al. on *nix) to access video related functionalities. These functionalities include initializing the video, setting the best video mode and loading the bitmapped images, among other things. However, SDL encapsulates all these within the Video subsystem. The functions that provides access to these are:
1. SDL_Init():
This function has been discussed in the first part, i.e. Introduction to SDL Programming. This function initializes the subsystem that has been passed as a parameter. To initialize video, the parameter would be SDL_INIT_VIDEO, i.e. SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) would initialize video as well as audio.
2. SDL_SetVideoMode():
Once the video is initialized, the next obvious step is setting the best video mode. To achieve this, SDL contains SDL_SetVideoMode method, which sets up a video mode with specified width, height and bits per pixel, i.e. depth. In short, by using this function one can set up the required resolution. The parameters are:
i. Width;
ii. Height;
iii. bpp (bits-per-pixel); and
iv. Flags.
The first three parameters take integer values while the fourth parameter needs some considerations. The flags parameter defines the properties of the surface of the screen. These are eleven in number. The important and most commonly used are:
a. SDL_SWSURFACE:
This instructs SDL to create the surface in the system memory. In other words, the rendering area is created using software renderer. This is useful in case support for software-based acceleration is on cards;
b. SDL_HWSURFACE:
To create surface in hardware memory, i.e. memory of the graphics card, use this value as the flag value. To support hardware acceleration this value is used. It can be ordered with SDL_SWSURFACE to support both;
c. SDL_ANYFORMAT:
When the passed depth value is unsupported on the target machine, then SDL emulates it with a shadow surface. To prevent this, pass SDL_ANYFORMAT as the flag value. By using SDL_ANYFORMAT, SDL can be instructed to use the video surface even if the required depth is not available;
d. SDL_DOUBLEBUF:
This enables hardware double buffering. The double buffering works only if called with SDL_HWSURFACE. Otherwise, when flipping function is called only updating of the surface takes place;
e. SDL_OPENGL:
It creates an OpenGL rendering context. This is useful when SDL is used in conjunction with OpenGL;
f. SDL_FULLSCREEN:
By passing it as the flag value, the mode can be change to full screen. If SDL is unable to do so, then it will use the next available higher resolution. However, the window will be centered on black screen; and
g. SDL_NOFRAME:
To set the window without decoration (without title bar and frame decoration), use this as the value. When setting SDL_FULLSCREEN as the flag value, this flag is automatically set.
All the above values are same as that of SDL_Surface. The SDL_SetVideoMode() function returns a pointer to the structure SDL_Surface. Now let us see how to use it in a program. Refer code 1.
Code 1
#include “SDL.h”
#include<stdio.h>
int main(int argc,char* argv[])
{
SDL_Surface *screen;
/*The following code does the initialization for Audio and Video*/
int i_error=SDL_Init(SDL_INIT_VIDEO);
/*If initialization is unsuccessful, then quit */
if(i_error==-1)
exit(1);
atexit(SDL_Quit);
/*
* Initialize the display in a 640x480 8-bit palettized mode,
* requesting a software surface
*/
screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE);
if ( screen == NULL )
{
fprintf(stderr, "Couldn't set 640x480x8 video mode: %s\n",
SDL_GetError());
exit(1);
}
}
If you recall, most of the above code is the same one discussed in part I. The changes are set in bold. First, a point to the structure SDL_Surface is declared. When the video mode is set, this comes into picture. Then the video is initialized using SDL_Init(). This part is not much different. If initialization fails, then exit the application.
As I have said earlier, the function to set video mode returns a pointer to the initialized SDL_Surface structure. The above code sets the resolution at 640 × 480 at 8-bit depth. It also sets the rendering to software based, i.e. the surface is created in system memory and not in the graphics card’s memory. Now that video mode has been set, we can move to the next section. That is loading a bitmap on to the returned surface.
Loading Bitmaps
To load a bitmap on to the surface, the following functions will come in handy:
1. SDL_LoadBMP:
This function forms the basis of loading a bitmap. It returns a pointer to surface for name of the bitmap given as the parameter. If the loading of bitmap has not been successful, null is returned. For example, if the file parameter is “Tux.bmp”, then following code will load it:
SDL_Surface *image=SDL_LoadBMP(“Tux.bmp”);
2. SDL_SetColors:
The default palette would be an 8 × 8 × 4 color cube. For better color matching, it is required to palletize the image itself. For this, SDL_Setcolors function is quite useful. The first parameter is the surface for which the palette has to be created while the second parameter is the SDL color component, which decides the number of displayable colors. The third and fourth parameters set the range of colors to be used. To palletize the loaded image, the first parameter would be the surface returned by the SDL’s initialization routine, second will be the color component of the image to be palletized. The third would be 0, that would lower the range of colors to be used, and the maximum value of the color palette of the image would be the fourth parameter. To put in code:
SDL_SetColors(screen, image->format->palette->colors, 0,
image->format->palette->ncolors);
where screen is the surface returned by the initialization and image is the loaded bitmap;
3. SDL_BlitSurface:
This function performs a fast blit from source surface to the destination surface. The parameters are source surface, source rectangle, destination surface and destination rectangle. If the source and destination rectangle are specified as null, then the entire surface is copied. For example, the following code copies loaded image surface to the screen surface:
SDL_BlitSurface(image, NULL, screen, NULL);
4. SDL_UpdateRect:
Once the loaded image surface is copied on to the screen surface, it must be ensured that the screen display is updated accordingly. The surface to be updated is the first parameter, the rectangle of screen to be updated is specified as the second parameter. The following fragment updates the screen according to the height and width of the loaded image:
SDL_UpdateRect(screen, 0, 0, image->w, image->h); and
5. SDL_FreeSurface:
Once work is completed with the loaded image, then the surface has to be freed so that memory occupied by the surface is released. To free a surface, SDL library contains SDL_FreeSurface. This parameter is the surface to be freed. In code, it would be:
SDL_FreeSurface(image);
where image is the surface to be freed.
Now that the functions to be used have been introduced, let us see them in action (code 2). First, define a function that loads a bitmap image passed to it as a parameter.
void display_bmp(char *file_name)
{
SDL_Surface *image;
/* Load the BMP file into a surface */
image = SDL_LoadBMP(file_name);
if (image == NULL) {
fprintf(stderr, "Couldn't load %s: %s\n", file_name, SDL_GetError());
return;
}
/*
* Palettized screen modes will have a default palette (a standard
* 8*8*4 colour cube), but if the image is palettized as well we can
* use that palette for a nicer colour matching
*/
if (image->format->palette && screen->format->palette) {
SDL_SetColors(screen, image->format->palette->colors, 0,
image->format->palette->ncolors);
}
/* Blit onto the screen surface */
if(SDL_BlitSurface(image, NULL, screen, NULL) < 0)
fprintf(stderr, "BlitSurface error: %s\n", SDL_GetError());
SDL_UpdateRect(screen, 0, 0, image->w, image->h);
/* Free the allocated BMP surface */
SDL_FreeSurface(image);
}
As you can observe, this code is a compilation of all the functions I had discussed earlier. The only difference is that the error handling code has been used. To use it first, declare a global variable of the type SDL_Surface:
SDL_Surface *screen=NULL;
Then call the display_bmp() as follows (code 3):
int main(int argc,char* argv[])
{
/*variable to hold the file name of the image to be loaded
*In real world error handling code would precede this */
char* filename=”Tux.bmp”;
/*The following code does the initialization for Audio and Video*/
int i_error=SDL_Init(SDL_INIT_VIDEO);
/*If initialization is unsuccessful, then quit */
if(i_error==-1)
exit(1);
atexit(SDL_Quit);
/*
* Initialize the display in a 640x480 8-bit palettized mode,
* requesting a software surface
*/
screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE);
if ( screen == NULL )
{
fprintf(stderr, "Couldn't set 640x480x8 video mode: %s\n",
SDL_GetError());
exit(1);
}
/* Now call the function to load the image and copy it to the screen surface*/
load_bmp(filename);
}
That brings us to the end of this part of the discussion on SDL. In the next part, I would discuss about working at pixel level and handling keyboard events. Till next time…
A.P. Rajshekhar is a Project Engineer with the Center for Development of Advanced Computing (C-DAC), Hyderabad. His email id is: a_p_rajshekhar@yahoo.co.in.
Graphics manipulation is the core of any game-programming library. Without graphics manipulation, games can never realize the actual potential of interactivity and environmental feedback – two facets of any game that make it engrossing. SDL provides for this through its graphics subsystem. In this part, we will deal with the graphics subsystem and get to know the various functionalities it offers.
Though the routines to work with graphics are present in the corresponding subsystem, the graphics sub-subsystem itself is a part of the video subsystem. Hence, all graphics related routines are present in the Video subsystem. The first section covers the basics, starting with initializing the video for best resolution. The second section will look in detail about loading a bitmap on to the screen.
Working with Video
When working with gaming libraries, one has to drop into system specific APIs (Win SDK on Windows and Xlib et al. on *nix) to access video related functionalities. These functionalities include initializing the video, setting the best video mode and loading the bitmapped images, among other things. However, SDL encapsulates all these within the Video subsystem. The functions that provides access to these are:
1. SDL_Init():
This function has been discussed in the first part, i.e. Introduction to SDL Programming. This function initializes the subsystem that has been passed as a parameter. To initialize video, the parameter would be SDL_INIT_VIDEO, i.e. SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) would initialize video as well as audio.
2. SDL_SetVideoMode():
Once the video is initialized, the next obvious step is setting the best video mode. To achieve this, SDL contains SDL_SetVideoMode method, which sets up a video mode with specified width, height and bits per pixel, i.e. depth. In short, by using this function one can set up the required resolution. The parameters are:
i. Width;
ii. Height;
iii. bpp (bits-per-pixel); and
iv. Flags.
The first three parameters take integer values while the fourth parameter needs some considerations. The flags parameter defines the properties of the surface of the screen. These are eleven in number. The important and most commonly used are:
a. SDL_SWSURFACE:
This instructs SDL to create the surface in the system memory. In other words, the rendering area is created using software renderer. This is useful in case support for software-based acceleration is on cards;
b. SDL_HWSURFACE:
To create surface in hardware memory, i.e. memory of the graphics card, use this value as the flag value. To support hardware acceleration this value is used. It can be ordered with SDL_SWSURFACE to support both;
c. SDL_ANYFORMAT:
When the passed depth value is unsupported on the target machine, then SDL emulates it with a shadow surface. To prevent this, pass SDL_ANYFORMAT as the flag value. By using SDL_ANYFORMAT, SDL can be instructed to use the video surface even if the required depth is not available;
d. SDL_DOUBLEBUF:
This enables hardware double buffering. The double buffering works only if called with SDL_HWSURFACE. Otherwise, when flipping function is called only updating of the surface takes place;
e. SDL_OPENGL:
It creates an OpenGL rendering context. This is useful when SDL is used in conjunction with OpenGL;
f. SDL_FULLSCREEN:
By passing it as the flag value, the mode can be change to full screen. If SDL is unable to do so, then it will use the next available higher resolution. However, the window will be centered on black screen; and
g. SDL_NOFRAME:
To set the window without decoration (without title bar and frame decoration), use this as the value. When setting SDL_FULLSCREEN as the flag value, this flag is automatically set.
All the above values are same as that of SDL_Surface. The SDL_SetVideoMode() function returns a pointer to the structure SDL_Surface. Now let us see how to use it in a program. Refer code 1.
Code 1
#include “SDL.h”
#include<stdio.h>
int main(int argc,char* argv[])
{
SDL_Surface *screen;
/*The following code does the initialization for Audio and Video*/
int i_error=SDL_Init(SDL_INIT_VIDEO);
/*If initialization is unsuccessful, then quit */
if(i_error==-1)
exit(1);
atexit(SDL_Quit);
/*
* Initialize the display in a 640x480 8-bit palettized mode,
* requesting a software surface
*/
screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE);
if ( screen == NULL )
{
fprintf(stderr, "Couldn't set 640x480x8 video mode: %s\n",
SDL_GetError());
exit(1);
}
}
If you recall, most of the above code is the same one discussed in part I. The changes are set in bold. First, a point to the structure SDL_Surface is declared. When the video mode is set, this comes into picture. Then the video is initialized using SDL_Init(). This part is not much different. If initialization fails, then exit the application.
As I have said earlier, the function to set video mode returns a pointer to the initialized SDL_Surface structure. The above code sets the resolution at 640 × 480 at 8-bit depth. It also sets the rendering to software based, i.e. the surface is created in system memory and not in the graphics card’s memory. Now that video mode has been set, we can move to the next section. That is loading a bitmap on to the returned surface.
Loading Bitmaps
To load a bitmap on to the surface, the following functions will come in handy:
1. SDL_LoadBMP:
This function forms the basis of loading a bitmap. It returns a pointer to surface for name of the bitmap given as the parameter. If the loading of bitmap has not been successful, null is returned. For example, if the file parameter is “Tux.bmp”, then following code will load it:
SDL_Surface *image=SDL_LoadBMP(“Tux.bmp”);
2. SDL_SetColors:
The default palette would be an 8 × 8 × 4 color cube. For better color matching, it is required to palletize the image itself. For this, SDL_Setcolors function is quite useful. The first parameter is the surface for which the palette has to be created while the second parameter is the SDL color component, which decides the number of displayable colors. The third and fourth parameters set the range of colors to be used. To palletize the loaded image, the first parameter would be the surface returned by the SDL’s initialization routine, second will be the color component of the image to be palletized. The third would be 0, that would lower the range of colors to be used, and the maximum value of the color palette of the image would be the fourth parameter. To put in code:
SDL_SetColors(screen, image->format->palette->colors, 0,
image->format->palette->ncolors);
where screen is the surface returned by the initialization and image is the loaded bitmap;
3. SDL_BlitSurface:
This function performs a fast blit from source surface to the destination surface. The parameters are source surface, source rectangle, destination surface and destination rectangle. If the source and destination rectangle are specified as null, then the entire surface is copied. For example, the following code copies loaded image surface to the screen surface:
SDL_BlitSurface(image, NULL, screen, NULL);
4. SDL_UpdateRect:
Once the loaded image surface is copied on to the screen surface, it must be ensured that the screen display is updated accordingly. The surface to be updated is the first parameter, the rectangle of screen to be updated is specified as the second parameter. The following fragment updates the screen according to the height and width of the loaded image:
SDL_UpdateRect(screen, 0, 0, image->w, image->h); and
5. SDL_FreeSurface:
Once work is completed with the loaded image, then the surface has to be freed so that memory occupied by the surface is released. To free a surface, SDL library contains SDL_FreeSurface. This parameter is the surface to be freed. In code, it would be:
SDL_FreeSurface(image);
where image is the surface to be freed.
Now that the functions to be used have been introduced, let us see them in action (code 2). First, define a function that loads a bitmap image passed to it as a parameter.
void display_bmp(char *file_name)
{
SDL_Surface *image;
/* Load the BMP file into a surface */
image = SDL_LoadBMP(file_name);
if (image == NULL) {
fprintf(stderr, "Couldn't load %s: %s\n", file_name, SDL_GetError());
return;
}
/*
* Palettized screen modes will have a default palette (a standard
* 8*8*4 colour cube), but if the image is palettized as well we can
* use that palette for a nicer colour matching
*/
if (image->format->palette && screen->format->palette) {
SDL_SetColors(screen, image->format->palette->colors, 0,
image->format->palette->ncolors);
}
/* Blit onto the screen surface */
if(SDL_BlitSurface(image, NULL, screen, NULL) < 0)
fprintf(stderr, "BlitSurface error: %s\n", SDL_GetError());
SDL_UpdateRect(screen, 0, 0, image->w, image->h);
/* Free the allocated BMP surface */
SDL_FreeSurface(image);
}
As you can observe, this code is a compilation of all the functions I had discussed earlier. The only difference is that the error handling code has been used. To use it first, declare a global variable of the type SDL_Surface:
SDL_Surface *screen=NULL;
Then call the display_bmp() as follows (code 3):
int main(int argc,char* argv[])
{
/*variable to hold the file name of the image to be loaded
*In real world error handling code would precede this */
char* filename=”Tux.bmp”;
/*The following code does the initialization for Audio and Video*/
int i_error=SDL_Init(SDL_INIT_VIDEO);
/*If initialization is unsuccessful, then quit */
if(i_error==-1)
exit(1);
atexit(SDL_Quit);
/*
* Initialize the display in a 640x480 8-bit palettized mode,
* requesting a software surface
*/
screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE);
if ( screen == NULL )
{
fprintf(stderr, "Couldn't set 640x480x8 video mode: %s\n",
SDL_GetError());
exit(1);
}
/* Now call the function to load the image and copy it to the screen surface*/
load_bmp(filename);
}
That brings us to the end of this part of the discussion on SDL. In the next part, I would discuss about working at pixel level and handling keyboard events. Till next time…
A.P. Rajshekhar is a Project Engineer with the Center for Development of Advanced Computing (C-DAC), Hyderabad. His email id is: a_p_rajshekhar@yahoo.co.in.
