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

Author Topic: Outlined text  (Read 28141 times)

0 Members and 1 Guest are viewing this topic.

dabo

  • Sr. Member
  • ****
  • Posts: 260
    • View Profile
    • http://www.dabostudios.net
Outlined text
« on: June 25, 2008, 12:08:43 am »
Is that something that could be implemented in the future? Don't know if it's possible but it doesn't hurt to ask ;)

For example in my game I have white text and occasionally the background becomes partly white which makes the text invisible. Having white text with a black outline would fix this. I guess I could draw a black rectangle behind the text but that's the last option.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Outlined text
« Reply #1 on: June 25, 2008, 02:41:19 am »
You could also draw the text twice and slightly offsetted, to create a kind of shadow.
Laurent Gomila - SFML developer

NewbiZ

  • Newbie
  • *
  • Posts: 44
    • View Profile
Outlined text
« Reply #2 on: June 25, 2008, 02:59:50 am »
Quote from: "Laurent"
You could also draw the text twice and slightly offsetted, to create a kind of shadow.

Example: http://newbiz.developpez.com/tutoriels/opengl/heightmap/#LVII-E
:)
~ Passer pour un idiot aux yeux d'un imbécile est une volupté de fin gourmet ~

dabo

  • Sr. Member
  • ****
  • Posts: 260
    • View Profile
    • http://www.dabostudios.net
Outlined text
« Reply #3 on: June 25, 2008, 12:57:49 pm »
That's a good option, thanks. But still, I think it would be nice if you could draw outlined text :)

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Outlined text
« Reply #4 on: June 25, 2008, 02:32:20 pm »
Well, I haven't thought much about it but right now I can't see any way of doing it efficiently.

The "brute force" option would simply be to draw the string 5 times : 1 time in every direction to create the outline, and 1 time on top of it.
Laurent Gomila - SFML developer

workmad3

  • Jr. Member
  • **
  • Posts: 71
    • View Profile
Outlined text
« Reply #5 on: June 25, 2008, 02:47:50 pm »
surely the 'brute force' method would really be to draw the string twice. Once with the outline colour at the requested size, and once on top of it at about 0.2p smaller in the text colour to create the outline?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Outlined text
« Reply #6 on: June 25, 2008, 03:02:52 pm »
2 times won't be enough, as each character won't be scaled around its own center. You would end up with an offset rather than a scale.

4 times to fake a uniform scale, not less ;)
Laurent Gomila - SFML developer

workmad3

  • Jr. Member
  • **
  • Posts: 71
    • View Profile
Outlined text
« Reply #7 on: June 25, 2008, 03:05:28 pm »
hmm... thinking about that, it would require a bit of trickery to change the letter spacing in the smaller version in order to centre them. Still, that's the method I would consider doing as it only involves some (probably minor) calculations and 2 string draws.

The other option I can think of that may be possible is to see if you could get a pixel shader to outline the text. That would manage it in a single pass rather than 2 (or 5) but would mean that with outlines, no other postFX effects could be done on text (unless it was exposed as a postFX function that could be called in another effect). It may not be possible to do entirely in pixel shaders though (the method I can think of would require having a vertex shader that creates a varying variable that can be used in the pixel shader to detect distance from the edge of the character)

NewbiZ

  • Newbie
  • *
  • Posts: 44
    • View Profile
Outlined text
« Reply #8 on: June 25, 2008, 04:30:03 pm »
The simple inner/outter border trick seems to provide pretty nice results as long as the size is >=20.


Code: [Select]
#include <cstdlib>

#include "SFML/Window.hpp"
#include "SFML/Graphics.hpp"
#include <SFML/Graphics/String.hpp>
#include <SFML/Graphics/FontManager.hpp>
#include <SFML/Graphics/Image.hpp>
#include <SFML/Graphics/OpenGL.hpp>
#include <locale>

namespace sf { namespace ext
{
  class OutlinedString : public sf::String
  {
  public:
    OutlinedString(const std::string& Text, const std::string& Font = "", float Size = 32.f):
      String(Text, Font, Size) {}

    OutlinedString(const std::wstring& Text = L"", const std::string& Font = "", float Size = 32.f):
      String(Text, Font, Size) {}

  protected:
    virtual void Render(const RenderWindow& Window) const
    {
      // No text, no rendering :)
    if (GetText().empty())
        return;

    // Get the bitmap font from the font manager
    const priv::FontManager::Font& BitmapFont = priv::FontManager::GetInstance().GetBitmapFont(GetFont(), static_cast<unsigned int>(GetSize()));

    // Set the scaling factor to get the actual size
    float FactorX = GetScaleX() * GetSize() / BitmapFont.CharSize;
    float FactorY = GetScaleY() * GetSize() / BitmapFont.CharSize;
    GLCheck(glScalef(FactorX, FactorY, 1.f));

    // Bind the font texture
    BitmapFont.Texture.Bind();

    float X = 0;
    float Y = 0;

    // Draw the sprite
    glBegin(GL_QUADS);
    for (std::size_t i = 0; i < GetText().size(); ++i)
    {
        // Get the current character
        wchar_t c = GetText()[i];

        // Check if the character is in the charset
        std::map<wchar_t, priv::FontManager::Font::Character>::const_iterator It = BitmapFont.Characters.find(c);
        if (It == BitmapFont.Characters.end())
        {
            // No : add a space and continue to the next character
            X += GetSize();
            continue;
        }
        const priv::FontManager::Font::Character& CurChar = It->second;

        // Get the dimensions of the current character from font description
        const IntRect&   Rect       = CurChar.Rect;
        const FloatRect& Coord      = CurChar.Coord;
        const unsigned int AdvanceX = CurChar.Advance;
        const unsigned int AdvanceY = BitmapFont.CharSize;

        // Handle special characters
        switch (c)
        {
            case L' ' :  X += AdvanceX;        continue;
            case L'\n' : Y += AdvanceY; X = 0; continue;
            case L'\t' : X += AdvanceX * 4;    continue;
            case L'\v' : Y += AdvanceY * 4;    continue;
        }

        // Draw a textured quad for the current character
        glColor4f(0,0,0,1); //TODO: Make the outline color settable
       
        // Outter outline
        glTexCoord2f(Coord.Left,  Coord.Top);    glVertex2f(X + Rect.Left -1, Y + Rect.Top   -1 + AdvanceY);
        glTexCoord2f(Coord.Left,  Coord.Bottom); glVertex2f(X + Rect.Left -1, Y + Rect.Bottom+1 + AdvanceY);
        glTexCoord2f(Coord.Right, Coord.Bottom); glVertex2f(X + Rect.Right+1, Y + Rect.Bottom+1 + AdvanceY);
        glTexCoord2f(Coord.Right, Coord.Top);    glVertex2f(X + Rect.Right+1, Y + Rect.Top   -1 + AdvanceY);

        // Inner outline
        glTexCoord2f(Coord.Left,  Coord.Top);    glVertex2f(X + Rect.Left +1, Y + Rect.Top   +1 + AdvanceY);
        glTexCoord2f(Coord.Left,  Coord.Bottom); glVertex2f(X + Rect.Left +1, Y + Rect.Bottom-1 + AdvanceY);
        glTexCoord2f(Coord.Right, Coord.Bottom); glVertex2f(X + Rect.Right-1, Y + Rect.Bottom-1 + AdvanceY);
        glTexCoord2f(Coord.Right, Coord.Top);    glVertex2f(X + Rect.Right-1, Y + Rect.Top   +1 + AdvanceY);

        // Text
        glColor4f( GetColor().r, GetColor().g, GetColor().b, GetColor().a );
        glTexCoord2f(Coord.Left,  Coord.Top);    glVertex2f(X + Rect.Left,  Y + Rect.Top    + AdvanceY);
        glTexCoord2f(Coord.Left,  Coord.Bottom); glVertex2f(X + Rect.Left,  Y + Rect.Bottom + AdvanceY);
        glTexCoord2f(Coord.Right, Coord.Bottom); glVertex2f(X + Rect.Right, Y + Rect.Bottom + AdvanceY);
        glTexCoord2f(Coord.Right, Coord.Top);    glVertex2f(X + Rect.Right, Y + Rect.Top    + AdvanceY);

        // Advance to the next character
        X += AdvanceX;
    }
    glEnd();
    }
  };
}}

int main( int argc, char** argv )
{
  sf::RenderWindow app( sf::VideoMode( 800, 600 ), "Outlined text example" );
  app.SetBackgroundColor( sf::Color::Red );
  app.Show( true );
  app.SetCurrent();

  sf::ext::OutlinedString str( "Example outlined string", "classic.ttf", 40.f);
  str.SetColor( sf::Color::White );

  bool done=false;
  while (!done)
  {
   
    // =================================== <Events>
    sf::Event Event;
    while ( app.GetEvent(Event) )
      if (Event.Type == sf::Event::Closed)
        done = false;
    // =================================== </Events>

    // =================================== <Display>
    str.SetColor( sf::Color::White   );str.SetPosition( 10,  10 ); str.SetSize( 50.f ); app.Draw( str  );
    str.SetColor( sf::Color::Blue    );str.SetPosition( 10, 100 ); str.SetSize( 40.f ); app.Draw( str  );
    str.SetColor( sf::Color::Green   );str.SetPosition( 10, 200 ); str.SetSize( 30.f ); app.Draw( str  );
    app.Display();
    // =================================== </Display>
  }

  return EXIT_SUCCESS;
}



~ Passer pour un idiot aux yeux d'un imbécile est une volupté de fin gourmet ~

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Outlined text
« Reply #9 on: June 25, 2008, 04:40:50 pm »
Yep, with shaders I could provide pixel perfect text rendering with the method you're thinking about, but that's clearly not an option ;)
Laurent Gomila - SFML developer

dabo

  • Sr. Member
  • ****
  • Posts: 260
    • View Profile
    • http://www.dabostudios.net
Outlined text
« Reply #10 on: June 25, 2008, 10:57:48 pm »
Well if there isen't an efficient way to do it the first suggested option will do, no problem.

NewbiZ: Looks good, but I usually have sizes around 10 - 14. I couldn't compile your code to test it on smaller sizes than the example you showed.

NewbiZ

  • Newbie
  • *
  • Posts: 44
    • View Profile
Outlined text
« Reply #11 on: June 25, 2008, 11:09:26 pm »
This is a quick and dirty implementation which only purpose is to show the result of the inner/outter trick.
You may have to add OpenGL.hpp & FontManager.hpp (from SFML sources headers) to your headers.

As Laurent said, it would be better to draw the outline offseted in each direction instead. Just some lines to change.
~ Passer pour un idiot aux yeux d'un imbécile est une volupté de fin gourmet ~

workmad3

  • Jr. Member
  • **
  • Posts: 71
    • View Profile
Outlined text
« Reply #12 on: June 26, 2008, 09:19:32 am »
I've been thinking about the drawing in all directions and realised there is a slight problem with it... it won't get corners, which would leave the outline looking very bad, especially at higher font sizes if the outline size increases with it.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Outlined text
« Reply #13 on: June 26, 2008, 09:30:23 am »
To get the corners, you can still draw the string 8 times :D
Laurent Gomila - SFML developer

dabo

  • Sr. Member
  • ****
  • Posts: 260
    • View Profile
    • http://www.dabostudios.net
Outlined text
« Reply #14 on: June 26, 2008, 12:30:16 pm »
Seems like a lot of code for just an outline, is that really how they do it in "professional" games?

Example