Glusoft

Display an image with SDL_image

The goal is to display an image with SDL_image, but before we need to set up the environnement.

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 have a working project, you can do the following or use this tutorial as reference : Make a health bar with SDL

First, we add SDL_image, then do the steps as before:

Then, copy the dlls in the lib directory, this time there are multiple dlls:

We can start writing the program.

Initialization of the program display an image with SDL_image

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;
    }

Next thing, the initilization of SDL_image with IMG_Init

The flags for IMG_init are:

After that, we initialize the format PNG:

    if (IMG_Init(IMG_INIT_PNG) == 0) {
        std::cout << "Error SDL2_image Initialization";
        return 2;
    }

The window

After the initialization, we create 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 the horizontal position of the window.

The third parameters indicate the vertical position of the window. 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

Next thing to do is 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 occurs.

The second parameter is the index of the rendering driver to initialize. If we set -1, the first driver to support the flags (next parameter) is initialized. The third parameter is the mode of the renderer:

The image used for display an image with SDL_image

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

A lettuce for the tutorial display an image with SDL_image

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

IMG_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

We use OpenGL, so if you are interested check out the image formats used by OpenGL

Then we 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 parameter is the renderer.
The second parameter is the surface to transform into a texture.

After the texture creation, we do not need the surface anymore so we can free the memory with SDL_FreeSurface.

The event loop for displaying an image with SDL_image

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?

But, 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 we begin with a SDL_Event e, but the event is empty.

We need to fill this event with the actions of the user. For instance, 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, then the loop stops?
Exactly, so for a game with some animations we will use SDL_PollEvent instead of SDL_WaitEvent because SDL_PollEvent suspend the loop.

In this case, we have only 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 still occurs such as SDL_WINDOWEVENT, when the window shows up.

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 all the resources in the right order.

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

Result

The full source code of the program to display an image with SDL_image is:

    #include <iostream>
        #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;
        }