[ SDL2 — Part 6] Loading textures
Loading textures
In this tutorial you will learn how to load images ( just .bmp
for now, the next part will cover.png
). First of all, a little info about the two new structs we’ll be using
Previous part
You can find the previous part here.
SDL_Surface
This is part of the old SDL
( before 2.0
) It was used for just about everything related to rendering.
SDL_Surface
is used for holding the raw texture data. Basically it says what the color for every pixels is. Sort of like this :
pixel[0, 0] = 128, 64, 255, 255 ( 128 r, 64 g, 255 b, 255 a )
pixel[1, 0] = 255, 32, 128, 128 ( 255 r, 32 g, 128 b, 128 a )
/// ....
( This might not be 100% true for all cases, but it’s generally how SDL_Surface
stores the pixels. )
The SDL_Surface
structure also holds other data about the texture :
width
andheight
( these are read-only )- the pixel format, stores as an SDL_PixelFormat
clip_rec
, an SDL_Rect used forbliting
( similar to rendering or drawing )
These values are useful to know about if you want to create your own SDL_Surface
s using code or want to use the old form of rendering ( bliting
). I do, however, not recommend learning about it. Bliting is less intuitive and it uses only software rendering. This basically means that the CPU
will be doing the rendering, something CPU
s are really bad at. We want optimized hardware rendering on the GPU
. And that’s where the second struct comes in.
SDL_Texture
Like SDL_Surface
, SDL_Texture
holds texture data. But unlike SDL_Surfac
`, SDL_Texture
holds an optimized, driver specific representation of the pixel data. This means rendering will be executed on the GPU
, where it belongs.
So what’s the point of SDL_Surface
s?
Well, as I said, the pixel data in SDL_Texture
is optimized. And what’s more, it can be very different depending on your driver and hardware. Basically this means that you can’t use the pixel data for anything other than rendering. A second reason will come up when we dive into another new part of SDL
, namely image loading.
Loading images
Loading a BMP
image in SDL
is very easy. It can be done using the following function
SDL_Surface* SDL_LoadBMP ( const char* file)
Parameters
file
: the path to the file we want to load
Return value
- The loaded image as a
SDL_Surface
ornull
if failed
Converting SDL_Surface to SDL_Texture
We’re not gonna use the SDL_Surface
, we want a SDL_Texture
instead. To get it, we need to use a second function :
SDL_Texture* SDL_CreateTextureFromSurface (
SDL_Renderer* renderer,
SDL_Surface* surface
)
Parameters
renderer
: ourSDL_Renderer
surface
: the surface we want to make aSDL_Texture
from
Return value
The resulting SDL_Texture
or null
if failed.
Since we won’t be using the SDL_Surface, we can delete it and free the memory :
void SDL_FreeSurface ( SDL_Surface* surface)
Parameters
surface
: theSDL_Surface
to delete
Now that all of this is in place, we can wrap it all up in a nice little function :
SDL_Texture* LoadTexture( const std::string &str ) {
// Load image as SDL_Surface
SDL_Surface* surface = SDL_LoadBMP( str.c_str() );
if ( surface == nullptr ) {
std::cout << "Failed to load surface " <<str
<< "error : " << SDL_GetError() << std::endl;
return nullptr;
} // SDL_Surface is just the raw pixels
// Convert it to a hardware-optimzed texture so we can render it
SDL_Texture* texture = SDL_CreateTextureFromSurface(
renderer,
surface
); // Don’t need the orignal texture, release the memory
SDL_FreeSurface( surface ); return texture;
}
Getting the images on screen
Now that we have loaded the textures, we need to render then on screen. That means no more SDL_RenderFillRect()
and THAT means no more rectangles and squares. Finally! To render them, we just need a simple function :
int SDL_RenderCopy (
SDL_Renderer* renderer,
SDL_Texture* texture,
const SDL_Rect* srcrect,
const SDL_Rect* dstrect
)
Parameters
renderer
: the SDL_Renderer we always use for renderingtexture
: the SDL_Texture we want to rendersrcrect
: which part of the SDL_Texture to render. null for everything.dstrect
: where to render it. Used exactly like inSDL_RenderFillRect
Return value
0
on success
Even though this function has a few parameters, it’s really quite simple. We can use nullptr
for srcrect
and the SDL_Rect
s we used for SDL_RenderFillRect()
as dstrect
. And then we can remove the SDL_SetRenderDrawColor
and end up with a shorter render()
function.
void render()
{
// Clear the window and make it all red
SDL_RenderClear( renderer ); SDL_RenderCopy( renderer, bgTexture, NULL, &backgroundPos ); SDL_RenderCopy( renderer, barTexture, NULL, &topBar );
SDL_RenderCopy( renderer, barTexture, NULL, &bottomBar ) SDL_RenderCopy( renderer, playerTexture, NULL, &playerPos ); for ( const auto &p : enemies )
SDL_RenderCopy( renderer, enemyTexture, NULL, &p.pos ); // Render the changes above
SDL_RenderPresent( renderer);
}
Putting it all together
I’m not gonna put the entire code on the blog anymore since this creates a whole wall of text to scroll through. And now there are images you’ll need too. But you can download the images and the source code here.
Next part
You can find the next part here.