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;
}
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
Others using Manjaro and the like can use this:
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.
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.
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.
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:
Top comments (4)
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 :
I did include SDL_Image.h and added the dlls and libs to my project and to my CMakeLists.txt
Replacing
SDL_FreeSurface(image_surface)
toreturn image_surface
worked.If that can help someone.
Some corrections, IMG_INIT_JPEG should be IMG_INIT_JPG and there's an extra parenthesis. The below replacement code has been updated.