DEV Community

Noah11012
Noah11012

Posted on • Edited on

Using SDL2: Optimizing Surface Blitting

This is a post about how to optimize your blitting and how to open images like PNGs in SDL2. This isn't a necessarily fun post, but it is an important one.

The problem

Most bitmaps are 24 bits and modern screens are not typically 24 bits. If we try to blit an image that is 24 bits onto a surface that isn't, then SDL2 will do the conversion everytime we blit it. This is an area where we can speed up the blitting process a bit by first converting to the screen's format. SDL2 provides such a function called SDL_ConvertSurface(). It takes a surface that you want to convert and the format you want converted to. The function does take a third argument, but that is something from the SDL 1.2 days and should always be set to 0.

SDL_Surface *load_bmp(char const *path, SDL_Surface *target_surface)
{
    SDL_Surface *optimized_version = nullptr;
    SDL_Surface *image_surface = SDL_LoadBMP(path);

    if(!image_surface)
        return 0;

    optimized_version = SDL_ConvertSurface(image_surface, image_surface->format, 0);

    if(!optimized_version)
    {
        SDL_FreeSurface(image_surface);
        return 0;
    }

    SDL_FreeSurface(image_surface);

    return optimized_version;
}
Enter fullscreen mode Exit fullscreen mode

Each SDL_Surface instance has a member variable called format that holds the type of format a surface is using.

Loading different image types

SDL2 by itself cannot load images besides BMPs. SDL2 have something called extension libraries that can extend the capabilities of SDL2.

An extension library we'll be using is called SDL2_image. On Windows, you will have to search for how to set up SDL2_image but on Linux distributions such as Ubuntu and its derivatives like Linux Mint can use the following command:

sudo apt-get install libsdl2-image-dev
Enter fullscreen mode Exit fullscreen mode

Others using Manjaro and the like can use this:

sudo pacman -S sdl2_image
Enter fullscreen mode Exit fullscreen mode

Like SDL2, we have to initialize the library before we can use it. The initialization function takes one argument for the types of images we want to use.

How is that possible if it takes only one argument?

Easy, we just OR the flags together.

int flags = IMG_INIT_PNG | IMG_INIT_JPEG;
Enter fullscreen mode Exit fullscreen mode

IMG_Init() returns a bitmask of the initiated image loaders. It may occur that we can use PNGs but not JPEGs because of an internal error that happened with the JPEG loader. We need to AND this bitmap with the flags variable and check if it equals to flags. If it doesn't, then not all the image loaders were initiated correctly.

int flags = IMG_INIT_PNG | IMG_INIT_JPEG;
int initiated_flags = IMG_Init(flags);

if((initiated_flags & flags) != flags))
{
     std::cout << "Failed to initialize all image loaders\n";
     std::cout << "IMG Error: " << IMG_GetError() << "\n";
}
Enter fullscreen mode Exit fullscreen mode

If an error occurred, we can use IMG_GetError() to get the latest error message.

Now with SDL2_image initialized, we can use IMG_Load() to open any images that have the same formats specified at initialization.

SDL_Surface *image_surface = IMG_Load("image.png");

if(!image_surface)
{
    std::cout << "Failed to open image\n";
    std::cout << "IMG Error: " << IMG_GetError() << "\n";
}

// use image_surface

SDL_FreeSurface(image_surface);
Enter fullscreen mode Exit fullscreen mode

Remember to use SDL_FreeSurface() on the returned image when you're done using it.

With this new power to open images that are more widespread, we are no longer restricted to using BMPs.

I'm glad. I was getting tired of having to convert PNGs into BMPs.

Documentation for SDL2_image can be found here:

https://www.libsdl.org/projects/SDL_image/docs/SDL_image_frame.html

What's next

In the next part, we'll start using the hardware to render the images onto the screen.

All source code can be found at my Github repository:

https://github.com/Noah11012/sdl2-tutorial-code

Top comments (5)

Collapse
 
akariiinnn profile image
Akariiinnn

I have an issue as my Window does not show anymore when i use a PNG image, it works when switching back to BMP tho...

Here's the function :

SDL_Surface *load_pngjpg(char const *path)
{
    int flags = IMG_INIT_PNG | IMG_INIT_JPG;
    int initiated_flags = IMG_Init(flags);

    if((initiated_flags & flags) != flags)
    {
        std::cout << "Failed to initialize all image loaders\n";
        std::cout << "IMG Error: " << IMG_GetError() << "\n";
    }

    SDL_Surface *image_surface = IMG_Load(path);

    if(!image_surface)
    {
        std::cout << "Failed to open image\n";
        std::cout << "IMG Error: " << IMG_GetError() << "\n";
    }

    SDL_FreeSurface(image_surface);
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
akariiinnn profile image
Akariiinnn

I did include SDL_Image.h and added the dlls and libs to my project and to my CMakeLists.txt

Collapse
 
akariiinnn profile image
Akariiinnn • Edited

Replacing SDL_FreeSurface(image_surface) to return image_surface worked.

If that can help someone.

Collapse
 
edr profile image
Edouard Delbar

Hello. This code SDL_ConvertSurface(image_surface, image_surface->format, 0); looks weird to me. Why would you convert a surface to its own format? Am I missing something? Also, what is the relation between "Using SDL2: Optimizing Surface Blitting" and "Loading different image types"? 🫤

Collapse
 
bushstar profile image
Peter John Bushnell

Some corrections, IMG_INIT_JPEG should be IMG_INIT_JPG and there's an extra parenthesis. The below replacement code has been updated.

int flags = IMG_INIT_PNG | IMG_INIT_JPG;
int initiated_flags = IMG_Init(flags);

if ((initiated_flags & flags) != flags)
{
     std::cout << "Failed to initialize all image loaders\n";
     std::cout << "IMG Error: " << IMG_GetError() << "\n";
}
Enter fullscreen mode Exit fullscreen mode