Chapter 4 – Images/Textures

Loading of images and using them as sprites or textures in a game (or other applications) is a major concept in programming nowadays. Let’s have a look at a simple example:

left: ball and paddle erased between each frame; right: ball and paddle not erased between framesHere are two screenshots from a simple game. The player has to move the yellow-green  paddle up- and downwards to prevent the blue ball from getting through to the right side. The game uses two sprites, the blue ball sprite and the yellow-green paddle sprite (see left screenshot). The background color is set to black. The left screenshot is how the game usually appears to the player, here between each frame that got drawn, the former frame has been erased. The right screenshot demonstrates what happens if the former frame hasn’t been erased before the next one is drawn. – Now it is clearly visible that the sprites are redrawn again and again with sligthly different coordinates, and that is how (game) graphics work (even for the most sophisticated 3d games):

  1. Draw the frame
  2. Show the frame (to the user)
  3. Erase the frame (and start at 1. again)

By the way, the terms picture/image, sprite and texture equal each other. All of them are pictures that are stored in the memory of your system. But in different contexts you may call them differently. If you use a paint program to draw something or you import some photos of your digital camera, you may call them images. In a 2d jump and run game, the hero you control, the enemies and maybe some clouds which float around are (animated or static) sprites. In a 3d shoot ’em up game all the level’s geometry is covered up by textures (like wallpaper). Nevertheless, they all have in common that they are images which are stored in your computer system’s memory.

Hardware relations for graphics and images

Actually there are just three locations where these images are stored in your computer system. All images (photos, drawings, sprites for 2d games, textures for 3d games) are stored on your harddrive somewhere. If you start a photo viewer, a paint program, a 2d game or a 3d game, in all cases the corresponding images need to be loaded from your harddrive to RAM (Random-Access Memory) since displaying and manipulation (e.g. rotation of a photo image by 90°) of images loaded to RAM is much, much faster. Especially for games a fast access to the image data is highly important! And finally there isn’t just one RAM but two, a CPU controlled one located on the motherboard used by every program/application that needs some RAM. The second RAM is located right at your graphic board and controlled by the so-called GPU (graphics processing unit). The graphic board is what we want to use if we develop games since it is dedicated, optimized and just hungry for tasks related to fast image manipulations.

Many games and applications do not only target at common computer systems, but for mobile devices, e.g. smart phones. The principles described are also true for these devices even though there may be differences in detail.

Images, textures and surfaces in SDL 2.0

Now, have a look at the following diagram.

SDL2 Texture and Surface relation diagram

Looks a little bit confusing but you will realize quickly that the two concepts of how images are treated in SDL 2.0 are quite simple. Let’s start on the left in the diagram. The easiest way to get an image for a game or application ready for usage is to create one in a drawing application or download one from the internet (there are plenty of sources to download free images for various purposes, e.g. game sprites or textures) and have them as image files (e.g., bmp, jpg, png, and so forth). If you have the image ready, there are two ways to make them available for usage in SDL 2.0. You may make it available to SDL 2.0 as a SDL_Texture (upper path in the diagram) or you may make it available to SDL 2.0 as a SDL_Surface (lower path). Both, as texture and as surface, you can copy the stored image to the screen finally to use as you wish in applications.

The prefered way is to go by the upper path, the image should be stored as SDL_Texture because that ensures right what we discussed before. The image is stored in the graphic board’s RAM and any manipulation is done by the graphic board’s GPU, so the tasks are hardware accelerated! The lower path via SDL_Surface means we go for “software rendering”. Instead of the graphic board’s RAM, the system RAM is used to store the image and instead of the graphic board’s GPU, the computer’s CPU has to calculate manipulations. Since the CPU and the system RAM are also used to calculate any other task to keep your system running, this way the graphic performance is much slower. Anyway, the lower path was the standard path in the peedecessor version of SDL 2.0 (that was SDL 1.2), although there were other ways to achieve hardware acceleration in SDL 1.2. You try to avoid going via SDL_Surfaces. So as a rule,

always use SDL_Texture to store your images for SDL 2.0,

then you go for high performance!

Three ways to SDL_Texture

So, how to get a SDL_Texture? In principle there are three ways to create textures. In short:

1) You create a SDL_Texture from scratch, so you set a pixel format and texture access format and have to fill in your texture data manually. This is the most complicated way and is usually not necessary.

2) You have or create a SDL_Surface from an image file first and then you create the SDL_Texture from the SDL_Surface. This way is shown in the diagram but it means two steps.

3) You create a SDL_Texture from and image file directly. This is shown in the diagram, too. This is the simplest way to create a SDL_Texture.

The SDL2_image unit and image formats

The function related to the direct creation of an SDL_Texture from an image file is not part of native SDL 2.0. SDL2_image is an official extension of SDL 2.0, which is developed and maintained by the same developers. Therefore, before right jumping into the code we need the corresponding library files. For Windows systems check the following table:

Software
Version
File name
Link
Description
SDL2_image dynamic link library
2.0.0
SDL2_image-2.0.0-win32-x86.zip (32-bit Windows)
SDL2_image-2.0.0-win32-x64.zip (64-bit Windows)
http://www.libsdl.org/projects/SDL_image/
This is the corresponding dynamic link library file for the unit and image formats for Windows.

Download the corresponding SDL2_image package depending on your system (32 bit or 64 bit) and extract the file. You will end up with a SDL2_image.dll and some further dlls which are necessary for support of some image formats. Copy all these files to your system folder, e.g. for Windows XP 32 bit C:\WINDOWS\system32\. If you are not sure about your system folder, you should copy all these files to the same folder where the source code file (.pas or .pp) of your SDL 2.0 program is. If you are running on another platform (e.g. iOS, Linux, …), please check the link given in the table above for further information on SDL2_image installation. If you got SDL2_image set up, you need an image file eventually. You may try the following image files (dimensions: 200 x 200 pixels, formats: bmp, png and jpeg) but feel free to use any other image file you like.

Free Pascal meets SDL sample image bmp format Free Pascal meets SDL sample image png format Free Pascal meets SDL sample image jpg format

So you have three images with three different formats: BMP, PNG and JPEG/JPG. SDL2_image allows for even more formats, the complete format list is: ICO, CUR, BMP, GIF, JPG, LBM, PCX, PNG, PNM, TIF, XCF, XPM, XV, WEBP. This list covers the most spread and used image formats. Make sure to use one of these formats as source files for your in-game graphics. Good choices are PNG and JPEG.

Coding example with SDL2_image

And now let’s start with some code. I will show the code as a whole first and after that I will discuss it in smaller pieces from top to bottom until the “end.” 🙂

Well, here it is. This code will create a window of dimensions 500 x 500 pixels which is showing the “Freepascal meets SDL” image for two seconds. After that the window closes automatically. The following screenshot shows the result of the example program.

Result screenshot for chapter 4

Now let’s get into the code step by step. The light blue marked lines indicate the code lines which will be discussed afterwards. For better orientation in the code, the whole code is still shown.

Now the first eight lines are discussed. Well, the first line starts a Pascal program as usual. In contrast to the previous chapter, the uses clause is extended by SDL2_image. To be clear again, native SDL 2.0 has no support for different image formats, except for BMP image files. Although native SDL 2.0 allows for loading of BMP image files, it just allows for creation of SDL_Surfaces, but we would like to create SDL_Textures.

We define three variables in the var clause. In SDL 2.0 you can create as many windows as you like, and each window is adressed by its PSDL_Window variable. We just need one window here, let’s call it “sdlWindow1”. Keep in mind, this variable just defines the window’s properties, e.g. size, appearance, border, title name and so on. It is not related to any content of the window. To draw some content to the window, we need a renderer. The PSDL_Renderer is responsible for drawing all the content in a window, be it some lines, a flat background, a texture, a 3d object, or whatever. Let’s call our PSDL_Renderer “sdlRenderer”. Finally we need a texture variable to which we can load the image information of an image file. This will be “sdlTexture1” of PSDL_Texture type. All these variable types are pointer types which is indicated by the captial “P” at the beginning of the variable type’s name.

As any Pascal program the main program’s begin-end block is initiated by “begin”. The initilization of SDL 2.0 is started as discussed in detail in the last chapter by SDL_Init().

After successful initialization of SDL 2.0 a window is created by the SDL_CreateWindow function which is defined as follows:

SDL_CreateWindow(const title: PAnsiChar; x: SInt32; y: SInt32; w: SInt32; h: SInt32; flags: UInt32): PSDL_Window

First of all we decide for a title, let’s say “Window1”, but you may choose any other title you like.

The coordinate system of SDL 2.0

Next we decide for a place of your window by setting some x/y coordinates in pixels which determines the position with respect of the left upper corner of the window. This is an important rule in programming:

Very often the origin from where to count to place a window or object is always the left upper corner of your screen.

So if you choose (0/0) as coordinates the window’s left upper corner will be placed right at the left upper corner of your screen. The diagram below may help to understand this. You may try SDL_WINDOWPOS_CENTERED for each or both coordinates which will lead to a centered window with respect of the screen. If you choose SDL_WINDOWPOS_UNDEFINED you don’t care for the window’s position.

SDL2 window and coordinates diagram

SDL 2.0 windows and their properties

With w and h you decide for the width and height of the window in pixels. And finally you may set some flags which control the appearance of your window, e.g. SDL_WINDOW_FULLSCREEN will create a fullscreen window and SDL_WINDOW_BORDERLESS will create a borderless window. Below you find a table with all possible flags. For our purpose SDL_WINDOW_SHOWN is a good choice because we just create a shown window without any further restrictions. By the way, you may combine several flags by OR. The following table with description (taken from the official SDL 2.0 Wiki) shows all possible flags. Don’t hesitate to experiment with these flags.

SDL_WINDOW_FULLSCREEN fullscreen window
SDL_WINDOW_FULLSCREEN_DESKTOP fullscreen window at the current desktop resolution
SDL_WINDOW_OPENGL window usable with OpenGL context
SDL_WINDOW_SHOWN window is visible
SDL_WINDOW_HIDDEN window is not visible
SDL_WINDOW_BORDERLESS no window decoration
SDL_WINDOW_RESIZABLE window can be resized
SDL_WINDOW_MINIMIZED window is minimized
SDL_WINDOW_MAXIMIZED window is maximized
SDL_WINDOW_INPUT_GRABBED window has grabbed input focus
SDL_WINDOW_INPUT_FOCUS window has input focus
SDL_WINDOW_MOUSE_FOCUS window has mouse focus
SDL_WINDOW_FOREIGN window not created by SDL
SDL_WINDOW_ALLOW_HIGHDPI window should be created in high-DPI mode if supported (available since SDL 2.0.1)

Finally as for the initilization of SDL 2.0 we check for the window being nil, which means something has gone wrong and we should stop the program.

The SDL 2.0 renderer

Let’s proceed to the next lines of code to discuss:

We have to set up a so-called renderer. In computer graphics rendering means the process of synthesizing the final image on your screen from the inididual basic data structures, e.g. textures or 3d models. To set up the renderer the function

SDL_CreateRenderer(window: PSDL_Window; index: SInt32; flags: UInt32): PSDL_Renderer

is used.

First we need the renderer to know where to render the finished/rendered output. That will be “Window1” in our case. Next the shown function asks for a cryptic “index”. Well, each driver which is capable of rendering (e.g. OpenGL, Direct3d, Software,…) is indexed in SDL 2.0. In principle you could choose one specific driver here by choosing the corresponding index. Since we don’t know too much about the drivers at the moment the best choice is -1. -1 means that the first driver which is supporting the chosen flag(s) is chosen. Talking about flags, there are four flags you may choose:

1) SDL_RENDERER_SOFTWARE

2) SDL_RENDERER_ACCELERATED

3) SDL_RENDERER_PRESENTVSYNC

4) SDL_RENDERER_TARGETTEXTURE

You should always prefer SDL_RENDERER_ACCELERATED because this means the graphics board is responsible for rendering, SDL_RENDERER_SOFTWARE in contrast means, the CPU has to do the rendering. As discussed before for best performance the graphic board is the best choice for rendering/graphic related tasks. SDL_RENDERER_PRESENTVSYNC allows for so called vertical synchronization which means that the display of the rendered image is synchronized with the refresh rate of the monitor. SDL_RENDERER_TARGETTEXTURE allows for rendering to a texture. You may have noticed that none of these flags but 0 was used in the example code. This automatically gives priority to hardware accelerated renderers.

Creation of SDL_Texture and rendering in SDL 2.0

Now we load the image file to the SDL_Texture we called “sdlTexture1”. The function to do this is

IMG_LoadTexture(renderer: PSDL_Renderer; _file: PAnsiChar): PSDL_Texture

This function is provided by SDL2_image. Its prefix is IMG instead of SDL 2.0 for native SDL 2.0 functions. That function is why we needed to insert SDL2_image in the uses clause. The parameters of this function are a renderer, that is “sdlRenderer” for us, and as a second the absolute path to an image file, for us it is “C:\fpsdl.bmp”. Of course you may use any other directory to store/load the image or even use a different image. The function will recognize the image file’s format automatically, so feel free to load any of the allowed formats (again, ICO, CUR, BMP, GIF, JPG, LBM, PCX, PNG, PNM, TIF, XCF, XPM, XV, WEBP). If the loading fails, for instance you gave a wrong path as argument, this function will return nil.

Next we would like the successfully loaded image in “sdlTexture1” to be rendererd for which reason we pass it to the renderer by function

SDL_RenderCopy(renderer: PSDL_Renderer; texture: PSDL_Texture; srcrect: PSDL_Rect; dstrect: PSDL_Rect): SInt32.

At first this function asks for a renderer (and indirectly for the related window) to which we would like to copy the texture. In our case this will be “sdlRenderer” again. Next the texture to be copied to the renderer/window is required, this is “sdlTexture1” here. The last two parameters are named “srcrect” and “dstrect” and of type PSDL_Rect. PSDL_Rect is a SDL 2.0 predefined record to define rectangles, hence the name. I will not go into details about this here, but in the next chapter (chapter 5) we will learn more about PSDL_Rect, although it will be in another context. For simplicity we just use nil as argument here. This makes the function to pass the full texture to the renderer/window and stretching it to the dimensions of the window. So the 200 x 200 pixel image is strechted to 500 x 500 pixels, the latter being the width and height of the window. This function returns 0 on success and the negative error code on failure.

SDL_RenderPresent(renderer: PSDL_Renderer)

is the procedure to finally render to the screen. As argument you have to supply a renderer, which is “sdlRenderer” in our case. This procedure corresponds to the SDL_Flip() procedure when working with surfaces (check the diagram above). If you strictly work with textures, and I suggest so, your command is SDL_RenderPresent() though.

Freezing (delaying) a running program in SDL 2.0

SDL_Delay(time in milliseconds) or

SDL_Delay(ms: UInt32)

is a simple, yet powerful and important procedure to stop the program running for a certain time in milliseconds. 2000 milliseconds are two seconds. This is kind of a twin of Pascal’s Delay procedure. If you go for SDL 2.0 programming, you should prefer SDL_Delay.

Clean up the memory in SDL 2.0

Now the final lines of code are discussed. One of the most important rules for sophisticated programming is followed here:

Always clean up the memory on program finish.

For nearly any pointer type generated by SDL 2.0, there is a destroy procedure to remove it from memory. These procedures are comparable to Pascal’s dispose procedure to remove pointer types from memory. Make sure to destroy the objects in the opposite sequence of their generation. We first created a window, then a renderer and finally a texture. So now we go the opposite way, first destroy the texture, then the renderer and finally the window. The procedures definitions are:

SDL_DestroyTexture(texture: PSDL_Texture)

SDL_DestroyRenderer(renderer: PSDL_Renderer)

and

SDL_DestroyWindow(window: PSDL_Window).

After removing the objects from memory, SDL 2.0 has to be quit as seen in the previous chapter.

Wow, we finally made it. Congratulations, Chapter 4 is finished :-). Chapter 5 is waiting though.

← Chapter 3 | Chapter 5 →

Leave a Reply