[ SDL2 — Part 6] Loading textures

Ole Vegard Mythe Moland
4 min readJan 16, 2022

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 :

  • widthand height ( these are read-only )
  • the pixel format, stores as an SDL_PixelFormat
  • clip_rec, an SDL_Rect used for bliting ( similar to rendering or drawing )

These values are useful to know about if you want to create your own SDL_Surfaces 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 CPUs 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_Surfaces?

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_Surfaceor null 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 : our SDL_Renderer
  • surface : the surface we want to make a SDL_Texture from

Return value

The resulting SDL_Textureor 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 : the SDL_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 rendering
  • texture : the SDL_Texture we want to render
  • srcrect : which part of the SDL_Texture to render. null for everything.
  • dstrect : where to render it. Used exactly like in SDL_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_Rects 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.

Behold my skills at using the spray can in MS Paint

Next part

You can find the next part here.

--

--