Screens and Images are rectangular, so this shape has a special importance to SDL2 and graphics programming in particular.
Rectangles: TSDL_Rect and PSDL_Rect in SDL 2.0
Often functions require an argument of PSDL_Rect type. This is the pointer counterpart to TSDL_Rect. It is declared as follows.
PSDL_Rect = ^TSDL_Rect; TSDL_Rect = record x,y: SInt32; w,h: SInt32; end;
Simply, this record describes a rectangle, hence the name. The variables x and y correspond to the x/y coordinates of the left upper corner of the rectangle, related to the origin 0/0 which is the left upper corner of, e.g. a texture, window,… The variable w is the width and h the height of the rectangle. That’s it. The next step is to define the rectangle by assign some values for x, y, w and h.
If you use PSDL_Rect, you free the required memory by Pascal’s new procedure as you would for any simple record pointer.
Using Rectangles for Movement and Scaling
The following code demonstrates the basic principle how to achieve the impression of movement of images (sprites) and how scaling works. You will be impressed how simple it actually is.
program SDL_RectanglesScaling;
uses SDL2;
var
sdlWindow1: PSDL_Window;
sdlRenderer: PSDL_Renderer;
sdlSurface1: PSDL_Surface;
sdlTexture1: PSDL_Texture;
sdlRectangle: TSDL_Rect;
begin
//initilization of video subsystem
if SDL_Init(SDL_INIT_VIDEO) < 0 then Halt;
if SDL_CreateWindowAndRenderer(500, 500, SDL_WINDOW_SHOWN, @sdlWindow1, @sdlRenderer) <> 0
then Halt;
// set scaling quality
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, 'nearest');
// create surface from file
sdlSurface1 := SDL_LoadBMP('fpsdl.bmp');
if sdlSurface1 = nil then
Halt;
// load image file
sdlTexture1 := SDL_CreateTextureFromSurface(sdlRenderer, sdlSurface1);
if sdlTexture1 = nil then
Halt;
// prepare rectangle
sdlRectangle.x := 12;
sdlRectangle.y := 25;
sdlRectangle.w := 178;
sdlRectangle.h := 116;
// render texture
SDL_RenderCopy(sdlRenderer, sdlTexture1, @sdlRectangle, nil);
SDL_RenderCopy(sdlRenderer, sdlTexture1, nil, @sdlRectangle);
// render to window for 2 seconds
SDL_RenderPresent(sdlRenderer);
SDL_Delay(2000);
// clear memory
SDL_DestroyTexture(sdlTexture1);
SDL_FreeSurface(sdlSurface1);
SDL_DestroyRenderer(sdlRenderer);
SDL_DestroyWindow (sdlWindow1);
//closing SDL2
SDL_Quit;
end.
We will get this as a result.
For comparison, here is the original 200×200 px image.
Let’s disect the code.
var
sdlWindow1: PSDL_Window;
sdlRenderer: PSDL_Renderer;
sdlTexture1: PSDL_Texture;
sdlRectangle: TSDL_Rect;
In the var clause we declare the known variables for window, renderer and a texture. Also we have a new variable of TSDL_Rect type.
// prepare rectangle
sdlRectangle.x := 12;
sdlRectangle.y := 25;
sdlRectangle.w := 178;
sdlRectangle.h := 116;
After initializing SDL2 and setting up the window and renderer as known, the rectangle is getting some values. It just encloses the words “Free Pascal meets SDL” in the original image (see above).
Scaling in SDL2
Scaling Quality
Right before creating the surface and texture, there is this line in code.
// set scaling quality
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, 'nearest');
It sets the render quality. It has to be set before creating the texture. The SDL_SetHint(hint name, hint value) function is no specific function for setting scaling quality, but here we use it for exactly that. Possible values are
- nearest or 0
- nearest pixel sampling
- nearest pixel sampling
- linear or 1
- linear filtering
- support by OpenGL and Direct3D
- linear filtering
- best or 2
- anisotropic filtering
- support by Direct3D.
All of the values have to be set as string values, so ‘nearest’ or ‘0’. Here is a comparision of the nearest- and the linear filter.
The anisotropic filter doesn’t do anything for me, even if I used Direct3D.
Scaling by using Rectangles
// render texture
SDL_RenderCopy(sdlRenderer, sdlTexture1, @sdlRectangle, nil);
SDL_RenderCopy(sdlRenderer, sdlTexture1, nil, @sdlRectangle);
At this point happens the magic that leads to the resulting image. By the way, since the SDL_RenderCopy() function requires the rectangle arguments to be of PSDL_Rect, we use the @-operator (pointer operator) here.
SDL_RenderCopy(sdlRenderer, sdlTexture1, @sdlRectangle, nil);
This means, copy the area described by “sdlRectangle” from the source (“sdlTexture1” here) to the whole area (because of nil value) of the destination, hence the window.
Since the window has a width and height of 500 px each, the source rectangle just a width of 178 px and a height of 116 px, SDL2 automatically scales the image to fit into the larger (or smaller) dimensions of the destination.
SDL_RenderCopy(sdlRenderer, sdlTexture1, nil, @sdlRectangle);
This means, copy the whole source (because of nil value) to the area described by “sdlRectangle”. The source is the 200×200 px image, which has to squeezed to the 178×116 px rectangle at position (12/25). This is just what you see in the resulting image (above) where the whole image is squeezed into this area.
Movement of Images (Sprites)
Although not covered directly by this code example, you get the picture how movement works. Every frame you adjust the (x/y) coordinates of the rectangle for the destination to bring the sprite about to move.
After cleaning up the memory the program finishes.
Hi there!
Sorry I didn’t really understand how to make the sprites move, can you help me?
Thank you…
Hi there,
well, basically you set up a loop and at its end you always render everything. For every loop-run you change the x- and y-value of the destination rectangle of the sprite (sdlRectangle in the example code). The destination rectangle should be the last argument in the render function: SDL_RenderCopy(sdlRenderer, sdlTexture1, nil, @sdlRectangle).
Best regards
Matthias
Ukrainian translation avaliable here https://lazarus-games.blogspot.com/p/8.html