Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Blurred text at non-integral coordinates  (Read 14502 times)

0 Members and 1 Guest are viewing this topic.

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6286
  • Thor Developer
    • View Profile
    • Bromeon
Blurred text at non-integral coordinates
« on: September 16, 2010, 05:02:40 pm »
Hi, the sf::Text class is not rendering properly when not aligned to integer coordinates. Example:


Complete and minimal code:
Code: [Select]
#include <SFML/Graphics.hpp>

int main()
{
sf::RenderWindow app(sf::VideoMode(640, 480), "Blurred text", sf::Style::Default);
app.SetFramerateLimit(30);

sf::Text normal("This is how it should look");
normal.SetCharacterSize(12);

sf::Text blurred("And this is the blurred version");
blurred.SetCharacterSize(12);
blurred.Move(0.5f, 20.5f);

for (;;)
{
sf::Event event;
while (app.GetEvent(event))
{
if (event.Type == sf::Event::Closed
|| event.Type == sf::Event::KeyPressed && event.Key.Code == sf::Key::Escape)
return 0;
}

app.Clear();
app.Draw(normal);
app.Draw(blurred);
app.Display();
}
}

I remember there has been (or still is) a similar issue with sf::Sprite. There may be use cases at sprites (although I don't see many of them), but hardly at text.

If this behaviour is intended, why? And can't there be a simpler solution than to round all the time? I mean, a big advantage of the drawables working with floats results of the possibility, that one can move a drawable continuously, for example. By rounding all the time, integers could as well be used.

Sorry if this topic has already come up quite often, I've so far only found this thread.
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Blurred text at non-integral coordinates
« Reply #1 on: September 16, 2010, 05:12:12 pm »
This is the intended behaviour. The problem, if I round the coordinates to avoid this kind of artifacts, is that it creates another kind of artifacts (moving entities are not smooth, etc.). So... there's no magical solution, the current behaviour is the best I can do. If you want pixel perfect rendering for static objects, you have to make sure that their coordinates are aligned with the current viewport.
Laurent Gomila - SFML developer

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6286
  • Thor Developer
    • View Profile
    • Bromeon
Blurred text at non-integral coordinates
« Reply #2 on: September 16, 2010, 05:34:40 pm »
Quote from: "Laurent"
The problem, if I round the coordinates to avoid this kind of artifacts, is that it creates another kind of artifacts (moving entities are not smooth, etc.).
Hm, what artifacts are those?

Because rendering a moving sf::Text appears better with rounded coordinates, too. If you test the following code, you will see that the integral-aligned text moves smoothly, while the other is constantly flickering. Even for high velocities v, the effect is well recognizable (you can try v=4.9, for example).
Code: [Select]
#include <SFML/Graphics.hpp>
#include <cmath>

// Workaround for integral-aligned drawables
void AlignProperly(sf::Drawable& drawable)
{
sf::Vector2f position = drawable.GetPosition();
drawable.SetPosition(std::floor(position.x + .5f), std::floor(position.y + .5f));
}

int main()
{
sf::RenderWindow app(sf::VideoMode(640, 480), "Blurred text", sf::Style::Default);
app.SetFramerateLimit(30);

sf::Text normal("This is how it should look");
normal.SetCharacterSize(12);

sf::Text blurred("And this is the blurred version");
blurred.SetCharacterSize(12);
blurred.SetPosition(0.f, 20.f);

float x = 0.f;
const float v = 0.3f;

for (;;)
{
sf::Event event;
while (app.GetEvent(event))
{
if (event.Type == sf::Event::Closed
|| event.Type == sf::Event::KeyPressed && event.Key.Code == sf::Key::Escape)
return 0;
}

// Move "normal" text
x += v;
normal.SetPosition(x, 0.f);
AlignProperly(normal);

// Move "blurred" text
blurred.Move(v, 0.f);

app.Clear();
app.Draw(normal);
app.Draw(blurred);
app.Display();
}
}
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Blurred text at non-integral coordinates
« Reply #3 on: September 16, 2010, 07:08:47 pm »
There was a french user who complained about  rotating/scaling sprites not transforming smoothly. And he was right: when coordinates are rounded, the sprite moves pixel by pixel, but if they are not, the sprite's edges slightly fade out when they are in between two pixels, which gives a much smoother/continuous move.

I guess that text doesn't produce he same effect, you should try with sprites.
Laurent Gomila - SFML developer

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6286
  • Thor Developer
    • View Profile
    • Bromeon
Blurred text at non-integral coordinates
« Reply #4 on: September 16, 2010, 08:27:38 pm »
To test rotation and scale in both ways (pixel-aligned and continuous), I would have to modify SFML's internals, wouldn't I? I experimented a little, and while moving a sprite (without rotation or scale), the sprite without rounded coordinates looks like animated... Some lines are slightly flickering, I guess you know the effect.

Hm... What would you suggest to enable/disable pixel-perfect rendering in a relatively comfortable way (on user side, if SFML doesn't implement it)?
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Blurred text at non-integral coordinates
« Reply #5 on: September 16, 2010, 08:43:04 pm »
I removed the automatic rounding code at revision 1390, so you can test it with the revision 1389. If there are too many API changes in this revision, you can extract the necessary code from the SFML/Graphics/Renderer.cpp & hpp files and apply it to the current revision.
Laurent Gomila - SFML developer

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Blurred text at non-integral coordinates
« Reply #6 on: September 16, 2010, 08:51:15 pm »
By the way, here is the french topic I was referring to:
http://www.sfml-dev.org/forum-fr/viewtopic.php?t=2257

In case you can find useful stuff in it (at least minimal codes).
Laurent Gomila - SFML developer

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6286
  • Thor Developer
    • View Profile
    • Bromeon
Blurred text at non-integral coordinates
« Reply #7 on: September 16, 2010, 09:36:12 pm »
Thank you for searching the revision and thread. My French is not very up-to-date, but it should be enough ;)
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6286
  • Thor Developer
    • View Profile
    • Bromeon
Blurred text at non-integral coordinates
« Reply #8 on: September 17, 2010, 03:01:49 pm »
I tested Spidyy's example code with the older SFML version (the one that rounds coordinates). I see the problem, a slow image scaling looks indeed smoother without rounding.

Hence, both approaches have their advantages and drawbacks. Depending on the use case, one approach is mostly more suitable. But how can the user decide to render pixel-perfectly without explicitly rounding every time before a drawable is drawn? I would rather round immediately before the rendering, so that the actual position can still be represented with continuous float values, and that the user doesn't have to bother about it. I thought about something like this:
Code: [Select]
void PixelPerfectDraw(sf::RenderTarget& target, sf::Drawable& drawable)
{
// Save old position and round coordinates
sf::Vector2f oldPosition = drawable.GetPosition();
drawable.SetPosition(std::floor(oldPosition.x + .5f), std::floor(oldPosition.y + .5f));

// Draw the object (now pixel-aligned)
target.Draw(drawable);

// Restore old position
drawable.SetPosition(oldPosition);
}

The problem is, the drawable is passed by non-const reference, thus strongly limits the use cases. Using a const reference would enforce const_cast, which has undefined behaviour if the original drawable object was declared const. A copy is no option either, because we don't know the dynamic type.

Do you have a suggestion? Would SFML be able to handle that internally in a more elegant way?
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Blurred text at non-integral coordinates
« Reply #9 on: September 17, 2010, 03:28:42 pm »
The problem is that it's much more complicated to round coordinates automatically:
- you must take the origin, rotation and scale in account as well, not only the position
- the coordinates must be aligned with the current view and viewport, integer coordinates only work with the startup default settings

The only place where all these things can be done automatically and easily, is exactly where it was implemented in revision 1389, in the Renderer::ProcessVertex function.
Laurent Gomila - SFML developer

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6286
  • Thor Developer
    • View Profile
    • Bromeon
Blurred text at non-integral coordinates
« Reply #10 on: September 18, 2010, 05:48:56 pm »
Quote from: "Laurent"
The problem is that it's much more complicated to round coordinates automatically
You're right, my approach is quite naive. Even though I could take the view into account, rotation and scale become quite difficult, if not impossible.

Quote from: "Laurent"
The only place where all these things can be done automatically and easily, is exactly where it was implemented in revision 1389, in the Renderer::ProcessVertex function.
Have you changed the implementation completely, or why doesn't SFML support the old way (automatic rounding) anymore, as the current way is sometimes even less suitable? Would you consider an option to choose between them?
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Blurred text at non-integral coordinates
« Reply #11 on: September 18, 2010, 11:13:43 pm »
Quote
Have you changed the implementation completely, or why doesn't SFML support the old way (automatic rounding) anymore, as the current way is sometimes even less suitable?

I removed it because it was the "less worse" solution. Automatic rounding produces artifacts that you can't solve no matter what you do, while the current behaviour produces artifacts that you may be able to solve with more or less complicated code.

Quote
Would you consider an option to choose between them?

I would be hard to integrate properly to the public API, and it would look like a workaround more than a feature. What would the documentation say? "if your entites look blurry, use this function" :?

But I'm open to discussion, as I have currently no satisfying solution.
Laurent Gomila - SFML developer

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6286
  • Thor Developer
    • View Profile
    • Bromeon
Blurred text at non-integral coordinates
« Reply #12 on: September 19, 2010, 05:09:44 am »
Thanks for explaining your decision, it sounds reasonable. The only place where a switch functionality could appear in the public API would be sf::Drawable anyway, wouldn't it? But I agree it were more a desperate attempt than a proper solution. Let's try to pick the advantages of both approaches. :)

I did some experiments again. Since the flickering artifact I mentioned in this thread seems to appear mostly at translations, it might be more effective to consider only them for the moment. I don't know whether there are many use cases where rounded translation coordinates are inappropriate, but for my code I assumed this is not the case. I modified Drawable.cpp of the current revision (1570) slightly:

Additional code before the begin of namespace sf:
Code: [Select]
// Flag to set in test program
extern bool DrawableRound = true;

// Rounds the coordinates of a vector
sf::Vector2f RoundVector(sf::Vector2f vector)
{
return DrawableRound ? sf::Vector2f(std::floor(vector.x + .5f), std::floor(vector.y + .5f)) : vector;
}

Modified code in Drawable::GetMatrix():
Code: [Select]
// myMatrix = Matrix3::Transformation(myOrigin, myPosition, myRotation, myScale); // replaced with
myMatrix = Matrix3::Transformation(myOrigin, RoundVector(myPosition), myRotation, myScale);

Of course, this is just a quick hack for testing purposes. ;)
I used Spidyy's code and added a moving and a rotating sf::Text:
Code: [Select]
#include <SFML/Graphics.hpp>

extern bool DrawableRound;

int main()
{
sf::RenderWindow window(sf::VideoMode(800, 600, 32), "Rounding is ON");

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

sf::Sprite sprite(image);

sf::Text text("A simple text to test");
text.SetCharacterSize(12u);

for (;;)
{
sf::Event Event;
while(window.GetEvent(Event))
{
if (Event.Type == sf::Event::Closed)
{
return 0;
}

// Mouse click: Switch between rounded/continuous implementation
else if (Event.Type == sf::Event::MouseButtonPressed)
{
DrawableRound = !DrawableRound;

if (DrawableRound)
window.SetTitle("Rounding is ON");
else
window.SetTitle("Rounding is OFF");
}
}

float X = 0.2f * (window.GetInput().GetMouseX() / static_cast<float>(window.GetWidth()));

window.Clear();
sprite.SetScale(0.8f + X, 0.8f + X);
window.Draw(sprite);

text.SetRotation(0.f);
text.SetPosition(window.GetInput().GetMouseX() / 2.43f + 350.f, 30.f);
window.Draw(text);

text.SetRotation(text.GetPosition().x / 2.f);
text.SetPosition(500.f, 200.f);
window.Draw(text);

window.Display();
}
}

You can switch between the rounded and normal mode with any mouse button. The moving text shows a significant difference, while the scaling image is still smooth – in contrast to the approach of revision 1389. For the moment, I didn't take the view into account, because I am not too familiar with the SFML source code. I'm pretty sure I've overlooked several other factors, but... Maybe this helps you, or inspires you to another idea. ;)

Btw: Nice, you're having 11111 posts now :D
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Blurred text at non-integral coordinates
« Reply #13 on: September 19, 2010, 12:25:47 pm »
Thank you for helping me so hard to find a solution ;)

Quote
Since the flickering artifact I mentioned in this thread seems to appear mostly at translations, it might be more effective to consider only them for the moment. I don't know whether there are many use cases where rounded translation coordinates are inappropriate, but for my code I assumed this is not the case

Actually, translation is not different from rotation and scale. It is less noticeable, but with very slow motion you can see the same effects, and rounded coordinates become worse than direct rendering, again. So I'm not sure if your attempt is a good starting point.
Laurent Gomila - SFML developer

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6286
  • Thor Developer
    • View Profile
    • Bromeon
Blurred text at non-integral coordinates
« Reply #14 on: September 19, 2010, 03:05:24 pm »
I think it's basically impossible to have "inter-pixel" rendering (so that you don't see the hard steps from one to the next pixel) and an exact, non-blurred representation. I mean, as soon as a drawable is located "between" two pixels, it has to be rendered accordingly, either representing the object in a continuous (and hence blurred) or an exact (and hence discrete) way. As you said, the current approach is more flexible since it allows the user to round himself at least for translations, which would not be the case in the old solution. I don't know what other users think about it: Whether they like and make use of the new feature, whether they round coordinates as well, or whether they don't even notice... ;)

Maybe we should rather concentrate on how to relieve the user from tedious manual rounding and separate coordinates, in case he doesn't need the continuity, instead of how to enforce pixel-perfect rendering in general. I see there can even be use cases where continuous translation is required, and it's probably not a good choice to disable it. In my opinion, a user-side solution covering the most artifacts would be okay. Maybe something like the proposed PixelPerfectDraw(), but without undefined behaviour. :)

Do you know how other graphic libraries handle this issue? When I used SDL, I always blitted whole pixels, maybe there would be another way. I don't about other libraries/engines.
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development: