Graphics/Text: add optional rounding for letter and line spacing#3638
Graphics/Text: add optional rounding for letter and line spacing#3638Praful-Joshi wants to merge 3 commits into
Conversation
| const float letterSpacingFactor = m_roundLetterSpacing ? roundToPixel(m_letterSpacingFactor) : m_letterSpacingFactor; | ||
| const float letterSpacing = (whitespaceWidth / 3.0f) * (letterSpacingFactor - 1.0f); | ||
| const float lineSpacingFactor = m_roundLineSpacing ? roundToPixel(m_lineSpacingFactor) : m_lineSpacingFactor; | ||
| const float lineSpacing = m_font->getLineSpacing(m_characterSize) * lineSpacingFactor; | ||
|
|
There was a problem hiding this comment.
Why is rounding applied to letterSpacingFactor and lineSpacingFactor instead of letterSpacing and lineSpacing, i.e. the final values used to adjust spacing?
There was a problem hiding this comment.
I wasn't sure if I should mess with letterSpacing and lineSpacing as they are also dependent on whitespaceWidth so I just decided to round m_letterSpacingFactor and m_lineSpacingFactor instead. But you're right, rounding should be applied to the final spacing values to avoid changing their semantic meaning. I’ll move the rounding accordingly.
| //////////////////////////////////////////////////////////// | ||
| /// \brief Round a value to the nearest pixel coordinate | ||
| /// | ||
| /// This function rounds the given floating-point value to | ||
| /// the nearest integer value, which corresponds to a pixel | ||
| /// boundary in window coordinates. | ||
| /// | ||
| /// \param value Value to round | ||
| /// | ||
| /// \return Rounded value | ||
| //////////////////////////////////////////////////////////// | ||
| float roundToPixel(float value) const; |
There was a problem hiding this comment.
I don't see why this method is needed as part of the public interface, or even as a helper method. It has nothing to do with an sf::Text instance, being just a wrapper around std::round. You can just use that directly in the implementation.
There was a problem hiding this comment.
Agreed. I was contemplating real hard on this one. roundToPixel doesn’t belong in the public interface and doesn’t add value over std::round. I’ll remove it and apply rounding directly in the implementation.
| //////////////////////////////////////////////////////////// | ||
| void Text::setLetterSpacingRounding(bool enabled) | ||
| { | ||
| if(m_roundLetterSpacing != enabled) | ||
| { | ||
| m_roundLetterSpacing = enabled; | ||
| } | ||
| } | ||
|
|
||
|
|
||
| //////////////////////////////////////////////////////////// | ||
| void Text::setLineSpacingRounding(bool enabled) | ||
| { | ||
| if(m_roundLineSpacing != enabled) | ||
| { | ||
| m_roundLineSpacing = enabled; | ||
| } | ||
| } |
There was a problem hiding this comment.
Changing these settings affect the geometry, so you do need to set m_geometryNeedUpdate = true; if the values have changed.
There was a problem hiding this comment.
Good catch! this was an oversight. I’ll make sure toggling the rounding mode properly invalidates geometry by setting m_geometryNeedUpdate = true.
| //////////////////////////////////////////////////////////// | ||
| /// \brief Tell whether letter spacing rounding is enabled | ||
| /// | ||
| /// \return True if letter spacing rounding is enabled, false otherwise | ||
| /// | ||
| /// \see `setLetterSpacingRounding` | ||
| /// | ||
| //////////////////////////////////////////////////////////// | ||
| [[nodiscard]] bool getLetterSpacingRounding() const; | ||
|
|
||
| //////////////////////////////////////////////////////////// | ||
| /// \brief Tell whether line spacing rounding is enabled | ||
| /// | ||
| /// \return True if line spacing rounding is enabled, false otherwise | ||
| /// | ||
| /// \see `setLineSpacingRounding` | ||
| /// | ||
| //////////////////////////////////////////////////////////// | ||
| [[nodiscard]] bool getLineSpacingRounding() const; |
There was a problem hiding this comment.
What is the use case of separating the rounding mode vertically and horizontally, instead of using a single setting for it?
There was a problem hiding this comment.
Yaah I was wondering this too. I just moved ahead with the separate implementation to be on the safer side. I’ll consolidate this into one spacing-rounding setting applied consistently to both directions.
|
@Marioalexsan Done. I made a commit with the fixes. |
This pull request introduces optional pixel rounding for text letter spacing and line spacing in sf::Text.
When using fractional spacing factors (e.g. setLetterSpacing(1.1f)), glyph positions may fall on sub-pixel coordinates, which can lead to blurred text due to texture filtering and rasterization behavior. This PR adds a way to opt into pixel-aligned spacing without changing the existing default behavior.
What’s new:
This allows users to choose between:
Related issue:
Fixes #3595
Checklist:
Tasks:
How to test this PR?
The effect is most visible when using fractional spacing values.
Minimal example:
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow window(sf::VideoMode({800, 600}), "Text spacing rounding test");
window.setFramerateLimit(60);
}
Without Rounding:

With Rounding:

Compare the output with rounding enabled vs disabled to clearly see the difference in sharpness.