Friday, August 24, 2012

Drawing in SDL

    Hopefully you have a working C and SDL setup by now and are ready to use more SDL functions.  First, we need a window that operates normally: exiting only when it is instructed rather than after a delay.  SDL has simple drawing, coloring, and updating functions which can be used with C to draw almost anything.

Window Setup:
    In order to instruct SDL to wait for an exit command, we must first understand how SDL handles input.  A structure called SDL_Event has data for many different types of input including mouse, keyboard, joystick, and exit commands.  To determine which type of event has occurred, the data field "type" of an SDL_Event variable can be compared against the enumerations SDL_MOUSEMOTION, SDL_KEYDOWN, SDL_JOYAXISMOTION, SDL_QUIT, and many more.

    If multiple events occur at the same time, they are given a priority and placed in order; this allows events to be processed one at a time.  For this example, only the quit event will be handled:
/*thanks to tohtml.com for syntax highlighting*/

#include <SDL/SDL.h>
#include <stdio.h>

/*holds data about the drawable surface*/
SDL_Surface *screen;

SDL_Event input; /*holds input*/
int loop = 1; /*set to zero to exit*/

int main(int argc, char** argv) {
    /*start up SDL and setup window*/
    SDL_Init(SDL_INIT_EVERYTHING);
    screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE);

    /*loop is needed to keep window open*/
    while (loop == 1) {
        /*read all events per cycle*/
        while (SDL_PollEvent(&input)) {
            if (input.type == SDL_QUIT) {
                printf("exiting...\n");
                loop = 0; /*exit if quit*/
            }
        }
    }

    /*perform final commands then exit*/
    SDL_Quit(); /*shutdown SDL*/
    fflush(stdout); /*update stdout*/
    return 0;
}
prompt/stdout:
exiting...
output inside window:
Still blank

    The variable screen is a pointer to the data type SDL_Surface.  It points to a structure which holds the information about a drawable surface in SDL; in this case, it is the background of the window.  You can find out the width, height, color depth, and even the color of individual pixels from the SDL_Surface structure, which makes it ideal for drawable surfaces and imported images.  It is assigned this information from the SDL_SetVideoMode function and will prove vital for drawing operations.

    The next variable is called input and is of type SDL_Event.  Note that screen and input are names I chose to use, you can use whichever titles you wish.  To give information about user input, the command SDL_PollEvent is given the address of our SDL_Event variable to change its data.  This function will become false once all input has been processed, so it is given its own loop within the program's loop to allow processing of any number of events.

    Integer variable loop is created to track the status of the program loop: when its value changes, the loop is broken and the final commands are called.  The value 0 is used to indicate no more looping because it usually means false, but the comparison "loop == 1" allows any value other than 1 to mean the same.

Rectangles:
    One shape SDL draws well is rectangles.  In fact, SDL has the SDL_Rect structure to describe rectangles based on position, width, and height.  The data fields for the struct are x, y, w, and h, which mean x coordinate, y coordinate, width, and height.  x and y are of type "Sint16" while w and h use the type "Uint16".

    These type names are not standard C: SDL simply gives different names to existing data types using a typedef command.  For Sint16 and Uint16, they are really the data types signed short and unsigned short respectively.  As for the int16 part, a short uses 16 bits and is an integer type of data.  SDL has titles for char (Sint8), unsigned char (Uint8), int (Sint32), unsigned int (Uint32), long (Sint64), and unsigned long (Uint64).

    Before we can draw the rectangle, two more functions are needed.  SDL_FillRect requires a pointer to an SDL_Surface struct, a pointer to an SDL_Rect struct, and a Uint32 to represent a color.  The SDL_Surface* parameter will be the screen variable since it is being drawn on and the SDL_Rect* parameter will be to an SDL_Rect we create.  For now, the Uint32 color will be given the value 0xFFFFFFFF (4,294,967,295 or the color white).

    The function SDL_Flip has a single SDL_Surface* and updates whichever surface is input.  This will be the screen for the following example:
/*thanks to tohtml.com for syntax highlighting*/

#include <SDL/SDL.h>
#include <stdio.h>

/*holds data about the drawable surface*/
SDL_Surface *screen;

SDL_Event input; /*holds input*/
int loop = 1; /*set to zero to exit*/

int main(int argc, char** argv) {
    SDL_Rect block;
    block.x = 200; /*200 pixels from left*/
    block.y = 100; /*100 pixels from top*/
    block.w = 400; /*400 pixels wide*/
    block.h = 300; /*300 pixels tall*/

    /*start up SDL and setup window*/
    SDL_Init(SDL_INIT_EVERYTHING);
    screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE);

    /*print out the properties of block variable*/
    printf("position: (%d, %d)\n", block.x, block.y);
    printf("dimensions: %u x %u\n", block.w, block.h);

    /*loop is needed to keep window open*/
    while (loop == 1) {
        /*draw block rectangle on screen*/
        SDL_FillRect(screen, &block, 0xFFFFFFFF);

        /*read all events per cycle*/
        while (SDL_PollEvent(&input)) {
            if (input.type == SDL_QUIT) {
                loop = 0; /*exit if quit*/
            }
        }

        SDL_Delay(20); /*wait 20ms*/
        SDL_Flip(screen); /*update the screen*/
    }

    /*perform final commands then exit*/
    SDL_Quit(); /*shutdown SDL*/
    fflush(stdout); /*update stdout*/
    return 0;
}
prompt/stdout:
position: (200, 100)
dimensions: 400 x 300
output inside window:
A White Rectangle

    For this example, multiples of 100 are used to set the properties of a rectangle, but any numbers may be used.  Once the position and dimensions of the rectangle are set, we can draw it giving SDL_FillRect our variables screen and block and an integer value used to indicate a color.

    The SDL_FillRect and SDL_Flip commands are executed every cycle of the program loop.  Since the rectangle does not move, drawing and updating could both be performed before "while (loop == 1)" to get the same result.  For later, interactive examples, this will not be the case.

    The command "SDL_Delay(20)" was added; this was to allow the computer to rest 20 milliseconds (for a maximum of 50 cycles or "frames" per second) before updating the screen.  By letting the computer rest, less effort is put towards drawing frames we may not notice.  Since video games attempt 60 frames per second, a delay of 16ms or less may be more ideal.

Colors:
    The only 32-bit value of a color we can know without doubt is the one used for white: 0xFFFFFFFF.  This is because within this value are the lesser values used for alpha (opacity), red, green, and blue (which are all maximum for white).  The format of a surface determines how these lesser values combine to form a 32-bit value.

    Since 32 bits are divided among 4 components, each component uses 8 bits like the char data type.  The most common format is (alpha << 24) + (red << 16) | (green << 8) | blue.  This means the 8 alpha bits shifted just left of the 8 red bits, which are left of the 8 green, and green is left of blue bits.  Luckily, hexadecimal shows this pattern nicely: for a number like 0x89ABCDEF, 0x89 is alpha, 0xAB is red, 0xCD is green, and 0xEF is blue (but only if the common format is in use).

    For other formats, the red and blue bits may be swapped or alpha may be on the other end.  To account for these possibilities, the function SDL_MapRGB requires a pointer to an SDL_PixelFormat struct in addition to Uint8 values for red, green, and blue.  Luckily, the screen variable has this information under its "format" data field.

    Another function is SDL_MapRGBA, which allows input for alpha.  For more information about this function (and SDL_MapRGB) visit http://sdl.beuc.net/sdl.wiki/SDL_MapRGBA.  However, alpha values are often ignored when drawing rectangles, so we can say alpha = 0 and still have an opaque rectangle drawn.  Also, 0x00FFFFFF is equal to 0xFFFFFF, allowing for colors of only three components (alpha ignored).  Here are some rectangles with color:
/*thanks to tohtml.com for syntax highlighting*/

#include <SDL/SDL.h>
#include <stdio.h>

/*holds data about the drawable surface*/
SDL_Surface *screen;

SDL_Event input; /*holds input*/
int loop = 1; /*set to zero to exit*/

int main(int argc, char** argv) {
    SDL_Rect magenta_block; /*three rectangles*/
    SDL_Rect yellow_block, cyan_block;

    Uint32 magenta, yellow, cyan; /*colors*/
    Uint8 red, green, blue; /*components*/

    /*setup block positions and dimensions*/
    magenta_block.x = 50;
    magenta_block.y = 75;
    magenta_block.w = 200;
    magenta_block.h = 150;

    yellow_block.x = 100;
    yellow_block.y = 200;
    yellow_block.w = 150;
    yellow_block.h = 200;

    cyan_block.x = 200;
    cyan_block.y = 100;
    cyan_block.w = 200;
    cyan_block.h = 200;

    /*start up SDL and setup window*/
    SDL_Init(SDL_INIT_EVERYTHING);
    screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE);

    /*setup colors after screen initialized*/
    magenta = SDL_MapRGB(screen->format, 255, 0, 255);
    yellow = 0xFFFF00; /*red and green*/
    cyan = SDL_MapRGB(screen->format, 0, 255, 255);

    /*Get and print yellow components*/
    SDL_GetRGB(yellow, screen->format, &red, &green, &blue);
    printf("yellow: RGB(%u, %u, %u)\n", red, green, blue);

    /*loop is needed to keep window open*/
    while (loop == 1) {
        /*draw rectangles on screen*/
        SDL_FillRect(screen, &magenta_block, magenta);
        SDL_FillRect(screen, &yellow_block, yellow);
        SDL_FillRect(screen, &cyan_block, cyan);

        /*read all events per cycle*/
        while (SDL_PollEvent(&input)) {
            if (input.type == SDL_QUIT) {
                loop = 0; /*exit if quit*/
            }
        }

        SDL_Delay(20); /*wait 20ms*/
        SDL_Flip(screen); /*update the screen*/
    }

    /*perform final commands then exit*/
    SDL_Quit(); /*shutdown SDL*/
    fflush(stdout); /*update stdout*/
    return 0;
}
prompt/stdout:
yellow: RGB(255, 255, 0)
output inside window:
3 Rectangles with Color

    Here we see three rectangles of different colors.  The three colors used are the secondary colors of light: magenta, yellow, and cyan, which are the basic ink colors for printers.  We can break down a color into its red, green, and blue components with the function SDL_GetRGB by giving it the Uint32 color, format, and addresses of three Uint8 variables to write to.  This is how the components of yellow were determined.

    The order of the drawing makes a difference: note that magenta is drawn first and cyan last.  In the final picture, the magenta block is obscured by the yellow and cyan blocks because every new draw command will overwrite the previous commands.  Here, several pixels are colored magenta, then yellow, and finally stop at cyan.

    To wrap up this post, some references are needed.  For information about RGB color, wikipedia has the page http://en.wikipedia.org/wiki/RGB_color_model.  For color ideas, here is a table of hexadecimal colors used with websites: http://www.tayloredmktg.com/rgb/.  For more documentation about SDL_FillRect, the page http://www.libsdl.org/docs/html/sdlfillrect.html is helpful, and the page http://www.libsdl.org/docs/html/index.html for SDL in general.

    Thank you for reading, the next post will cover keyboard interaction.

Saturday, August 18, 2012

SDL setup

    Simple DirectMedia Layer, or SDL for short, is a great library for building applications which run outside the terminal or command prompt in their own window.  Although the program does not require a prompt to run, I believe it helps to have something simple to print values to, so the following posts will use a mixture of output from the prompt and graphics in the window.

Setup:
    To prepare the SDL library (and most other libraries), we need header files and library files.  Header files have a .h extension and are used to access the functions and structures found in the library.  Library files have several extensions: .lib, .a, .la, .dll, and possible others; and contain the code necessary for the library functions to work.  Library files must be linked into a program during its transition from C code to machine language.
To learn more about this transition, an informative video can be found at http://www.youtube.com/watch?v=gUKXew-0L1I and an in-depth page at http://www.tenouk.com/ModuleW.html.

    I will attempt to cover setup for multiple systems, but please review the pages http://lazyfoo.net/SDL_tutorials/lesson01/index.php and http://content.gpwiki.org/index.php/SDL:Tutorials:Setup because they will be much more insightful.  To obtain the header and library files for your system, visit the SDL 1.2 download page at http://www.libsdl.org/download-1.2.php.  Unfortunately, I have not found a working online compiler with SDL support.

For Linux:
    Downloading from the site is optional because you can use package managers.  There are the packages "libsdl1.2-dev" or "SDL-devel-1.2" for SDL and "build-essentials" or "Development Tools" if one of them is not already installed.  These should place the necessary header and library files in the "include" and "lib" folders.  Otherwise, try running the RPM file from the site.

    To link the libraries to the program with GCC, add -l(library name) after the usual compile command.  For example, "gcc (file name).c -o (program name) -lSDLmain -lSDL" will link the basic SDL library to the created program.

For Mac:
    Currently, there does not appear to be a link under "development libraries" as seen in the Lazy Foo Tutorial pages.  Hopefully, the page http://macemulators.wordpress.com/2009/11/05/how-to-install-sdl/ will provide better assistance.

    I believe the GPwiki link will be more thorough explaining how to setup a working SDL project in xcode.  I apologize for not knowing much about SDL on Mac OSX :(.

For Windows:
    If you use visual studio, download the VC package under "development libraries" and extract the contents.  There should be an "include" and "lib" folder as well as some DLL's in "bin".  To get the include and library files to visual studio, you can add the lib and include folder locations to Tools->options->VC++ Directories.  Alternatively, you can take the contents of these folders and add them to the existing include and lib folders where visual studio is installed (usually C:/Program Files/Microsoft Visual Studio/VC).  Also, in linker settings, specify the subsystem to be "console" preferably, or "windows" if that does not work.

    If you use MinGW without a compiler, place the lib and include folder contents in MinGW's lib and include folders.  This can usually be found in C:/MinGW/.  To link libraries, see the above instructions for GCC under For Linux.  If you downloaded the vc package, the .lib libraries can be linked with -l:(library file with extension).  For example, "gcc (file name).c -o (program name) -lmingw32 -l:SDLmain.lib -l:SDL.lib" will link the basic SDL library to the program.

    The DLL's are not needed during compilation, but must be present when your program is run.  This means they can be placed right next to the program or in the system32 or syswow64 folder in C:/windows/.  For portability, it is recommended you place the DLL's in the same folder as your program.

Linking libraries in IDE's:
    Most IDE's, like Code::Blocks, Eclipse, Visual Studio, and Xcode, have features to help link libraries called linker options.  This usually allows you to find the library files manually and add them to a project.  For IDE's running the GCC/MinGW compiler (Xcode, Code::Blocks, or Eclipse) link the library files "libSDL.la" and "libSDLmain.a" or the .lib files "SDL.lib" and "SDLmain.lib".  For Visual Studio, simply link the .lib files from the VC package.

Sample Program:
    To test whether SDL is now working with your C compiler, here is a simple C program with SDL integrated:
/*thanks to tohtml.com for syntax highlighting*/

#include <SDL/SDL.h>
#include <stdio.h>

/*parameters of main are command-line arguments*/
int main(int argc, char** argv) {
    SDL_Init(SDL_INIT_EVERYTHING); /*initialize SDL*/
    SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE); /*window setup*/

    printf("C running with SDL!\n");

    SDL_Delay(3000); /*wait 3 seconds*/
    SDL_Quit(); /*close SDL*/
    fflush(stdout); /*update stdout*/
    return 0;
}
prompt/stdout:
C running with SDL!
output inside window:
Blank SDL window

    This short program starts by including the header file "SDL.h", which is all that is required for the basic SDL library.  The function main has two parameters used to read from a prompt when starting a program: the first parameter is the number of inputs and the second is an array of strings representing the input.  For example, if you run the program with the command "program_name", where program_name is the name of your program, argc will be 1 and argv[0] will be the string "program_name".  You can add command-line arguments by typing "program_name argument1 argument2" which makes argc 3 and argv[1] and argv[2] "argument1" and "argument2" respctively.

    These posts will rarely use this feature, but it is required to work with c++ programs under visual studio.  Next, SDL_Init is given the flag SDL_INIT_EVERYTHING (the value 65535 or 0x0000FFFF), which starts up all parts of the SDL library.  SDL_SetVideoMode sets the properties of the window created: here we use a 640 pixels wide, 480 pixels tall, 32-bit color window with software (or processor) rendering, as indicated by the SDL_SWSURFACE flag (value of 0).  Finally, a message is printed to the prompt, the program waits 3000 milliseconds, then shuts down the SDL library and terminates.

Text Output:
    In order to test the value of variables, it is recommended that printf be able to print to a prompt.  For most cases, this will not be a problem, but for instances where the prompt is not visible, check the folder containing the program for a file called "stdout" (with possible extension) after testing the program.  This file should contain the text that normally prints to the prompt.

    The command "fflush(stdout);" writes any text currently in memory into the file stdout.  However, this does not mean changing the input from stdout to file_name_here will create a file called "file_name_here"; stdout is a built-in file for C which refers to the standard output.  Without the command, sometimes text output can remain in memory and not be written to the stdout file.

    That's all for this post, the next post will cover events and drawing in SDL.  For more SDL tutorials, check out http://lazyfoo.net/SDL_tutorials/ and http://content.gpwiki.org/index.php/SDL:Tutorials.  Thank you for reading.