SFML community forums

Help => General => Topic started by: ineed.help on July 24, 2014, 01:43:46 pm

Title: Problem with character positioning
Post by: ineed.help on July 24, 2014, 01:43:46 pm
Hello!

I have been working for quite a while now on a system to draw a series of sprite as if they were an sf::Text.
However, I recently took notice of a problem: if you look at this picture...
(http://i.imgur.com/JQELFcn.png)
As you can see, there are some rather large gaps between some of the characters, and I don't quite understand why.
There is also a very large difference between the y-coordinates of the lines.
This is a sample of the code I have been using which contains my problem.

int main()
{
        sf::RenderWindow window(sf::VideoMode(360, 240, 32), "Title");
        sf::Font font;
        font.loadFromFile("fonts/font.ttf");
        std::vector<sf::Sprite> chars;
        sf::Sprite tempSprite;
        std::string str = "Hello! This is a string! This string is rather long, so it needs to be divided into multiple lines.";
        unsigned short xPos = 0, yPos = 0;
        unsigned char prevChar = 'A';

        for (unsigned short i = 0; i < str.length(); i++)
        {
                tempSprite.setTexture(font.getTexture(24));
                tempSprite.setTextureRect(font.getGlyph(str[i], 24, false).textureRect);
                tempSprite.setColor(sf::Color::Black);

                if (xPos + (2 * tempSprite.getTextureRect().width) >= window.getSize().x)
                {
                        xPos = 0;
                        yPos += font.getLineSpacing(24);
                }

                else
                { if (i > 1) { xPos += font.getKerning(prevChar, str[i], 24); } }

                tempSprite.setPosition(xPos, yPos + font.getGlyph(str[i], 24, false).bounds.top + font.getGlyph('A', 24, false).bounds.height); // should this be 'A' (?)
                chars.push_back(tempSprite);
                xPos += font.getGlyph(str[i], 24, false).advance;
                prevChar = str[i];
        }

        unsigned short crtCharNo = 0;

        while(window.isOpen())
        {
                window.clear(sf::Color::White);
                if (crtCharNo < chars.size() - 1) { crtCharNo++; }
                for (unsigned short i = 0; i <= crtCharNo; i++)
                { window.draw(chars[i]); }
                window.display();
        }
}
 

What am I doing wrong? I thought "getKerning" moves the characters closer, and "advance" moved them apart, but I am clearly doing something wrong.
(This assumes there is nothing wrong with the font I am using, which is called "Linux Libertine", although I doubt that's the case.)

Hopefully I explained myself in a decent manner. I would greatly appreciate any help.
Title: Re: Problem with character positioning
Post by: Nexus on July 24, 2014, 02:54:18 pm
You should use sf::Text::findCharacterPos(), it handles the positioning for you. Unfortunately, this may require the creation of a useless sf::Text object.
Title: Re: Re: Problem with character positioning
Post by: Strelok on July 24, 2014, 03:14:18 pm
You should use sf::Text::findCharacterPos(), it handles the positioning for you. Unfortunately, this may require the creation of a useless sf::Text object.
The irony ;D
Title: Re: Problem with character positioning
Post by: ineed.help on July 24, 2014, 03:18:59 pm
... I don't understand; how am I supposed to use it? I understand if it's just one line, but with multiple lines, how would I go about doing that?
Title: Re: Problem with character positioning
Post by: Nexus on July 24, 2014, 04:33:54 pm
This functionality might be outsourced one day. For now, you could copy the algorithm from sf::Text::findCharacterPos (or at least have a look at it and see what you need to do), because creating a sf::Text object is of course not ideal if you want to draw sf::Sprites ;)

What difference do multiple lines make to find the position? The text then just starts at another character... and when you use the algorithm directly without sf::Text, you can probably even find a more elegant solution than reassigning the string.
Title: Re: Problem with character positioning
Post by: ineed.help on July 24, 2014, 05:06:17 pm
I have read it. I did as it said, too, in my previous code:

xPos += font.getKerning(prevChar, str[i], fontSize);
tempSprite.setPosition(xPos, yPos + font.getGlyph(str[i], fontSize, false).bounds.top + font.getGlyph(0, fontSize, false).bounds.height);
chars.push_back(tempSprite);
xPos += font.getGlyph(str[i], fontSize, false).advance;
prevChar = str[i];
 

As the source code says... Am I missing something?
Title: Re: Problem with character positioning
Post by: ineed.help on July 24, 2014, 09:25:20 pm
Anyone?
Title: Re: Problem with character positioning
Post by: Nexus on July 24, 2014, 10:24:19 pm
Your other post is only a few hours old... Why are people so impatient? This is a forum of contributors spending their free time, and in my opinion it is slightly disrespectful to push topics after such a short amount of time. People will see your post and answer if they want to, it doesn't need additional attention.

You could use the time to debug further. Does it work with findCharacterPos()? If so, find the difference to your code. And double-check the documentation again, it has been a while since I last worked with glyphs...
Title: Re: Problem with character positioning
Post by: ineed.help on July 24, 2014, 10:34:45 pm
I don't know how these forums work, if people only generally reply to the top topic or not, as this is the case in some other forums. I did not mean to be disrespectful. I am just an impatient person.

As I said earlier, the problem is not one that can be debugged; it doesn't work with "findCharacterPos()", because sf::Text doesn't do automatic line breaks like my program does. For this to work, I would have to create a sf::Text object, assign a line break every time the automatic line break occurs, and then use "findCharacterPos()", and I feel that this is a bit reduntant, which is why I am asking for help from those more experienced with SFML.
Title: Re: Problem with character positioning
Post by: binary1248 on July 24, 2014, 11:18:24 pm
I have read it. I did as it said, too, in my previous code:
...
As the source code says... Am I missing something?
I don't know what source code you read, but the one in sf::Text::ensureGeometryUpdate() contains way more than those few lines of code you mentioned. Since I really can't be bothered explaining each line of the code that you didn't read properly, I'll just paste this here and let you spot the differences.
int main()
{
    sf::RenderWindow window(sf::VideoMode(360, 240, 32), "Title");
    sf::Font font;
    font.loadFromFile("font.ttf");
    std::vector<sf::Sprite> chars;
    sf::Sprite tempSprite;
    std::string str = "Hello! This is a string! This string is rather long, so it needs to be divided into multiple lines.";
    unsigned short xPos = 0, yPos = 24;
    unsigned char prevChar = 0;

    for (unsigned short i = 0; i < str.length(); i++)
    {
        const sf::Glyph& glyph = font.getGlyph(str[i], 24, false);

        sf::Vector2f scale(1.f, 1.f);

        if (glyph.textureRect.width && glyph.textureRect.height)
        {
            scale.x = glyph.bounds.width / glyph.textureRect.width;
            scale.y = glyph.bounds.height / glyph.textureRect.height;
        }

        tempSprite.setTexture(font.getTexture(24));
        tempSprite.setTextureRect(glyph.textureRect);
        tempSprite.setColor(sf::Color::Black);
        tempSprite.setScale(scale);

        if (xPos + (2 * tempSprite.getTextureRect().width) >= window.getSize().x)
        {
            xPos = 0;
            yPos += font.getLineSpacing(24);
            prevChar = 0;
        }
        else
        {
            xPos += font.getKerning(prevChar, str[i], 24);
        }

        tempSprite.setPosition(xPos + glyph.bounds.left, yPos + glyph.bounds.top);
        chars.push_back(tempSprite);
        xPos += glyph.advance;
        prevChar = str[i];
    }

    unsigned short crtCharNo = 0;

    while(window.isOpen())
    {
        window.clear(sf::Color::White);
        if (crtCharNo < chars.size() - 1) { crtCharNo++; }
        for (unsigned short i = 0; i <= crtCharNo; i++)
        { window.draw(chars[i]); }
        window.display();
    }
}
Oh and by the way... there is nothing wrong with the vertical spacing between the lines in your original picture. You just picked the worst possible spot to measure the distance at.
Title: Re: Problem with character positioning
Post by: ineed.help on July 24, 2014, 11:36:07 pm
Thank you, I will check this out first thing in the morning (it's nearly midnight in my country).

(Also, I checked "findCharacterPos()", not "ensureGeometryUpdate()".)
Title: Re: Problem with character positioning
Post by: ineed.help on July 25, 2014, 10:25:28 am
All right, it works... mostly. How come it doesn't start at (0, 0)? This happens with sf::Text as well; if you set its position to (0, 0), the y-position is never at 0.
Title: Re: Problem with character positioning
Post by: Laurent on July 25, 2014, 10:36:02 am
Because text is aligned on the baseline, not on the top position. The extra space at the top would be filled if you had the highest possible glyph in your text.
Title: Re: Problem with character positioning
Post by: ineed.help on July 25, 2014, 11:05:36 am
What do you mean by the "highest possible glyph"? Can I do something with the base y-position to correct this for all characters?
Title: Re: Problem with character positioning
Post by: Laurent on July 25, 2014, 11:17:33 am
I mean the tallest one, like 'É'. The behaviour is the same as in any text editor.

If you really want your text to be aligned on top, you can subtract getLocalBounds().top from its Y position.
Title: Re: Problem with character positioning
Post by: ineed.help on July 25, 2014, 12:06:42 pm
You mean like this?:

yPos = 0 - font.getGlyph('A', fontSize, false).bounds.top;
 
This seems to work for me; it places the "l", which is one of the longest characters, at the y-position it's supposed to.

With "getLocalBounds().top", I do it like this:
tempSprite.setPosition(xPos + font.getGlyph(str[i], fontSize, false).bounds.left, yPos + font.getGlyph(str[i], fontSize, false).bounds.top - tempSprite.getLocalBounds().top);
 
Yet it does absolutely nothing.

Well, I guess it doesn't matter, since I seem to have fixed everything.