Rectangles, Image Movement and Scaling

Last updated on November 20th, 2021

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.

Creative Commons License This image by https://www.freepascal-meets-sdl.net is licensed under a Creative Commons Attribution 4.0 International License.
The result of the example program (500×500 px window). A part of the full image is stretched in the background while the full image is also squeezed into an area above the word “Free”.

For comparison, here is the original 200×200 px image.

Free Pascal meets SDL sample image bmp format
Original image (200×200 px).

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

  1. nearest or 0
    • nearest pixel sampling
  2. linear or 1
    •  linear filtering
    • support by OpenGL and Direct3D
  3. 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.

← previous Chapter | next Chapter →

3 thoughts on “Rectangles, Image Movement and Scaling

  1. Hi there!

    Sorry I didn’t really understand how to make the sprites move, can you help me?
    Thank you…

    1. 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

Leave a Reply

Your email address will not be published. Required fields are marked *