Home > SFML, Tutorial > SFML Graphics – Images and Sprites

SFML Graphics – Images and Sprites

So welcome to your second SFML tutorial. This time we’ll be covering basic graphics: displaying images. First, I provide the videos corresponding to this tutorial. Read on for the textual version.

What are you waiting for? open up your project from the Empty Window tutorial, and get coding!

Well first, we need to load an image. Add this code at the start of our program, before the main loop starts.

sf::Image Image;
if (!Image.LoadFromFile("image.png"))
	return 1;

So we create an object of type sf::Image, which describes actual image data in SFML. We then load image data from a file called image.png. You will have to provide such a file in your project directory – either download mine below or provide your own. You could also use, among others, .jpg, .bmp and .tga files.

The image.png file

The sf::Image::LoadFromFile method returns false if loading failed. Thus we check for failure and exit with failure status in that case. Note that we need not output an error message as SFML does this for us.

But an image is a resource intensive object. You can’t just create them mid frame, nor should you be duplicating them in memory in order to have multiple copies of the same image on screen. Enter the SFML sprite. This type provides a means to display an image. It is lightweight, as it does not actually contain the image data, so you can freely copy them and create them on the fly.

sf::Sprite Sprite;
Sprite.SetImage(Image);

We create an instance of sf::Sprite and then us the sf::Sprite::SetImage method to associate the sprite with the image. Already, we are prepared to draw our image to the screen with the sprite. But first, let’s briefly consider what else we can do to sprites.

Sprites, and indeed all other drawable objects in SFML (such as text), have member functions for setting their colour, position, r0tation and scaling. Let’s use a few now.

Sprite.SetPosition(100.0f, 30.0f);
Sprite.SetRotation(30.0f);
Sprite.SetScale(.5f, .3f);
Sprite.SetColor(sf::Color(255, 0, 0, 128));

Now, the first line sets the sprite’s position, providing two floating point values for the X and Y (horizontal and vertical) positions respectively. Next we set the rotation and then the X and Y scaling factors. Finally, we set our image to it to a red colour, but semi transparent, i.e. a colour with RGBA values of 255, 0, 0, and 128 respectively.

Note that all the functions to manipulate sprites may also be used on text and other drawable objects. You can find the complete list of manipulation functions here - http://sfml-dev.org/tutorials/1.6/graphics-sprite.php.

Now let’s display our sprite. This code should go between the calls to sf::RenderWindow::Clear and sf::RenderWindow::Display.

Window.Draw(Sprite);

Note that this sf::RenderWindow::Draw function works equally well with any other drawable object, such as text.

Remember, I said you should duplicate sprites and not images? Well let’s do that now – we’ll create a second sprite that will allow us to draw the same image in a different location.

So let’s go ahead and code that.

sf::Sprite Sprite2;
Sprite2.SetImage(Image);
Sprite2.SetPosition(200.0f, 200.0f);
Sprite2.SetScale(.8f, .8f);

We associate our second sprite with the same image and then set its position and scaling factors. We then just need to add drawing code for it, next to the other draw call.

Window.Draw(Sprite2);

Now then. Let’s load a second image and associate it with another sprite, just to get feel of things.

sf::Image Image2;
if (!Image2.LoadFromFile("image.jpg"))
	return 1;

sf::Sprite Sprite3;
Sprite3.SetImage(Image2);

Sprite3.SetPosition(100.0f, 50.0f);
Sprite3.SetScale(.4f, .4f);

We load a second image, this time called image.jpg. We associate it with a sprite and set the sprite’s position and scale. Finally we have to add drawing code.

Window.Draw(Sprite3);

Make sure you add this sprite at the top of all the draw calls. That way it will be on the bottom so we will see it blended with the semi transparent sprite.

At this stage, you can compile and run your application to see how the images show up, manipulated by our rotation/position/scale function calls.

Images in SFML also have a function sf::Image::SaveToFile, which allows us to save image data to a file. Why would we want to save an image? Well we might have directly modified the pixel data of the image, but that’s not covered today. On the other hand, we can also load the current contents of the screen into the image. Then we can save it. In other words, we can take screenshots!

Create another image, this time called Screenshot.

sf::Image Screenshot;

Now then, I know I said that images are resource heavy objects and we should not process them on the fly, but this time we’ve not much choice. We want to take a screenshot, so we’ll have to do this in the main loop when the window contains content.

So add the following code inside your main loop after the drawing code:

Screenshot.CopyScreen(Window);

The sf::Image::CopyScreen function copies the contents of the screen into the image as image data. The function accepts two arguments, the first of which is the window to take a screenshot of. The second one (which is not used here) specifies the rectangular area of the screen to capture. By omitting it, we ensure that the entire screen is captured.

Finally, after our main loop we must save the screenshot data to a file.

Screenshot.SaveToFile("screenshot.jpg");

This function saves the image data to the file screenshot.jpg. You can now build and run and you’ll notice that when moving the window about, it’s a lot less smooth than before. This is the price we pay for putting image processing code in the main loop.

Next time, when I introduce more advanced event handling, I’ll show you how to only execute the screenshot operation when a key is pressed. For now, however, we only have the option of doing it every frame (which is clearly a bad idea…).

You can find the screenshot.jpg file and you’ll see that it does indeed contain the contents of the window.

A final point to be made is this: the sf::Image::CopyWindow and sf::Image::SaveToFile do in fact return bool just as sf::Image::LoadFromFile does. Therefore, you could and probably should, test for errors when calling these functions just as we did for sf::Image::LoadFromFile.

Finally, we’ll see how to fit a sprite to the whole client area of the window. Let’s do this to Sprite2. So replace the current position and scale lines for Sprite2 with the following:


	Sprite2.SetPosition(0.0f, 0.0f);
	Sprite2.SetScale((float)Window.GetWidth()/(float)Image.GetWidth(), (float)Window.GetHeight()/(float)Image.GetHeight());

Now, you can build and run this right away, and in the current program it will work. Notice the ratios I used to appropriately scale the sprite. Note also that the width and height are associated with the image, not the sprite.

However, I have purposefully added a couple of problems to this so I can explain why to avoid coding it as we just did.

Problem 1 - We have resized the sprite to fit the client area of the window, rather than the actual viewport size. The viewport was defined by our video mode: sf::VideoMode VMode(800, 600, 32). By default our render window thus had a client area of 800 x 600 pixels, but this need not be the case. If the window is a different size to the viewport, the view is automatically scaled to fit the window. Thus we need only scale the sprite to fit the viewport size.

Problem 2 - We accessed the image width and height through the image itself. Now suppose we changed the image the sprite is associated with. Then we would be resizing it incorrectly as we would be getting the wrong image width and height. Instead we should get a pointer to the image from the sprite and get the width and height via that.

So then, our new, improved code:

Sprite2.SetScale((float)VMode.Width/(float)Sprite2.GetImage()->GetWidth(), (float)VMode.Height/(float)Sprite2.GetImage()->GetHeight());

It does exactly the same thing as the other sample, but it resolves the two issues discussed above. Well there we saw how to resize a sprite the hard way. I did that first so you saw what the scale factors were. Now, the easy way.

Sprite2.Resize((float)VMode.Width, (float)VMode.Height);

In fact, the sprite has a Resize function which simply takes two floating point values and resizes the sprite to that size for us.

Finally, there is one point to be made. Because we scale the sprite to the viewport, and the rendered image from the viewport is automatically scaled to the client area of the window, our sprite will stay fitted to the window even if we resize it!

Anyway, that’s all for today. As I’ve said, the next lesson will be on event handling!

 Source Code

For your reference, here is the complete source code used in this tutorial.

#include <SFML/Graphics.hpp>

int main()
{
	sf::VideoMode VMode(800, 600, 32);
	sf::RenderWindow Window(VMode, "SFMLCoder Tutorial - Empty Window");

	sf::Image Image;
	if (!Image.LoadFromFile("image.png"))
		return 1;

	sf::Sprite Sprite;
	Sprite.SetImage(Image);
	Sprite.SetPosition(100.0f, 30.0f);
	Sprite.SetRotation(30.0f);
	Sprite.SetScale(.5f, .3f);
	Sprite.SetColor(sf::Color(255, 0, 0, 128));

	sf::Sprite Sprite2;
	Sprite2.SetImage(Image);
	Sprite2.SetPosition(0.0f, 0.0f); // to reveal other sprites, change the values to 200.0f, 200.0f
	Sprite2.Resize((float)VMode.Width, (float)VMode.Height); // to reveal other sprites, change the values to .8f, .8f

	sf::Image Image2;
	if (!Image2.LoadFromFile("image.jpg"))
		return 1;

	sf::Sprite Sprite3;
	Sprite3.SetImage(Image2);

	Sprite3.SetPosition(100.0f, 50.0f);
	Sprite3.SetScale(.4f, .4f);

	sf::Image Screenshot;

	while (Window.IsOpened())
	{
		sf::Event Event;
		while (Window.PollEvent(Event))
		{
			switch (Event.Type)
			{
			case sf::Event::Closed:
				Window.Close();
				break;
			default:
				break;
			}
		}

		Window.Clear(sf::Color(0, 255, 255));

		Window.Draw(Sprite3);
		Window.Draw(Sprite);
		Window.Draw(Sprite2);

		Window.Display();

		Screenshot.CopyScreen(Window);
	}

	Screenshot.SaveToFile("screenshot.jpg");

	return 0;
}
About these ads
  1. 12.09.2011 at 15:02 | #1

    Ï tried to run it in vc++ 2010 express, but “error C2039: ‘PollEvent’ : is not a member of ‘sf::RenderWindow” appeared, any suggestions?

    • 12.09.2011 at 15:51 | #2

      Which version of SFML are you using? 2.0 or 1.6? I recommend you use 2.0

      If you are already using 2.0, I suggest you re-download the source and re-build it. SFML 2.0′s sf::Window definitely has the member PollEvent, so this could just be a little error in one particular version of the SFML source.

  2. Brock
    22.10.2011 at 08:03 | #3

    Please make more videos :) and text tutorials are great too!

    • 22.10.2011 at 08:22 | #4

      I’m on holiday from school so I should be able to post a few times. However, for the most part I am hoping a stable release of SFML 2 will emerge soon so that my tutorials won’t keep getting outdated.

  3. Jeagle
    12.01.2012 at 21:13 | #5

    I’m using SFML 2.0 yet when i use Sprite.SetImage(Image);

    i get an error stating that sprite does not contain this function? I still see all the other functions for sprite that you listed just this one is missing. I see you recommended rebuilding to someone else who has a similar problem, do i have to re-link everything and run through all the steps from the building post for Visual Studio?

    • 12.01.2012 at 22:43 | #6

      In this case, rebuilding isn’t it. sf::Image has now been split into two classes: sf::Texture and sf::Image. The one we now use for most of what we previously did with sf::Image, is sf::Texture. Therefore, the corresponding member of sf::Sprite is SetTexture.

      However, if you did have to rebuild SFML, that wouldn’t be the best way about it. I have posted a way to do it using NMake on the command line, which is quicker. This post will soon be updated with newer information.

  4. Ashley Matthews
    28.02.2012 at 22:18 | #7

    Just a not that it appears sf::Image, Sprite and texture no longer have a Resize function. In fact I can’t find a resize function for images anywhere.

    • Ashley Matthews
      28.02.2012 at 22:21 | #8

      And also CopyScreen no longer exists for Image either.

      • 28.02.2012 at 22:26 | #9

        This has been replaced by sf::RenderWindow::Capture(), which returns an sf::Image.

        According to the documentation, this function is slow. It says that you should use the function only for screenshots. If you want to use the window content to draw something, you can apparently use sf::Texture::Update(sf::Window&), but I haven’t tried this.

    • 28.02.2012 at 22:29 | #10

      Yeah, sf::Sprite::Resize is gone for whatever reason.

      You can just do this though:

      Sprite.SetScale((float)new_width/Sprite.GetImage()->GetWidth(), (float)new_height/Sprite.GetImage()->GetHeight());

      I haven’t tried compiling this, but it should work okay. The casts to [i]float[/i] are necessary so the division isn’t performed with integers (resulting in serious rounding problems, e.g. 1/2 = 0).

  5. Vot1_Bear
    28.07.2012 at 14:42 | #11

    Another great tutorial from you, thanks a lot.
    I know you’re busy and probably won’t update the tutorial in a while, but i really recommend you add the setTextureRect() function to the list of modifications.

    Picking only a portion of the image will be really useful especially considering how most tilesets i found on the internet was crammed into a single image.
    Thanks again for the great guide btw, really helped a lot

    • 04.08.2012 at 16:06 | #12

      Yes that is a useful function. I’ll be sure to mention it when I revise the tutorial.

  6. Adron505
    29.09.2012 at 21:44 | #13

    as you said sf::Sprite::Resize is gone, and there is a sf::Texture instead of sf::Image now.
    So i written this scarry thing:

    Sprite.setScale((float)VMode.width/(float)Sprite1.getTexture()->getSize().x, (float)VMode.height/(float)Sprite1.getTexture()->getSize().y);

    and it’s work. But it’s a little bit complicated and if you everyone have any better idea to set sf::Sprite fullscreen?

    • 29.09.2012 at 22:28 | #14

      Unfortunately I don’t think there is a better way with SFML 2.0. However, it would be easy enough to resolve with a small inline function:


      inline void ScaleSprite(sf::Sprite& sprite, float new_x_size, float new_y_size)
      {
      Sprite.setScale(new_x_size/(float)Sprite1.getTexture()->getSize().x, new_y_size/(float)Sprite1.getTexture()->getSize().y);
      }

      Then to fit your sprite Sprite to the window you would simply do
      ScaleSprite(Sprite, VMode.width, VMode.height);

      You can’t get rid of the mess, but this way you can hide it away in a function.

  1. 31.05.2011 at 21:48 | #1
  2. 01.08.2011 at 23:01 | #2
  3. 24.10.2011 at 22:05 | #3

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 72 other followers

%d bloggers like this: