Search code examples
c++sfml

SFML - Why does a sprite drawn to a window scale with the window (resize)


Here is a MWE

#include <SFML/Graphics.hpp>


#include <string>
#include <iostream>


int main(int argc, char *argv)
{
    sf::RenderWindow window(sf::VideoMode(200, 200), "Title");

    std::string image_filename;
    image_filename = "image.png";

    sf::Image image;
    if(!image.loadFromFile(image_filename))
    {
        std::cout << "Error: Could not load file " << image_filename << std::endl;
    }

    sf::Texture texture;
    texture.loadFromImage(image);

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

    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();
        }

        window.clear();
        window.draw(sprite);
        window.display();
    }

    return 0;
}

When starting the application, the window looks like this:

SFML Application with Sprite

After resizing the window, the window looks like this:

SFML Application with Sprite Resize

The global and local bounds of the sprite are the same before and after re-sizing.

The behaviour (as seen in the screenshot) is not what I would expect the default behaviour to be.

  • The number of pixels (width and height) of the window has changed.
  • The same region of the image/texture/sprite is drawn to the window.
  • An interpolation algorithm of some kind has been applied to scale the initial window width and height to the resized window width and height.

Maybe that was already really obvious. This behaviour makes sense when designing something like a game**, but it doesn't make sense for the application I am intending to implement.

** (because the background will "scale" as the window is resized, whereas the rendering resolution would be fixed in advance, likely by a config file)

Before attempting to change the behaviour I want to understand why the behaviour is like this.

Presumably opening a RenderWindow with some initial default resolution (width x height) starts some kind of rendering instance on the GPU with that width and height. Resizing the window on the other hand presumably changes that width and height of the window in the "window system" (window manager, regardless of which OS it is running on) but does not update or change the corresponding width and height on the GPU rendering instance.

Or at least this is my best guess as to what happens.

Is there a way to change this behaviour? Perhaps using something like a SFML RenderWindow is fundamentally the wrong approach to use?

Note: I checked and the "scale" of the sprite does not change during a resize operation. So my assumption would be that the initial window size fixes the resolution of the array of pixels to be rendered and then this is upscaled/downscaled to fit the size of the actual window when drawing pixel data into the display pixel buffer using some kind of interpolation algorithm.


Solution

  • You can add the following event listener to the events loop:

    if(event.type == sf::Event::Resized)
    {
        sf::FloatRect view(0, 0, event.size.width, event.size.height);
        window.setView(sf::View(view));
    }
    

    This will update the render area to the dimensions of the window every time the window is resized rather than scaling the render area to fit the window.