-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
sf::Text::findCharacterPos()
doesn't report the correct Y location
#2988
Comments
I am not sure this is a bug. I think there is a misunderstanding of what You can achieve what you want with glyph bounds provided by the font. #include <SFML/Graphics.hpp>
int main() {
sf::RenderWindow window(sf::VideoMode(400, 400), "SFML Works!");
window.setFramerateLimit(60);
window.setView(sf::View(sf::FloatRect({}, {100, 100})));
sf::Font font;
if (!font.loadFromFile("SourceCodePro-SemiBold.ttf")) {
return 1;
}
sf::Text text("M", font, 32);
text.setPosition({50.f, 10.f});
text.setScale({2.f, 2.f});
text.setRotation(20.f);
const sf::FloatRect glyphBounds =
font.getGlyph(text.getString()[0], text.getCharacterSize(), false).bounds;
sf::RectangleShape box;
box.setOutlineColor(sf::Color::Blue);
box.setOutlineThickness(2);
box.setFillColor(sf::Color::Transparent);
// relative to text coordinate system
box.setPosition(text.getInverseTransform() * text.findCharacterPos(0) +
sf::Vector2f(0, static_cast<float>(text.getCharacterSize())) +
glyphBounds.getPosition());
box.setSize(glyphBounds.getSize());
const sf::FloatRect boxRect(box.getPosition(), box.getSize());
while (window.isOpen()) {
for (sf::Event event; window.pollEvent(event);) {
switch (event.type) {
case sf::Event::Closed:
window.close();
break;
case sf::Event::MouseMoved: {
const sf::Vector2i mouseInWindow(event.mouseMove.x, event.mouseMove.y);
const sf::Vector2f mouseInWorld =
window.mapPixelToCoords(mouseInWindow);
const sf::Vector2f mouseInText =
text.getInverseTransform() * mouseInWorld;
box.setOutlineColor(boxRect.contains(mouseInText) ? sf::Color::Green
: sf::Color::Blue);
} break;
default:
break;
}
}
window.clear();
window.draw(text);
window.draw(box, text.getTransform());
window.display();
}
} |
For reference, this is the documentation you are talking about: https://www.sfml-dev.org/documentation/2.6.1/classsf_1_1Text.php#a2e252d8dcae3eb61c6c962c0bc674b12 It says it applies all transformations including translation. Currently it doesn't look like it is applying the local bound translation, hence why I added it. Also, it seems a little weird from a user perspective that I have to do all of that to find the actual character position. If the current implementation is correct, wouldn't that suggest we should rename the function? |
Also i really think it's a bug now that I've done more digging. If you look at my test code, you will see the code I'm using to run this test. So the blue box is an sf::Rect that uses the global bounds from the text object to represent itself. This is what it looks like: But with the current 2.6.x code, whenever you use the value from "findCharacterPos" as the top left corner of your rect that surrounds the character, you get this: I don't think it makes sense for the return value of the "top left" coordinate to be above the global bounds value that you get from rect. For reference, if you do the fix lapinozz suggests in the PR I opened, you will see the top left corner match the global bounds top left corner. This is a picture running the same exact test code, but with the SFML fix: I really think it's a bug here and not a documentation issue. |
This works for the first letter but it is wrong in general. Consider the following program and a corresponding screenshot. I think To get a tight bounding box on a specific character, glyph bounds give the needed information. #include <SFML/Graphics.hpp>
int main()
{
auto window = sf::RenderWindow{sf::VideoMode{800, 600}, "Text", sf::Style::Default};
window.setFramerateLimit(60);
auto font = sf::Font{};
if (!font.loadFromFile("resources/tuffy.ttf"))
return 1;
constexpr auto characterSize = 200;
auto text = sf::Text{L"ïyÏ~Ô @!", font, characterSize};
text.setPosition(100.f, 100.f);
text.setRotation(10.f);
auto backgroundRects = std::vector<sf::RectangleShape>{};
for (auto i = std::size_t{}; i < text.getString().getSize(); i++)
{
const auto position = text.getInverseTransform() * text.findCharacterPos(i);
const auto next = text.getInverseTransform() * text.findCharacterPos(i + 1);
const auto size = sf::Vector2f{next.x - position.x, font.getLineSpacing(characterSize)};
auto& rect = backgroundRects.emplace_back(size);
rect.setPosition(position);
rect.setFillColor(i % 2 ? sf::Color{64, 64, 0} : sf::Color{0, 64, 64});
}
auto letterBounds = std::vector<sf::RectangleShape>{};
for (auto i = std::size_t{}; i < text.getString().getSize(); i++)
{
const auto bounds = font.getGlyph(text.getString()[i], characterSize, false).bounds;
const auto position = text.getInverseTransform() * text.findCharacterPos(i) + sf::Vector2f{0.f, characterSize} +
bounds.getPosition();
auto& rect = letterBounds.emplace_back(bounds.getSize());
rect.setPosition(position);
rect.setFillColor(sf::Color::Transparent);
rect.setOutlineColor(sf::Color::Green);
rect.setOutlineThickness(1.f);
}
auto textBounds = sf::RectangleShape{};
{
const auto bounds = text.getLocalBounds();
textBounds.setPosition(bounds.getPosition());
textBounds.setSize(bounds.getSize());
textBounds.setFillColor(sf::Color::Transparent);
textBounds.setOutlineColor(sf::Color::Red);
textBounds.setOutlineThickness(1.f);
}
while (window.isOpen())
{
for (auto event = sf::Event{}; window.pollEvent(event);)
if (event.type == sf::Event::Closed)
window.close();
window.clear();
for (const auto& rect : backgroundRects)
window.draw(rect, text.getTransform());
window.draw(text);
for (const auto& bounds : letterBounds)
window.draw(bounds, text.getTransform());
window.draw(textBounds, sf::RenderStates{sf::BlendAdd, text.getTransform(), nullptr, nullptr});
window.display();
}
return 0;
} |
So after looking at this image, I ask myself if people really want a feature like this. For me, I don't need this. If I'm the only one advocating for something like this, then we shouldn't add more features than necessary. I was definitely using / expecting the wrong things from the "findCharacterPos" function. It's truly finding the glyph's global charactar position which seems like that is what it really should do. Whenever I was comparing it to the text's global bounds, it made it look weird. Thing is, that wasn't the point of the function. I definitely think there could be some more touching up in the documentation as to what this function does specifically. |
5bae08a#diff-22c1fd53bbab429b4a348662278b21abb1d7a063aa14b175a8f3a5451c783b6cR152-R193 Then again, the function is about the visual representation and not the glyph bounding box, so @kimci86 might be right 🤔 |
This seems related to #2172, i.e. lacking documentation on font metrics. |
This pr addresses that the findcharactarpos will return the top of the line position. |
sf::Text::findCharacterPos()
doesn't report the correct Y location
Prerequisite Checklist
Describe your issue here
findCharacterPos is offsetting the Y position too high and reporting incorrect values.
Your Environment
Steps to reproduce
Run this code:
Expected behavior
That blue box around the letter should actually be around the letter.
Actual behavior
The blue box is offset up due to an incorrect calculation from findCharacterPos
The text was updated successfully, but these errors were encountered: