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

Author Topic: Slight differences with Text object position  (Read 528 times)

0 Members and 1 Guest are viewing this topic.

abcd1234567

  • Newbie
  • *
  • Posts: 23
    • View Profile
Slight differences with Text object position
« on: December 24, 2023, 09:35:44 pm »
I'm having sligt differences between the positions I set my Text object and where they are in the 2D world (according to getGlobalBounds).
Lets take a look at the following simplified code:

#include <iostream>
#include <SFML/Graphics.hpp>

int main() {
    sf::Font font;
    if (!font.loadFromFile("arial.ttf")){
        exit(-1);
    }
    sf::Text str("Hello!", font, 30);
    str.setPosition(0,0);
    sf::FloatRect global_rect = str.getGlobalBounds();
    sf::RenderWindow window(sf::VideoMode(640, 480), "Window");

    while(window.isOpen()){
        window.clear();
        window.draw(str);
        window.display();
    }

    return 0;
}
 

If you would check the top and left values of global_rect, it would be 2 and 8 respectively, instead of 0 and 0, despite setting the top-left corner to be at (0,0). Why is that?

kojack

  • Sr. Member
  • ****
  • Posts: 314
  • C++/C# game dev teacher.
    • View Profile
Re: Slight differences with Text object position
« Reply #1 on: December 24, 2023, 11:50:16 pm »
Each character has a separate quad generated. The quads are different sizes depending on the look of the character. When you set the position, this is the top left of the region characters could be in, but getGlobalBounds returns the actual rectangle that fits the visible characters. So the bounds will change if you change the text. If you just had a bunch of short characters (like "mmmm") the top bounds would be even further down.

abcd1234567

  • Newbie
  • *
  • Posts: 23
    • View Profile
Re: Slight differences with Text object position
« Reply #2 on: December 25, 2023, 10:10:02 am »
Each character has a separate quad generated. The quads are different sizes depending on the look of the character. When you set the position, this is the top left of the region characters could be in, but getGlobalBounds returns the actual rectangle that fits the visible characters. So the bounds will change if you change the text. If you just had a bunch of short characters (like "mmmm") the top bounds would be even further down.

Is it possible to know in advance, just based on my setPosition() and characterSize, the bounds of a rectangle that would contain the Text object?

My problem is this:

#include <iostream>
#include <SFML/Graphics.hpp>

int main() {
    sf::Font font;
    if (!font.loadFromFile("arial.ttf")){
        exit(-1);
    }
    sf::Text str("GoodBye", font, 20);
    str.setPosition(0,0);
    sf::FloatRect global_rect = str.getGlobalBounds();
    std::cout << "top: " << global_rect.top << " height: " << global_rect.height << std::endl;

    return 0;
}
 

I set the position in (0,0) with characterSize of 20. But top is 5 and height is 19.
Basically, a rectangle with top-left (0,0) and height of 20 won't contain the Text object.

For reference: I'm writing a menu in which there's Text objects stacked above each other, and I want each one of them to be in a fixed size rectangle.

kojack

  • Sr. Member
  • ****
  • Posts: 314
  • C++/C# game dev teacher.
    • View Profile
Re: Slight differences with Text object position
« Reply #3 on: December 25, 2023, 11:26:08 am »
The way it works is there is a baseline that characters sit on (the bottom of normal text). This baseline is set at a Y of the position + characterSize.
So if your text position is 0,0 and the font is size 20, the baseline will be at a Y of 20.

Each glyph (character) in a font has a relative size compared to the baseline. For example 'G' has a relative rectangle of 1,-15,13,15. This means it starts 1 pixel to the right of the current cursor and 15 pixels above the baseline, then is for 13 pixels wide and 15 pixels high. So it sits on the baseline.
A character like 'y' hangs the tail below the baseline. 'y' is 0,-11,10,15. So it goes 11 above the baseline and 4 below (and including) the baseline. That means the actual global bounds of the letter 'y' with a character size of 20 would be from Y=9 to Y=23.

I did a quick test, in the standard ascii characters (0-127) with arial font at size 20, the tallest characters (like '$') would have a top at Y=3 and the most hanging down characters have a bottom at Y=23 (like brackets, 'y', etc).

Yeah, it's a bit annoying, but that's how fonts work.

You can ask a font for the size of each character using font.getGlyph. (Got to do it one at a time).

So basically if you have arial.ttf at size 20, you need a text box at least 24 pixels high to fit in characters that go below the baseline.

I don't think there's an easy way to know the min/max of an entire font other than to loop over every glyph and find them yourself. Although I also haven't looked hard, I just wanted to find the numbers so a loop was fine. :)


eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 10823
    • View Profile
    • development blog
    • Email
Re: Slight differences with Text object position
« Reply #4 on: December 25, 2023, 09:14:31 pm »
You have to take into account the local bounds, top and left can be non-zero, to offset to the baseline.
If you combine the global and offset for the local bounds, you should be able to position the texts correctly.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Hapax

  • Hero Member
  • *****
  • Posts: 3351
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Slight differences with Text object position
« Reply #5 on: December 30, 2023, 04:52:12 pm »
Is it possible to know in advance, just based on my setPosition() and characterSize, the bounds of a rectangle that would contain the Text object?
In advance here would be before drawing (or entering the loop), I presume, so you can set up the text and then get its bounds before setting its origin and position. Remember that the string needs to be set too so that it knows which characters will be displayed and how big - and whereabouts - they are.
For aligning to the top-left, for a simple common character set, I'd probably set the string to something with a large capital (like "X") and position it for that and then change the string however I like afterwards.

One thing you can do with top-left alignment is to set the "origin" to its top-left (local) bounds. This allows you to place the text's position to wherever you want the top-left of the text to be and then just update the origin whenever the string changes (if it does).
You can do similar things with alignments to other corners too but be aware that updating the origin from a string change may make the text appear to "move" when characters change (e.g. when you add a "y" to the bottom line when previously it was only capitals and you're aligning to its bottom).
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*