Wiki
Wiki
This wiki is aimed to be more like a tutorial to SDLU, than a mere library reference. ## Table of Contents ### About SDLU * [Installing SDLU](Install.md) * [Contribute to SDLU](Contribute.md) * [License](License.md) ### Documentation * [Capping the framerate](FPS.md) * [Rendering functions](Render.md) * [Buttons](Button.md) * [Collision Detection Routines](Collide.md) * [Pixel-value mapping functions](Pixels.md) * [SDLU structures](Structures.md) -------------------------------------------------------------------------------Copyright (c) 2013 Aggelos Kolaitis <[email protected]> ##### note that this wiki is currently a work in progress, so stuff may be inval id or missing. # The SDL Utility library ## 1. Button structure Below is the definition of the SDLU_button structure as seen in SDLU_defs.h ``` #!c typedef struct SDLU_Button SDLU_Button; /** * \brief Button callback prototype * * It returns nothing (void*), and takes two arguments. The first is * a button structure (SDLU_Button*), the 'button-caller' and the other is a * (void*) pointer, which can hold userdata. */ typedef void (SDLCALL * SDLU_ButtonCallback) (SDLU_Button*, void*); /** * \brief The button structure */ struct SDLU_Button { int id; SDLU_ButtonType type;
**/ **/
union SDLU_ButtonData { char *text; SDL_Texture* image; } data; SDL_Rect rect; SDL_Window* window; SDLU_ButtonCallback callback; void *arg; /* text-button-only attributes */ SDL_Color c_text; SDL_Color c_outline; SDL_Color c_fill; Uint32 font_size; }; ```
/**< data /**< size and position /**< associated window /**< callback function /**< argument to the callback /**< text color /**< outline color /**< fill color /**< font size of the text
| Field | Description | |:---------:|:----------------------------------------------------------------| | id | The unique identifier of the button. READ-ONLY. | | type | Enum that holds the type of the button. Used internally. | | data | Holds the data of the button, depending on its type. | | rect | The size and position of the button on the window. | | window | The window associated with the button. | | callback | Callback function of the window. | | arg | Argument passed to the callback function. | | c_text | The text color of a button. ONLY FOR TEXT BUTTONS | | c_fill | The fill color of a button. ONLY FOR TEXT BUTTONS | | c_outline | The color of the button outline. ONLY FOR TEXT BUTTONS | | font_size | Font size of the button text ONLY FOR TEXT BUTTONS | # The SDL Utility Library ## 1. Loading textures The normal way of loading images in older versions of SDL (1.2-), consisted of one and only function: *SDL_LoadBMP()* which would load the contents of a BMP file into a surface and then work with it. As of SDL2, there is an extra step: After loading the surface, to display it, you must convert it to a texture, which is later used in rendering. So, including error checking and freeing the temporary surface, the procedure is a little more complicated. So, why not just a function to load a BMP into a texture in one step? Cause it's still common to load all of your images in surfaces, blit them in a temporary screen surface and then converting just the latter and render it. Anyway, since there is no such function in the SDL2 core, SDLU takes care of this task with two functions: ``` #!c /* Load a BMP file into an SDL_Texture */ SDL_Texture* SDLU_LoadTexture(
/* The render target */ /* BMP filename */ WITH a colorkey */ The render target */ BMP filename */ A pixel value to use as colorkey */
/* Load a BMP file into an SDL_Texture SDL_Texture* SDLU_LoadTextureEx( SDL_Renderer* renderer, /* const char* fname, /* Uint32 color, /* ); ```
An easy way to distinguish the two of them is to think of the 'Ex' as 'extended', which in this case is passing a colorkey to the texture. The only thing to note here is that the colorkey pixel value of the second function must be generated via SDL_MapRGB() or SDL_MapRBGA(). If however, you know the window associated with the renderer, you can use the SDLU_WinMapRGB functions, see [here](Pixels.md). ## 2. Rendering functions ### 2.1 Rendering text SDL_ttf is an awesome library that allows you to render glyphs from truetype fonts and load them in your program as surfaces, and use them as normal images. However, it's not always fast and easy, and for small text messages the whole procedure of SDL_ttf seems very complicated ( NOTE that I don't want in any way to 'attack' SDL_ttf, I just make a point. ) A function that will render text on the screen in a quick manner can be useful. And that's what *SDLU_RenderText* is all about: ``` #!c SDLU_RenderText( SDL_Renderer* renderer, int x, int y, char *format, ... ); /* render text on the window */ ``` By default, each char is 8x8 in size, but you can change that with a mere call to *SDLU_SetTextSize*: ``` #!c SDLU_SetTextSize( Uint32 size ); ``` But the fact that it accepts the input in printf-format is its ultimate advantage. If you want to ie output a framerate message, you would normally have /* the new text size */ /* renderer to draw /* (x,y) coordinates /* text to draw, in printf-format */ */ */
to format a char* string containing the message, render a glyph from it and then copy it on its destination. `SDLU_RenderText(target,0,0,"FPS %d", fps);` however seems much more clear and intuitive. ### 2.2 Polygons The rendering API introduced in SDL2 is a major update from previous versions. The programmer no longer has to write the functions to draw lines, rectangles, etc. Also, the API allows setting global options such as change the viewport, scale the rendering, setting a cliprect... But still, there's room for improvement. Rendering polygons is far from intuitive with SDL_RenderDrawLines(). So, SDLU introduces a new way of rendering such polygons, and it's inspired from the OpenGL immediate-mode rendering. There are three functions provided by SDLU. These are: ``` #!c /* start collecting points */ SDLU_RenderBegin( void ); /////////////////////////////////////////// /* set a new point */ SDLU_RenderPoint( int x, int y );
/////////////////////////////////////////// /* render the polygon */ SDLU_RenderEnd( SDL_Renderer* renderer /* renderer to draw */ ); ``` If you have even the least experience with OpenGL, you might have probably guessed that these are identical to glBegin, glVertex and glEnd. But still, since there is no uniform way, in the last step (*SDLU_RenderEnd*), you have to specify the renderer to draw, because SDLU can't possibly know it. Below, you can see this calls in action: (find full source [here](src/polygon.c)) ``` #!c int main(int argc, char** argv) { ... initialization ... SDL_RenderClear(renderer);
SDLU_RenderBegin(); /* start */ SDLU_RenderPoint(200, 100); SDLU_RenderPoint(300, 170); SDLU_RenderPoint(250, 300); SDLU_RenderPoint(150, 300); SDLU_RenderPoint(100, 170); SDLU_RenderEnd(renderer); /* render the polygon */ SDL_RenderPresent(renderer); ... exit ... } ``` ### 2.3 Rendering circles The SDL documentation advises that all your graphics should be image files that you load at runtime. But having a fast function that renders a circle (a geometrical element commonly used) comes handy at times. ``` #!c /* Renders a circle */ SDLU_RenderDrawCircle( SDL_Renderer* renderer /* renderer to draw */ int x, int y, /* (x,y) coordinates of the circle's center */ int r /* radius of the circle */ ); ``` I think the example above is quite self-explanatory, so not much to say here. # 3. Rendering textures If you want to render any image with SDL2, you have to setup two rects beforehan d. One of them specifies the source rect (*which* part of the texture should be ren derer), and the other the destination rect (*where* on the screen should the image be re nderer). This technique is very useful, but setting up two rects when you want to display the whole image with its real dimensions (no scaling) is unintuitive and sometimes time-wa sting. SDLU deals with this problem by adding one simple function ``` #!c SDLU_CopyTexture( SDL_Renderer* renderer, /* renderer to draw */ SDL_Texture* texture, /* source texture */ int x, int y /* (x,y) coordinates */ ); /* render the texture at (x,y) */
``` The *x* and *y* arguments specify the coordinates of the top-left corner. -----------------------------------------------------------------------------Copyright (c) 2013 Aggelos Kolaitis <[email protected]> # The SDL Utility Library Pixel-mapping functions are very useful, because they generate the exact pixel v alue (a Uint32 value) for any pixel format. But these require a SDL_PixelFormat structure, so that they know how to generate the pixel value. In SDL-1.2 or older, when there only SDL_Surface structures for everything, mapp ing a pixel value was as simple as: ``` #!c SDL_Surface* surface; Uint32 pixel_value; /* surface had a 'format' field */ pixel_value = SDL_MapRGB( surface->format, red, green, blue ); ``` But now, there are SDL_Window, SDL_Renderer and SDL_Texture, whose fields are re ad-only. So, you can't do: ``` #!c SDL_Renderer *renderer; Uint32 pixel_value; /* wrong */ pixel_value = SDL_MapRGB( renderer->format, red, green, blue ); ``` Also, there is not a standard format for an SDL_Renderer. It has a number of sup ported pixel formats that you can get from a *SDL_RendererInfo* structure. But that's n ot optimal. But what you can do is getting the pixel format of a window to map a certain pix el value. You can do this: ``` #!c SDL_Window* window; Uint32 fmt;
SDL_PixelFormat* p_fmt; Uint32 pixel_value; fmt = SDL_GetWindowPixelFormat( window ); p_fmt = SDL_AllocFormat( fmt ); pixel_value = SDL_MapRGB( p_fmt, red, green, blue ); ``` Again, as you can see, this is much of work to do. So, SDLU provides a new famil y of functions, ``` #!c /* returns the pixel value for the pixel format of window */ Uint32 SDLU_WinMapRGB ( SDL_Window* window, /* window for which to generate the pixel value */ Uint8 r, /* red component [0, 255] */ Uint8 g, /* green component [0, 255] */ Uint8 b, /* blue component [0, 255] */ Uint8 a /* alpha component [0, 255] */ ); /* returns the pixel value for the pixel format of window */ Uint32 SDLU_WinMapRGB( SDL_Window* window, /* window for which to generate the pixel value */ Uint8 r, /* red component [0, 255] */ Uint8 g, /* green component [0, 255] */ Uint8 b /* blue component [0, 255] */ ); ``` These functions make it easier to map a pixel value. It is also assured that the value returned will work for all your textures, as well as the renderer, as long as they are associated with that window. With these functions, mapping a pixel value is as easy as: ``` #!c SDL_Window* window; Uint32 pixel_value; /* works great */ pixel_value = SDLU_WinMapRGB( window, red, green, blue ); ```
----------------------------------------------------------------------------------Copyright (c) 2013 Aggelos Kolaitis <[email protected]> ### The SDL Utility Library Copyright (c) 2013 Aggelos Kolaitis <[email protected]> This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. # The SDL Utility Library In order to build and install the SDLU library, follow the instructions above: ## 1. Setting up premake SDLU uses [Premake][1] as its build tool. Thanks to premake's small size and BSD license, it is bundled with SDLU. Inside the toplevel `scripts` folder, you will find three executables for Windows, Linux and Mac OS X. Rename it to plain *premake4* and put it in your system path or anywhere else convenient. ## 2. Generating project files for SDLU After that, you are ready to generate the project files for SDLU. Do so by running: ``` #!sh premake4 [options] [target] ``` | Target | Action | |:----------:|:--------------------------------------------------------| | vs2012 | Generate project files for MS Visual Studio 2012 | | vs2010 | Generate project files for MS Visual Studio 2010 | | vs2008 | Generate project files for MS Visual Studio 2008 | | vs2005 | Generate project files for MS Visual Studio 2005 | | xcode4 | Generate project files for Apple XCode 4 | | xcode3 | Generate project files for Apple XCode 3 | | codeblocks | Generate project files for [Code::Blocks][2] | | gmake | Generate makefiles for GNU Make (and [MinGW][3]) | You may also specify several options: | Option | Description | |:------------------:|:------------------------------------------------| | --sdl-include=PATH | Specify the path of the SDL2 headers |
| | | |
| | | |
Specify the path of the SDL2 library | Build SDLU without support for OpenGL functions | Build SDLU as a static library | Extra messages |
For more information about premake and how you can use it, you can either run `premake4 --help` or check out their [website][1]. *NOTE that all options must appear BEFORE the `[target]` argument.* ## 3. Building SDLU In order to build the library, just load the generated files in your IDE and build SDLU with it. ## 4. Installing SDLU An `install` action is currently provided for all systems, but is stable only for Linux. Please test it on your system and send me feedback. All you have to do is run: $ premake4 [--prefix=PATH] install Use the option `--prefix` to set the PATH where SDLU will be installed. If you omit it, the default directory is chosen. [1]: http://industriousone.com/premake/ "Premake website" [2]: http://www.codeblocks.org/ "Code::Blocks website" [3]: http://www.mingw.org/ "MinGW website" -------------------------------------------------------------------------------Copyright (c) 2013 Aggelos Kolaitis <[email protected]> # The SDL Utility Library One common task for SDL applications is the need to cap the framerate. That means that in every loop, the program stops the execution for a while, which happens for two primary reasons: 1. 2. It prevents your program from being a CPU hog, which happens when it does not it give some time to rest. Just because a program runs normally in the developer's system, that is not enough to assure that it will run at an acceptable speed in computers with higher or lower specs.
The key to capping the framerate is watching how much time each frame lasts, and if it finishes faster, delay the application for the appropriate amount of time. For example, if we want 60 frames per second, the duration of each frame must be exactly 1/60th of a second, which consider a "breakpoint". If the frame finishes faster before the breakpoint, we delay the required amount of time to reach this breakpoint, which is when the next frame starts. The diagram below shows how capping the framerate works. 
The thick black lines are the 'breakpoints', and their time interval is 1/FPS. **Green** color marks the duration of each frame, and **blue** marks the time that the program delays its execution, until it reaches the breakpoint. Note that the last frame takes longer to execute, and it passes the breakpoint before finishing. In that case, the program is 'behind schedule', so the next frame begins immediately. ## Capping the framerate with SDLU Taking into account the stuff above, there are three functions that we need to cap the framerate: 1. 2. 3. An initializer function, which will setup the internal state. A function to inform the state that a new frame has started. A function to tell the state that a frame has finished, so that it delays the required amount of time.
These three functions are: 1. 2. 3. *SDLU_FPS_Init*: called before entering the main loop, takes as argument the desired framerate. *SDLU_FPS_Start*: the very first function called from the main loop. *SDLU_FPS_Cap*: the very last function called from the main loop, delays the required amount of time and calculates the actual framerate of the application.
Below is a "diagram" of how they should be used: ``` #!c ... initialization ... SDLU_FPS_Init( FPS ); /* called right before entering the main loop */ /* the main loop /* first function */ /* last function */ */
while( !quit ) { SDLU_FPS_Start( ); .... main loop.... SDLU_FPS_Cap( ); } ... quit ... ```
-------------------------------------------------------------------------------Copyright (c) 2013 Aggelos Kolaitis <[email protected]> # The SDL Utility library Copyright (c) 2013 Aggelos Kolaitis <[email protected]> If you would like to contribute to SDLU, see what you can do below:
1. Make a pull request. If you want to send a patch, add a new feature or fix a bug, you can make a pull request in bitbucket. 2. Send me an e-mail. You have no time to write a patch yourself? You are not quite sure how pull requests work? Don't worry, feel free to contact me at: <[email protected]>. I will take the time to reply to any questions. and even accept patches. 3. Create a logo. SDLU has no official logo yet, so if you are willing to help you can design it. Possibly, it's going to become the official logo!
# The SDL Utility Library A common task for applications that use the SDL library is detecting collisions between entities. ## 1. Collision Detection between rectangles Detecting a collision between two rectangles is very easy with SDLU: ``` #!c int SDLU_CollideRects( SDL_Rect a, /* first rect */ SDL_Rect b /* second rect */ ); /* check if two rectangles collide */ ``` *SDLU_CollideRects* is more than just a 'boolean' function. If there is no collision, it will return 0. But it detects an intersection, it will return one of: | Return value | Meaning | |-------------------------|-----------------------------------| | SDLU_COLLIDE_VERTICAL | Indicates a vertical collision | | SDLU_COLLIDE_HORIZONTAL | Indicates an horizontal collision | These values are defined in SDLU_collide.h. Their actual values shouldn't matter, but take a peak if you want to. Returning a special code for each type of collision is helpful for games such as pong or arkanoid, where the type of collision must be known. -------------------------------------------------------------------------------Copyright (c) 2013 Aggelos Kolaitis <[email protected]> # The SDL Utility library ## 1. Working with buttons
SDLU provides simple yet powerful button functionality. The program below should get you started: ``` #!c /* * For simplicity reasons, there is little to no error checking * for things other than those that cover button functionality. * That is why I use assert. */ #include "SDLU.h" #include <stdlib.h> #include <assert.h> SDL_Window SDL_Renderer SDL_Event SDLU_Button *window; *renderer; event; *button;
void cleanup(void) { if (window) SDL_DestroyWindow(window); if (renderer) SDL_DestroyRenderer(renderer); /* button deletion */ if (button) SDLU_DestroyButton(button); } int main(int argc, char** argv) { /* SDL initialization */ assert( SDL_Init(SDL_INIT_VIDEO) != -1); atexit(cleanup); SDL_CreateWindowAndRenderer(640, 480, 0, &window, &renderer); assert(window && renderer); /* Button creation */ button = SDLU_CreateTextButton( renderer, "Press me" ); if ( !button ) { puts( SDLU_GetError() ); exit(-1); } SDLU_SetButtonGeometry( button, 200, 220, 240, 40 ); /* create a new text button */ /* the associated renderer */ /* window title */
/* x,y coordinates of top left corner */ /* width and height of the button */
/* * for our example, the button needs to be displayed only once, * so it's left out of the main loop */ SDL_RenderClear(renderer); SDLU_RenderButton(button); /* display the button */ SDL_RenderPresent(renderer);
while( 1 ) { SDL_PollEvent(&event); if (SDLU_GetButtonPress(button, event)) /* check press */ exit(0); } } ``` * You create a button by calling *SDLU_CreateTextButton*, giving as arguments the render target and the button title. You may give NULL as the second argument, in which case the default title "button" is specified. ``` #!c SDLU_Button* SDLU_CreateTextButton( SDL_Renderer* renderer, const char* title ); ``` * After creating it, you must set its position with *SDLU_SetButtonGeometry*. The first two arguments are the (x,y) coordinates of the top left corner and the two others the desired width and height of the button. ``` #!c SDLU_SetButtonGeometry( SDLU_Button *button, /* button to update */ int x, int y, /* new (x,y) coordinates */ int w, int h /* new width and height */ ); /* The (x,y) coordinates specify the upper-left corner of the button */ ``` * The button is not displayed by itself on the specified renderer. It is required that you display it with *SDLU_RenderButton*, which takes as the only parameter the button that you want to display. ``` #!c SDLU_RenderButton( SDLU_Button* button ); ``` * Of course, what's the use of a button, if you can't check that it was pressed? *SDLU_GetButtonPress*, which given the button that you want to check and an event structure filled by SDL_PollEvent, returns 1 if the button was pressed, 0 otherwise. /* button to render */ /* The render target */ /* The button title */
``` #!c SDLU_GetButtonPress( SDLU_Button* button, /* button to check */ SDL_Event event /* event structure */ ); /* Returns 1 if the button was pressed, 0 otherwise */ ``` * Once it's of no use any more, a button should be destroyed to free its resources. That is done with *SDLU_DestroyButton*. ## 2. Setting a callback function SDLU lets you set your own callback functions for buttons, that will be automatically triggered when the button is pressed. ``` #!c SDLU_SetButtonCallback( SDLU_Button* button, /* desired butto n */ void (*callback)(SDLU_Button*, void* ), /* function */ void *arg /* argument */ ); ``` The function a button. It the callback the third is *SDLU_SetButtonCallback* is used to assign a callback function to takes three arguments: The first is the button we want to assign to, the second is a pointer to a function (more on this below), and an optional argument that will be passed to the callback function.
Now let's see again the prototype of the callback. ``` #!c void (*callback) (SDLU_Button*, void*) ``` The callback function **must** take two arguments. The first one is a pointer to a button structure (the one that it was called from, and the other is a pointer to void (void*), which is the third argument of *SDLU_SetButtonCallback*. It might sound weird at first, but an example will clear things up: #### find full source code [here](src/cb.c): ``` #!c int quit = 0; void button_func( SDLU_Button *button, void* arg ) { printf("Button with id %d was pressed!\n", button->id);
printf("Argument: %s\n", (char*) arg); /* to quit the loop as well, see below */ quit = 1; } int main() { SDLU_Button *button; ... initialization ... button = SDLU_CreateTextButton(renderer, "Press Button"); SDLU_SetButtonGeometry(button, 220, 230, 200, 20); SDLU_SetButtonCallback( button, button_func, /* callback function */ (void*) "test argument" /* argument, note the (void*) cast */ ); while( !quit ) { SDL_PollEvent( &event ); /* the callback will quit the loop */ SDLU_GetButtonPress( button, event ); } ... quit ... } ``` Below you can see the program output: ``` ... (button pressed) ... Button with id 1 was pressed! Argument: test argument ``` ## 3. Custom colors SDLU also allows setting custom colors to your buttons. There is only one function for that matter (and it's just enough): ``` #!c SDLU_SetButtonColor( SDLU_Button* SDL_Color SDL_Color SDL_Color ); ``` NOTE that custom color capability also allows for custom alpha of the different button, text_color, fill_color, outline_color /* /* /* /* button to change colors for */ new text color */ new fill color */ new outline color */
button elements ( text, outline, fill ), and can be used for ie setting a transparent fill_color. ## 4. Image buttons Other than text buttons, there is also support for image buttons. The ONLY actual difference (from the programmer's point of view) is the content that you give. For a text button, you would make a call to *SDLU_CreateTextButton*. Well, for image buttons, the function is *SDLU_CreateImageButton*, and instead of a character string, you pass a valid texture structure as the second argument. See the prototype below: ``` #!c SDLU_Button* SDLU_CreateImageButton( SDL_Renderer* renderer, /* the render target */ SDL_Texture* image /* a valid texture */ ); /* The texture must be created for the render target */ ``` Also, note that custom colors discussed above have on meaning for image buttons. -------------------------------------------------------------------------------Copyright (c) 2013 Aggelos Kolaitis <[email protected]>