SDL (Simple DirectMedia Layer) - Rendering images

In this section, we'll demonstrate how to render an BMP image to a window. In this chapter, we'll start writing in multiple files to keep the project organised.

You can download the source code of this section in this GitLab repository. All source code is stored in this group.

wikibook.h edit

#include <stdio.h>

#ifdef _WIN32
#include <SDL/SDL.h> /* Windows-specific SDL2 library */
#else
#include <SDL2/SDL.h> /* macOS- and GNU/Linux-specific */
#endif

/* Define constants for struct wikibook */
#define WB_NAME "WikiBook SDL"
#define WB_WIDTH 500
#define WB_HEIGHT 500
#define WB_IMAGE_PATH "wikibooks.bmp" /* File path to image relative to binary */

/* 
 * We'll be encapsulating the SDL objects into struct wikibook and write up 
 * functions into useable subroutines. Remember, to initialise wikibook as static
 * to make sure that the member pointers are declared to NULL. */
struct wikibook {
  SDL_Window *window; /* Window to be rendered */
  SDL_Surface *screen; /* Surface contained by the window */
  SDL_Surface *image; /* Surface to be loaded with the image */
};

/* Function prototypes for struct wikibook */
int wb_init(struct wikibook*);
int wb_loadImage(struct wikibook*);
void wb_close(struct wikibook*);

We declare a SDL_Window and two SDL_Surface pointers. We use the first one as it is rendered for the window and the second as a buffer to load the image.

SDL_Window *window; /* Window to be rendered */
SDL_Surface *screen; /* Surface contained by the window */
SDL_Surface *image; /* Surface to be loaded with the image */

wikibook.c edit

Initialisation edit

/* Initialise wikibook object */
int wb_init(struct wikibook *wb) {

  /* Initialise SDL video subsystem */
  if (SDL_Init(SDL_INIT_VIDEO) < 0) {
    fprintf(stderr, "SDL failed to initialise: %s\n", SDL_GetError());
    return -1;
  }

  /* Create SDL_Window */
  wb->window = SDL_CreateWindow(WB_NAME,
			SDL_WINDOWPOS_UNDEFINED,
			SDL_WINDOWPOS_UNDEFINED,
			WB_WIDTH,
			WB_HEIGHT,
			0);

  if (wb->window == NULL) {
    fprintf(stderr, "WikiBook window failed to initialise: %s\n", SDL_GetError());
    return -1;
  }

  /* Get SDL_Surface of SDL_Window */
  wb->screen = SDL_GetWindowSurface(wb->window);

  if (wb->screen == NULL) {
    fprintf(stderr, "WikiBook screen failed to initialise: %s\n", SDL_GetError());
    return -1;
  }

  return 0;
}

We set the SDL_Window a SDL_Surface using SDL_GetWindowSurface:

wb->screen = SDL_GetWindowSurface(wb->window);

if (wb->screen == NULL) {
  fprintf(stderr, "WikiBook screen failed to initialise: %s\n", SDL_GetError());
  return -1;
}

Image rendering edit

/* Load and render the image to wikibook object */
int wb_loadImage (struct wikibook *wb) {

  /* Load the image from its file path to a SDL_Surface */
  wb->image = SDL_LoadBMP(WB_IMAGE_PATH);

  if (wb->image == NULL) {
    fprintf(stderr, "WikiBook failed to load image: %s\n", SDL_GetError());
    return -1;
  }

  /* Blit the SDL_Surface, containing the image, to the SDL_Surface contained by
   * the SDL_Window  */
  SDL_BlitSurface(wb->image, NULL, wb->screen, NULL);

  /* Update SDL_Window so it shows the updated SDL_Surface therefore the image */
  SDL_UpdateWindowSurface(wb->window);
  
  return 0;
}

We first load the BMP image into a SDL_Surface using SDL_LoadBMP.

wb->image = SDL_LoadBMP(WB_IMAGE_PATH);

if (wb->image == NULL) {
  fprintf(stderr, "WikiBook failed to load image: %s\n", SDL_GetError());
  return -1;
}

Now we've loaded the image onto its surface, we'll need to blit it to another surface, the one contained by the window, using SDL_BlitSurface. We'll then have to update the window in order to render the image using SDL_UpdateWindowSurface.

/* Blit the SDL_Surface, containing the image, to the SDL_Surface contained by
 * the SDL_Window  */
SDL_BlitSurface(wb->image, NULL, wb->screen, NULL);

/* Update SDL_Window so it shows the updated SDL_Surface therefore the image */
SDL_UpdateWindowSurface(wb->window);

Freeing the memory edit

/* Free the memory of wikibook's member objects */
void wb_close(struct wikibook *wb) {

  /* Destroy surfaces */
  SDL_FreeSurface(wb->image);
  SDL_FreeSurface(wb->surface);
  wb->image = NULL;
  wb->surface = NULL;

  /* Destroy window */
  SDL_DestroyWindow(wb->window);
  wb->window = NULL;

  /* Shutdown SDL subsystems*/
  SDL_Quit();
}

Destroying the surfaces is much the same as destroying the window.

main.c edit

#define TIME_DELAY 3000

int main (int argc, char **argv)
{
  static struct wikibook wb; /* Initialise as static as to initialise all members to 0 */

  if (wb_init(&wb) < 0) {
    fprintf(stderr, "WikiBook failed to initialise.");
    return -1;
  }

  if (wb_loadImage(&wb) < 0) {
    fprintf(stderr, "WikiBook failed to load image.");
    return -1;
  }

  SDL_Delay(TIME_DELAY);

  wb_close(&wb);

  return 0;
}

We define the time delay to be 3000 milliseconds and declare the wikibook object as static, making sure its member pointers are set to NULL. We run the appopriate member functions and that's it!