GFX with SDL. Lesson 1: Getting started with SDL
Translations of this material:
- into Russian: Графика с использованием SDL. Урок 1. Начало работы с SDL.. Translation complete.
-
Submitted for translation by algor_1 13.07.2009
Published 2 years, 5 months ago.
Text
Welcome to the first tut in the "GFX with SDL" series.
Using SDL with Dev-C++
The first thing you need is to download some files that you will need. They are in the zip sdlDevCPP-1.2.4.zip (click the name to download). Extract the zip INSIDE your Dev-C++ directory so that the files from the folders lib and include (from the zip) get extracted to the folders lib and include in your Dev-C++ directory. On my computer the Dec-C++ folder is c:\Dev-C++. So after extracting the zip I would have the files libSDL.a, libSDL.la, libSDLmain.a and SDL.dll inside c:\Dev-C++\lib and lots of .h files inside c:\Dev-C++\include\SDL.
Now in Dev-C++ start a new console project. Now go to the project options dialog (found in the menu Project). The thing we need to set here is the field that says "Further object files or linker options:". Type in "-lmingw32 -lSDLmain -lSDL" (w/o the quotes) inside it. Now click OK.
One last thing: when you do printf(...) when having the SDL stuff in the project options in Dev-C++ the output of printf is written to a file called stdout.txt, not the screen.
And that's all the Dev-C++ related stuff.
Using SDL in Microsoft Visual C++ 6.0
To use SDL in MSVC6, download the file SDL-devel-1.2.4-VC6.zip (if a newer version of sdl is released, look in www.libsdl.org for a newer version of this file). In that file you'll find 2 important folders (amongst the others) - include and lib. Extract all the contents from the lib folder in the zip into your MSVC6 lib folder. On my system it's: C:\Program Files\Microsoft Visual Studio\VC98\Lib. Now create a new folder called SDL in your MSVC6 include folder and copy all the .h files from the include folder in the zip file into the newly created folder (on my system it would be C:\Program Files\Microsoft Visual Studio\VC98\Include\SDL) AND into the include folder itself (C:\Program Files\Microsoft Visual Studio\VC98\Include on my system).
Now in Visual C++, create a new project. Choose 'WIN32 Application' and, when prompted, select 'an empty project'. Now you need to create some .cpp file for your project. Click file->new and select 'c++ source file'. Name it 'main.cpp'. Now go to the project settings (from the menu: project->settings). Click the 'LINK' tab and add 'sdl.lib sdlmain.lib' to the end of the long line of other listed .lib files (Object/library modules). One last thing: click the 'C/C++' tab in 'Project Settings'. From the drop-down menu choose 'Code Generation'. Now select 'Multithreaded DLL' from the 'Use run-time library' drop-down menu. And you're basically all set with MSVC6. Now just code :).
SDL.dll
One important file with SDL is the file SDL.dll (it's included with the Dev-C++ and MSVC6 SDL zips (look above...)). If you would want to run SDL programs, then you MUST have the file SDL.dll inside c:\windows\system (win 95, 98, ME) or c:\windows\system32 (on windows NT, 2000 and XP). OR you may have the file SDL.dll in the SAME folder as the executable of your program. If you would want to distirbute your SDL programs among friends then you MUST also give them the file SDL.dll. SO copy the file SDL.dll from c:\Dev-C++\lib into c:\windows\system or c:\windows\system32 (on NT, 2000 and XP) on your system and distirbute it with your executables.
Getting started with SDL
You are basically all set. You only need to add the include file SDL/SDL.h to the top of your program like this:
#include <SDL/SDL.h>
Initalizing SDL is done through the SDL_Init() function. SDL_Init returns less than 0 on failure. It takes one parameter: what to initalize. To initalize the video screen pass to it the constant SDL_INIT_VIDEO. To initalize the audio, pass to it the constant SDL_INIT_AUDIO. To initalize the video and audio, pass to it SDL_INIT_VIDEO|SDL_INIT_AUDIO. There are many more things that you can pass (seperating them with |'s when passing many at once). Here are the things you can pass:
SDL_INIT_TIMER
SDL_INIT_AUDIO
SDL_INIT_VIDEO
SDL_INIT_CDROM
SDL_INIT_JOYSTICK
SDL_INIT_NOPARACHUTE
SDL_INIT_EVENTTHREAD
SDL_INIT_EVERYTHING
So in conclusion if we would want to init the video and the audio we get:
if( SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) <0 )
{
printf("Unable to init SDL: %s\n", SDL_GetError());
return 1;
}
If an error occured, then the function SDL_GetError() would return a string about the error.
When exiting your C++ program, you must execute the function SDL_Quit(). That will clear up everything. If you wouldn't execute it before exiting your program, starge things may occur. To tell the compiler that you want to run SDL_Quit on exit, tell it like this:
atexit(SDL_Quit);
That way you don't need to put SDL_Quit() before every return [nr]; in main().
In SDL you have many surfaces. Everything is a surface. You can draw on a surface and you can also draw a surface on an other surface. In SDL the screen is also a surface. A surface in our program is actually a pointer to the structure SDL_Surface. To get the screen surface do this:
SDL_Surface *screen;
I'm sure you have seen at some point in your life that some game asked you for a screen resolution to run at. If not, well then PLAY MORE GAMES. If you would want to use the surface screen (remember, screen is just the name of a pointer to the structure SDL_Surface) as the surface you can draw on (and you will see what you have drawn on your monitor) then use the function SDL_SetVideoMode();
screen = SDL_SetVideoMode(640, 480, 32,
SDL_HWSURFACE|SDL_DOUBLEBUF);
The first three parameters are the width, height and bits per pixel of the screen. If you type in 0 for the BPP then SDL would automatically select the best available BPP. The fourth parameter is used to give some special flags. You must (almost) always give it SDL_HWSURFACE (or SDL_SWSURFACE) if you want a screen to draw on. Here's a list of what you may give it:
* SDL_SWSURFACE - Create the video surface in system memory
* SDL_HWSURFACE - Create the video surface in video memory
* SDL_ASYNCBLIT - Enables the use of asynchronous updates of the display surface. This will usually slow down blitting on single CPU machines, but may provide a speed increase on SMP systems.
* SDL_ANYFORMAT - Normally, if a video surface of the requested bits-per-pixel (bpp) is not available, SDL will emulate one with a shadow surface. Passing SDL_ANYFORMAT prevents this and causes SDL to use the video surface, regardless of its pixel depth.
* SDL_HWPALETTE - Give SDL exclusive palette access. Without this flag you may not always get the the colors you request with SDL_SetColors or SDL_SetPalette.
* SDL_DOUBLEBUF - Enable hardware double buffering; only valid with SDL_HWSURFACE. Calling SDL_Flip will flip the buffers and update the screen. All drawing will take place on the surface that is not displayed at the moment. If double buffering could not be enabled then SDL_Flip will just perform a SDL_UpdateRect on the entire screen.
* SDL_FULLSCREEN - SDL will attempt to use a fullscreen mode. If a hardware resolution change is not possible (for whatever reason), the next higher resolution will be used and the display window centered on a black background.
* SDL_OPENGL - Create an OpenGL rendering context. You should have previously set OpenGL video attributes with SDL_GL_SetAttribute.
* SDL_OPENGLBLIT - Create an OpenGL rendering context, like above, but allow normal blitting operations. The screen (2D) surface may have an alpha channel, and SDL_UpdateRects must be used for updating changes to the screen surface.
* SDL_RESIZABLE - Create a resizable window. When the window is resized by the user a SDL_VIDEORESIZE event is generated and SDL_SetVideoMode can be called again with the new size.
* SDL_NOFRAME - If possible, SDL_NOFRAME causes SDL to create a window with no title bar or frame decoration. Fullscreen modes automatically have this flag set.
My recommendation: give it SDL_HWSURFACE|SDL_DOUBLEBUF and in case of an error try again with SDL_SWSURFACE.
SDL_SetVideoMode returns a pointer to SDL_Surface if sucessful or NULL if not. To check for errors use this block of code:
if ( screen == NULL )
{
printf("Unable to set 640x480 video: %s\n", SDL_GetError());
return 1;
}
And that's all about initalizing SDL. You can now start drawing. But before we get into drawing stuff I'll tell you about some new datatypes that SDL gave us so you don't get confused when you run into them. They are:
Uint8 - the equilant of an unsigned char
Uint16 - a 16 bit (2 byte) unsigned integer
Uint32 - a 32 bit (4 byte) unsigned integer
Uint64 - a 64 bit (8 byte) unsigned integer
Sint8 - the equilant of a signed char
Sint16 - a 16 bit (2 byte) signed integer
Sint32 - a 32 bit (4 byte) signed integer
Sint64 - a 64 bit (8 byte) signed integer
And one more thing: Sometimes when you get errors on initalizing stuff you don't need to exit completly. For example when initalizing SDL_INIT_VIDEO passed and SDL_INIT_AUDIO did not, you can still continue with the program, only without audio. To check (for example) if the audio initalization suceeded, use the SDL_WasInit() function. Here's some code:
Uint32 init = SDL_WasInit(SDL_INIT_AUDIO);
if(init & SDL_INIT_AUDIO)
{
sound = 1; // Audio init sucessful, use sound
} else {
sound = 0; // Audio init unsucessful, don't use sound
}
You should add the code somewhere between the
if( SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) <0 )
{
printf("Unable to init SDL: %s\n", SDL_GetError());
return 1;
}
code in your own programs (I won't add it in the tuts (for now) for simplicity).
Drawing pixels isn't very easy at first, but when you have a function that does it for you it can't get any easier. The function that I use for drawing pixels is taken from the SDL intro (found on www.libsdl.org). It looks like this:
NOTE: you don't need to understand all of it. Just know that is works
void DrawPixel(SDL_Surface *screen, int x, int y,
Uint8 R, Uint8 G, Uint8 B)
{
Uint32 color = SDL_MapRGB(screen->format, R, G, B);
switch (screen->format->BytesPerPixel)
{
case 1: // Assuming 8-bpp
{
Uint8 *bufp;
bufp = (Uint8 *)screen->pixels + y*screen->pitch + x;
*bufp = color;
}
break;
case 2: // Probably 15-bpp or 16-bpp
{
Uint16 *bufp;
bufp = (Uint16 *)screen->pixels + y*screen->pitch/2 + x;
