Using SDL2: Optimizing Surface Blitting
Tags: sdl, cpp
Published: 2018-11-03 01:39:36

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.

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

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:

```bash
sudo apt-get install libsdl2-image-dev
```

Others using Manjaro and the like can use this:

```bash
sudo pacman -S sdl2_image
```

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.

```cpp
int flags = IMG_INIT_PNG | IMG_INIT_JPEG;
```

`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.

```cpp
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";
}
```

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.

```cpp
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);
``` 

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