Chapter 6 – Resolution, Viewport, Image manipulation

In the last two chapter you learned much about basic procedures to display images and draw to the screen. Now let’s proceed to more sophisticated possibilities. Be aware that some of the functions presented here are very important for game and application development. Let’s right jump into the code.

As you may notice there are several blocks of code here, and we will discuss one after another since each of them treats a different new feature. The code appears to be long. Actually you will find many known functions again and again :-).

The final result should look and behave like this: The loops of the code are worked through one after another and always the fpsdl.bmp is manipulated. First you see a colour change of the basic image, then a fade out effect, then change of colours in a clipped rectangle, then in a changed viewport, then with a changed logical size resolution and finally two cycles which demonstrate rotation and scaling of the basic image. The following image shows a screenshot of the rotation loop.

Result screenshot for chapter 6

And now let’s start with the initial lines of code.

The program is called “Chapter6_SDL2”. We will need unit SDL2_image additionally to SDL2 again to load a texture from an image file as demonstrated in chapter 4. We declare a counter variable “i” of Pascal type integer. Then we declare the known variables for the window, the renderer and the texture, a rectangle and a point as known from previous chapters. New is the “sdlFlip” variable of PSDL_RendererFlip type. We go into detail on this later. It is needed to allow rotations of textures.

After variable declaration the program’s main block starts. As seen in previous chapters, SDL 2.0 is initialized, a window is created with 500 x 500 pixels dimensions, a renderer is created and the texture is created by loading an image file into it. Absolutly nothing is new here so I will right proceed to the next lines of code.

Colour modulation in SDL 2.0

The two code blocks shown here demonstrate two new functions which are quite related. The first code block starts with a for-loop which counts from 0 to 255. Within the loop the renderer is cleared by SDL_RenderClear(). The function SDL_SetTextureColorMod() allows to modulate a texture’s colour information on the fly.

SDL_SetTextureColorMod(texture: PSDL_Texture; r: UInt8; g: UInt8; b: UInt8): SInt32

The first parameter asks for the texture whos colours you would like to modulate. What does modulation mean at all? Well, each pixel’s colour information on the screen consists of three (actually four, but let’s neglect alpha component for now) components: red, green and blue (RGB). A fully red pixel is corresponds to RGB (255/0/0), which corresponds to 100% red, 0% green and 0% blue. If you use SDL_SetTextureColorMod() and change the value of red from 255 (100%) to 128 (50%) it means that every pixel’s red colour value is lowered by 50% relatively to the value before. If the red component had a value of 255 (100%) before it will end up with 128 (50%) but if the red component had a value of 128 in the original image (which is 100% then) it will end up with 64 (50%), and so on. The relative values always refer to the values in the original image. In the example the loop is changing the red and green component from 100% (fully red and green) successivly to 0% (black). The blue component is successivly raised from 0% (black) to full blue at the same time. After each modulation the texture is passed to the renderer and presented to the screen. To ensure that things are not running too fast each cycle is delayed by 20 milliseconds. To restore colour information on leaving the loop, the RGB components are modulated to 100% once after the loop.

The second part of the marked code looks very similar to the first one. This time we don’t modulate one of three RGB colour components but the alpha component to achieve a fade out effect. But if we fade out the image there has to be some solid background without transparency which is achieved by the known SDL_SetRenderDrawColor() function with RGBA values of 0/255/255/255 (opaque cyan background). Before playing around with transparency, as also discussed in the last chapter, the blend mode has to be changed to SDL_BLENDMODE_BLEND by SDL_SetTextureBlendMode() since it is set to SDL_BLENDMODE_NONE by default. The loop counts from 0 to 255. The screen is cleared first, then SDL_SetTextureAlphaMod() is used to modulate the alpha component of the texture.

SDL_SetTextureAlphaMod(texture: PSDL_Texture; alpha: UInt8): SInt32

This works in a very similar way as the function to modulate the colours. Both functions return 0 on success and -1 if the texture is not valid or colour/alpha modulation is not supported. After leaving the loop the alpha component is restored by calling SDL_SetTextureAlphaMod() again with value 255.

Notice, for modulation of colours and the alpha value you need two different functions to achieve this.

Clipping rectangle and viewport in SDL 2.0

Let’s proceed to the next lines of code.

The renderer usually renders to the full area of the window. In certain cases you would want to render to a specific part of the window. Therefore you can define a rectangle of PSDL_Rect type, which defines the dimensions of the area to render to. The function SDL_RenderSetClipRect() finally is clipping the area for rendering. Notice though, this function does not re-scale or fit the texture to the clipping rectangle.

SDL_RenderSetClipRect(renderer: PSDL_Renderer; rect: PSDL_Rect): SInt32

You just have to specifiy the renderer and the rectangle. The SDL_RenderSetClipRect() function returns 0 on success and -1 on error.

The loop subsequently shown is the completely same loop from the previous lines of code and modulates the colours. After the loop is finished the clipping has to be turned off which is achieved by nil as argument for the clipping rectangle. Well, actually the clipping rectangle is reset to the window’s dimensions. The colour modulation has to be reset, too.

Maybe you would want to draw to a specific area, but this time this area should behave like the whole window (windowed mode) or screen (fullscreen mode), that’s a viewport. Actually if you don’t specifiy a certain viewport, the window area in windowed mode or in fullscreen mode the full screen is set as viewport. To create a viewport with certain dimensions, its location and dimensions are again stored in a PSDL_Rect. The function SDL_RenderSetViewport() is used to create the actual viewport then. It returns 0 on success and -1 on failure.

SDL_RenderSetViewport(renderer: PSDL_Renderer; const rect: PSDL_Rect): SInt32

You have to specify the renderer and the rectangle which determines the viewport. In the example code the viewport covers 190 x 190 pixels. After that all rendering actions related to this renderer refer to this viewport. The viewport’s coordinates are relative to the SDL 2.0 window. So if you draw in the viewport, the coordinate (0/0) corresponds to the left upper corner of the viewport. The right lower corner corresponds to the width and height. This is similar to the relation between the coordinates of a SDL 2.0 window and the screen. This diagram may help to understand the relation:

SDL2 window and viewport coordinates diagram

After creation of the new viewport the loop known from the previous code appears again. Within the viewport the colour modulation is done. Finally, on finishing the loop the vierport is reset to the target’s dimension, which means the window’s dimensions in our case.

The logical size resolution in SDL 2.0

Now an awesome feature of SDL 2.0 will be presented which is fixing a big major problem for many programmers. It concerns the resolution of the application or game.

If you create a game, often there are a lot of questions you just can solve partly by planning. The main question is: What exact resolution are the users going to use to play the game? Do their monitors support the resolution you decide for? Do the user/gamer use a wide screen monitor? And so on. Depending on the answers to these questions you may choose for sprite/image file resolutions. If you are going for a target resolution of 640 x 480 (VGA), you will choose less detailed source images as if you go for 1024 x 768 (XGA), or even widescreen 1680 x 1050 (WSXGA+) or any other high detail resolution. If every monitors/graphic boards would be capable of displaying all possible resolutions, this wouldn’t be much of a problem. The system would just switch to the necessary resolution and voila the game is running and looking like expected. Unfortunately though, you cannot expect a system whose maximum resolution allows for XGA to display WSXGA+ resolution. Also, even if the system is capable of displaying very high resolutions, there is no guarantee that it will be able to switch down to VGA resolution.

Furthermore, if you optimize a game for VGA resolution, you will choose the sprites/images according to fit into a 640 x 480 pixels screen. Lets say you are doing a racing game. You choose a car image of 64 pixels width and 32 pixels height. This fills a good amount of the screen in VGA resolution. In WSXGA+ resolution the car’s width and height will appear less than half, it will appear to be tiny. SDL 2.0 provides a solution to circumvent many of the troubles related to resolution settings.

SDL_RenderSetLogicalSize(renderer: PSDL_Renderer; w: SInt32; h: SInt32): SInt32

This function returns 0 on success and the negative error code on failure. It will make your game appear as much as possible as you planned it to appear. In other words you set a device independent resolution for rendering. It will make use of scaling functions internally to fit the target resolution to the actual screen/device resolution. If you want to display a game in VGA (640 x 480) resolution on a XGA (1024 x 768) resolved screen, SDL_RenderSetLogicalSize() will scale up everything as necessary to fill the screen. Even if the ration of the device is different, for example you are trying to have a 4:3 ratio resolution on a wide screen 16:9 monitor, this function will just put some bars at appropriate places and fit the targeted resolution to the device resolution as good as possible. This is demonstrated in the example code. A target resolution of 640 pixels width x 480 pixels height (4:3 ratio) should be achieved in a device (window) of width x height of 500 x 500 pixels (1:1 ratio). SDL_RenderSetLogicalSize() now resizes the 640 x 480 pixels resolution target by keeping the ratio of 4:3 to 500 x 375 pixels. Of course this means two bars of a combined height of 125 pixels necessary.

Rotation and scaling of textures in SDL 2.0

To perform a rotation we need to specify around which point the texture should be rotated. That is why we need a “sdlPoint1” of type SDL_Point here. We set its x and y coordinate to 250 pixels each because it should be performed right around the center of the window/vierport. There is a second argument we need to prepare in order to perform a rotation, and that is “sdlFlip” of type SDL_RenderFlip. A SDL_RenderFlip allows for three values shown in the table below:

SDL_FLIP_NONE Don’t flip the texture.
SDL_FLIP_HORIZONTAL Flip the texture horizontally.
SDL_FLIP_VERTICAL Flip the texture vertically.

There is a strong relationship between flipping or mirroring and rotating, though these two manipulations are not the same. By the way you can also combine horizontal and vertical flipping by OR’ing both values. If you combine these two flipping operations the result equals a 180° rotation. In the example code we choose SDL_FLIP_NONE since we just want to demonstrate the rotation ability.

A for-loop is initialized which runs from 0 to 359. We would like to rotate the texture about 360° in steps of 1°. For each step the screen is cleared by SDL_RenderClear. Instead of rendering the texture by the SDL_RenderCopy() function, the SDL_RenderCopyEx() function is used. It allows for rotation and flipping and is declared as:

SDL_RenderCopyEx(renderer: PSDL_Renderer; texture: PSDL_Texture; const srcrect: PSDL_Rect; dstrect: PSDL_Rect; angle: Double; center: PSDL_Point; flip: TSDL_RendererFlip): SInt32

The first four parameters are well known from the SDL_RenderCopy() function. For SDL_RenderCopyEx() the renderer, the texture to be copied, the source and the destination rectangles are needed, too. In case we want to copy the whole texture to the whole render context (window or screen) we can set the source and destination rectangles to be nil. Actually the three new parameters are straight forward. You set the angle about which the texture should be rotated, followed by the center about which the texture should be rotated (at the destination render context) and finally the flip option. Remark: In earlier versions of Tim Blume’s headers, the argument flip was expected to be PSDL_RendererFlip, so the argument would be a pointer (sdlFlip), now it expects the pointer content, hence sdlFlip^. – Here is why, if you are interested.

By the way, if you set nil as argument for the flip parameter (the last parameter), the function automatically expects you to rotate around the center (half width and height of destination context). Setting the x and y coordinate to 250 pixels as in the example code if the window has a size of 500 x 500 pixels can actually be achieved more easily by just setting nil as flip argument. This function returns 0 on success or the negative error code.

Scaling of images is kind of simple…. actually…. (you will see soon, there is a strange issue about that)

Let’s start with a for loop counting from 500 to 1. Every cycle the screen is cleared by SDL_RenderClear(), then the scaling factors are set by SDL_RenderSetScale() declared as

SDL_RenderSetScale(renderer: PSDL_Renderer; scaleX: Float; scaleY: Float): SInt32

This looks simple, just use the SDL_RenderSetScale() function to re-scale the texture and that’s it. But check the example code: Actually it is expected to re-scale the texture from 500 x 500 pixels to finally 1 x 1 pixels. So it should continously shrink. Instead it shrinks in a strange behavour, especially when the count is lower than 250 x 250 pixels. Maybe someone has a suggestion what happens here, let me know.

Nevertheless, you should get the idea behind this function. If it works properly it is a great function. Better, let’s quickly proceed to the next lines of code.

Finally, let’s free all the reserved memory and shut SDL 2.0 down. We made it :-).

Let’s have a look at Chapter 7.

← Chapter 5 | Chapter 7 →

Leave a Reply