Tag Archives: sdl 1.2

Chapter 4a: Primitives, Image rotation/zooming, Framerate (JEDI-SDL)

This is an SDL 1.2 chapter. SDL 1.2 is obsolete since it has been replaced by SDL 2.0. Unless you have good reasons to stay here you may prefer to go for the modern SDL 2.0 :-).

In the previous chapter it was shown how to draw one pixel to the screen. Of course you could now develop routines to draw primitives from this single pixel manipulation but to make life easier someone (Andreas Schiffler) did this already for you. And furthermore the JEDI project already translated it for you to use it in Free Pascal :).

Maybe you remember the Turbo Pascal GRAPH unit which allowed to draw simple primitives like lines, squares, rectangles, circles, polygons and so on. Exactly this part is now fulfilled by the SDL_GFX unit in a SDL environment. So from now on you can also draw sprites directly to the surfaces and don’t have to draw them externally in a painting program. If you choose to draw sprites directly with this set of primitives or create them externally depends on your needs and preferences. Of course you can combine both features, load up an image first, then manipulate it further by the features shown in this chapter.

As an example you could imagine a game where you have game objects of the same type for each player which only differ by the same coloured elements. You now could create several object images with the possible different colours externally. However, if you want to provide 100 different colours you have to load up the object’s image 100 hundred times into your painting program, colour it, save it. You need 100 times the colour images space on hard disk. – With the provided functions you could just create ONE image of the units with a “wild card colour”, let’s say white. Now you just have to write a function which recognizes white areas on the image and colour it in 100 different colours, save it to hard disk (same space usage as first method) or in heap (no further hard disk space is needed!). Especially useful is the SDL_GFX unit if the white areas are of shape rectangle, circle, polygon or just a single pixel.

Before starting with SDL_GFX be aware that it is distributed by a third party developer, Andreas Schiffler. Unfortunately he is not distributing the pre-compiled shared library (DLL file) but instead he provides the source code only. So you are in need of compiling this file yourself or download the compiled SDL_gfx.dll file (zipped) (version 2.0.19) I compiled for you. The source code is written in C++ so you can’t use the Free Pascal compiler to do the compilation yourself. In the first part of this chapter I will deal with the compilation of your own DLL. The provided a pre-compiled SDL_gfx.dll (zipped) was compiled by me under 32 Bit Windows XP Professional on an AMD processor and usage might cause problems for 64 Bit systems and differing operating systems and processors. Furthermore it might be outdated, it’s version 2.0.19. In all these cases you should really consider to compile your own DLL file if the provided file isn’t working for you. If you use the pre-compiled DLL file you can skip the compilation procedure, just go on reading after the 15 step procedure for compilation below.

For compilation we need several things. We need a free C++ compiler to compile the DLL file from the source code. Of course we need the source code of SDL_GFX itself. Furthermore we need the SDL Development Library for Windows. The following table shows the names of the most recent files and their location.

Software Version Source Description
MinGW C++ Compiler 5.1.4 http://www.mingw.org/ The mainpage of the MinGW C++ Compiler, there look for “downloads” and get the most recent version, preferably with autoinstaller.
SDL_gfx-2.0.22.tar.gz 2.0.22 http://www.ferzkopp.net/ SDL_gfx C++ source code from Andreas Schiffler’s homepage. Look for “Software”, there for “SDL_gfx”.
SDL-devel-1.2.14-mingw32.tar.gz 1.2.14 http://www.libsdl.org/ SDL development library. Look for “SDL 1.2” in the menu, then for the development libraries.

1) First of all, get the MinGW C++ compiler installed. Make sure you check the g++ compiler, which is actually the C++ compiler we are looking for! The other compilers you can leave unchecked. See the picture below.

MinGW Install window

2) Extract the Development Library of SDL (SDL-devel-1.2.14-mingw32.tar.gz or newer).

3) Among other things there should be one folder called “lib” within the newly created folder of step 2. Copy the complete content of folder “lib” (3 files) into a folder also named “lib” in the MinGW directory. Make sure only to copy these three files, not the folder “lib” itself.

4) There is also a folder “include” in the extracted SDL Development Library folder. It contains one further folder called “SDL” (contains 34 files). This time copy this folder “SDL” as a whole into the “include” folder of the MinGW installation.

5) Now MinGW is fully prepared for the compiling of SDL_GFX.

6) Extract now the source code of SDL_GFX (SDL_gfx-2.0.22.tar.gz or newer).

7) Within the newly created folder you find a folder called “Other Builds”. Enter this folder. Within this folder you will find several zip files.

8) Extract file “mingw.zip”. This creates a new folder “mingw” and contains exact two files, “Makefile” and “README”.

9) Copy “Makefile” into the root folder of SDL_gfx.

10) Before using this makefile some changes have to be applied. The following script shows the first few lines of the makefile. The bold red printed parts have to be changed according to your needs. [REMARK: Now the lines are marked.]

CC = gcc
AR=ar rc
RANLIB=ranlib

prefix=c:/dev/local
bin_dir=$(prefix)/bin
include_dir=$(prefix)/include
lib_dir=$(prefix)/lib

CFLAGS = -O3 -march=athlon-xp -mmmx -msse -m3dnow -DBUILD_DLL -DWIN32 -Ic:/dev/local/include/SDL
LIBS = -Lc:/dev/local/lib -lSDL

OBJS = SDL_framerate.o SDL_gfxPrimitives.o SDL_imageFilter.o SDL_rotozoom.o

.
.
.
.

11) “c:/dev/local” has to be the path to the MinGW root folder [Line 5]. Remove flag “-DWIN32” [Line 10] and change the include flag/path to the corresponding (MinGW root folder)/include/SDL [Line 10, “-I(your path)/include/SDL”]. By the way, make sure to use slashes “/” instead of backslashes “\” to separate folders. Also give the library path as (MinGW root folder)/lib [Line 11, “-L(your path)/lib”]. Now save this makefile.

12) To compile the DLL file now, open the command window of Windows by using Start–>Run…, there type “cmd” and the n “OK”. A new “DOS-like” window will pop up.

12) Change the directory to the SDL_GFX root folder by using the DOS commands “cd (foldername)” and “cd..” to enter or leave a folder.

13) Now just give the full path to the MinGW make program located at “(MinGW root folder)\bin\mingw32-make”. Now press enter and see how your DLL file gets compiled.

14) If everything worked well you find a brand new SDL_gfx.dll in your SDL_GFX root folder.

15) Copy this file to your WINDOWS\system32 folder or to the folder where the application is located which is using SDL_GFX.

Now, since compilation is finished (or you skipped the compilation procedure) just a short hint about the licensing, SDL_GFX is licensed under the LGPL, which essentially means you can also use it in proprietary software.

Before proceeding make sure Free Pascal finds the SDL and SDL_GFX units (Options–>Directories…), however, if you installed a recent binary Free Pascal package you don’t have to care about this. Free Pascal then is already well configured to work with SDL and SDL_GFX.

Now we can proceed to the source code. Here is the full source code of the example program. It will rotate and at the same time zoom an image constantly and additionally draw some primitives to the screen.

PROGRAM chap4a;
USES SDL, SDL_GFX, CRT;

CONST
x_array:ARRAY[0..5] OF SINT16 = (50, 150, 250, 250, 150, 50);
y_array:ARRAY[0..5] OF SINT16 = (100, 50, 100, 200, 250, 200);

VAR
screen,original_image,modified_image:pSDL_SURFACE;
angle_value, zoom_value:DOUBLE;
framerate:pFPSMANAGER;
calc_width, calc_height:LONGINT;

BEGIN
SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(400,400,32,SDL_SWSURFACE);
IF screen=NIL THEN HALT;

original_image:=SDL_LOADBMP('C:\FPC\2.2.2\bin\i386-win32\test\fpsdl.bmp');
IF original_image=NIL THEN HALT;

NEW(modified_image);
angle_value:=0.0;
zoom_value:=0.0;

NEW(framerate);
SDL_INITFRAMERATE(framerate);
SDL_SETFRAMERATE(framerate, 30);

REPEAT
  angle_value:=angle_value+1.0;
  zoom_value:=zoom_value+0.05;
  IF angle_value>=359 THEN angle_value:=0.0;
  IF zoom_value>=2.0 THEN zoom_value:=0.0;

  ROTOZOOMSURFACESIZE(400, 400, angle_value, zoom_value, calc_width, calc_height);
  WRITELN('Width: ',calc_width,' Height: ',calc_height);
  modified_image:=ROTOZOOMSURFACE(original_image, angle_value, zoom_value, 1);

  CIRCLECOLOR(screen, 200, 200, 100, $FFFF00FF);
  FILLEDCIRCLECOLOR(screen, 200, 200, 50, $00FF00FF);
  ELLIPSECOLOR(screen, 200, 200, 175, 75, $00FFFFFF);
  FILLEDPIECOLOR(screen, 200, 200, 110, 10, 100, $FF0000FF);
  POLYGONCOLOR(screen, @x_array[0], @y_array[0], 6, $000000FF);
  BEZIERCOLOR(screen, @x_array[3], @y_array[3], 3, 2, $FFFFFFFF);


  SDL_BLITSURFACE(modified_image,NIL,screen,NIL);
  SDL_FLIP(screen);

  SDL_FRAMERATEDELAY(framerate);
UNTIL keypressed;

SDL_FREESURFACE(original_image);
SDL_FREESURFACE(modified_image);
SDL_FREESURFACE(screen);

DISPOSE(framerate);

SDL_QUIT;
END.

Now that you know the whole code, let’s discuss it step by step.

PROGRAM chap4a;
USES SDL, SDL_GFX, CRT;

CONST
x_array:ARRAY[0..5] OF SINT16 = (50, 150, 250, 250, 150, 50);
y_array:ARRAY[0..5] OF SINT16 = (100, 50, 100, 200, 250, 200);

VAR
screen,original_image,modified_image:pSDL_SURFACE;
angle_value, zoom_value:DOUBLE;
framerate:pFPSMANAGER;
calc_width, calc_height:LONGINT;

The program is called “chap4a” and uses the known units SDL and CRT (CRT for easy recognition of user pressing a key on the keyboard). Additionally the new unit SDL_GFX has to be included here. The latter unit provides the functionality described below.

There is a constant block defining two constant arrays “x_array” and “y_array” containing six elements of SINT16 (16 bit signed integer) corresponding to six x and y values. These will be needed later to define a polygon and a Bézier curve.

Three surface variables are defined. The “screen” variable represents the screen surface. The “original_image” surface stores an image, which then is manipulated (rotation and zooming) and the result is stored in the “modified_image” surface. The float number variables “angle_value” and “zoom_value” are storing a certain rotation angle and a zoom factor. Finally variable “framerate” is defined as a framerate manager variable pFPSMANAGER which is new and provided by SDL_GFX. This helpful tool is discussed in much detail later. Finally two variables “calc_width” and “calc_height” are defined and have to be of type LONGINT. They will store the estimated new size of the surface after rotation and zooming.

BEGIN
SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(400,400,32,SDL_SWSURFACE);
IF screen=NIL THEN HALT;

original_image:=SDL_LOADBMP('C:\FPC\2.2.2\bin\i386-win32\test\fpsdl.bmp');
IF original_image=NIL THEN HALT;

NEW(modified_image);
angle_value:=0.0;
zoom_value:=0.0;

The “screen” surface is initialized as known from previous chapters with 400 x 400 px. The “Free Pascal-meets-SDL” bitmap image from Chapter 3 is loaded to the surface variable “original_image”. For those who don’t remember the image, here it is again:

Free Pascal meets SDL sample image bmp format
download (right click and “save as”)

Notice, this image has 200 x 200 dimension. The “original_surface” surface will be the source for any manipulation. The second surface “modified_image” gets some space allocated and is ready for usage. However until now it stays empty. The rotation angle and the zoom factor are set to zero.

NEW(framerate);
SDL_INITFRAMERATE(framerate);
SDL_SETFRAMERATE(framerate, 30);

The framerate manager “framerate” gets some space and is initialized by PROCEDURE SDL_INITFRAMERATE(manager:PFPSmanager). Then the framerate is set in Hz (Hertz) by FUNCTION SDL_SETFRAMERATE(manager:PFPSmanager; rate:INTEGER):INTEGER which returns 0 on success and -1 if something is wrong. The default value is 30 Hz. It is also stored in flag FPS_DEFAULT. Additionally there are FPS_UPPER_LIMIT and FPS_LOWER_LIMIT which correspond to 200 and 1 Hz. Not shown in the example code is FUNCTION SDL_GETFRAMERATE(manager:PFPSMANAGER):INTEGER which will just return the set FPS value, however it will not return the real framerate. Even if the framerate dropped to 1 Hz it will return 30 Hz since this is the set value.

Well, some notes about the framerate: The framerate is usually a value indicating how many frames per second are drawn and shown at the screen. 30 Hz means there are 30 frames drawn to the screen within one second. Often the frequency if abbreviated by FPS meaning Frames Per Second, thus 30 FPS is the same as 30 Hz. If you perform many drawing operation or costly drawing operations (especially 3d applications know this) then it might be that the framerate drops because the system (processor and/or graphic hardware) isn’t capable of drawing fast enough to keep 30 frames per second.

So what about the framerate manager, how does it help? In simple programs the SDL_DELAY(delay in milli seconds) function is used to control the framerate if placed in the rendering loop (the loop which flips the scene to the screen surface). Assuming you set is to 33, so SDL_DELAY(33) it means every 33 ms one frame is drawn, this means approximately 30 frames are drawn within one second! Thus the framerate is 30 Hz here as well. However this only applies if you assume that the drawing itself doesn’t need time, which is a good approximation for simple applications and simple scenes. When drawing more complex scenes the drawing itself will take some milli seconds or even more time, the SDL_DELAY function will just add its delay time, so this leads to a remarkable delay. In contrast a framerate manager recognizes that the framerate dropped and will skip the delay time to keep the framerate at 30 Hz (or whatever value is set). Additionally the framerate manager is keeping the actual framerate more accurately at the framerate, in the contrary SDL_DELAY is quite inaccurate.

REPEAT
  angle_value:=angle_value+1.0;
  zoom_value:=zoom_value+0.05;
  IF angle_value>=359 THEN angle_value:=0.0;
  IF zoom_value>=2.0 THEN zoom_value:=0.0;

  ROTOZOOMSURFACESIZE(400, 400, angle_value, zoom_value, calc_width, calc_height);
  WRITELN('Width: ',calc_width,' Height: ',calc_height);
  modified_image:=ROTOZOOMSURFACE(original_image, angle_value, zoom_value, 1);

Now the rendering loop is entered. Every cycle the rotation angle is increased by 1.0 degree. Also the zoom factor is increased by 0.05 every cycle. A zoom factor of 1.0 means no change of the picture. Values smaller than 1.0 mean shrinkage of the image, values larger than 1.0 mean enlargement of the image. A value of 0.5 and 2.0 mean half the size and double the size of the original image respectively. The two IF clauses ensure that the value of “angle_value” is restored to 0 (equals 360) degree (no rotation), if the image is rotated by 259 degree. If the image got zoomed to twice its original size, “zoom_value” gets restored to 0.0.

PROCEDURE ROTOZOOMSURFACESIZE(width:INTEGER; height:INTEGER; angle:DOUBLE; zoom:DOUBLE; VAR dstwidth:INTEGER; VAR dstheight:INTEGER) calculates the new surface size after rotation and zooming. The first two parameters are the width and height of the initial surface (in our example the image/surface has 200 x 200 dimension). The next two parameters expect the rotation and zoom values (in our example they are stored in “rotation_angle” and “zoom_angle”). The last two parameters store the return values. The results will be returned to “calc_width” and “calc_height” in our example. They are written to the screen. Now one of the most amazing functions of the SDL_GFX unit is introduced. It allows the rotation and zooming of an image. In FUNCTION ROTOZOOMSURFACE(src:pSDL_SURFACE; angle:DOUBLE; zoom:DOUBLE; smooth:INTEGER):pSDL_SURFACE you give the source surface first, which is the surface with the original image in our case, then the angle and zoom factor which we discussed right before and finally if antialiasing should be performed. Antialiasing means to smooth edges. 0 means no antialiasing, 1 means antialiasing, well, also two flags you could use, SMOOTHING_OFF and SMOOTHING_ON. (However I experienced that antialiasing didn’t work out even though a 32 bit surface is used which is necessary to perform antialiasing as the author stated.)

There are some accompied functions not demonstrated in the example code which should be mentioned now. FUNCTION ZOOMSURFACE(src:pSDL_SURFACE; zoomx:DOUBLE; zoomy:DOUBLE; smooth:INTEGER):pSDL_SURFACE zooms only without rotation but zooming in x direction and y direction can be set independently. FUNCTION ROTOZOOMSURFACEXY(src:pSDL_SURFACE; angle:DOUBLE; zoomx:DOUBLE; zoomy:DOUBLE; smooth:INTEGER):pSDL_SURFACE is the same function as before but with rotation additionally. For both of these functions also corresponding procedures exist which return the size of the newly created surfaces. Thay are PROCEDURE ZOOMSURFACESIZE(width:INTEGER; height:INTEGER; zoomx:DOUBLE; zoomy:DOUBLE; VAR dstwidth:INTEGER; VAR dstheight:INTEGER) and PROCEDURE ROTOZOOMSURFACEXYSIZE(width:INTEGER; height:INTEGER; angle:DOUBLE; zoomx:DOUBLE; zoomy:DOUBLE; VAR dstwidth:INTEGER; VAR dstheight:INTEGER). They work the very same way as demonstrated in the example code.

It is very important to keep in mind that every manipulating procedure (rotation and zooming) is distorting the image information. So the number of manipulations should be keep as small as possible. In the example every cycle of the loop the original image gets manipulated exactly two times. It gets rotated once and zoomed once. Instead of this you could also implement a recursive solution, namely rotating the original image in the first cycle by one degree, take the resulting image and rotate it in the second cycle again by one degree, and so on. Let’s check for the result after 25 cycles, in fact both methods rotated the image by 25 degree, but well, the quality is remarkable different. And this neglecting the zooming which has an even worse impact on quality if implemented recursively. The following drawing will illustrate what the results will look like. On the left the rotation of the original image by 25 degree once, on the right the rotation of the same image 25 times by one degree.

Results of different rotation implementations
Left: Rotation once by 25 degree; Right: 25 Rotations by one degree

The loss of information with each cycle will add up for each manipulation leading to ugly results like the right one. Compare this to the left image where no loss of information is noticable.

CIRCLECOLOR(screen, 200, 200, 100, $FFFF00FF);
  FILLEDCIRCLECOLOR(screen, 200, 200, 50, $00FF00FF);
  ELLIPSECOLOR(screen, 200, 200, 175, 75, $00FFFFFF);
  FILLEDPIECOLOR(screen, 200, 200, 110, 10, 100, $FF0000FF);
  POLYGONCOLOR(screen, @x_array[0], @y_array[0], 6, $000000FF);
  BEZIERCOLOR(screen, @x_array[3], @y_array[3], 3, 2, $FFFFFFFF);

As promised SDL_GFX is able to draw a lot of primitives. The primitives shown in the example code are just a few of them. The principle of implementing them is demonstrated anyway. The table below gives the complete list of primitives you can use. Most of the functions are intuitive, so a circle is defined by its position (x/y values), its radius r and its colour. The same applies for a filled circle. An ellipse is defined by its position (x/y values), its horizontal and vertical axes rx and ry, and its colour.

Especially polygon and Bézier curve calls may be not so intuitive. The polygon function is function POLYGONCOLOR(dst:pSDL_SURFACE; const vx:pSINT16; const vy:pSINT16; n:INTEGER; color:UINT32):INTEGER and the Bézier curve function is function BEZIERCOLOR(dst:pSDL_SURFACE; const vx:pSINT16; const vy :pSINT16; n:INTEGER; s:INTEGER; color:UINT32):INTEGER. As for all the other functions you define the surface first onto which you would like to draw these primitives. The next parameters expect a pointer (pSINT16) to an array of x values of type SINT16 (16 bit unsigned integer) and an array of y values of the same type. This is achieved by the @ operator at the first and fourth element of the arrays respectivly. These vectors were defined initially in the constant block, you should remember. The next parameter n is the number of points the polygon has or the number of reference points the Bézier curve has. If the arrays contain six elements, n should be six as well. Since in the case of the Bézier curve only the last three elements are of interest (at least in our example), n has to be three and the resulting curve is of order four (quadratic Bézier curve). The s value defines the smoothness of the curve. The higher s the higher the smoothness.

Some general information about these primitive functions: All functions presented here have the same suffix …COLOR. This means that the last parameter expects the colour you desire in hexadecimal form, $RRGGBBAA. The hexadecimal digit 00 corresponds to decimal digit 0, and hexadecimal digit FF corresponds to decimal digit 255. All of these function are accompanied by a function with the suffix …RGBA instead of …COLOR. Here, all parameters are the same except from the colour parameter, instead of ONE colour code in hexadecimal form, you enter four parameters r, g, b, a (red, green, blue, alpha/transparency). For example instead of $FF0000FF you put 255, 0, 0, 255. All functions return 0 as INTEGER value on success.

In many cases there are further accompanied functions with the prefix aa. This means the very same function with the same parameter list but the result is antialiased. For many functions furthermore the prefix filled is possible as shown for the circle in the example code. Also here the parameter list is completely the same but the resulting primitive is filled with the given colour.

SDL_BLITSURFACE(modified_image,NIL,screen,NIL); SDL_FLIP(screen); SDL_FRAMERATEDELAY(framerate); UNTIL keypressed;

Primitive Definition Description
Of all functions two types exist: …Color and …RGBA. They are in fact equal and differ only in the way you put in the colour information. For …Color functions there is the color:Uint32 argument where you put in the colour code in hexadecimal form, e.g. $FF0000FF for red without transparency. For …RGBA functions there are four arguments r:Uint8; g:Uint8; b:Uint8; a:Uint8 where you enter 255; 0; 0; 255 for red without transparency.
Functions with the prefix aa are equal to the functions without this prefix but are printing antialiased primitives. Functions with the prefix filled are equal to the functions without this prefix but the enclosed area of the primitive is filled with the given colour. Antialiased primitives are never filled and vice versa.
All functions return 0 as INTEGER on success.
Pixel (Dot) function pixelColor( dst : PSDL_Surface; x : Sint16; y : Sint16; color : Uint32 ) : integer Draws one pixel at position (x/y) in the given colour.
function pixelRGBA( dst : PSDL_Surface; x : Sint16; y : Sint16; r : Uint8; g : Uint8; b : Uint8; a : Uint8 ) : integer
Line function hlineColor( dst : PSDL_Surface; x1: Sint16; x2 : Sint16; y : Sint16; color : Uint32 ) : integer Draws a horizontal line from x1 to x2 at height y in the given colour.
function hlineRGBA( dst : PSDL_Surface; x1: Sint16; x2 : Sint16; y : Sint16; r : Uint8; g : Uint8; b : Uint8; a : Uint8 ) : integer
function vlineColor( dst : PSDL_Surface; x : Sint16; y1 : Sint16; y2 : Sint16; color : Uint32 ) : integer Draws a vertical line at position x from y1 to y2 in the given colour.
function vlineRGBA( dst : PSDL_Surface; x : Sint16; y1 : Sint16; y2 : Sint16; r : Uint8; g : Uint8; b : Uint8; a : Uint8 ) : integer
function lineColor( dst : PSDL_Surface; x1 : Sint16; y1 : Sint16; x2 : Sint16; y2 : Sint16; color : Uint32 ) : integer Draws a free line from position (x1/y1) to (x2/y2) in the given colour.
function lineRGBA( dst : PSDL_Surface; x1 : Sint16; y1 : Sint16;
x2 : Sint16; y2 : Sint16; r : Uint8; g : Uint8; b : Uint8; a : Uint8 ) : integer
function aalineColor( dst : PSDL_Surface; x1 : Sint16; y1 : Sint16; x2 : Sint16; y2 : Sint16; color : Uint32 ) : integer
function aalineRGBA( dst : PSDL_Surface; x1 : Sint16; y1 : Sint16;
x2 : Sint16; y2 : Sint16; r : Uint8; g : Uint8; b : Uint8; a : Uint8 ) : integer
Rectangle (Box) function rectangleColor( dst : PSDL_Surface; x1 : Sint16; y1 : Sint16; x2 : Sint16; y2 : Sint16; color : Uint32 ) : integer Draws a rectangle from position (x1/y1) to (x2/y2) in the given colour.
function rectangleRGBA( dst : PSDL_Surface; x1 : Sint16; y1 : Sint16;
x2 : Sint16; y2 : Sint16; r : Uint8; g : Uint8; b : Uint8; a : Uint8 ) : integer
function boxColor( dst : PSDL_Surface; x1 : Sint16; y1 : Sint16; x2 : Sint16; y2 : Sint16; color : Uint32 ) : integer
function boxRGBA( dst : PSDL_Surface; x1 : Sint16; y1 : Sint16; x2 : Sint16;
y2 : Sint16; r : Uint8; g : Uint8; b : Uint8; a : Uint8 ) : integer
Circle function circleColor( dst : PSDL_Surface; x : Sint16; y : Sint16; r : Sint16; color : Uint32 ) : integer Draws a circle with the center at position (x/y) and the radius r in the given colour.
function circleRGBA( dst : PSDL_Surface; x : Sint16; y : Sint16; rad : Sint16; r : Uint8; g : Uint8; b : Uint8; a : Uint8 ) : integer
function aacircleColor( dst : PSDL_Surface; x : Sint16; y : Sint16; r : Sint16; color : Uint32 ) : integer
function aacircleRGBA( dst : PSDL_Surface; x : Sint16; y : Sint16;
rad : Sint16; r : Uint8; g : Uint8; b : Uint8; a : Uint8 ) : integer
function filledCircleColor( dst : PSDL_Surface; x : Sint16; y : Sint16; r : Sint16; color : Uint32 ) : integer
function filledCircleRGBA( dst : PSDL_Surface; x : Sint16; y : Sint16;
rad : Sint16; r : Uint8; g : Uint8; b : Uint8; a : Uint8 ) : integer
Pie function pieColor( dst : PSDL_Surface; x : Sint16; y : Sint16; rad : Sint16;
start : Sint16; finish : Sint16; color : Uint32 ) : integer
Draws a pie chart with the pie’s edge being at position (x/y) with the radius rad. “start” and “finish” define the arc length of the pie in degree. If “start” is 0 then the pie starts at the right (east) side of a circle. To start at the top, “start” should be 270 or -90. If “start” is 0 and “finish” is 90, you get one quarter of a circle.
function pieRGBA( dst : PSDL_Surface; x : Sint16; y : Sint16; rad : Sint16;
start : Sint16; finish : Sint16; r : Uint8; g : Uint8; b : Uint8; a : Uint8 ) : integer
function filledPieColor( dst : PSDL_Surface; x : Sint16; y : Sint16; rad : Sint16;
start : Sint16; finish : Sint16; color : Uint32 ) : integer
function filledPieRGBA( dst : PSDL_Surface; x : Sint16; y : Sint16; rad : Sint16;
start : Sint16; finish : Sint16; r : Uint8; g : Uint8; b : Uint8; a : Uint8 ) : integer
Ellipse function ellipseColor( dst : PSDL_Surface; x : Sint16; y : Sint16; rx : Sint16; ry : Sint16; color : Uint32 ) : integer Draws an ellipse with its center at position (x/y) and the two radii rx and ry for its horizontal and vertical axes in the given colour.
function ellipseRGBA( dst : PSDL_Surface; x : Sint16; y : Sint16;
rx : Sint16; ry : Sint16; r : Uint8; g : Uint8; b : Uint8; a : Uint8 ) : integer
function aaellipseColor( dst : PSDL_Surface; xc : Sint16; yc : Sint16; rx : Sint16; ry : Sint16; color : Uint32 ) : integer
function aaellipseRGBA( dst : PSDL_Surface; x : Sint16; y : Sint16;
rx : Sint16; ry : Sint16; r : Uint8; g : Uint8; b : Uint8; a : Uint8 ) : integer
function filledEllipseColor( dst : PSDL_Surface; x : Sint16; y : Sint16; rx : Sint16; ry : Sint16; color : Uint32 ) : integer
function filledEllipseRGBA( dst : PSDL_Surface; x : Sint16; y : Sint16;
rx : Sint16; ry : Sint16; r : Uint8; g : Uint8; b : Uint8; a : Uint8 ) : integer
Polygon function trigonColor( dst : PSDL_Surface; x1 : Sint16; y1 : Sint16; x2 : Sint16; y2 : Sint16; x3 : Sint16; y3 : Sint16; color : Uint32 ) : integer Draws a trigon with its edges at (x1/y1), (x2/y2) and (x3/y3) in the given colour.
function trigonRGBA( dst : PSDL_Surface; x1 : Sint16; y1 : Sint16; x2 : Sint16; y2 : Sint16; x3 : Sint16; y3 : Sint16;
r : Uint8; g : Uint8; b : Uint8; a : Uint8 ) : integer
function aatrigonColor( dst : PSDL_Surface; x1 : Sint16; y1 : Sint16; x2 : Sint16; y2 : Sint16; x3 : Sint16; y3 : Sint16; color : Uint32 ) : integer
function aatrigonRGBA( dst : PSDL_Surface; x1 : Sint16; y1 : Sint16; x2 : Sint16; y2 : Sint16; x3 : Sint16; y3 : Sint16;
r : Uint8; g : Uint8; b : Uint8; a : Uint8 ) : integer
function filledTrigonColor( dst : PSDL_Surface; x1 : Sint16; y1 : Sint16; x2 : Sint16; y2 : Sint16; x3 : Sint16; y3 : Sint16; color : Uint32 ) : integer
function filledTrigonRGBA( dst : PSDL_Surface; x1 : Sint16; y1 : Sint16; x2 : Sint16; y2 : Sint16; x3 : Sint16; y3 : Sint16;
r : Uint8; g : Uint8; b : Uint8; a : Uint8 ) : integer
function polygonColor( dst : PSDL_Surface; const vx : PSint16; const vy : PSint16; n : integer; color : Uint32 ) : integer Draws a polygon with x values from array of SINT16 in vx. y values are from array of SINT16 in vy. n is the number of edges/points the polygon has.
function polygonRGBA( dst : PSDL_Surface; const vx : PSint16; const vy : PSint16;
n : integer; r : Uint8; g : Uint8; b : Uint8; a : Uint8 ) : integer
function aapolygonColor( dst : PSDL_Surface; const vx : PSint16; const vy : PSint16; n : integer; color : Uint32 ) : integer
function aapolygonRGBA( dst : PSDL_Surface; const vx : PSint16; const vy : PSint16;
n : integer; r : Uint8; g : Uint8; b : Uint8; a : Uint8 ) : integer
function filledPolygonColor( dst : PSDL_Surface; const vx : PSint16; const vy : PSint16; n : integer; color : Uint32 ) : integer
function filledPolygonRGBA( dst : PSDL_Surface; const vx : PSint16;
const vy : PSint16; n : integer; r : Uint8; g : Uint8; b : Uint8; a : Uint8 ) : integer
Bézier curve function bezierColor( dst : PSDL_Surface; const vx : PSint16; const vy : PSint16; n : integer; s : integer; color : Uint32 ) : integer Draws a Bézier curve of any order. n is the number of points, well n+1 is the order of the curve then. The x and y values are from arrays of SINT16. The higher value s the smoother the curve gets. s should be 2 at least.
function bezierRGBA( dst : PSDL_Surface; const vx : PSint16; const vy : PSint16;
n : integer; s : integer; r : Uint8; g : Uint8; b : Uint8; a : Uint8 ) : integer

 

  SDL_BLITSURFACE(modified_image,NIL,screen,NIL);
  SDL_FLIP(screen);

  SDL_FRAMERATEDELAY(framerate);
UNTIL keypressed;

Nothing really new here. The “modified_image” surface with the rotated, zoomed image and the primitives is blitted to the screen surface. The result is flipped to the screen. Then the extensivly discussed framerate manager decides by procedure SDL_FRAMERATEDELAY(manager:PFPSMANAGER) how long actually to wait until the loop is repeated. This procedure replaces the common SDL_DELAY command. The loop is stopped when the user presses any key.

SDL_FREESURFACE(original_image);
SDL_FREESURFACE(modified_image);
SDL_FREESURFACE(screen);

DISPOSE(framerate);

SDL_QUIT;
END.

Finally all the used surfaces are free’d and the framerate manager is disposed. SDL is quit and the program finished.

This file contains the source code: chap4a.pas (right click and “save as”)
This file is the executable: chap4a.exe (right click and “save as”)

The final result should look and behave like this: The image fpsdl.bmp is rotated and zoomed continouisly and some primitives (circle, filled circle, ellipse, pie, hexagon, Bézier curve) are drawn onto the image. If you use the pre-compiled exe file make sure to have the image copied to c:\.

Result of JEDI-SDL Chapter 4a

[Downloads transferred from old website]

SDL_gfx.dll; The pre-compiled SDL_gfx.dll (Version 2.0.19) for chapter 4a.

SDL_gfx-2.0.19.tar.gz; For license reasons here you can download the source code to compile the SDL_gfx.dll for chapter 4a. However, the most recent version for compilation you will find on the authour’s page.

Chapter 1a: Compilation and configuration of Lazarus (JEDI-SDL)

This is an SDL 1.2 chapter. SDL 1.2 is obsolete since it has been replaced by SDL 2.0. Unless you have good reasons to stay here you may prefer to go for the modern SDL 2.0 :-).

Before you start following the instructions given here to install Lazarus make sure you really need it. The Lazarus package is completly independent of SDL. If you are not interested in a DELPHI-like RAD (Rapid Application Development) tool skip this chapter. You could come back later after you finished all SDL chapters :). This chapter isn’t required to do the following chapters.

Otherwise the combination of Free Pascal together with the SDL Library and Lazarus’ abilities can lead to a very flexible and highly powerful development environment. The advantages of Free Pascal, like OS independence, open source code, a license which allows you to develop commercial programs (license: LGPL), and so on apply for Lazarus, too! Furthermore you gain all advantages modern OS and IDE provide. The DOS-like IDE of the Free Pascal compiler is legendary but nowadays not too comfortable anymore. The Lazarus environment provides a flexible and comfortable IDE.

The following description on how to compile/install and configure Lazarus is for WINDOWS ONLY. I am sorry that I am currently not able to make such a description to linux users. Any reader is welcome to send me one, so I could publish it here (you would get full credit of course). Futhermore it is necessary that you have installed Free Pascal.

The following table describes what software you will need additionally to the Free Pascal compiler:

Software Version Size, MB Source Description
Lazarus source files Latest revision depends directly through SVN How to get them will be described next!
TortoiseSVN 1.4.7 ~2 http://tortoisesvn.net/ Download the latest version of SVN client (except you already have such a client).
fpcbuild-2.2.0.zip 2.2.0 ~38 http://www.Free Pascal.org Download the latest source files of the Free Pascal compiler. Go to “Download” and scroll down to “Source”.

Now let’s begin. First of all I want you to know that there are some binaries downloadable from the official Lazarus page. These binaries shouldn’t be used because they are very old and the resulting Lazarus environment is terribly unstable! The environment is much more improved and gets improved daily. So I advise you to get the latest development state through SVN (it’s like the outdated CVS). If you neither know SVN nor CVS here a short description: SVN provides the latest state of development and ensures that any of several hundreds developers of a project (like Lazarus) is working on the latest version. Whenever a developer is changing anything at the project SVN generates a new revision number. Today the revision number of Lazarus is 13688.

How to get the latest development state (revision) of lazarus then? – That is described very clearly and easy here: http://wiki.lazarus.FreePascal.org/index.php/Getting_Lazarus. Therefore I will just describe short how to get it.

1) get the mentioned SVN client (TortoiseSVN) for Windows and install it

2) create a directory where you want to have Lazarus installed later (e.g. C:\FPC\Lazarus)

3) right-click on the new created lazarus folder and chose “SVN Checkout…”; you get the following window

Lazarus SVN Checkout window4) type in field URL of repository: “http://svn.Free Pascal.org/svn/lazarus/trunk” (without quotationmarks) as shown in the picture above; if you use a command line client (e.g. Subversion you have to type “svn co http://svn.Free Pascal.org/svn/lazarus/trunk lazarus” (without quotationmarks))

5) click “OK”

6) another window pops up called “trunk – SVN Checkout…”; now it will take some minutes; after all files got downloaded you receive a window similar to this:

Lazarus finished checkout(you can go on reading while waiting)

The files you download through SVN aren’t binaries. That means you have to compile them. The compiler you use to do so is the Free Pascal compiler of course (that is why you should have it installed already).

7) If the downloading is finished you have to go to the folder where all the files are placed, e.g. C:\FPC\lazarus\.

8) Now you have to run a command line tool like “cmd”. Choose “Start” and then “Run…” from Windows’ start menu.

9) Enter “cmd” or “command.com” (latter only if “cmd” doesn’t work) in the next pop-up window and click “OK”. Now a window like this should pop up:

Command window cmd.exe for make procedure10) Go to the directory where you downloaded the Lazarus files. (Use the DOS commands “cd [directory]” and “cd..” to enter or leave directory.)

11) When you entered the directory (C:\FPC\lazarus\) just type “make” and the make.exe of your Free Pascal compiler will compile Lazarus for you. Now relax and wait some minutes.

If this doesn’t work for you, you should try to copy the make.exe from your Free Pascal compiler (located in bin directory) to the Lazarus directory (or you could check if you set the environment variable/user variable PATH=C:\FPC\2.2.0\bin\i386-win32 in system control)

12) After successful compilation there should be a Lazarus.exe and a Startlazarus.exe right in the lazarus directory. Use Startlazarus.exe to run your brand new RAD tool :). After passing some error messeges you should get something similar to this:

Lazarus editor if installed successfully13) close Lazarus

The next steps describe how to configure Lazarus together with JEDI-SDL. This includes telling Lazarus where to find Free Pascal compiler, where to find Free Pascal source files and where to find JEDI-SDL units. ATTENTION: It is necessary to have the source files of Free Pascal, otherwise Lazarus won’t run properly. If you installed Free Pascal and JEDI-SDL as described in Chapter 1 you don’t have the source files because they are not included in the binary package. Fortunately this doesn’t matter because you can download them separately. If you already have the source files for some reason you can skip step 14 and 15.

14) Download the latest source files of the Free Pascal compiler (usually it doesn’t matter if the version of the source files differ slightly from the version of actual compiler).

15) Now extract the file. It doesn’t matter where the source files are located but you may prefer a folder like “C:\FPC\2.2.0\source\”.

16) The extraction generates a folder “fpcbuild-2.2.0”. The whole path should be “C:\FPC\2.2.0\source\fpcbuild-2.2.0\”.

17) Now run Lazarus and skip any error message. Go to “Environment” in the menu.

Lazarus editor environment options
Choose “Environment options” from the tab. The following window should come up.

Environment options window18) The text field “Lazarus directory (default for all projects)” should be filled with your Lazarus path, if not do so. The text field “Compiler path (ppc386.exe)” should be filled with your Free Pascal compiler path, if not do so.

19) The text field “FPC source directory” is probably empty. The sources are needed for Lazarus to compile projects successfully. Fill in the path where you extracted the source files in step 15. If you followed my suggestion the correct path would be “C:\FPC\2.2.0\source\fpcbuild-2.2.0\fpcsrc\”. Confirm by clicking “OK”.

20) If you haven’t done any mistakes there shouldn’t be any error anymore if you close and restart Lazarus.

21) If you didn’t install SDL manually you can skip steps 21 and 22, you are finished then! Now let’s tell Lazarus where to find the JEDI-SDL units. Go to “Project” in menu.

Lazarus compiler optionsChoose “Compiler Options…” from the tab. The following window should come up but without any entry in the text field “Other Unit Files (-Fu) (Delimiter is semicolon);”.

Lazarus compiler options window22) Enter the path to the JEDI-SDL units. If you installed JEDI-SDL as mentioned in Chapter 1 you should enter “C:\FPC\2.2.0\units\JEDI\SDL\Pas”. Now you are ready to write Free Pascal programs in Lazarus using JEDI-SDL. Note: The environment settings are saved for any project but the compiler settings (steps 20-22) are just related to the current project so you have to set the unit path to JEDI-SDL units separate for each project.

Now you have a brand new RAD tool installed. Well done! This tool you can use to write just plain Free Pascal or/and Free Pascal/SDL programs. You also can combine these programs with the features typical RAD kits provide but to learn how to use those features is described anywhere else. Have fun.

Chapter 6: Event handling (JEDI-SDL)

This is an SDL 1.2 chapter. SDL 1.2 is obsolete since it has been replaced by SDL 2.0. Unless you have good reasons to stay here you may prefer to go for the modern SDL 2.0 :-).

Whenever the user of your program is doing something related to it, for instance if the user is moving the mouse, pressing/releasing a button on the keyboard or pressing/releasing the fire key on the joystick, then you speak of events. Further events are resizing a window or switching between several applications. For games though the events described first are much more important. SDL provides a quite easy way to notice and react to such events, which is called event handling.

Each event has a special structure, for example a pressed key is an event or a moved mouse is an event. SDL differs altogether sixteen such events (check the table below). Every event stores different information depending on its kind. For example the key board event stores the information which key was pressed. The mouse motion event stores the information to which position the mouse got moved. If you get a mouse motion event you can’t read out key information, and from a key pressed event you can’t read out its mouse coordinates, and so on. Therefore you have the following general structure to the event data: event.eventstructure.data. The following list will give you an overview of all possible eventstructures in SDL:

eventstructure Description
SDL_NOEVENT Whenever the user is not raising an actual event.
SDL_ACTIVEEVENT Notices if application is active or inactive (for example when you switch to another windowed application and your applications gets into the background or gets minimized).
SDL_KEYDOWN Both have the same record structure (tSDL_KEYBOARDEVENT) which stores the triggering key with state SDL_PRESSED or SDL_RELEASED
SDL_KEYUP
SDL_MOUSEMOTION Notices the movement of the mouse cursor and stores it position and the relative movement from former origin.
SDL_MOUSEBUTTONDOWN Both have the same record structure (tSDL_MOUSEBUTTONEVENT) which stores the triggering mouse and its key with state SDL_PRESSED or SDL_RELEASED. Futhermore the position is stored.
SDL_MOUSEBUTTONUP
SDL_JOYAXISMOTION Notices the usage of the stick of a joystick.
SDL_JOYBALLMOTION Notices the usage of a joyball and the relative movement from its origin.
SDL_JOYHATMOTION Notices the triggering joystick and its hat. Furthermore one of the nine positions is stored (1=up, 2=right upper corner, 3=right and so on; 0=center).
SDL_JOYBUTTONDOWN Both have the same record structure (tSDL_JOYBUTTONEVENT) which stores the triggering joystick and its key with state SDL_PRESSED or SDL_RELEASED.
SDL_JOYBUTTONUP
SDL_VIDEORESIZE Notices when apllication window gets resized and stores the new height and width.
SDL_QUITEV Notices when user quits the application (by clicking application’s X-button at right upper corner)
SDL_USEREVENT Unedfined event which can be definied by user.
SDL_SYSWMEVENT Notices system window manager events.

The event handling subsystem is automatically initialized along with the video subsystem. Since all the events are related to the program’s window there is no sense of intilizing it individually from the video subsystem. Here is the code of the program all at once.

PROGRAM chap6_1;

USES SDL;

VAR
screen:pSDL_SURFACE;
loopstop:boolean=FALSE;
test_event:pSDL_EVENT;

BEGIN
SDL_INIT(SDL_INIT_VIDEO);

screen:=SDL_SETVIDEOMODE(200,200,32,SDL_SWSURFACE);
IF screen=NIL THEN HALT;

NEW(test_event);

WHILE loopstop=FALSE DO
BEGIN
  IF SDL_POLLEVENT(test_event)=1 THEN
  BEGIN
    write('pending event: ');
    CASE test_event^.type_ OF
      SDL_ACTIVEEVENT: writeln('Application is/is not active');
      SDL_KEYDOWN: BEGIN
                     write('Key pressed ');
                     writeln('(SDLKey=',test_event^.key.keysym.sym,')');

                     {SDLKey 27 = ESCAPE}
                     IF test_event^.key.keysym.sym=27 THEN loopstop:=TRUE;
                   END;
      SDL_KEYUP: writeln('Key released');
      SDL_MOUSEMOTION: writeln('Mouse motion');
      SDL_MOUSEBUTTONDOWN: writeln('Mouse button down');
      SDL_MOUSEBUTTONUP: writeln ('Mouse button up');
      SDL_JOYAXISMOTION: writeln ('Joystick axis motion');
      SDL_JOYBALLMOTION: writeln('Joystickïs trackball motion');
      SDL_JOYHATMOTION: writeln('Joystickïs hat position changed');
      SDL_JOYBUTTONDOWN: writeln('Joystick button pressed');
      SDL_JOYBUTTONUP: writeln('Joystick button released');
      SDL_QUITEV: writeln('User-requested quit');
    END;
  END
  ELSE writeln('no pending events');
  SDL_DELAY(150);
END;


DISPOSE(test_event);
SDL_FREESURFACE(screen);
SDL_QUIT;
END.

Okay, let’s right start with the first part.

PROGRAM chap6_1;

USES SDL;

VAR
screen:pSDL_SURFACE;
loopstop:boolean=FALSE;
test_event:pSDL_EVENT;

BEGIN
SDL_INIT(SDL_INIT_VIDEO);

screen:=SDL_SETVIDEOMODE(200,200,32,SDL_SWSURFACE);
IF screen=NIL THEN HALT;

NEW(test_event);

The first part to be discussed doesn’t contain a lot of new things. The program is called chap6_1, it uses the SDL unit and the screen variable is defined. New is the event variable test_event which is of pointer type pSDL_EVENT. We will create a second variable of boolean type just to control the while loop. Then SDL is initialized and the program’s window set with the width and height of 200 pixels. Finally the event variable gets allocated.

WHILE loopstop=FALSE DO
BEGIN
  IF SDL_POLLEVENT(test_event)=1 THEN
  BEGIN
    write('pending event: ');

We want to check if any event occured and if so, we want to know which kind of event happened. So we make a while loop which will run until loopstop gets true. This will be if the user presses the application’s X (right upper corner) or pressing ESC key.

The command SDL_POLLEVENT(parameter), or more precise SDL_POLLEVENT(event: pSDL_EVENT): INTEGER, checks if there are pending events and if so it will take the oldest and save it to parameter which is an event record of pSDL_EVENT type. For example if the user presses (then releases) left mouse button, then presses (then releases) space button on keyboard and finally moves the mouse there are altogether five events: 1. left mouse button pressed, 2. left mouse button released, 3. space key pressed, 4. space key released, 5. mouse moved. If you poll for events now you will get the first event (left mouse button pressed) and saved it’s properties to the event variable we specified as parameter. The next poll will save the properties of next event (left mouse button released) to event variable and so on. SDL_POLLEVENT(parameter) will return 1 if it has found pending event or 0 if there isn’t any pending event.

So, if SDL_POLLEVENT has an event, then the program writes “pending event:” and after that will add the type of event found. Notice that therefore write instead of writeln has been used here. The actual event is determined as follows:

CASE test_event^.type_ OF
      SDL_ACTIVEEVENT: writeln('Application is/is not active');
      SDL_KEYDOWN: BEGIN
                     write('Key pressed ');
                     writeln('(SDLKey=',test_event^.key.keysym.sym,')');

                     {SDLKey 27 = ESCAPE}
                     IF test_event^.key.keysym.sym=27 THEN loopstop:=TRUE;
                   END;
      SDL_KEYUP: writeln('Key released');
      SDL_MOUSEMOTION: writeln('Mouse motion');
      SDL_MOUSEBUTTONDOWN: writeln('Mouse button down');
      SDL_MOUSEBUTTONUP: writeln ('Mouse button up');
      SDL_JOYAXISMOTION: writeln ('Joystick axis motion');
      SDL_JOYBALLMOTION: writeln('Joystickïs trackball motion');
      SDL_JOYHATMOTION: writeln('Joystickïs hat position changed');
      SDL_JOYBUTTONDOWN: writeln('Joystick button pressed');
      SDL_JOYBUTTONUP: writeln('Joystick button released');
      SDL_QUITEV: writeln('User-requested quit');
    END;
END

Fortunately you don’t have to check manually for every event which is made by the user. In general by event^.eventtype you can easily check which type of event you got. The event is stored in test_event and the type is checked by type_ so the expression is test_event^.type_. Whatever event is stored in test_event, the corresponding string expression is then added to the previous “pending event:” string. For the example described before the returned event types would be SDL_MOUSEBUTTONDOWN, 2. SDL_MOUSEBUTTONUP, 3. SDL_KEYDOWN, 4. SDL_KEYUP, 5. SDL_MOUSEMOTION. It is senseful to check for the event type by using the CASE command.

You may have noticed that in case of SDL_KEYDOWN not just a string gets added. Actually in this case also the corresponding SDLKey code is determined and given. Furthermore if the pressed key is the escape key (Esc) the program should stop. An keyboard event record contains a field called keysym. Keysym is a record of SDL_KEYSYM. It contains four fields. Scancode which is a hardware dependent scancode and should be avoided if you want to make hardware independent programs. It is usually an INTEGER variable. Next is sym which stores the SDLKEY. These SDL keys are independent and it is strongly recommended to use them! In the example we want break up if the escape key gets pressed. Its SDL key code is 27. If you want to know what SDL keys are defined look up in the table page. The variable modifier stores modifier keys (like shift, ctrl,…) pressed and stores SDLMod. SDL modifier keys can be found at table page as well. The fourth variable is unicode which may be used to read out unicode characters which is enabled by function SDL_ENABLEUNICODE(enable: INTEGER): INTEGER. 1 enables and 0 disables the unicode translation, however by default it is turned off.

To read out or compare the key the user pressed we must use the expression test_event^.key.keysym.sym. test_event^ is the event, the event structure is defined as an key event by key and the data we want to know is the key pressed contained in keysym.sym. The key code is printed to th screen but if it corresponds to the code for the ESC key, it sets loopstop to TRUE meaning the the while loop will be stopped and finally stop the program.

  ELSE writeln('no pending events');
  SDL_DELAY(150);
END;


DISPOSE(test_event);
SDL_FREESURFACE(screen);
SDL_QUIT;
END.

In the case SDL_POLLEVENT finds no event all the checking for event is skipped and just “no pending events” is written. To slow down the program the known SDL_DELAY is used.

Finally the event and the screen are disposed and SDL as well as the program are quit.

Now I will present a command without showing its appliance at any code since it is easy to understand and very important. Imagine you open any text editor and just hold pressed any letter on the keyboard. What will happen? First the letter will be written to the text editor once and after a short break the letter will be written in a loop (without break). – You may want to change this behaviour, especially for action games and such. The command SDL_ENABLEKEYREPEAT(DELAY, INTERVAL), or more precise SDL_ENABLEKEYREPEAT(delay: INTEGER; interval: INTEGER): INTEGER, is used for changing this behaviour. If delay is set to 0 the key repetition is completly disabled and a held pressed key will just trigger ONCE. If you enable the the delay by setting it to 1 and setting an interval in ms the pressed key will trigger anytime the set interval has passed. E.g. SDL_ENABLEKEYREPEAT(1,1000) will trigger the key any second if pressed. Note: When using this command the specific text edior behaviour is removed and the first triggering will happen immediatly. To get back to the common behaviour use SDL_DEFAULT_REPEAT_DELAY and SDL_DEFAULT_REPEAT_INTERVAL.

Now we will discuss mouse handling. Since it is rather similar to keyboard handling I will keep it short. Here we go:

PROGRAM chap6_2;

USES SDL;

VAR
screen:pSDL_SURFACE;
loopstop:boolean=FALSE;
test_event:pSDL_EVENT;

PROCEDURE mouse_check;
BEGIN
  writeln('X: ',test_event^.motion.x,' Y: ',test_event^.motion.y,
        ' dX: ',test_event^.motion.xrel,' dY: ',test_event^.motion.yrel,
        ' button state: ',test_event^.motion.state);
  writeln('button state (pressed/released): ',test_event^.button.state,
        ' button index: ',test_event^.button.button);
END;

BEGIN
SDL_INIT(SDL_INIT_VIDEO);

screen:=SDL_SETVIDEOMODE(200,200,32,SDL_SWSURFACE);
IF screen=NIL THEN HALT;

NEW(test_event);

SDL_EVENTSTATE(SDL_ACTIVEEVENT,SDL_DISABLE);

WHILE loopstop=FALSE DO
BEGIN
  IF SDL_POLLEVENT(test_event)=1 THEN
  BEGIN
    CASE test_event^.type_ OF
      SDL_ACTIVEEVENT: writeln('Application is/is not active');
      SDL_KEYDOWN:   {SDLKey 27 = ESCAPE}
                     IF test_event^.key.keysym.sym=27 THEN loopstop:=TRUE;

      SDL_MOUSEMOTION: mouse_check;
      SDL_MOUSEBUTTONDOWN: mouse_check;
      SDL_MOUSEBUTTONUP: mouse_check;
    END;
  END;
END;


DISPOSE(test_event);
SDL_FREESURFACE(screen);
SDL_QUIT;
END.

Anyhow this code looks complicated, it is not.

PROGRAM chap6_2;

USES SDL;

VAR
screen:pSDL_SURFACE;
loopstop:boolean=FALSE;
test_event:pSDL_EVENT;

PROCEDURE mouse_check;
BEGIN
  writeln('X: ',test_event^.motion.x,' Y: ',test_event^.motion.y,
        ' dX: ',test_event^.motion.xrel,' dY: ',test_event^.motion.yrel,
        ' button state: ',test_event^.motion.state);
  writeln('button state (pressed/released): ',test_event^.button.state,
        ' button index: ',test_event^.button.button);
END;

BEGIN
SDL_INIT(SDL_INIT_VIDEO);

screen:=SDL_SETVIDEOMODE(200,200,32,SDL_SWSURFACE);
IF screen=NIL THEN HALT;

NEW(test_event);

This is exactly the same code as for the first part of this chapter, however a PROCEDURE mouse_check is created which will be used several times later. You can read out the position of the mouse (relative to your program window!) by test_event^.motion.x and test_event^.motion.y. The relative movement which means the difference from the actual position compared to last polled position you can get by test_event^.motion.xrel and test_event^.motion.yrel. Furthermore it is possbile to return the pressed buttons by test_event^.motion.state. All this data belongs to tSDL_MOUSEMOTIONEVENT record. All these information are just printed out by the mouse_check PROCEDURE later.

SDL_EVENTSTATE(SDL_ACTIVEEVENT,SDL_DISABLE);

Function SDL_EVENTSTATE(type_: UInt8; state: INTEGER): UInt8 allows you to enable/disable certain event structures. For example in the code SDL_ACTIVEEVENT got disabled by SDL_EVENTSTATE(SDL_ACTIVEEVENT, SDL_DISABLE). That is why you never will receive a note about an active or inactive application even though the status may change when you run the program. The same result you get by using SDL_IGNORE instead of SDL_DISABLE. You can reenable the event structure by SDL_ENABLE.

WHILE loopstop=FALSE DO
BEGIN
  IF SDL_POLLEVENT(test_event)=1 THEN
  BEGIN
    CASE test_event^.type_ OF
      SDL_ACTIVEEVENT: writeln('Application is/is not active');
      SDL_KEYDOWN:   {SDLKey 27 = ESCAPE}
                     IF test_event^.key.keysym.sym=27 THEN loopstop:=TRUE;

      SDL_MOUSEMOTION: mouse_check;
      SDL_MOUSEBUTTONDOWN: mouse_check;
      SDL_MOUSEBUTTONUP: mouse_check;
    END;
  END;
END;


DISPOSE(test_event);
SDL_FREESURFACE(screen);
SDL_QUIT;
END.

This part now looks also very similar to the code of the first part of this chapter. Since we are only treating mouse handling here all the other possible events are not treated in the CASE block. The SDL_ACTIVEEVENT event is just introduced here to demonstrate that it is succesfully disabled by SDL_EVENTSTATE as discussed right before.

The SDL_KEYDOWN event is introduced to make sure the user can exit the program by pressing the ESC key.

Similar to the handling of keyboard events the mouse key events are handled. It also provides the possibility to return the pressed button by test_event^.button.button. It has the very same meaning as test_event^.motion.state! Don’t confuse it with test_event^.button.state which here means to check if a button is pressed (SDL_PRESSED) or released (SDL_RELEASED). This data belongs to the tSDL_MOUSEBUTTONEVENT record. Whatever the user is doing with the mouse, moving or clicking, the mouse_ckeck PROCEDURE already discussed is entered and the corresponding data written to the screen.

By the way, you should check what happens if you combine several mouse buttons or use the mouse wheel. Some button combinations lead to the same button index as the usage of the mouse wheel and so on.

With this chapter you got introduced into a major concept of game programming.

This file contains the source code: chap6_1.pas (right click and “save as”)
This file is the executable: chap6_1.exe (right click and “save as”)
This file contains the source code: chap6_2.pas (right click and “save as”)
This file is the executable: chap6_2.exe (right click and “save as”)

The final result should look and behave like this: While being with the mouse onto the SDL application window any event from the keyboard is recognized and it is said what exactly happened (button down, up, …)

Result of JEDI-SDL Chapter 6, keyboard handling

The final result should look and behave like this: While being with the mouse onto the SDL application window any event from the mouse is recognized and its (x/y) position, the difference between last recognized position and new position (dX and dY) and if a button is pressed or released and the corresponding button’s index is shown.

Result of JEDI-SDL Chapter 6, mouse handling

Chapter 4: Drawing pixels (JEDI-SDL)

This is an SDL 1.2 chapter. SDL 1.2 is obsolete since it has been replaced by SDL 2.0. Unless you have good reasons to stay here you may prefer to go for the modern SDL 2.0 :-).

Okay. Maybe you think, why to learn how to display pictures first and then learning about the more basic feature to draw single pixels to the screen? – First of all blitting is one of the key features whereas drawing of single pixels to the screen is quite rarely used in applications and games. Though this chapter will get a little bit longer because several things have to be explained more detailed to impart a fully understanding.

We have to know about the meaning of pixels. The physical screen consists of many small units. Every unit consists itself of three different coloured lights. These colours are red, green and blue. If you mix them (additive) you can get every other colour. For example if you mix red and green you get yellow. For three colours that can be mixed with each other there are eight combinations possible which lead to different colours (RGB, RG, RB, R, GB, G, B, all lights off). Some of you may say RGB (white) and all lights off (black) are no colours. That is right but doesn’t matter here and to keep simpliness I will talk of colours even if I talk of black and white.

Did you get confused? Your screen definitvly has more than eight colours, doesn’t it? The reason is, your screen isn’t just able to switch lights on or off. Besides it is able to differ the intensities of the lights. The more intensity levels you have the more colours you can display. The case that you have eight colours as discussed before means that you just have one intensity level. If your screen is in 8 bit mode every pixel on the screen has the possibility to display 2 power 8 colours. That are 256 different colours. Every of the three lights has therefore a certain amount of different intensity levels. If you have 16 bit mode you have 2 power 16 and that are 65536 colours. Each light therefore has the appropriate amount of intensity levels. Since we prefer 32 bit mode, we have 4.29 billion different colours!

All the examples will be at 32 bit mode. If you want to write directly to the screen surface it is possibly necessary that you lock it before performing direct pixel manipulations. We work in software mode (SDL_SWSURFACE) so we can leave locking the screen surface. However, other modes will require you to lock it. Not shown in the example code you can check it by function SDL_MUSTLOCK(surface:pSDL_SURFACE):BOOLEAN. If it evaluates to FALSE or 0 you don’t need to worry about locking. Otherwise you have to lock the surface by function SDL_LOCKSURFACE(surface:pSDL_SURFACE):INTEGER for direct pixel manipulations. It will return 0 if locking was succesful and -1 if locking failed. After pixel manipulations you have to unlock the surface by procedure SDL_UNLOCKSURFACE(surface:pSDL_SURFACE) straight forward.

Now the whole example code is presented. The goal is to have 200 x 200 pixels wide black surface. A single yellow pixel will start to move from the top-left corner along the surface from left to right and line by line until it reaches the bottom of the surface. It then will restart at the top-left corner.

PROGRAM chap4;
USES CRT, SDL;

VAR
screen:pSDL_SURFACE;
pixellocation:^LONGWORD;
pixelcolor,i:LONGWORD;

BEGIN

SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(200,200,32,SDL_SWSURFACE);
IF screen=NIL THEN HALT;

NEW(pixellocation);
WRITE('Pitch value: ',screen^.pitch DIV screen^.format^.BytesPerPixel);

i:=0;
REPEAT
inc(i); IF i>39999 THEN i:=0;

pixellocation:=screen^.pixels+i;               //delete old pixel
pixelcolor:=SDL_MAPRGB(screen^.format,0,0,0);
pixellocation^:=pixelcolor;


pixellocation:=screen^.pixels+i+1;             //draw new pixel
pixelcolor:=SDL_MAPRGB(screen^.format,255,255,0);
pixellocation^:=pixelcolor;

SDL_FLIP(screen);
SDL_DELAY(5);
UNTIL keypressed;

DISPOSE(pixellocation);
SDL_FREESURFACE(screen);
SDL_QUIT;
END.

Now we will go through the whole code step by step. Especially direct pixel manipulations are often misunderstood by newcomers to SDL.

PROGRAM chap4;
USES CRT, SDL;

VAR
screen:pSDL_SURFACE;
pixellocation:^LONGWORD;
pixelcolor,i:LONGWORD;

BEGIN

SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(200,200,32,SDL_SWSURFACE);
IF screen=NIL THEN HALT;

The program is called chap4. As known from previous chapters unit CRT is needed here for a simple input handling and unit SDL is needed for SDL support. The screen variable is known from previous chapters as well and will contain the content which is displayed at the physical screen later. The pixellocation variable is a pointer type variable indicated by the tilde (^). Furthermore the space it’ll point at needs to be of size for a Longword value. The two variables pixelcolor and i are common Longword variables.

The SDL program is initilized as known and the display surface is of size 200 x 200 px and has 32 bit. Except from the pointer variable pixellocation there is nothing new up to here.

NEW(pixellocation);
WRITE('Pitch value: ',screen^.pitch DIV screen^.format^.BytesPerPixel);

i:=0;
REPEAT
inc(i); IF i>39999 THEN i:=0;

In the first line of this part of the code the pointer pixellocation gets some space by the common Free Pascal function NEW(pointer).

Next the pitch value of the surface is printed. Please note, if you create a surface of a certain width and height the true surface can be a little bit wider. In our example this means you have a window of width and height of 200 pixels displayed but there could be an additional unvisible piece of surface. Maybe its true width is 210, so the moving pixel wouldn’t restart at a new line after 200 cycles but 210 cycles. To solve problems caused by this pitch to the surface you can read the pitch out. The data type pitch will allow you to do this (e.g. screen^.pitch). Unfortunately the pitch is not given in pixels (as the width value) but in bytes so you have to devide its value by the BytesPerPixel value which is for our 32 bit surface format usually 4. This value can be read out by screen^.format^.BytesPerPixel. You will see that in our example the width and the pitch are equal (both 200) which means there is no exceeding surface part.

Counter variable i is set to 0. Then the repeat loop is entered. Variable i is increased by 1 each cycle until it reaches 39999. It controls the position of the dot. When i reaches 39999 exactly 40000 cycles are done and the dot should restart at the top-left corner.

Now it gets interesting.

pixellocation:=screen^.pixels+i;               //delete old pixel
pixelcolor:=SDL_MAPRGB(screen^.format,0,0,0);
pixellocation^:=pixelcolor;

Let’s first consider the second line concerning the colour of pixels. If you want to add colour to a single pixel you need to know the corresponding pixel value defining the colour of it. The function SDL_MAPRGB(format:pSDL_PIXELFORMAT; r:UInt8; g:UInt8; b:UInt8):UInt32 or in short SDL_MAPRGB(pixel format, red, green, blue) will make it easy for you to find it out. Just add the pixel format of the surface which is stored in the surface’s format field. For the example surface the expression is screen^.format. A RGB triple of (0,0,0) will lead to black, all lights off (which is also the background colour of the surface). The expected variable type for the RGB triple values are of UInt8 meaning an unsigned 8 bit integer which corresponds to a Free Pascal Byte type variable with a range from 0 to 255. The function will return an unsigned 32 bit integer (UInt32) value which corresponds Free Pascal’s Longword type variable.

Now we are concerned about the actual allocation of a colour to a specific pixel. Every surface provides the possibility to access its pixels, for example by screen^.pixels. The marker pixels is of type Pointer and points to a certain pixel of the surface. By adding to or substraction from it you can easily access any pixel on the surface. So any pixel’s coordinate on the screen can be accessed by using ONE number instead of a pair of (x,y) values. This is an important concept. The integer value screen^.pixels is pointing at represents the colour of this specific pixel. So, don’t confuse these two values. The value of the pixels pointer itself determines the pixel addressed, the value it is pointing at determines the pixel’s colour. The following diagram may help to explain this.

Diagram explaining pixel locationFor our 32 bit surface all the pixels are of size Uint32 (unsigned integer 32 bit which equals Longword). That is why we initially made variable pixellocation of type Pointer which will point at values of size Longword. In the first cycle i has the value of 1. So from the first line we see that pixellocation points at the colour value of the screens first pixel. This pixel is black. In the second line the colour value for a black pixel is generated and given to pixelcolor. The third line allocates the black colour value to the first pixel. This actually doesn’t change anything.

pixellocation:=screen^.pixels+i+1;             //draw new pixel
pixelcolor:=SDL_MAPRGB(screen^.format,255,255,0);
pixellocation^:=pixelcolor;

If you compare these three lines of code with the previous ones you will see just minor differences. In the first line pixelcolor additionally is increased by 1 which means we refer here to the pixel next to the previous one. The pixel colour defined here (line 2) is another one, it is yellow. Therefore red has to be 255, green has to be 255 and blue has to be 0. The third line is identical.

Now you get a clear picture of what actually is happening to get the effect of a moving pixel. For each cycle the yellow pixel from the previous cycle is overdrawn by a black pixel and then the very next pixel is drawn yellow. This leads to the impression of a moving pixel.

SDL_FLIP(screen);
SDL_DELAY(5);
UNTIL keypressed;

DISPOSE(pixellocation);
SDL_FREESURFACE(screen);
SDL_QUIT;
END.

When all pixel manipulations are done the screen has to be refreshed as known by SDL_FLIP. To be able to recognize all the pixel movement the loop has to be slown down by known SDL_DELAY. The loop is left if the user presses a key. Finally the allocated memory of the Pointer variable pixellocation and the screen surface are free’d by known DISPOSE and SDL_FREESURFACE. The SDL system is quit by known SDL_QUIT and the program stopped by END.

This file contains the source code: chap4.pas (right click and “save as”)
This file is the executable: chap4.exe (right click and “save as”)

If you didn’t check the demo03.pas program from last chapter you really should check it now. It demonstrates how you can use the pixel by pixel manipulation to create nice effects and the basis for a simple game.

This file contains the source code: demo03.pas (right click and “save as”)
This file is the executable: demo03.exe (right click and “save as”)

The final result should look and behave like this: You have to look quite carefully to recognize a single yellow pixel moving from the left top corner, line by line down until it reaches the right bottom corner.

Result of JEDI-SDL Chapter 4

Chapter 3: Displaying a picture (JEDI-SDL)

This is an SDL 1.2 chapter. SDL 1.2 is obsolete since it has been replaced by SDL 2.0. Unless you have good reasons to stay here you may prefer to go for the modern SDL 2.0 :-).

Working with the video subsystem assumes that you understand the concept behind. In SDL the surface is a central concept. Surfaces are parts of the memory (RAM) where an image is saved. Every surface can have its own size (pixels x pixels, e.g. 640 x 480) and properties (e.g. with or without alpha channel for transparency effects). You can copy(=blit) a part or the whole surface to any other surface. This is the second very important concept and is called blitting

There is a special surface: the display surface. Everything that got blit to this surface gets displayed on the physical screen (monitor) after refreshing (SDL_FLIP(…), SDL_UPDATERECT(…))!

For example in demo03.pas / demo03.exe from a modified SDL4Free Pascal demo file the ball, the paddle and the black background (display surface background color) are surfaces. The ball and the paddle are copied(=blitted) onto the black screen surface again and again slightly moved up. So the ball and the paddle seem to move.

left: ball and paddle erased between each frame; right: ball and paddle not erased between framesIf you think carefully about this or check the source code you will notice that between every blitting process the ball and paddle of the previous frame have to be overdrawn by black rectangles erasing them (left), otherwise the result would look like the right example.

diagram showing path from image file to displaySo our task is to create a display surface. Furthermore we need a surface that contains a picture. Eventually the picture should get copied onto the display surface. After that the picture should be displayed at the physical screen. The whole code looks like this.

PROGRAM chap3;
USES SDL, CRT;
VAR
screen,picture:pSDL_SURFACE;
source_rect,destination_rect:pSDL_RECT;

BEGIN
SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(200,200,32,SDL_SWSURFACE);
IF screen=NIL THEN HALT;

picture:=SDL_LOADBMP('C:\FPC\2.0.4\bin\i386-win32\test\fpsdl.bmp');
IF picture=NIL THEN HALT;

NEW(source_rect);
source_rect^.x:=0;
source_rect^.y:=0;
source_rect^.w:=200;
source_rect^.h:=200;
NEW(destination_rect);
destination_rect^.x:=0;
destination_rect^.y:=0;
destination_rect^.w:=200;
destination_rect^.h:=200;

REPEAT
  SDL_BLITSURFACE(picture,source_rect,screen,destination_rect);
  SDL_FLIP(screen);
  DEC(source_rect^.w);
  DEC(source_rect^.h);
  INC(destination_rect^.x);
  INC(destination_rect^.y);
  DEC(destination_rect^.w);
  DEC(destination_rect^.h);
  SDL_DELAY(30);
  IF source_rect^.w=1 THEN
  BEGIN
    source_rect^.x:=0;
    source_rect^.y:=0;
    source_rect^.w:=200;
    source_rect^.h:=200;
    destination_rect^.x:=0;
    destination_rect^.y:=0;
    destination_rect^.w:=200;
    destination_rect^.h:=200;
  END;
UNTIL keypressed;

SDL_FREESURFACE(picture);
SDL_FREESURFACE(screen);

DISPOSE(source_rect);
DISPOSE(destination_rect);

SDL_QUIT;
END.

Now we need a command to create a display surface. Creating a surface is always introduced by setting up a surface variable. The variable type is pSDL_SURFACE and is kind of pointer type as indicated by the “p” in front of it. pSDL_SURFACE is used for the display surface and any other surface.

PROGRAM chap3;
USES SDL, CRT;
VAR
screen,picture:pSDL_SURFACE;
source_rect,destination_rect:pSDL_RECT;

The first line determines the program name. Then we again define that we want to use the SDL unit to be loaded. We introduce the CRT unit which is a basic Pascal unit (check Chapter 2 for more details on CRT). It is needed to detect in a simple way when the user presses a key to stop the repeat/until loop (in Chapter 6 we will learn how SDL handles keyboard interactions). Finally we decide to set up two surfaces. The display surface (screen) has some special properties that can be set. The surface “picture” will store the picutre from a bitmap file. The two variabes “source_rect” and “destination_rect” will be used later to define some rectangles. More details on this later in this chapter.

After using surfaces you have to clean the memory if you don’t need them anymore. Therefore you use the procedure SDL_FREESURFACE(surface:pSDL_Surface) which you have to call for any surface (including the display surface).

BEGIN
SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(200,200,32,SDL_SWSURFACE);
IF screen=NIL THEN HALT;

After initializing SDL as seen in the previous chapter already we now go into details on the set up of the display surface. The function to define a surface as display surface is SDL_SETVIDEOMODE(width,height,bpp:INTEGER; flags:UInt32):pSDL_SURFACE and returns NIL if an errors occurs meaning no pointer for a display surface could be set up. The first parameter determines the width, the second one the height in pixels. The third parameter determines the colordepth in bits (32 bit for now) and the last one the appearance (windowed or fullscreen, with or without border) and space handling (software or hardware memory used). The first three parameters are of integer type, the last one UInt32 (check Chapter 2 for more details on UInt32). There are some very interesting flags. The chosen one, SDL_SWSURFACE, is used if you want to store the surface in system memory (RAM). Alternatively you could chose SDL_HWSURFACE what causes a storage in video memory. Both will create windowed screens that cannot be resized. SDL_NOFRAME would create a windowed screen without a frame. Eventually there is SDL_FULLSCREEN which leads to a fullscreen display. You also can combine several flags by “OR” keyword. So setting up this same window without a frame would be screen:=SDL_SETVIDEOMODE(200,200,32,SDL_SWSURFACE OR SDL_NOFRAME).

Direct loading of a picture to the screen surface is impossible so it is necessary to create another surface that contains the picture. Fourtunately there is a function called SDL_LOADBMP(filename:pCHAR):pSDL_SURFACE. Make sure you give the FULL path for “filename”; relative pathes are not allowed. If you drew something onto the screen and would like to save it as a bitmap file the corresponding function is called SDL_SAVEBMP(surface:pSDL_SURFACE; filename:pCHAR):INTEGER. The surface you want to save doesn’t have to be necessarily the screen surface. It returns 0 on success and -1 on error. Incidentally there is no such import/export feature for other graphic formats (that doesn’t mean there are no other easy ways to use other important graphic formats ;), check Chapter 3a for details on this).

Free Pascal meets SDL sample image bmp format
download (right click and “save as”)

This picture (8 bit) is the one that will be copied to the screen. The pictures width and height are both exactly 200 pixels.

{add the correct path here}
picture:=SDL_LOADBMP('C:\FPC\2.0.4\test\fpsdl.bmp');
{if the path is wrong you will get an abortion}
IF picture=NIL THEN HALT;

The surface is named “picture” here and the file fpsdl.bmp is located at the given directory, “C:\FPC\2.0.4\test”. As already known from setting of the screen variable the SDL_LOADBMP(path) function returns NIL if something is wrong. The path has to be of type pCHAR.

Actually we now could copy the loaded picture from the picture surface to the display surface. Remember: This process is called blitting. But to demonstrate another quite usable feature strongly related to picture blitting we will now define some rectangles. Instead of blitting the whole image we are then also able to blit just parts of the source image.

NEW(source_rect);
source_rect^.x:=0;
source_rect^.y:=0;
source_rect^.w:=200;
source_rect^.h:=200;
NEW(destination_rect);
destination_rect^.x:=0;
destination_rect^.y:=0;
destination_rect^.w:=200;
destination_rect^.h:=200;

Now let’s have a look at the code. We define the rectangles called source_rect and destination_rect which store what part of the source image will be copied to what position at the destination surface (here: display surface). The command NEW(pointer) is a common Pascal command and should be known. source_rect and destination_rect are of type pSDL_RECT and are simple records which store a (x,y) and a (width, height) pair. The values set for both rectangles are identical. They are further exactly of the same size as our display surface and our image. The coordinates of x and y relate to the corresponding surface so (x/y) is (0/0) here for both rectangles and corresponds to the left upper corner. From this point to the right direction the width is 200 pixels. The height is from this point 200 pixels down to the bottom side of the display surface. The following image may explain this.

The display surfaceNext we will enter the repeat/until loop.

REPEAT
  SDL_BLITSURFACE(picture,source_rect,screen,destination_rect);
  SDL_FLIP(screen);
  DEC(source_rect^.w);
  DEC(source_rect^.h);
  INC(destination_rect^.x);
  INC(destination_rect^.y);
  DEC(destination_rect^.w);
  DEC(destination_rect^.h);
  SDL_DELAY(30);

SDL_BLITSURFACE(src:pSDL_SURFACE; srcrect:pSDL_RECT; dst:pSDL_SURFACE; dstrect:pSDL_RECT):INTEGER is the needed command for blit processes. For successful blitting (copying) four parameters are requested. The first determines the source surface, the third the destination surface. The second and the fourth parameter can be NIL. Then the whole source surface is blitted to the (0,0) position (upper left corner) of the destination surface. If you want to blit the source surface to any other position on the destination surface (which is usually the case) you have to provide the coordinates by usage of the pSDL_RECT record. In the first run the content of surface “picture” within the defined rectangle of “source_rect” (x/y)=(0/0) and (w/h)=(200/200) is blitted to the display surface within the defined rectangle of “destination_rect” (x/y)=(0/0) and (w/h)=(200/200). Actually this means the complete content of the “picture” surface is blitted to the display surface.

After the blit process is finished the display surface has to be refreshed. Procedure SDL_UPDATERECT(screen:pSDL_SURFACE; x,y:SInt32; w,h:UInt32) or function SDL_FLIP(screen:pSDL_SURFACE):INTEGER are doing that. The SDL_FLIP(screen surface) command you use if you want to refresh the whole screen surface and furthermore if you want to use double buffering. For most applications and games therefore the SDL_FLIP(screen surface) is much more common. It is equal to SDL_UPDATERECT(screen surface,0,0,0,0) if double buffering is disabled. In comparision to SDL_UPDATERECT, SDL_FLIP furthermore returns an error value, 0 if successful and -1 on error.

The SDL_UPDATERECT command requests five parameters. The first parameter determines which surface has to be refreshed (usually the display surface). The next four ones determine a rectangel with: x-position, y-position (from top to bottom), width (relative to x,y-position), height (relative to x,y-position). If all of these paramters are “0” the whole surface will get updated.

In the example for any further cycle though the x, y, w and h values get incremented or decremented as shown. The destination-x and y values get incremented by one each cycle so it is progressing to the right bottom corner. This leads to the impression that the picture is slipping to the right bottom corner. The width and height values are fit respectivly.

The new introduced SDL_Delay(msec: UInt32) procedure works the same way as the common delay command of the CRT unit in Pascal. It delays the run of the program for the given time in milli seconds. This is a key procedure for nearly any program. If you would remove the delay procedure the program is running so fast, you wouldn’t be able to recognise the shifting but see some flickering. Since this sometimes even leads to abortions of the program I do not advise you to try it out.

ATTENTION: For a fast and clean blitting you should note that both surfaces (source and destination surface) should have the same bit depth! In our example we use a 32 bit screen surface (screen) and blit an 8 bit source surface (picture) onto it (because we loaded an 8 bit image file (fpsdl.bmp) to this surface). This is just an example and therefore it is okay, but you should avoid this especially if you plan to create games or applications where the FPS (frames per second) count is important.

IF source_rect^.w=1 THEN
  BEGIN
    source_rect^.x:=0;
    source_rect^.y:=0;
    source_rect^.w:=200;
    source_rect^.h:=200;
    destination_rect^.x:=0;
    destination_rect^.y:=0;
    destination_rect^.w:=200;
    destination_rect^.h:=200;
  END;
UNTIL keypressed;

The Repeat loop shrinkens the source rectangle from bottom right corner. At the same time the position at the destination surface is increased at the same rate. When the rectangles are of size 1 all values get restored.

When any key is pressed the loop is exited.

SDL_FREESURFACE(picture);
SDL_FREESURFACE(screen);

DISPOSE(source_rect);
DISPOSE(destination_rect);

SDL_QUIT;
END.

The allocated memory of the picture and the display surface and of both rectangles get disposed. Finally SDL has to be quit and so has the program.

This file contains the source code: chap3.pas (right click and “save as”).
This file contains the executable: chap3.exe (right click and “save as”).

Hope you are successful and have fun. The final result should look and behave like this: The blue box with the text “Free Pascal meets SDL” is slipping to the right bottom corner. When it disappears the scene is reset.

Result of example program

Chapter 2: First steps using SDL (JEDI-SDL)

This is an SDL 1.2 chapter. SDL 1.2 is obsolete since it has been replaced by SDL 2.0. Unless you have good reasons to stay here you may prefer to go for the modern SDL 2.0 :-).

You should be slightly experienced in programming Free Pascal. That means you should be familiar with procedures, functions, loops and usual commands of Free Pascal. If this is not the case you may have problems because this tutorial deals with the features and usage of the SDL library and will not explain basic concepts of Pascal programming. Here I have to set the CRT unit off which was provided along with Turbo Pascal and is also available for Free Pascal by default. It was used in Turbo Pascal for screen and keyboard handling. In many chapters this unit will be used becaused it allows a simple way to recognize if a user pressed a key by a REPEAT … UNTIL keypressed statement. However, this unit is not related to SDL. SDL’s own advanced keyboard handling described in Chapter 6 is not used to keep the code short.

In the original SDL library there are several standard units (called headers in C/C++) whereas in JEDI-SDL all of them got merged to only one unit called “SDL”. To prepare your Free Pascal program for the usage of SDL you just have to call this unit at the uses clause. The SDL unit is the heart of every SDL application!

The different features of SDL (screen handling, audio handling, keyboard handling, cdrom handling, and so on) have to be initilized using the SDL_INIT(flags:UInt32):INTEGER function. You may wonder about the variable type UInt32 of “flags”. The U stands for “unsigned” (values greater or euqal zero only!), the Int stands for “integer” and the number stands for the bit value. So it is an 32 bit unsigned integer (which corresponds to Free Pascal’s longword). Such integer types (which we will see in other chapters again) originate from the original C/C++ SDL code, and for an easier translation to Pascal they were kept.

In the first example we want to initialize the SDL video subsystem for screen handling. The following code example shows the frame of a SDL application.

PROGRAM chap2;
USES SDL;

BEGIN
SDL_INIT(SDL_INIT_VIDEO);

{further code}

SDL_QUIT;
END.

Instead of SDL_INIT_VIDEO you could initilize the respective subsystem by using SDL_INIT_AUDIO, SDL_INIT_CDROM, SDL_INIT_JOYSTICK, SDL_INIT_TIMER, SDL_INIT_NOPARACHUTE, SDL_INIT_EVENTTHREAD, SDL_INIT_EVERYTHING. So if you want to use the features provided by the library for sound handling you should initialize it by using SDL_INIT_AUDIO. If you use SDL_INIT_EVERYTHING all subsystems are initialized. You also can combine several components by the OR keyword, e.g. “SDL_INIT(SDL_INIT_VIDEO OR SDL_INIT_AUDIO)” to initilize video and audio support. If you are running some subsystems already but need to load further ones you would use SDL_INITSUBSYSTEM(flags:UInt32):INTEGER respectivly.

If you look at the exact definition carefully you may wonder about the integer value the function returns. You shoud notice that every SDL function usually returns an error value for you to check if the function runs properly at runtime. There is no general rule what values correspond to which status. Usually values like 0 and 1 correspond to a status “function runs successfully”, values 0 and -1 correspond to a status “function couldn’t be run, something is wrong”. For SDL_INIT the function is run successfully if it returns 0, otherwise there is something wrong. However, in most cases I won’t do error checking to keep code examples short but will mention the error values to be expected.

Every SDL program has to be closed by SDL_QUIT. It cleans up your system. Never forget it! This procedure ensures that all subsystems initilized get unloaded. There is a corresponding procedure to unload specific subsystems defined as SDL_QUITSUBSYSTEM(flags:UInt32 ). The same arguments you use to load subsystems you can use here to unload them.

Now you can try the example program.

Well, we have initilized the video subsystem and released it afterwards. Simple Directmedia Layer deserves its name, isn’t it?

Chapter 6: Event handling (SDL4FP)

This is an SDL 1.2 chapter. SDL 1.2 is obsolete since it has been replaced by SDL 2.0. Unless you have good reasons to stay here you may prefer to go for the modern SDL 2.0 :-).

If the user is moving the mouse, pressing/releasing a button on the keyboard or pressing/releasing the fire key on the joystick then you speak of events. Further events are resizing a window or switching between several applications. For games though the events described first are much more important. SDL provides a quite easy way to notice and react to such events, which is called event handling.

First of all using event handling means to understand the concept of event handling. Events have a special structure you should know about. For example an pressed key is an event or a moved mouse is an event. SDL differs altogether fourteen such events. Every event stores different information depending on its kind. For example the key board event stores the information which key was pressed. The mouse motion event stores the information to which position the mouse got moved. If you got a mouse motion event you can’t read out key information and so on. Therefore you have the following general structure to the event data: event.eventstructure.data.

To make event handling possible you have to include the unit SDL_EVENTS. The event handling subsystem is automatically initialized along with the video subsystem.

We have to create an event variable which is of pointer type pSDL_EVENT. We will create a second variable of boolean type just to control the while loop.

PROGRAM chap6_1;
USES crt, SDL, SDL_VIDEO, SDL_EVENTS;

VAR
screen:pSDL_SURFACE;
loopstop:boolean=FALSE;
test_event:pSDL_EVENT;

BEGIN
CLRSCR;
SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(200,200,8,SDL_SWSURFACE);
IF screen=NIL THEN HALT;

NEW(test_event);

DISPOSE(test_event);
SDL_FREESURFACE(screen);
SDL_QUIT;
END.

We want to check the program if any event occured and if so, we want to know which kind of event happened. So we make a while loop which will run until loopstop gets true.

The command SDL_POLLEVENT(parameter) checks if there are pending events and if so it will take the oldest and save it to parameter which is an event record of pSDL_EVENT type. For example if the user presses (then releases) left mouse button, then presses (then releases) space button on keyboard and finally moves the mouse there are altogether five events: 1. left mouse button pressed, 2. left mouse button released, 3. space key pressed, 4. space key released, 5. mouse moved. If you poll for events now you will get the first event (left mouse button pressed) and saved it’s properties to the event variable we specified as parameter. The next poll will save the properties of next event (left mouse button released) to event variable and so on. SDL_POLLEVENT(parameter) will return 1 if it has found pending event or 0 if there wasn’t any pending event.

Fortunately you don’t have to notice any event which is made by the user ;). By event^.eventtype you can easily check which type of event you got. In the case described before it would be 1. SDL_MOUSEBUTTONDOWN, 2. SDL_MOUSEBUTTONUP, 3. SDL_KEYDOWN, 4. SDL_KEYUP, 5. SDL_MOUSEMOTION. It should be senseful to check for the event kind by using the case command.

PROGRAM chap6_1;
USES crt, SDL, SDL_VIDEO, SDL_EVENTS;

VAR
screen:pSDL_SURFACE;
loopstop:boolean=FALSE;
test_event:pSDL_EVENT;

BEGIN
CLRSCR;
SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(200,200,8,SDL_SWSURFACE);
IF screen=NIL THEN HALT;

NEW(test_event);

WHILE loopstop=FALSE DO
BEGIN
IF SDL_POLLEVENT(test_event)=1 THEN
BEGIN
write('pending event: ');

CASE test_event^.eventtype OF
SDL_EVENTACTIVE: writeln('Application is/is not active');
SDL_KEYDOWN: writeln('Key pressed ');
SDL_KEYUP: writeln('Key released');
SDL_MOUSEMOTION: writeln('Mouse motion');
SDL_MOUSEBUTTONDOWN: writeln('Mouse button down');
SDL_MOUSEBUTTONUP: writeln('Mouse button up');
SDL_JOYAXISMOTION: writeln('Joystick axis motion');
SDL_JOYBALLMOTION: writeln('Joystick's trackball motion');
SDL_JOYHATMOTION: writeln('Joystick's hat position changed');
SDL_JOYBUTTONDOWN: writeln('Joystick button pressed');
SDL_JOYBUTTONUP: writeln('Joystick button released');
SDL_EVENTQUIT: writeln('User-requested quit');
END;

END ELSE writeln('no pending events');
DELAY(150);
END;
DISPOSE(test_event);
SDL_FREESURFACE(screen);
SDL_QUIT;
END.

Actually you shouldn’t try this source as it is written there because it will end up in an endless loop. The DELAY command is used because otherwise you won’t notice any pending events because they are polled too fast and it seems as if there would never be any pending events (just remove DELAY to confirm).

We now want to try the program to read out which key the user pressed on the keyboard. If the key is the escape-key (Esc) the program should stop. We already check the type of the event we polled. So if the event is a keyboard event the further checking should proceed. In our case especially if a key gets pressed. An keyboard event record contains a field called keysym. Keysym is a record of SDL_KEYSYM. SDL_KEYSYM is defined in SDL_KEYBOARD unit. It contains four fields. Scancode which is hardware dependent scancode should be avoided if you want to make hardware independent programs. It is usual integer variable. Next is sym which stores SDLKEY. These SDL keys are independent and it is strongly recommended to use them! In the example we want break up if escape key gets pressed. Its SDL key code is 27. If you want to know what SDL keys are defined look up in the table page. The variable modifier stores modifier keys (like shift, ctrl,…) pressed and stores SDLMod. SDL modifier keys can be found at table page as well. The fourth variable is unicode which may be used to read out unicode characters but to use this you have to enable it what is not described in this tutorial.

To read out or compare the key the user pressed we must use the expression test_event^.key.keysym.sym. Do you remember the general structure of an event (event.eventstructure.data)? – test_event^ is the event, the event structure is defined as an key event by key and the data we want to know is the key pressed contained in keysym.sym.

PROGRAM chap6_1;
USES crt, SDL, SDL_VIDEO, SDL_EVENTS;

VAR
screen:pSDL_SURFACE;
loopstop:boolean=FALSE;
test_event:pSDL_EVENT;

BEGIN
CLRSCR;
SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(200,200,8,SDL_SWSURFACE);
IF screen=NIL THEN HALT;

NEW(test_event);

WHILE loopstop=FALSE DO
BEGIN
IF SDL_POLLEVENT(test_event)=1 THEN
BEGIN
write('pending event: ');

CASE test_event^.eventtype OF
SDL_EVENTACTIVE: writeln('Application is/is not active');
SDL_KEYDOWN:

BEGIN
write('Key pressed ');
writeln('(SDLKey=',test_event^.key.keysym.sym,')');
IF test_event^.key.keysym.sym=27 THEN loopstop:=true;
END;

SDL_KEYUP: writeln('Key released');
SDL_MOUSEMOTION: writeln('Mouse motion');
SDL_MOUSEBUTTONDOWN: writeln('Mouse button down');
SDL_MOUSEBUTTONUP: writeln('Mouse button up');
SDL_JOYAXISMOTION: writeln('Joystick axis motion');
SDL_JOYBALLMOTION: writeln('Joystick's trackball motion');
SDL_JOYHATMOTION: writeln('Joystick's hat position changed');
SDL_JOYBUTTONDOWN: writeln('Joystick button pressed');
SDL_JOYBUTTONUP: writeln('Joystick button released');
SDL_EVENTQUIT: writeln('User-requested quit');
END;

END ELSE writeln('no pending events');
DELAY(150);
END;
DISPOSE(test_event);
SDL_FREESURFACE(screen);
SDL_QUIT;
END.

Actually this is easy, isn’t it?

This file contains the source code: chap6_1.pas (right click and “save as”)
This file is the executable: chap6_1.exe (right click and “save as”)

ATTENTION: This is just the first part. The second part will describe mouse handling, which is much easier.

Chapter 3: Displaying a picture (SDL4FP)

This is an SDL 1.2 chapter. SDL 1.2 is obsolete since it has been replaced by SDL 2.0. Unless you have good reasons to stay here you may prefer to go for the modern SDL 2.0 :-).

Working with the video subsystem assumes that you understand the concept behind. The screen is supposed to be a surface (everything on this surface gets displayed on the physical screen!). Every object you want to display on the screen can be an own surface. Every object can be copied to the screen surface. Surfaces can be manipulated.

For example in the demo03.pp from the SDL4Freepascal files the ball, the paddle and the black background (screen) are surfaces. The ball and the paddle are copied onto the screen surface again and again slightly moved up. So the ball and the paddle are giving the impression that they are moving.

So our task is to create a screen surface. Furthermore we need a surface that contains a picture. Eventually the picture should get copied onto the screen surface. After that the picture should be displayed at the physical screen.

PROGRAM test;
USES crt, SDL, SDL_video;

BEGIN
SDL_INIT(SDL_INIT_VIDEO);

{further code}

SDL_QUIT;
END.

That is where we stopped last chapter. Now we need a command to create a screen surface. Creating a surface is always introduced by setting up a surface variable. The variable type is pSDL_SURFACE and is kind of pointer type as indicated by the “p” in front of it.

The screen surface is a special kind of surface. It has some special properties that can be set. You can set appearance, height, width and colordepth that is displayed by it. The command to make a surface to a screen surface is SDL_SETVIDEOMODE(parameters).

Similar to the SDL_QUIT procedure, after using surfaces you have to clean the memory if you don’t need them anymore. Therefore you use SDL_FREESURFACE(surface).

PROGRAM test;
USES crt, SDL, SDL_video;

VAR
screen:pSDL_SURFACE;

BEGIN
CLRSCR;
SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(200,200,8,SDL_SWSURFACE);
if screen=nil then HALT;

readln;
SDL_FREESURFACE(screen);
SDL_QUIT;
END.

The function to set the screen surface returns nil if an errors occurs. The first parameter determines the width, the second one the height. The third parameter determines the colordepth (8 bit for now) and the last one the appearance (video mode) and space handling. The first three parameters are longint typ, the last one Uint32, where the U stands for “unsigned” (values greater or euqal nil only!), the int stands for “integer” and the number stands for the bit per pixel count. So it is an 32 bit unsigned integer. There are some very interesting values. The chosen one, SDL_SWSURFACE, is used if you want to store the surface in system memory (RAM). Alternatively you could chose SDL_HWSURFACE what causes a storage in video memory. Both will create windowed screens that cannot be resized. SDL_NOFRAME would create a windowed screen without a frame. Eventually there is SDL_FULLSCREEN which leads to a fullscreen display. There are some other possibilities which may be told about in further chapters.

If you run the program you see a black window because nothing is located on the screen surface.

It is necessary to create another surface that contains the picture. Then we need a possibility to load the picture to the surface. Fourtunately there is a command called SDL_LOADBMP(file). The corresponding command is called SDL_SAVEBMP(surface,file) if you drew something onto the screen and would like to save it as a bitmap file. Incidentally there is no such import/export feature for other graphic formats (that doesn’t mean there are no other easy ways to use other important graphic formats ;)).

Free Pascal meets SDL sample image bmp format
download (right click and “save as”)

This picture (8 bit) is the one that will be copied to the screen. The pictures width and height are both exactly 200 pixels.

PROGRAM test;
USES crt, SDL, SDL_video;

VAR
screen,picture:pSDL_SURFACE;

BEGIN
CLRSCR;
SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(200,200,8,SDL_SWSURFACE);
if screen=nil then HALT;

picture:=SDL_LOADBMP('fpsdl.bmp');
if picture=nil then HALT;

readln;
SDL_FREESURFACE(picture);
SDL_FREESURFACE(screen);
SDL_QUIT;
END.

The surface is named “picture” here and the file fpsdl.bmp is located in the same directory as the source file. As already known from setting of the screen variable the SDL_LOADBMP command returns nil if something’s wrong.

In the end we need to copy the loaded picture from the picture surface to the screen surface. This process is called blitting and is made by this command: SDL_BLITSURFACE(parameters).

After the blit process the screen surface has to be refreshed. SDL_UPDATERECT(parameters) is doing that.

PROGRAM test;
USES crt, SDL, SDL_video;

VAR
screen,picture:pSDL_SURFACE;

BEGIN
CLRSCR;
SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(200,200,8,SDL_SWSURFACE);
if screen=nil then HALT;

picture:=SDL_LOADBMP('fpsdl.bmp');
if picture=nil then HALT;

SDL_BLITSURFACE(picture,nil,screen,nil);
SDL_UPDATERECT(screen,15,15,170,170);

readln;
SDL_FREESURFACE(picture);
SDL_FREESURFACE(screen);
SDL_QUIT;
END.

For successful blitting four parameters are requested. The first determines the source surface, the third the destination surface. The second and the fourth parameter can be used if you don’t want to blit the whole surface but a certain rectangle. So you could determine that you want to blit just the upper left quarter of the picture surface (second parameter). Furthermore you could want it copied to the lower right quarter of the screen surface (fourth parameter). If these parameters are nil it means the whole surface is blitted.

As you can see the SDL_UPDATERECT command requests five parameters. The first of them determines which surface has to be refreshed. The next four determine a rectangel with: x-direction, y-direction (from top to bottom), width (relative to x,y-position), height (relative to x,y.position). If all of these paramters are “0” the whole surface will get updated.

This file contains the source code: test.pas (right click and “save as”). The file and the graphic file have to be in the same folder.

Hope you are successful and have fun.

Chapter 5: Writing and using fonts (SDL4FP)

This is an SDL 1.2 chapter. SDL 1.2 is obsolete since it has been replaced by SDL 2.0. Unless you have good reasons to stay here you may prefer to go for the modern SDL 2.0 :-).

What you need:

Software Source Description
sdl_ttf.pp http://cvs.sourceforge.net/viewcvs.py/sdl4fp/extras/
Downloads
Unit file to handle fonts.
SDL_ttf.dll http://www.freetype.org/index2.html
Windows: http://gnuwin32.sourceforge.net/packages/freetype.htm
This is the corresponding dynamic link library file.

This chapter will introduce you on how to load fonts and write to the screen. There are two things to do for preparation. First of all we have to change the compiler settings because a new unit, called SDL_TTF, gets included. This unit uses the “result” command to return values of functions. This is a command from Object Pascal. So the compiler has to support it’s syntax otherwise he won’t accept this command. To let the compiler support the Object Pascal syntax go to “Options” and then “Compiler…” A new window will pop up. From “Syntax” mark the option “Object pascal support”. Now SDL_TTF can be used.

In version 1.2.0.0 of SDL4Freepascal this unit isn’t included. It has to be separately downloaded. You can download it here: http://cvs.sourceforge.net/viewcvs.py/sdl4fp/extras/ or from the download section. The file “sdl_ttf.pp” has to be copied to the sdl unit folder where all the other sdl units are located (e.g. C:\FPC\units\).

Analogous to SDL.dll in chapter 1 you have to download the last stable release of FreeType which can be found here: http://www.freetype.org/index2.html (Windows: http://gnuwin32.sourceforge.net/packages/freetype.htm ). You should extract the file and get two files. A text file and the important SDL_ttf.dll. These files you copy to the system32-folder.

Now let’s begin with the interessting part. The goal is to get a text with a certain font and colour to the screen. Of course we need the screen surface again and a surface onto which can be written. Furthermore the new unit has to be loaded.

PROGRAM chap5;
USES crt, SDL, SDL_VIDEO, SDL_TTF;

VAR
screen,fontface:pSDL_SURFACE;

BEGIN
CLRSCR;
SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(200,200,8,SDL_SWSURFACE);
if screen=nil then HALT;

readln;
SDL_FREESURFACE(screen);
SDL_FREESURFACE(fontface);
SDL_QUIT;
END.

To initialize the TTF (True type font) system you have to use TTF_INIT which returns -1 if something goes wrong. Notice that the whole true type support is an own additional project to the SDL library, so this has not to be initilized by the SDL_INIT command.

To load a certain font you use TTF_OPENFONT(font,point size). This command is a function that returns a usual pointer! The parameters are the path to the font (e.g. C:\WINDOWS\fonts\arial.ttf) and the point size which detemines the size of the letters. Furthermore as SDL surface has to be free’d and SDL system has to be quit so has TTF system. The commands TTF_CLOSEFONT(font) and TTF_QUIT have to be used to do this.

PROGRAM chap5;
USES crt, SDL, SDL_VIDEO, SDL_TTF;

VAR
screen,fontface:pSDL_SURFACE;
loaded_font:pointer;

BEGIN
CLRSCR;
SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(200,200,8,SDL_SWSURFACE);
if screen=nil then HALT;

if TTF_INIT=-1 then HALT(1);
loaded_font:=TTF_OPENFONT('C:\WINDOWS\fonts\arial.ttf',12);

readln;
SDL_FREESURFACE(screen);
SDL_FREESURFACE(fontface);
TTF_CLOSEFONT(loaded_font);
TTF_QUIT;
SDL_QUIT;
END.

So next we have to determine the colour of the letters and the messege itself. The colour is determined by a pSDL_COLOR record, which is of course kind of pointer. In pSDL_COLOR record there are three elements (actually four, but the forth is unused) which can be accessed by .r,.g and .b and determine as common the shares of red, green and blue colour. You will agree that writing a function for this can be senseful, especially if you have to handle many different colours. The fact that pSDL_COLOR is a pointer type we have to set up it by NEW command and free it finally by DISPOSE. This commands you should familiar with because they are usual Pascal commands.

PROGRAM chap5;
USES crt, SDL, SDL_VIDEO, SDL_TTF;

VAR
screen,fontface:pSDL_SURFACE;
loaded_font:pointer;
colour_font:pSDL_COLOR;

BEGIN
CLRSCR;
SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(200,200,8,SDL_SWSURFACE);
if screen=nil then HALT;

if TTF_INIT=-1 then HALT;
loaded_font:=TTF_OPENFONT('C:\WINDOWS\fonts\arial.ttf',12);

NEW(colour_font);
colour_font^.r:=255; colour_font^.g:=0; colour_font^.b:=0;

readln;
DISPOSE(colour_font);
SDL_FREESURFACE(screen);
SDL_FREESURFACE(fontface);
TTF_CLOSEFONT(loaded_font);
TTF_QUIT;
SDL_QUIT;
END.

Now we use TTF_RENDERTEXT_SOLID which returns a pSDL_SURFACE. We return it to fontface. The parameters are the used font as pointer (loaded_font), the messege string and the colour as value of pSDL_COLOR (colour_font).

Finally we have to blit the fontface surface, on which now the messege is written, to the screen surface and update the screen surface.

PROGRAM chap5;
USES crt, SDL, SDL_VIDEO, SDL_TTF;

VAR
screen,fontface:pSDL_SURFACE;
loaded_font:pointer;
colour_font:pSDL_COLOR;

BEGIN
CLRSCR;
SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(200,200,8,SDL_SWSURFACE);
if screen=nil then HALT;

if TTF_INIT=-1 then HALT;
loaded_font:=TTF_OPENFONT('C:\WINDOWS\fonts\arial.ttf',12);

NEW(colour_font);
colour_font^.r:=255; colour_font^.g:=0; colour_font^.b:=0;

fontface:=TTF_RENDERTEXT_SOLID(loaded_font,'HELLO WORLD',colour_font^);

SDL_BLITSURFACE(fontface,NIL,screen,NIL);
SDL_UPDATERECT(screen,0,0,0,0);

readln;
DISPOSE(colour_font);
SDL_FREESURFACE(screen);
SDL_FREESURFACE(fontface);
TTF_CLOSEFONT(loaded_font);
TTF_QUIT;
SDL_QUIT;
END.

Now you are able to write ;).

This file contains the source code: chap5.pas (right click and “save as”)
This file is the executable: chap5.exe (right click and “save as”)

[Downloads transferred from old website]

sdl_ttf.zip; FOR OUTDATED SDL4FP PACKAGE! Unit file for font support. (right click and “save as”)

Chapter 4: Drawing pixels (SDL4FP)

This is an SDL 1.2 chapter. SDL 1.2 is obsolete since it has been replaced by SDL 2.0. Unless you have good reasons to stay here you may prefer to go for the modern SDL 2.0 :-).

Okay. Maybe you think, why to learn how to display pictures first and then learning about the more basic feature to draw single pixels to the screen? – In chapter 3 you learned beside displaying a picture how common SDL programs are strctured. Furthermore this chapter will get a little bit longer because several things have to explained more detailed to impart a fully understanding. In chapter 3 that was not necessary.

So here we go. First of all we have to know about the meaning of pixels. The physical screen consists of many small units. Every unit consists itself of three different coloured lights. These colours are red, green and blue. If you mix them (additive) you can get every colour. For example if you mix red and green you receive yellow. For three colours that can be mixed with each other there are eight combinations possible which lead to different colours (RGB, RG, RB, R, GB, G, B, all lights off). Some of you may say RGB (white) and all lights off (black) are no colours. That is right but doesn’t matter here and to keep simpliness I will talk of colours even if I talk of black and white.

Did you get confused? Your screen definitvly has more than eight colours, doesn’t it? The reason is, your screen isn’t just able to put on or off lights. Besides it is in position to differ the intensities of the lights. The more intensity levels you have the more colours you can display. The case that you have eight colours as discussed before means that you just have one intensity level. If your screen is in 8 bit mode every pixel on the screen has the possibility to display 2 power 8 colours. That are 256 different colours. Every of the three lights has therefore a certain amount of different intensity levels. There are the red and the green light with eight intensity levels each and the blue light with four intensity levels. If you have 16 bit mode you have 2 power 16 and that are 65536 colours. Each light therefore must have the appropriate amount of intensity levels.

All the examples will be made in 8 bit mode. If you want to write directly to the screen surface it is possibly necessary that you lock it. We work in software mode (SDL_SWSURFACE) so we can leave locking the screen surface. The screen surface is a RECORD of many different data types. To read or write pixels of or to a surface there is a typ marker called “pixels” which is of pointer type. It points at some pixel of the surface. The location gets allocated to a pointer of WORD, namely “pixellocation” we define.

PROGRAM chap4;
USES crt, SDL, SDL_video;

VAR
screen:pSDL_SURFACE;
pixellocation:^WORD;

BEGIN
CLRSCR;
SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(200,200,8,SDL_SWSURFACE);
if screen=nil then HALT(1);

pixellocation:=screen^.pixels;

readln;
SDL_FREESURFACE(screen);
SDL_QUIT;
END.

It is unknown to us where to the surface it is pointing. This may not matter now; this we will treat later. Before we should allocate a colour to the pixel. Up to now we just have handed over the location to “pixellocation”. Yellow is the pixel colour we want to get. Red has to be 255, green has to be 255 and blue has to be 0 to get yellow. The function SDL_MAPRGB(pixel format, red, green, blue) is used to transform that input values into one longinteger (Uint32) code. The pixel format is given by the screen surface. We will use its pixel format by using screen^.format what contains the pixel format information of the screen surface. The calculated “colour code” is handed over to a new defined variable called “pixelcolor”.

So you have stored the location of the pixel to manipulate in variable pixellocation. You have stored the information of the colour you want to receive in pixelcolor. Eventually you have to allocate the pixel colour to the pointed pixel at the surface. That’s it. Of course we have to update the screen surface after the drawing.

PROGRAM chap4;
USES crt, SDL, SDL_video;

VAR
screen:pSDL_SURFACE;
pixellocation:^WORD;
pixelcolor:WORD;

BEGIN
CLRSCR;
SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(200,200,8,SDL_SWSURFACE);
if screen=nil then HALT(1);

pixellocation:=screen^.pixels;
pixelcolor:=SDL_MAPRGB(screen^.format,255,255,0);
pixellocation^:=pixelcolor;

SDL_UPDATERECT(screen,0,0,0,0);
readln;
SDL_FREESURFACE(screen);
SDL_QUIT;
END.

If you look careful to the screen (when running program) you may see the pixel (left upper corner). After this success we want to see the pixel run along the screen! That will help you understand easily how the positioning of the pixel works, probably better as if I try to explain. We define a repeat-unitl loop what will delete old pixel (colour: black) and write a new one exaktly moved up by one pixel.

PROGRAM chap4;
USES crt, SDL, SDL_video;

VAR
screen:pSDL_SURFACE;
pixellocation:^WORD;
pixelcolor,i:WORD;

BEGIN
CLRSCR;
SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(200,200,8,SDL_SWSURFACE);
if screen=nil then HALT(1);

i:=-1;
REPEAT
inc(i); IF i>39999 THEN i:=0;

pixellocation:=screen^.pixels+i;
pixelcolor:=SDL_MAPRGB(screen^.format,0,0,0);
pixellocation^:=pixelcolor;

pixellocation:=screen^.pixels+i+1;
pixelcolor:=SDL_MAPRGB(screen^.format,255,255,0);
pixellocation^:=pixelcolor;

SDL_UPDATERECT(screen,0,0,0,0);
delay(5);
UNTIL keypressed;

SDL_FREESURFACE(screen);
SDL_QUIT;
END.

So the position which is pointed by pixels pointer is set by adding to or substratcing from the position before. As can be seen in the program the pixel position is increased by one and so is the value of where screen^.pixels pointer is pointing at.

If you create a surface of width and height of 200 pixels as in the example the true surface can be a little bit wider. This means you have a window of width and height of 200 pixels displayed but there is an additional unvisible piece of surface. Maybe its width is 210, so the moving pixel in the last example would restart a few lines before end of window. To solve problems cause by this pitch to the surface you can read it out. The data type pitch will allow you to (e.g. screen^.pitch).

This file contains the source code: chap4.pas (right click and “save as”)
This file is the executable: chap4.exe (right click and “save as”)