For many applications and games drawing operations are essential. The most famous case is, if you create your own graphical user interface (GUI). This means there are buttons (and other GUI elements) of dynamic dimensions and coloring in your application. Explanation of another important case, especially in game development, can be read about here.
Supported Primitives: Points, Lines, Rectangles
SDL3 supports natively the drawing of
- Points
- Lines
- Rectangles (outlines)
- Filled Rectangles.
Drawing in SDL3
Now lets jump right into the code:
program SDL_DrawingPrimitives;
uses
SDL3;
var
i: Integer;
sdlWindow1: PSDL_Window;
sdlRenderer: PSDL_Renderer;
sdlRect1: TSDL_FRect;
sdlPoints: array[0..499] of TSDL_FPoint;
begin
//initilization of video subsystem
if not SDL_Init(SDL_INIT_VIDEO) then Halt;
sdlWindow1 := SDL_CreateWindow('Window1', 500, 500, 0);
if sdlWindow1 = nil then Halt;
sdlRenderer := SDL_CreateRenderer(sdlWindow1, nil);
if sdlRenderer = nil then Halt;
//render and show cleared window with grey background color
SDL_SetRenderDrawColor(sdlRenderer, 50, 50, 50, SDL_ALPHA_OPAQUE);
SDL_RenderClear(sdlRenderer);
SDL_RenderPresent(sdlRenderer);
SDL_Delay(1000);
//render and show a red line
SDL_SetRenderDrawColor(sdlRenderer, 255, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderLine(sdlRenderer, 10, 10, 490, 490);
SDL_RenderPresent(sdlRenderer);
SDL_Delay(1000);
//render and draw yellow points diagonally with distance between each other
SDL_SetRenderDrawColor(sdlRenderer, 255, 255, 0, SDL_ALPHA_OPAQUE);
for i := 0 to 47 do
SDL_RenderPoint(sdlRenderer, 490-i*10, 10+i*10);
SDL_RenderPresent(sdlRenderer);
SDL_Delay(1000);
//prepare, render and draw a series of random connected black lines
Randomize;
for i := 0 to 499 do
begin
sdlPoints[i].x := Random(500);
sdlPoints[i].y := Random(500);
end;
SDL_SetRenderDrawColor(sdlRenderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderLines(sdlRenderer, @sdlPoints, 500);
SDL_RenderPresent(sdlRenderer);
SDL_Delay(1000);
//prepare, render and draw a green rectangle outline at upper-right corner
sdlRect1.x := 260;
sdlRect1.y := 10;
sdlRect1.w := 230;
sdlRect1.h := 230;
SDL_SetRenderDrawColor(sdlRenderer, 0, 255, 0, SDL_ALPHA_OPAQUE);
SDL_RenderRect(sdlRenderer, @sdlRect1);
//render and draw the rectangle with 50% opacity at lower-left corner
sdlRect1.x := 10;
sdlRect1.y := 260;
SDL_SetRenderDrawBlendMode(sdlRenderer, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(sdlRenderer, 0, 255, 0, 128);
SDL_RenderFillRect(sdlRenderer, @sdlRect1);
SDL_RenderPresent(sdlRenderer);
SDL_Delay(1000);
//clean memory
SDL_DestroyRenderer(sdlRenderer);
SDL_DestroyWindow (sdlWindow1);
//shut down SDL3
SDL_Quit;
end.
Wow, that looks like a big load of new functions, but I promise, drawing in SDL3 is very simple. Running the program should result in something like this (it’s alright, nothing went wrong here 😉

Now, let’s have a closer look to the first lines of code.
program SDL_DrawingPrimitives;
uses
SDL3;
var
i: Integer;
sdlWindow1: PSDL_Window;
sdlRenderer: PSDL_Renderer;
sdlRect1: TSDL_FRect;
sdlPoints: array[0..499] of TSDL_FPoint;
begin
//initilization of video subsystem
if not SDL_Init(SDL_INIT_VIDEO) then Halt;
sdlWindow1 := SDL_CreateWindow('Window1', 500, 500, 0);
if sdlWindow1 = nil then Halt;
sdlRenderer := SDL_CreateRenderer(sdlWindow1, nil);
if sdlRenderer = nil then Halt;
The program is called “SDL_DrawingPrimitives” and uses the SDL3 unit. In the var clause we have a counter variable “i” of native Pascal type integer we need later. We need a window and a renderer and call them “sdlWindow1” and “sdlRenderer” as known from the previous chapters. And in the bottom part of the code snippet they are created as known from last chapter and SDL3 is initialized.
The interesting and new part about this code snippet is in the var clause.
About Rectangles and Points in SDL3
Next we declare a variable “sdlRect1” which is of type TSDL_FRect. The same is true for “sdlPoints” which is an array of 500 elements of type TSDL_FPoint.
TSDL_FRect is a record which is used to describe rectangles in SDL3 by four values:
- x: x-coordinate of the rectangle
- y: y-coordinate of the rectangle
- w: width of the rectangle
- h: height of the rectangle.
The F indicates that the values are of float point type (Pascal: Single). This allows for sub-pixel precision for coordinates and dimensions.
TSDL_FRect has an older brother, TSDL_Rect, which only allows integer values. Other than that they are identical.
Unsurprisingly, TSDL_FPoint describes a point in SDL3 by just two values:
- x: x-coordinate of the point
- y: y-coordinate of the point.
By definition, points have no dimension, so that’s it. Again, the F indicates their values being of float point type. And also TSDL_FPoint has an older counter-part TSDL_Point with integer values instead.
Colours and RGB(A) Notation in SDL3
//render and show cleared window with grey background color
SDL_SetRenderDrawColor(sdlRenderer, 50, 50, 50, SDL_ALPHA_OPAQUE);
SDL_RenderClear(sdlRenderer);
SDL_RenderPresent(sdlRenderer);
SDL_Delay(1000);
Now we have something new here: SDL_SetRenderDrawColor(renderer, red value, green value, blue value, alpha value) sets a colour for drawing operations, just as if you choose for a pencil colour to draw something. This function doesn’t draw anything though.
First you need to set the renderer for which this drawing colour is meant. After that you have to set the colours red, green, blue and the alpha value. They can range from 0 to 255 (integer values only). Think in terms of 255 being 100% of that colour and 0 being 0%. As a start let’s neglect the alpha value.
If you like to have a red colour, you need to set the value for the red colour high, e.g. 100%, the maximum value is 255, and since you don’t want to mix in green and blue, they get the minimum value of 0 (hence 0%). Setting up red therefore corresponds to 255/0/0 in terms of r/g/b. For comparison, 0/255/0 leads to green, 0/0/255 to blue, 255/255/255 is white and 0/0/0 is black, and so on. In the example code we used 50/50/50 which leads to a dark grey (mixing up red, green and blue of the same ratio additively). With these three values you can generate every colour possible. For more information on colours in computer graphics, go here.
The Alpha Value
The alpha value determines the transparency. 255 means fully opaque and 0 means full transparency. This is very important for blend effects in computer graphics and will be demonstrated later on in the example. By the way, instead of 255 you could use SDL_ALPHA_OPAQUE.
Setting up a Background in SDL3
The function SDL_RenderClear(renderer) is for clearing the screen with the drawing colour. As argument you just need to tell the renderer. It is simple as that :-). Since we set the drawing colour to dark grey before, the screen will be cleared with that colour.
The cleared screen will be shown for one second by SDL_RenderPresent() and SDL_Delay(). These two procedures are known from the previous chapter.
Drawing Lines and Points in SDL3
//render and show a red line
SDL_SetRenderDrawColor(sdlRenderer, 255, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderLine(sdlRenderer, 10, 10, 490, 490);
SDL_RenderPresent(sdlRenderer);
SDL_Delay(1000);
Now we change the drawing colour by SDL_SetRenderDrawColor() to red and use SDL_RenderDrawLine(renderer, x1, y1, x2, y2) to draw a simple line, where x1, y1, x2 and y2 refers to coordinate pairs, hence (x1/y1) and (x2/y2). The first pair is (10/10), the second pair is (490/490). The red line should be drawn from coordinate (10/10) to coordinate (490/490). But where are these coordinates exactly in the window?
The Coordinate System in SDL3
This rule applies:
The origin from where to place an object is always the left upper corner of your screen or window.
In a SDL3 window (or screen if fullscreen) the cooodinate (0/0) refers to the left upper corner. The diagram below may help to understand this.


The window is placed with respect to the left upper corner of the screen.
The coordinate (10/10) means to start at the point ten pixels to the right and ten pixel to the bottom relative to the origin (0/0). Thus, the coordinate (490/490) for the second point is nearly at the right bottom edge of the window and will lead to a diagonal line across. This diagonal line will be 10 pixels short with respect to the window edges.
After that again we ask to render this line to the screen by SDL_RenderPresent() and wait one second by SDL_Delay().
//render and draw yellow points diagonally with distance between each other
SDL_SetRenderDrawColor(sdlRenderer, 255, 255, 0, SDL_ALPHA_OPAQUE);
for i := 0 to 47 do
SDL_RenderPoint(sdlRenderer, 490-i*10, 10+i*10);
SDL_RenderPresent(sdlRenderer);
SDL_Delay(1000);
Now we change the colour to yellow and draw some points by the function SDL_RenderDrawPoint(renderer, x, y). It is nearly identical to SDL_RenderDrawLine() but instead of four coordinates you need just two coordinates where the point should be drawn.
I thought it would be nice to have more than just one point to be drawn, so the function is used in a for-loop to draw altogether 48 points. Here we need the counter variable “i”. Maybe you can guess from the code where the points are and how they are arranged, if not, just run the code ;-). Finally the result is rendered to the screen by SDL_RenderPresent() and the program waits one second by SDL_Delay().
Spread Points Randomly using Arrays
//prepare, render and draw a series of random connected black lines
Randomize;
for i := 0 to 499 do
begin
sdlPoints[i].x := Random(500);
sdlPoints[i].y := Random(500);
end;
SDL_SetRenderDrawColor(sdlRenderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderLines(sdlRenderer, @sdlPoints, 500);
SDL_RenderPresent(sdlRenderer);
SDL_Delay(1000);
Randomize is a Free Pascal procedure (from system unit) to initilize the random number generator. Imagine this as shaking the dice.
Free Pascal’s Random(max. number – 1) function generates random numbers between 0 and the number which is used as argument substracted by one. So we generate 500 times a random x and y value between 0 and 499 and save them into the 500 SDL_Point records of the “sdlPoints” array.
Colour black is set by SDL_SetRenderDrawColor().
To draw the lines which connects the points in the array “sdlPoints” we use SDL_RenderDrawLines(renderer, pointer to point array, number of array elements). First you need to set the renderer, then just a pointer to an array of TSDL_FPoint and finally the number of points. The latter should be consistent with the array. So, if your array has 500 elements, the count should be 500 at maximum. Notice how we are not using a loop here to draw all 500 lines by calling a certain draw function 500 times, which is a slow solution. Instead we just pass an array of points once. This way we save a lot of time at runtime, especially if you think of a real application where even more lines have to be drawn. There are similar functions for points, rectangles and filled rectangles. They are not used in the example but it may be interesting to know, so here they are:
- SDL_RenderDrawPoints(renderer, pointer to points array, number of array elements)
- SDL_RenderDrawRects(renderer, pointer to rectangles array, number of array elements)
- SDL_RenderFillRects(renderer, pointer to rectangles array, number of array elements)
As a hint, try replacing SDL_RenderDrawLines by SDL_RenderDrawPoints.
Often, you will have functions in SDL3, where you can use batches of something
Drawing Rectangles in SDL3
//prepare, render and draw a green rectangle outline at upper-right corner
sdlRect1.x := 260;
sdlRect1.y := 10;
sdlRect1.w := 230;
sdlRect1.h := 230;
SDL_SetRenderDrawColor(sdlRenderer, 0, 255, 0, SDL_ALPHA_OPAQUE);
SDL_RenderRect(sdlRenderer, @sdlRect1);
The drawing colour is set to green by SDL_SetRenderDrawColor() and a rectangle’s outline is drawn by SDL_RenderDrawRect(renderer, pointer of rectangle). Prior to using a rectangle we define it at position (260/10) with 230 pixels width and 230 pixels height.
It requires the renderer, which is “sdlRenderer” for us and a PSDL_Rect , thus we use the @-operator for the declared rectangle of TSDL_FRect type to get its pointer value. Notice, we neither render the result to the screen now nor do we delay here. We want a second rectangle immediately! 🙂
//render and draw the rectangle with 50% opacity at lower-left corner
sdlRect1.x := 10;
sdlRect1.y := 260;
SDL_SetRenderDrawBlendMode(sdlRenderer, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(sdlRenderer, 0, 255, 0, 128);
SDL_RenderFillRect(sdlRenderer, @sdlRect1);
SDL_RenderPresent(sdlRenderer);
SDL_Delay(1000);
We change the rectangles (x/y) coordinate for the second rectangle to (10/260), but keep its width and height the same. What we are looking for is a filled rectangle that has some transparency. Until now we always used SDL_ALPHA_OPAQUE (opaque) as alpha value. We keep the colour to draw the second rectangle to green by SDL_SetRenderDrawColor(). Notice that the fourth value is 128 (half-transparent) instead of the opaque constant. So everything behind the green rectangle should therefore shine through. To generate a filled rectangle SDL_RenderFillRect(renderer, pointer to rectangle) is used.
The Blend Mode in SDL3
But to be honest, even if you change the alpha value it will be opaque, unless you change the blend mode, which we did by SDL_SetRenderDrawBlendMode(renderer, blend mode). The default settings don’t allow for blending. We need to change this to be able to use the alpha value as desired.
First the renderer for which the blend mode has to be set is chosen. In our case it is “sdlRenderer” again. Then there are seven blend modes available in SDL3 and they determine how the new colours are combined with the already present colours at a certain pixel coordinate. Here is an overview of the four most important ones:
- SDL_BLENDMODE_NONE
- no blending
- dstRGBA = srcRGBA
- the final pixel colour is just the new pixel colour
- SDL_BLENDMODE_BLEND
- alpha blending
- dstRGB = (srcRGB * srcA) + (dstRGB * (1-srcA))
- dstA = srcA + (dstA * (1-srcA))
- the final pixel colour is an addition of the previous pixel colour the new pixel colour; the ratio is determined by the alpha value
- SDL_BLENDMODE_ADD
- additive blending
- dstRGB = (srcRGB * srcA) + dstRGB
- dstA = dstA
- the final pixel colour is an addition of the previous pixel colour and the new pixel colour; only the previous pixel colour is affected by the alpha value though
- SDL_BLENDMODE_MOD
- color modulate
- dstRGB = srcRGB * dstRGB
- dstA = dstA
- the final pixel colour is a multiplication of the previous and the new pixel colour
We are looking for (common) alpha blending, so we use SDL_BLENDMODE_BLEND as argument for the blend mode.
After doing so the result is rendered to the screen by SDL_RenderPresent() and shown for one second by SDL_Delay(). Both rectangles appear at the same time.
//clean memory
SDL_DestroyRenderer(sdlRenderer);
SDL_DestroyWindow (sdlWindow1);
//shut down SDL3
SDL_Quit;
Finally all the memory reserved for points, the rectangle, the renderer and the window is free’d and SDL3 shut down by SDL_Quit.
Congratulations, you just finished this chapter :-).