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

Author Topic: Strange font rendering problem  (Read 1106 times)

0 Members and 1 Guest are viewing this topic.

nacreous

  • Newbie
  • *
  • Posts: 1
    • View Profile
Strange font rendering problem
« on: July 04, 2014, 12:36:33 am »
I have some example code here in which 2 attempts to draw text to the window behave very differently, for some unknown reason. In the first attempt I draw text elements in a grid, and they display improperly; in the second attempt the text is drawn correctly. As far as I am aware both attempts use the same method.

I included a png image of the program running.

Here's the reproducible example code:

#include <string>
#include <vector>
#include <SFML/Graphics.hpp>

sf::RenderWindow win;
sf::Font         font;

struct GridData
{
    bool a      = false;
    int  x      = 0;
    int  y      = 0;
    int  height = 0;
    int  width  = 0;
};

struct GuiWindow;

struct GuiElement
{
    enum class Type { Button, CheckBox, DropList, Text, TextBox };

    // relational pointers
    GuiWindow* pGW;

    //data
    bool                     active          = true;
    bool                     checked         = false;
    bool                     cursor          = false;
    bool                     expandHor       = true;
    bool                     expandVer       = true;
    bool                     visible         = true;
    int                      charSize        = 0;
    int                      xGrid           = 0;
    int                      yGrid           = 0;
    int                      id              = 0;
    Type                     type            = Type::Text;
    std::vector<std::string> dropListOptions;
    sf::Color                clickColour;
    sf::Color                highlightColour;
    sf::Color                normalColour;
    sf::Color                textColour;
    sf::Text                 text;

    // constructors
    GuiElement(GuiWindow*);
    GuiElement()                             = delete;
    ~GuiElement()                            = default;
    GuiElement(const GuiElement&)            = default;
    GuiElement(GuiElement&&)                 = delete;
    GuiElement& operator=(const GuiElement&) = delete;
    GuiElement& operator=(GuiElement&&)      = delete;

    // relational functions
    const GuiWindow& parent()       const { return *pGW; }
          GuiWindow& parent_write()       { return *pGW; }

    // functions
    int  getWidth() const;
    void draw();
    void handleEvent(const sf::Event&);
    void makeText(Type, const std::string&, sf::Color, int, int, int, bool, bool, bool, bool);

};

struct GuiWindow
{
    // data
    int                     x          = 0;
    int                     y          = 0;
    int                     w          = 0;
    int                     h          = 0;
    GridData                grid[8][4];
    std::vector<GuiElement> elements;

    // constructors
    GuiWindow()                            = default;
    ~GuiWindow()                           = default;
    GuiWindow(const GuiWindow&)            = delete;
    GuiWindow(GuiWindow&&)                 = delete;
    GuiWindow& operator=(const GuiWindow&) = delete;
    GuiWindow& operator=(GuiWindow&&)      = delete;

    // functions
    void open();
    void resize();
};

GuiElement::GuiElement(GuiWindow* pGW_)
{
    pGW   = pGW_;
}

void GuiElement::makeText(Type type_, const std::string& text_, sf::Color colour, int s, int x, int y, bool active_, bool visible_, bool expandHor_, bool expandVer_)
{
    type       = type_;
    textColour = colour;
    charSize   = s;
    xGrid      = x;
    yGrid      = y;
    active     = active_;
    visible    = visible_;
    expandHor  = expandHor_;
    expandVer  = expandVer_;

    if (xGrid < 0) xGrid = 0;
    if (yGrid < 0) yGrid = 0;
    if (xGrid > 3) xGrid = 0;
    if (yGrid > 7) yGrid = 0;

    text.setString(text_);
    text.setFont(font);
    text.setCharacterSize(charSize);
    text.setColor(textColour);
}

int GuiElement::getWidth() const
{
    return text.getLocalBounds().width;
}

void GuiElement::draw()
{
    text.setPosition(parent().grid[yGrid][xGrid].x,
                     parent().grid[yGrid][xGrid].y);

    win.draw(text);
}

void GuiWindow::open()
{
    sf::Event event;

    for (;;)
    {
        while (win.pollEvent(event))
        {
            if (event.type == sf::Event::KeyPressed)
                return;
        }

        for (auto& it : elements)
            it.draw();

        win.display();
    }
}

void GuiWindow::resize()
{
    int xGridReach = 0;
    int yGridReach = 0;
    int width      = 0;

    for (const auto& it : elements)
    {
        grid[it.yGrid][it.xGrid].a = true;

        if (it.getWidth() > width)
        {
            width = it.getWidth();
        }
    }

    for (int j = 0; j < 8; ++j)
    {
        for (int i = 0; i < 4; ++i)
        {
            if (grid[j][i].a)
            {
                xGridReach = i;
                yGridReach = j;
            }

            grid[j][i].width  = width;
            grid[j][i].height = 24;
            grid[j][i].x      = i * width;
            grid[j][i].y      = j * 24;
        }
    }

    w = xGridReach * width;
    h = yGridReach * 24;
}

int main()
{
    win.create(sf::VideoMode(512, 512, 32), "Test 1", sf::Style::Titlebar | sf::Style::Close);

    font.loadFromFile("DejaVuSans.ttf");

    GuiWindow  w;
    GuiElement e(&w);

    e.makeText(GuiElement::Type::Text, "text", sf::Color::White, 18, 0, 0, true, true, true, false); w.elements.push_back( e );
    e.makeText(GuiElement::Type::Text, "text", sf::Color::White, 18, 1, 0, true, true, true, false); w.elements.push_back( e );
    e.makeText(GuiElement::Type::Text, "text", sf::Color::White, 18, 2, 0, true, true, true, false); w.elements.push_back( e );
    e.makeText(GuiElement::Type::Text, "text", sf::Color::White, 18, 3, 0, true, true, true, false); w.elements.push_back( e );
    e.makeText(GuiElement::Type::Text, "text", sf::Color::White, 18, 0, 1, true, true, true, false); w.elements.push_back( e );
    e.makeText(GuiElement::Type::Text, "text", sf::Color::White, 18, 1, 1, true, true, true, false); w.elements.push_back( e );
    e.makeText(GuiElement::Type::Text, "text", sf::Color::White, 18, 2, 1, true, true, true, false); w.elements.push_back( e );
    e.makeText(GuiElement::Type::Text, "Hello World", sf::Color::White, 18, 3, 1, true, true, true, false); w.elements.push_back( e );

    w.resize();
    w.open();

    sf::Text text2nd;

    text2nd.setString("Hello World!");
    text2nd.setFont(font);
    text2nd.setCharacterSize(18);
    text2nd.setPosition(100, 100);

    win.draw(text2nd);
    win.display();

    sf::Event event;

    for (;;)
    {
        while (win.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
            {
                win.close();
                return 0;
            }
        }
    }
}

Ixrec

  • Hero Member
  • *****
  • Posts: 1241
    • View Profile
    • Email
Re: Strange font rendering problem
« Reply #1 on: July 04, 2014, 12:58:06 am »
Just glancing at the .png, that looks like what happens when you draw things at non-integer coordinates.  Check to see if you're doing that.

I can't see an obvious cause in that code, but it's hardly a minimal example and you've already confirmed the pure-SFML version works fine, so it's probably a subtle mistake somewhere in your custom GUI code.

Totally unrelated advice:
- Have you looked at SFGUI?  No need to make this stuff yourself.
- Do not create multimedia objects at global scope.  Ever.  win and font should be declared in main() and passed to whatever needs them.
- Always call clear/draw/display in your main game loop, and design your custom drawables around this pattern.  Currently you seem to be calling draw()/display() for each drawable exactly once for the entire lifespan of the program.
- Your interface for constructing gui elements is bizarre. You appear to instantiate a single element, turn it into a text object, put a copy into the containing window, then turn it into a different text object and repeat.  It would make more sense to have subclasses for each type of gui element and construct them directly.
- Why do you have to pass a Text type to a function called makeText?
- Always check the return value on loadFromFile() calls.