This article was originally posted as “SDL2: Displaying an Image in the Window” on 17th November 2013 at Programmer’s Ranch. It has been updated, and the part about detecting and displaying errors has been removed as it is better addressed by configuring the project’s working directory. The source code for this article is available at the Gigi Labs BitBucket repository.
In this article we’re going to learn how to load a bitmap image from disk and display it in an SDL2 window. In order to follow along, you’ll need to set up a project to work with SDL2 (see Setting up SDL2 with Visual Studio 2015), and start off with the following basic empty window code, which is practically the same as what we did in Showing an Empty Window in SDL2:
#include <SDL.h> int main(int argc, char ** argv) { bool quit = false; SDL_Event event; SDL_Init(SDL_INIT_VIDEO); SDL_Window * window = SDL_CreateWindow("SDL2 Displaying Image", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0); while (!quit) { SDL_WaitEvent(&event); switch (event.type) { case SDL_QUIT: quit = true; break; } } SDL_Quit(); return 0; }
OK, we can now create an SDL_Renderer:
SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, 0);
We use SDL_CreateRenderer() to get an instance of SDL_Renderer. This renderer represents the output device (usually your graphics card) to which your code will be drawing. In fact it’s the final destination of our image, because we’ll be following the steps below:
SDL_CreateRenderer() takes three parameters. The first is the window where we are drawing. The second allows us to select different rendering drivers; in our case we don’t care, and we can set it to -1 and get the default one. The last parameter allows us to set SDL_RendererFlags to control how the rendering occurs. We can set it to zero to default to hardware rendering. If any of this sounds confusing, don’t worry about it and just use SDL_CreateRenderer()
as it is here.
We can now load the image from disk using the SDL_LoadBMP() function, which takes a path to a bitmap and loads it into an SDL_Surface:
SDL_Surface * image = SDL_LoadBMP("image.bmp");
I’m using the following photo of the Grand Harbour in Valletta, Malta, which I took back in 2005, and which has a width of 320 pixels and a height of 240 pixels:
Place the image in your Debug folder, where your executable is located.
Next, we need to create a texture.
SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, image);
A texture is memory close to the graphics card (see image below), and we use SDL_CreateTextureFromSurface() to directly map our surface (which contains the image we loaded) to a texture.
Now that we have a texture, let’s display it in the window. At the end of the while
loop, just after the switch
statement, add the following:
SDL_RenderCopy(renderer, texture, NULL, NULL); SDL_RenderPresent(renderer);
We use SDL_RenderCopy() to copy the texture to the output device. There are also a couple of other parameters that we’re setting to NULL
– more on these in a minute. Finally, SDL_RenderPresent() is what commits the texture to the video memory, displaying the image.
Before we run the program, we must always remember to clean up every resource that we initialise. In our case, that means adding the following just before the call to SDL_Quit()
:
SDL_DestroyTexture(texture); SDL_FreeSurface(image); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window);
If you now run the program from within Visual Studio, you should see the image in your window:
Fantastic! The image is right there in the window! But… can you see what happened? The image, which has dimensions 320×240, has been stretched to fill the window, which has dimensions 640×480. That’s sometimes convenient, because we get scaling for free. However, it makes photos look messy. Let’s change our call to SDL_RenderCopy()
to fix this:
SDL_Rect dstrect = { 5, 5, 320, 240 }; SDL_RenderCopy(renderer, texture, NULL, &dstrect);
Remember those two parameters at the end of SDL_RenderCopy()
that we were setting to NULL
? If you look at the documentation for SDL_RenderCopy(), you’ll see that the last one defines a destination region (which part of the texture will the image be copied to). If it’s set to NULL
, then the image is stretched to fill the texture – which is what happened above.
By setting dstrect (the last parameter), we can render to only a portion of the window. We set it to an SDL_Rect, with x and y set to 5, and width and height corresponding to the dimensions of the image. When we run the program, we get this:
Excellent! 🙂 In this article we looked at how to create renderers, surfaces and textures. We used these to load bitmaps and display them on the screen. We also saw the difference between copying the image to a region of the window, and making it fill the entire window.
Thank you for this great tutorial, but I must ask, is there a way to convert a SDL_Surface into a SDL_Window (or vice versa)?
Because the function “CreateWindow” seems much more obvious than creating textures, renderings and all…
Thanks again for this awesome tutorial !
Well, a window and a surface are not quite the same thing.
A window is a platform-specific thing (e.g. it’s different between Windows and Linux) and is used as a means of displaying whatever you want to render, within the context of that platform.
A surface is just a block of memory where you prepare the stuff that you want to render. The contents of the surface go through a process (involving textures etc) that ultimately gets them onto the window.
This was somewhat simpler in SDL 1.x, where you mainly just used surfaces directly with the window. I suppose in SDL2 they streamlined the process to be closer to what you’d really do when doing complex 3D rendering. I suppose it may look like overkill for simple 2D, but I’m pretty sure they had good reasons for this.
Hi again,
Just after posting this (and 1 day looking for the error!), the error was that in the source code you give the rect height and width is set to 0.
Thank you though! Love your website.
x
Hi, are you referring to dstrect? The width and height shouldn’t be zero, as I’m setting x, y, w and h all at once in the initialisation.
Hi, I couldn’t get the image loading.
It just shows a black screen. I tried following the IMG_Load tutorial yet it also shows a black screen, no image at all.
Can you help me?
My code:
#include
#include
#include
int main(int argc, char **argv) {
SDL_Init(SDL_INIT_VIDEO); // initialize the window
// IMG_Init(IMG_INIT_PNG);
SDL_Window *window = SDL_CreateWindow(“My Window”, SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED, 1280, 720, 0); // specify window
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, 0); // arg: window, rendering device, r. flags
SDL_Surface *image = SDL_LoadBMP(“a.bmp”); // load image
// SDL_Surface *image = IMG_Load(“ace_of_hearts.jpg”); // load image
SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, image);
bool is_running = true;
SDL_Event event; // instantiate event class, to register events
while (is_running) {
SDL_WaitEvent(&event);
switch (event.type) {
case SDL_QUIT:
is_running = false;
break;
}
SDL_RenderCopy(renderer, texture, NULL, NULL); // copy texture to output device
SDL_RenderPresent(renderer); // commit texture to vram
}
SDL_DestroyTexture(texture);
SDL_FreeSurface(image);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
// IMG_Quit();
SDL_Quit();
return 0;
}
I suggest doing some error handling, i.e. check if any of the SDL2 function calls are returning NULL and see if any errors are occurring. Also check the path of the image compared to where the executable is running from… for instance if you run it from Visual Studio the path would be different from simply double-clicking the executable.
I have an unusual problem.
The image appears… corrupted?
Random aberrant blocks of pixels, frenetic flickering when the cursor moves.
I copied the image that you used here, .bmp or .png doesn’t seem to have any effect on the result.
I even copied the source code directly and had the same result. Any ideas what could be causing this?
It’s hard to say just from your description. It’s best if you post a question on Stack Overflow with your code as well as screenshots of the problem so that other people have enough information to investigate.
Nevermind, I’m on Ubuntu and the bmp file is a bit finicky, according to SDL.
I went into imagemagick and overwrote the bmp file with itself.
It worked afterwards!
So is it the thing that if I’m not using a graphics card, then I needn’t to create the texture? (:P)
There is always some kind of graphics card. It might not be a dedicated unit and you may be using the motherboard’s onboard video, but whatever you have in-memory needs to make its way to the screen, and that’s done via a Texture in SDL2.