SFML2.6 graphics module–sprites and textures

Glossary

Most, if not all, of you are already familiar with these two very common objects, so let’s briefly define them.

Textures are pictures. But we call it a “texture” because it has a very specific purpose: to be mapped onto a 2D entity.

Sprites are nothing but textured rectangles.

Okay, that’s short, but if you really don’t understand what sprites and textures are, you can find a better description on Wikipedia.

Load texture

Before creating any sprites, we need a valid texture. The class that encapsulates textures in SFML is sf::Texture. Since the only purpose of a texture is to load and map it to a graphics entity, almost all of its functions are about loading and updating it.

The most common way of loading textures is from an image file on disk, which can be done using the loadFromFile function.

sf::Texture texture;
if (!texture.loadFromFile("image.png"))
{<!-- -->
    // error...
}

The loadFromFile function sometimes fails for no apparent reason. First, check the error messages SFML prints to standard output (check the console). If the message is that the file cannot be opened, make sure that the working directory (any file paths will be interpreted relative to the directory to which it is interpreted) is the one you think it is: when you run the application from the desktop environment, the working directory is the executable folder. However, when you launch the program from an IDE (Visual Studio, Code::Blocks, etc.), the working directory may sometimes be set to the project directory. This can usually be easily changed in the project settings.

You can also load image files from memory (loadFromMemory), a custom input stream (loadFromStream), or an already loaded image (loadFromImage). The latter loads textures from sf::Image, a utility class that helps store and manipulate image data (modify pixels, create transparency channels, etc.). The pixels of an sf::Image remain in system memory, which ensures that operations on them will be as fast as possible, whereas the pixels of a texture reside in video memory, so are slow to retrieve or update, but very fast to draw.

SFML supports most common image file formats. The complete list can be found in the API documentation.

All these loading functions have an optional parameter that you can use if you want to load a smaller part of the image.

// load a 32x32 rectangle that starts at (10, 10)
if (!texture.loadFromFile("image.png", sf::IntRect(10, 10, 32, 32)))
{<!-- -->
    // error...
}

The IntRect class is a simple utility type that represents a rectangle. Its constructor accepts the coordinates of the upper left corner and the size of the rectangle.

If you don’t want to load a texture from an image, but instead want to update it directly from a pixel array, you can create an empty texture and update it later:

// create an empty 200x200 texture
if (!texture.create(200, 200))
{<!-- -->
    // error...
}

Note that the contents of the texture are undefined at this time.

To update the pixels of an existing texture, you must use the update function. It has multiple overloads for use with many kinds of data sources:

// update a texture from an array of pixels
sf::Uint8* pixels = new sf::Uint8[width * height * 4]; // * 4 because pixels have 4 components (RGBA)
...
texture.update(pixels);

// update a texture from a sf::Image
sf::Image image;
...
texture.update(image);

// update the texture from the current contents of the window
sf::RenderWindow window;
...
texture.update(window);

These examples all assume that the source and texture sizes are the same. If this is not the case, i.e. if you only want to update part of the texture, you can specify the coordinates of the subrectangle to be updated. You can refer to the documentation for more details.

Additionally, a texture has two properties that affect how it is rendered.

The first property allows smooth textures. Smooth textures make pixel boundaries less noticeable (but the image will be blurrier), which may be ideal if you’re upscaling.

texture.setSmooth(true);


Because smoothing samples adjacent pixels in the texture, it can have the undesirable side effect of considering pixels outside the selected texture area. This can happen if the sprite is at non-integer coordinates.
The second property allows the texture to be reused in a tiled manner within a single sprite.

texture.setRepeated(true);


This property only works if your sprite is configured to display a rectangle larger than the texture. Otherwise, this property has no effect.

Can I create sprites now?

Yes, you can create sprites now.

sf::Sprite sprite;
sprite.setTexture(texture);

…and finally draw it.

// inside the main loop, between window.clear() and window.display()
window.draw(sprite);

If you don’t want the sprite to use the entire texture, you can set its texture rectangle.

sprite.setTextureRect(sf::IntRect(10, 10, 32, 32));

You can change the color of the sprite. The color you set will be blended (multiplied) with the sprite’s texture. This can also be used to change the sprite’s global transparency (Alpha).

sprite.setColor(sf::Color(0, 255, 0)); // green
sprite.setColor(sf::Color(255, 255, 255, 128)); // half transparent

These sprites all use the same texture, but have different colors:

Sprites can also be transformed: they have a position, an orientation and a scale.

// position
sprite.setPosition(sf::Vector2f(10.f, 50.f)); // absolute position
sprite.move(sf::Vector2f(5.f, 10.f)); // offset relative to the current position

// rotation
sprite.setRotation(90.f); // absolute angle
sprite.rotate(15.f); // offset relative to the current angle

// scale
sprite.setScale(sf::Vector2f(0.5f, 2.f)); // absolute scale factor
sprite.scale(sf::Vector2f(1.5f, 3.f)); // factor relative to the current scale

By default, the origin of these three transformations is the sprite’s upper left corner. If you want to set the origin to a different point (such as the center of the sprite or another corner), you can use the setOrigin function.

sprite.setOrigin(sf::Vector2f(25.f, 25.f));

Since transformation functions are common to all SFML entities, they are explained in a separate tutorial: Transforming Entities.

White square problem

You successfully load a texture and build a sprite correctly, but all you see on the screen is a white square. what happened?

This is a common mistake. When you set the texture of an elf, the elf simply stores a pointer to the texture instance internally. Therefore, if the texture is destroyed or moved to another location in memory, the sprite will get an invalid texture pointer.

The problem happens when you write a function like this:

sf::Sprite loadSprite(std::string filename)
{<!-- -->
    sf::Texture texture;
    texture.loadFromFile(filename);

    return sf::Sprite(texture);
} // error: the texture is destroyed here

You must properly manage the lifecycle of textures and ensure that they exist when used by any sprite.

The importance of using as few textures as possible

Using as few textures as possible is a good strategy for one simple reason: changing the current texture is an expensive operation that takes up a lot of the graphics card’s resources. Therefore, using many sprites using the same texture will yield the best performance.

Additionally, using a single texture allows you to group static geometry into a single entity (only one texture can be used per draw call), which is much faster than drawing many entities. Batch processing static geometry involves other classes and is therefore beyond the scope of this tutorial, see the Vertex Array tutorial for details.

When creating an animation sheet or tileset, try to keep this in mind: try to use as few textures as possible.

Using sf::Texture with OpenGL code

If you are using OpenGL rather than SFML’s graphics entities, you can still use sf::Texture as a wrapper around OpenGL texture objects and use it with other OpenGL code.

To bind an sf::Texture (basically glBindTexture) for drawing, you can call the bind static function:

sf::Texture texture;
...

//bind the texture
sf::Texture::bind( & amp;texture);

// draw your textured OpenGL entity here...

// bind no texture
sf::Texture::bind(NULL);