Hope Game
Logo Glusoft
Glusoft

Display an image with SDL_image

Installation of SDL_image

For the first program we will make a program display an image on the screen.
You will need to have a working project with SDL, and to load an image we will use an SDL extension : SDL_image

To add SDL_image, do the same steps aas before for SDL :

Copy the dlls in the lib directory, this time there are multiple dlls:
We can start writing the program.

Initilization

The first thing to do is initialize SDL, there are multiple modules in SDL :

In this tutorial we use SDL_INIT_VIDEO and SDL_INIT_EVENTS.
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) {
	std::cout << "Error SDL2 Initialization : " << SDL_GetError();
	return 1;
}
And then the initilization of SDL_image with IMG_Init

The flags for IMG_init are : Here we load a png images so :
if (IMG_Init(IMG_INIT_PNG) == 0) {
	std::cout << "Error SDL2_image Initialization";
	return 2;
}

The window

Everything is initilized, then we start by creating the window:

SDL_Window* window = SDL_CreateWindow("First program", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_OPENGL);
if (window == NULL) {
	std::cout << "Error window creation";
	return 3;
}
The first parameter is the name of the program.
The second parameter indicate where the window should be displayed horizontally.

The third parameters indicate where the window should be displayed vertically. You have two choice for the position of the window:

The fourth parameter is the width of the window.

The fifth is the height of the window.

The sixth is a flag to specify the mode of the window :

The renderer

Then to render the texture we need a renderer:

SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
if (renderer == NULL) {
	std::cout << "Error renderer creation";
	return 4;
}
The first parameter is the window where rendering is displayed.
The second parameter is the index of the rendering driver to initialize. If -1 is passed the first driver to support the flags (next parameter) is initilized. the third parameter is the mode of the renderer

The image

We can start to load the image, for this tutorial I am using this image

A lettuce drawn

SDL_Surface* lettuce_sur = IMG_Load("lettuce.png");
if (lettuce_sur == NULL) {
	std::cout << "Error loading image: " << IMG_GetError();
	return 5;
}
ING_Load take one parameter, the path of the file

What is a SDL_Surface?
An array of pixel stored in RAM encoded in a specific format, most of the time RGBA.

Right, that is good can we display the image?
Well we can, but only if we use a software renderer, for that you need to use the function SDL_CreateSoftwareRenderer.
Here the renderer use hardware acceleration
In computing, hardware acceleration is the use of computer hardware specially made to perform some functions more efficiently than is possible in software running on a general-purpose CPU.
So the image need to be diplayed by the graphic card, not the CPU. To do that we need to create a texture.
What is a SDL_Texture?
An array of pixel stored in VRAM encoded in a driver-specific format

In this case we use OpenGL, so if you are interested you can see theimage formats used by OpenGL

So to create the texture :
SDL_Texture* lettuce_tex = SDL_CreateTextureFromSurface(renderer, lettuce_sur);
if (lettuce_tex == NULL) {
	std::cout << "Error creating texture";
	return 6;
}

SDL_FreeSurface(lettuce_sur);

The first paramater is the renderer used.
The second parameter is the surface to transform into a texture.

After the texture is created, we do not need the surface anymore so we can free the memory used by the surface with SDL_FreeSurface

The event loop

For the program to continu running indefinitely, we some kind of an infinite loop => An infinite loop with an exit condition

How about we use red the cross a the top right of the window as the exit condition. Sound good?

To make this exit condition we need to know when the user click on the red cross.
To catch the action "clicking on the red cross" we need to use events.

while (true) {
	SDL_Event e;
	if (SDL_WaitEvent(&e)) {
		if (e.type == SDL_QUIT) {
			break;
		}
	}

So here we have an SDL_Event e, for the moment the event is empty.

We need to fill this event with the actions of the user. In this case we use SDL_WaitEvent to fill the event.
This function wait indefinitely for an event to happen, if the event happen we fill the event and the loop continu.

So if we do nothing the loop is not executed?
Exactly, so for a game with animations we will SDL_PollEvent instead of SDL_WaitEvent because SDL_PollEvent do suspend the loop.

In this case we only have one image, so we can render the image once and wait for the user to exit the program.

But how can the image can be render if we block the loop at the second line of the loop ?

That's simple even if we do nothing (no key press, no mouse click...) some event occurs such as SDL_WINDOWEVENT, when the window is shown.

Render the texture

To render the texture we need three lines:
	SDL_RenderClear(renderer);
	SDL_RenderCopy(renderer, lettuce_tex, NULL, NULL);
	SDL_RenderPresent(renderer);
}
The first one clear the renderer with the default color : black
The second line render the texture into a backbuffer:
The first parameter is the current renderer.
The second parameter is the texture we want to render.
The third parameter is the portion of texture to render, type SDL_Rect* passing NULL take the entire texture.
The fourth parameter is the destination of the texture to render, type SDL_Rect* passing NULL stretch the texture into the window.

Free the memory

The last thing to do after the loop is to free the resources allocated.

SDL_DestroyTexture(lettuce_tex);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
IMG_Quit();
SDL_Quit();

Result

The source code of the program is:

#include 
#include "SDL.h"
#include "SDL_image.h"

int main(int argc, char* argv[]) {
	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) {
		std::cout << "Error SDL2 Initialization : " << SDL_GetError();
		return 1;
	}
		
	if (IMG_Init(IMG_INIT_PNG) == 0) {
		std::cout << "Error SDL2_image Initialization";
		return 2;
	}

	SDL_Window* window = SDL_CreateWindow("First program", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_OPENGL);
	if (window == NULL) {
		std::cout << "Error window creation";
		return 3;
	}

	SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
	if (renderer == NULL) {
		std::cout << "Error renderer creation";
		return 4;
	}

	SDL_Surface* lettuce_sur = IMG_Load("lettuce.png");
	if (lettuce_sur == NULL) {
		std::cout << "Error loading image: " << IMG_GetError();
		return 5;
	}

	SDL_Texture* lettuce_tex = SDL_CreateTextureFromSurface(renderer, lettuce_sur);
	if (lettuce_tex == NULL) {
		std::cout << "Error creating texture";
		return 6;
	}

	SDL_FreeSurface(lettuce_sur);

	while (true) {
		SDL_Event e;
		if (SDL_PollEvent(&e)) {
			if (e.type == SDL_QUIT) {
				break;
			}
		}

		SDL_RenderClear(renderer);
		SDL_RenderCopy(renderer, lettuce_tex, NULL, NULL);
		SDL_RenderPresent(renderer);
	}

	SDL_DestroyTexture(lettuce_tex);
	SDL_DestroyRenderer(renderer);
	SDL_DestroyWindow(window);
	IMG_Quit();
	SDL_Quit();

	return 0;
}
Dark theme