Index


RISC World

SDL for RISC OS

Neil White

Disclaimer:

Please refer to the article in the last issue for the disclaimer and basic information about using GCC and SDL. Under the disclaimer I now disclaim that there may or may not be any working examples with this article at time of going to press. This is because Alan Buckley and I are currently in the process of building a full working suite of SDL related libraries, and right now I am in London and the only fully working copies I have of SDL image and mixer are on a machine in the Isle of Wight with only my father to have to slog through my endless supply of broken files until we find a working one. I now have to kick myself into setting up the SDL SVN service to auto build the latest versions of the libraries, if you would like to donate to our efforts please contact via email, I will try to have some nice fresh libraries and a complete, fresh website ready for the next article. I promise to get my own RiscPC to my flat before the next article. I have tried to find the stuff you need for this article on the web, but it seems that it is me who has to maintain it.

Well, I've been hacking away trying to produce a !sdlimage and a !sdlmixer using the cross compiler system, but alas, I have failed. You will therefore have to wait for the next issues for some working examples. Sorry.

If you would like to be informed when the latest libraries are available or make a donation please email me.

This Article

In this article I will be introducing using image formats other than windows bitmap with the SDL Image library, adding some WAV file sound effects using the SDL Mixer library and using the keyboard as an input device.

  SDL Image ( #include <SDL/SDL_image.h> )

First let's look at some other image formats we can use with SDL Image. As with most of SDL and related libraries they are constantly being improved and bug fixed, which can leave us in RISC OS land behind a bit, so we will be using an early version of SDL Image that is a little more complex to use than the latest version, but I know it works.

Last time we used two BMP images, one for the background and one for the RISC World logo. Now we shall use an XPM for the background. XPM is the X-Windows equivalent to Windows BMP. Images are stored as an array in a text file with a palette and lines of characters representing the pixels in the image, so perhaps a two colour image might have colour 1 as A and colour 2 as B, then there would be lines consisting of A's and B's representing the image. These can then be stored in a header file and compiled into the final binary rather than having separate image files that could then be altered by the end user.

We shall use the PNG format for the logo. PNGs are my image format of choice. They have a good compression ratio and most converters now handle them. I do on occasion have issues with palettes, but this is all part of the fun of building SDL stuff on RISC OS.

Note: to convert images to these formats can be a bit of a slog, I use a combination of !Translator and ImageMagick, for XPMs, first converting a sprite to a format suitable for ImageMagick, then use ImageMagick to convert to an XPM, though you will have to be careful that ImageMagick doesn't replace the hex value for the colour with a colour name, like ›Green“ or ›Black“ as the SDL Image we are using only accepts hex values for colours. PNG's can be converted using just Translator, or there are a few other freeware sprite to PNG converters available.

XPM's

The XPM looks something like this :

  static char * background_xpm[] = {
  "320 320 5 1",
  "       c #737373",
  ".      c #9C9C9C",
  "+      c #BDBDBD",
  "@      c #DEDEDE",
  "#      c #525252",
  " ..+...  ....++. ... (** large snip  **)..++.....++++++@+....++++..++"};

Which can now be included in our headers directory, h, as gfx and at the top of the code we include it:

 #include ›gfx.h“

and with the function

  SDL_Surface *loadxpm(char **xpm)
  {
        SDL_Surface *surface;
        SDL_Surface *image;
        surface = IMG_ReadXPMFromArray(xpm);
        SDL_SetColorKey(surface, SDL_SRCCOLORKEY, SDL_MapRGB(surface->format,255,255,255));
        image = SDL_DisplayFormat(surface);
        SDL_FreeSurface(surface);

        return image;
  }

can be loaded into a previously defined SDL surface (background) with this line

  background=loadxpm(background_xpm);

PNG's

The latest SDL Image (not the one we are using) will allow most image formats to be loaded with one simple function, but we have to go a bit old school with this version and the function looks like this:

  SDL_Surface *loadpng(char *name)
  {
  // define a couple of surfaces for use in this function
        SDL_Surface *surface;
        SDL_Surface *image;
  // define an SDL_Rwop, an area of memory used for storing data files
        SDL_RWops *rwop;
  // A character string for holing or file name
        char path[256];
  // copy the base directory ( <obey$dir>/ ) to the start of the path
        strcpy(path, DATA_PREFIX);
  // add /gfx ( path in RISC OS is now <obey$dir>.gfx. )	
     strcat(path, "gfx/");
  // add the name of the image ( <obey$dir>.gfx.name )
        strcat(path, name);
  // add the png suffix ( <obey$dir>.gfx.name.png )
        strcat(path, ".png");
  // load the data file into the rwop
        rwop = SDL_RWFromFile(path,"rb");
  // convert the rwop from png into a SDL surface
        surface = IMG_LoadPNG_RW( rwop );
  // tell the surface to use white ( 255,255,255 ) as transparent or the mask.
        SDL_SetColorKey(surface, SDL_SRCCOLORKEY, SDL_MapRGB(surface->format,255,255,255));
  // set our new image surface
        image = SDL_DisplayFormat(surface);
  // clear the working surface
        SDL_FreeSurface(surface);
  // return our new SDL surface to the one defined and used with name=loadpng(›name“);
        return image;
}

and loading the image into a pre defined SDL surface looks like this:

  rwlogo=loadpng(›rwlogo“);

Note we don't need to add the .PNG suffix, or the full directory listing as the .PNG is added in the function and the DATA_PREFIX definition in the makefile points to where our working directory is (-DDATA_PREFIX=“<obey$dir>“) and we also add the gfx/ part in the code, so all our PNG images can be stored in the gfx directory and simply loaded using their file name without the PNG suffix, but the actual files do have the PNG suffix.

  SDL Mixer ( #include <SDL/SDL_mixer.h> )

SDL Mixer now supports lots of sound file formats, including MIDI music, Microsoft WAV, OGG and MP3, but decoding MP3's or OGG's while running an SDL application is a little processor intensive, so generally on RISC OS it's best to use MIDI for any music and WAVs can be used for sound effects.

This is the function I use to initialise SDL_mixer and load sound files ready for use. As with images, SDL Mixer needs to refer and store the sound file so similar SDL_Surface for images, a Mix_Chunk is used for sound, we will be using one sound called boing.wav. The Mix_Chunk defined at the top of the code like this:

Note: When our code is shutting down it is best to call MixCloseAudio();

  Mix_Chunk *boing_sound;

then this will initialise SDL Mixer and load the wav file into our mix chunk:

  static void initandloadsounds()
  {
    char path[256];
     Mix_OpenAudio(22050, AUDIO_S16, 2, 2048);
    strcpy(path, DATA_PREFIX "sounds/shot.wav");
    shot_sound = Mix_LoadWAV_RW(SDL_RWFromFile(path, "rb"), 1);
  }

and to play the sound is now simply:

  Mix_PlayChannel(1,died_sound,0);

From the documentation for SDL Mixer:

4.1.2 Mix_OpenAudio
int Mix_OpenAudio(int frequency, Uint16 format, int channels, int chunksize)

frequency = Output sampling frequency in samples per second (Hz).
      You might use MIX_DEFAULT_FREQUENCY(22050) since that is a good value for most games
format = Output sample format
channels = Number of sound channels in output
      Set to 2 for stereo, 1 for mono. This has nothing to do with mixing channels.
chunksize = Bytes used per output sample.

4.3.3 Mix_PlayChannel
int Mix_PlayChannel(int channel, Mix_Chunk *chunk, int loops)

channel = Channel to play on, or -1 for the first free unreserved channel.
chunk = Sample to play.
loops = Number of loops. -1 is infinite loops.
      Passing '1' here plays the sample twice (1 loop).

This is SDL Mixer at its most basic functionality. You may want to refer to the official SDL Mixer documentation for more functionality.

Using The Keyboard.

In the example in the last issue we simply waited for the mouse to be clicked then ran a little bit of code that made the logo bounce up and down, this function looks for key presses, not the mouse click.

Key presses are detected using an event loop, each key on the keyboard having it's own definition, key W for example is referred to as SDLK_W, using our own predefined integers we can set a variable for when the key is pressed down - case SDL_KEYDOWN:, and for when the key has been released - case SDL_KEYUP:. So after defining four integers up_pressed, down_pressed, left_pressed and right_pressed, we can use this function in combination with the state of our variables to use the keyboard to do things in the SDL program. This example uses the arrow keys.

static void checkevents(void)
  {
  SDL_Event event;

  while (SDL_PollEvent(&event) != 0)
  {      
   switch(event.type)
        {
        case SDL_KEYDOWN:
                switch(event.key.keysym.sym)
                {
                case SDLK_ESCAPE: quit = 1; break;
                case SDLK_UP: up_pressed = 1; break;
                case SDLK_DOWN: down_pressed = 1; break;
                case SDLK_LEFT: left_pressed = 1; break;
                case SDLK_RIGHT: right_pressed = 1; break;
                default:   break;
                }
                break;

        case SDL_KEYUP:
                switch(event.key.keysym.sym)
                {
                case SDLK_UP: up_pressed = 0; break;
                case SDLK_DOWN: down_pressed = 0; break;
                case SDLK_LEFT: left_pressed = 0; break;
                case SDLK_RIGHT: right_pressed = 0; break;
                default:    break;
                }
                break;

        case SDL_QUIT:
                quit = 1;
                break;
        }
     }
  }

So here we can use the values of left_pressed to determine if the left arrow key is pressed or depressed for use with moving things around the screen using the arrow keys.

Animation The Slack Way

The slackers way to animate bitmaps is to simply have a number of single file bitmaps in a directory, load them into SDL then use a simple loop to change the image every so often.

  Main Game Loop ( args )
  {
  int boolean_game=1,anim_dude=1;

  do
  {

  draw_background();

  if ( anim_dude=1 )  { imageplot(dude1,dudex,dudey); }
  if ( anim_dude=2 )  { imageplot(dude2,dudex,dudey); }
  if ( anim_dude=3 )  { imageplot(dude3,dudex,dudey); }
  if ( anim_dude=4 )  { imageplot(dude4,dudex,dudey); }

  anim_dude+=1;
  if ( anim_dude=5 ) { anim_dude=1; }

  SDLFlip(screen);

  }
  while ( boolean_game=1; )

  }

Next time... Some working examples for image and mixer (cough), some collision detection methods, and hopefully a look at SDL_gfx or SDL_ttf. I will be collecting my own RiscPC in a couple of weeks, which unlike my brothers has just about every version of sdl_image and sdl_mixer you would ever want. Why havn't I been developing on RISC OS? Well basically I have been playing with alpha blending in SDL, and my trusty Risc PC just ain't powerful enough for a lot of alpha blending. Buy me one of those new blue risc boxes ;-)

Neil White

 Index