mirror of
https://github.com/SFML/SFML.git
synced 2024-11-28 22:31:09 +08:00
Added support for outlined text
This commit is contained in:
parent
7ff9478061
commit
957cabb816
@ -71,7 +71,7 @@ int main()
|
||||
pauseMessage.setFont(font);
|
||||
pauseMessage.setCharacterSize(40);
|
||||
pauseMessage.setPosition(170.f, 150.f);
|
||||
pauseMessage.setColor(sf::Color::White);
|
||||
pauseMessage.setFillColor(sf::Color::White);
|
||||
pauseMessage.setString("Welcome to SFML pong!\nPress space to start the game");
|
||||
|
||||
// Define the paddles properties
|
||||
|
@ -300,12 +300,12 @@ int main()
|
||||
// Create the description text
|
||||
sf::Text description("Current effect: " + effects[current]->getName(), font, 20);
|
||||
description.setPosition(10, 530);
|
||||
description.setColor(sf::Color(80, 80, 80));
|
||||
description.setFillColor(sf::Color(80, 80, 80));
|
||||
|
||||
// Create the instructions text
|
||||
sf::Text instructions("Press left and right arrows to change the current shader", font, 20);
|
||||
instructions.setPosition(280, 555);
|
||||
instructions.setColor(sf::Color(80, 80, 80));
|
||||
instructions.setFillColor(sf::Color(80, 80, 80));
|
||||
|
||||
// Start the game loop
|
||||
sf::Clock clock;
|
||||
|
@ -166,14 +166,18 @@ public:
|
||||
/// might be available. If the glyph is not available at the
|
||||
/// requested size, an empty glyph is returned.
|
||||
///
|
||||
/// \param codePoint Unicode code point of the character to get
|
||||
/// \param characterSize Reference character size
|
||||
/// \param bold Retrieve the bold version or the regular one?
|
||||
/// Be aware that using a negative value for the outline
|
||||
/// thickness will cause distorted rendering.
|
||||
///
|
||||
/// \param codePoint Unicode code point of the character to get
|
||||
/// \param characterSize Reference character size
|
||||
/// \param bold Retrieve the bold version or the regular one?
|
||||
/// \param outlineThickness Thickness of outline (when != 0 the glyph will not be filled)
|
||||
///
|
||||
/// \return The glyph corresponding to \a codePoint and \a characterSize
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
const Glyph& getGlyph(Uint32 codePoint, unsigned int characterSize, bool bold) const;
|
||||
const Glyph& getGlyph(Uint32 codePoint, unsigned int characterSize, bool bold, float outlineThickness = 0) const;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Get the kerning offset of two glyphs
|
||||
@ -277,7 +281,7 @@ private:
|
||||
////////////////////////////////////////////////////////////
|
||||
// Types
|
||||
////////////////////////////////////////////////////////////
|
||||
typedef std::map<Uint32, Glyph> GlyphTable; ///< Table mapping a codepoint to its glyph
|
||||
typedef std::map<Uint64, Glyph> GlyphTable; ///< Table mapping a codepoint to its glyph
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Structure defining a page of glyphs
|
||||
@ -302,14 +306,15 @@ private:
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Load a new glyph and store it in the cache
|
||||
///
|
||||
/// \param codePoint Unicode code point of the character to load
|
||||
/// \param characterSize Reference character size
|
||||
/// \param bold Retrieve the bold version or the regular one?
|
||||
/// \param codePoint Unicode code point of the character to load
|
||||
/// \param characterSize Reference character size
|
||||
/// \param bold Retrieve the bold version or the regular one?
|
||||
/// \param outlineThickness Thickness of outline (when != 0 the glyph will not be filled)
|
||||
///
|
||||
/// \return The glyph corresponding to \a codePoint and \a characterSize
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
Glyph loadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold) const;
|
||||
Glyph loadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold, float outlineThickness) const;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Find a suitable rectangle within the texture for a glyph
|
||||
@ -344,6 +349,7 @@ private:
|
||||
void* m_library; ///< Pointer to the internal library interface (it is typeless to avoid exposing implementation details)
|
||||
void* m_face; ///< Pointer to the internal font face (it is typeless to avoid exposing implementation details)
|
||||
void* m_streamRec; ///< Pointer to the stream rec instance (it is typeless to avoid exposing implementation details)
|
||||
void* m_stroker; ///< Pointer to the stroker (it is typeless to avoid exposing implementation details)
|
||||
int* m_refCount; ///< Reference counter used by implicit sharing
|
||||
Info m_info; ///< Information about the font
|
||||
mutable PageTable m_pages; ///< Table containing the glyphs pages by character size
|
||||
|
@ -159,16 +159,63 @@ public:
|
||||
void setStyle(Uint32 style);
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Set the global color of the text
|
||||
/// \brief Set the fill color of the text
|
||||
///
|
||||
/// By default, the text's color is opaque white.
|
||||
/// By default, the text's fill color is opaque white.
|
||||
/// Setting the fill color to a transparent color with an outline
|
||||
/// will cause the outline to be displayed in the fill area of the text.
|
||||
///
|
||||
/// \param color New color of the text
|
||||
/// \param color New fill color of the text
|
||||
///
|
||||
/// \see getColor
|
||||
/// \see getFillColor
|
||||
///
|
||||
/// \deprecated There is now fill and outline colors instead
|
||||
/// of a single global color.
|
||||
/// Use setFillColor() or setOutlineColor() instead.
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
void setColor(const Color& color);
|
||||
SFML_DEPRECATED void setColor(const Color& color);
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Set the fill color of the text
|
||||
///
|
||||
/// By default, the text's fill color is opaque white.
|
||||
/// Setting the fill color to a transparent color with an outline
|
||||
/// will cause the outline to be displayed in the fill area of the text.
|
||||
///
|
||||
/// \param color New fill color of the text
|
||||
///
|
||||
/// \see getFillColor
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
void setFillColor(const Color& color);
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Set the outline color of the text
|
||||
///
|
||||
/// By default, the text's outline color is opaque black.
|
||||
///
|
||||
/// \param color New outline color of the text
|
||||
///
|
||||
/// \see getOutlineColor
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
void setOutlineColor(const Color& color);
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Set the thickness of the text's outline
|
||||
///
|
||||
/// By default, the outline thickness is 0.
|
||||
///
|
||||
/// Be aware that using a negative value for the outline
|
||||
/// thickness will cause distorted rendering.
|
||||
///
|
||||
/// \param thickness New outline thickness, in pixels
|
||||
///
|
||||
/// \see getOutlineThickness
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
void setOutlineThickness(float thickness);
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Get the text's string
|
||||
@ -224,14 +271,48 @@ public:
|
||||
Uint32 getStyle() const;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Get the global color of the text
|
||||
/// \brief Get the fill color of the text
|
||||
///
|
||||
/// \return Global color of the text
|
||||
/// \return Fill color of the text
|
||||
///
|
||||
/// \see setColor
|
||||
/// \see setFillColor
|
||||
///
|
||||
/// \deprecated There is now fill and outline colors instead
|
||||
/// of a single global color.
|
||||
/// Use getFillColor() or getOutlineColor() instead.
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
const Color& getColor() const;
|
||||
SFML_DEPRECATED const Color& getColor() const;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Get the fill color of the text
|
||||
///
|
||||
/// \return Fill color of the text
|
||||
///
|
||||
/// \see setFillColor
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
const Color& getFillColor() const;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Get the outline color of the text
|
||||
///
|
||||
/// \return Outline color of the text
|
||||
///
|
||||
/// \see setOutlineColor
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
const Color& getOutlineColor() const;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Get the outline thickness of the text
|
||||
///
|
||||
/// \return Outline thickness of the text, in pixels
|
||||
///
|
||||
/// \see setOutlineThickness
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
float getOutlineThickness() const;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Return the position of the \a index-th character
|
||||
@ -305,8 +386,11 @@ private:
|
||||
const Font* m_font; ///< Font used to display the string
|
||||
unsigned int m_characterSize; ///< Base size of characters, in pixels
|
||||
Uint32 m_style; ///< Text style (see Style enum)
|
||||
Color m_color; ///< Text color
|
||||
mutable VertexArray m_vertices; ///< Vertex array containing the text's geometry
|
||||
Color m_fillColor; ///< Text fill color
|
||||
Color m_outlineColor; ///< Text outline color
|
||||
float m_outlineThickness; ///< Thickness of the text's outline
|
||||
mutable VertexArray m_vertices; ///< Vertex array containing the fill geometry
|
||||
mutable VertexArray m_outlineVertices; ///< Vertex array containing the outline geometry
|
||||
mutable FloatRect m_bounds; ///< Bounding rectangle of the text (in local coordinates)
|
||||
mutable bool m_geometryNeedUpdate; ///< Does the geometry need to be recomputed?
|
||||
};
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include FT_GLYPH_H
|
||||
#include FT_OUTLINE_H
|
||||
#include FT_BITMAP_H
|
||||
#include FT_STROKER_H
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
@ -143,6 +144,15 @@ bool Font::loadFromFile(const std::string& filename)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load the stroker that will be used to outline the font
|
||||
FT_Stroker stroker;
|
||||
if (FT_Stroker_New(static_cast<FT_Library>(m_library), &stroker) != 0)
|
||||
{
|
||||
err() << "Failed to load font \"" << filename << "\" (failed to create the stroker)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
m_stroker = stroker;
|
||||
|
||||
// Select the unicode character map
|
||||
if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) != 0)
|
||||
{
|
||||
@ -197,6 +207,15 @@ bool Font::loadFromMemory(const void* data, std::size_t sizeInBytes)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load the stroker that will be used to outline the font
|
||||
FT_Stroker stroker;
|
||||
if (FT_Stroker_New(static_cast<FT_Library>(m_library), &stroker) != 0)
|
||||
{
|
||||
err() << "Failed to load font from memory (failed to create the stroker)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
m_stroker = stroker;
|
||||
|
||||
// Select the Unicode character map
|
||||
if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) != 0)
|
||||
{
|
||||
@ -261,6 +280,15 @@ bool Font::loadFromStream(InputStream& stream)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load the stroker that will be used to outline the font
|
||||
FT_Stroker stroker;
|
||||
if (FT_Stroker_New(static_cast<FT_Library>(m_library), &stroker) != 0)
|
||||
{
|
||||
err() << "Failed to load font from stream (failed to create the stroker)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
m_stroker = stroker;
|
||||
|
||||
// Select the Unicode character map
|
||||
if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) != 0)
|
||||
{
|
||||
@ -289,13 +317,15 @@ const Font::Info& Font::getInfo() const
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
const Glyph& Font::getGlyph(Uint32 codePoint, unsigned int characterSize, bool bold) const
|
||||
const Glyph& Font::getGlyph(Uint32 codePoint, unsigned int characterSize, bool bold, float outlineThickness) const
|
||||
{
|
||||
// Get the page corresponding to the character size
|
||||
GlyphTable& glyphs = m_pages[characterSize].glyphs;
|
||||
|
||||
// Build the key by combining the code point and the bold flag
|
||||
Uint32 key = ((bold ? 1 : 0) << 31) | codePoint;
|
||||
// Build the key by combining the code point, bold flag, and outline thickness
|
||||
Uint64 key = (static_cast<Uint64>(*reinterpret_cast<Uint32*>(&outlineThickness)) << 32)
|
||||
| (static_cast<Uint64>(bold ? 1 : 0) << 31)
|
||||
| static_cast<Uint64>(codePoint);
|
||||
|
||||
// Search the glyph into the cache
|
||||
GlyphTable::const_iterator it = glyphs.find(key);
|
||||
@ -307,7 +337,7 @@ const Glyph& Font::getGlyph(Uint32 codePoint, unsigned int characterSize, bool b
|
||||
else
|
||||
{
|
||||
// Not found: we have to load it
|
||||
Glyph glyph = loadGlyph(codePoint, characterSize, bold);
|
||||
Glyph glyph = loadGlyph(codePoint, characterSize, bold, outlineThickness);
|
||||
return glyphs.insert(std::make_pair(key, glyph)).first->second;
|
||||
}
|
||||
}
|
||||
@ -442,6 +472,10 @@ void Font::cleanup()
|
||||
// Delete the reference counter
|
||||
delete m_refCount;
|
||||
|
||||
// Destroy the stroker
|
||||
if (m_stroker)
|
||||
FT_Stroker_Done(static_cast<FT_Stroker>(m_stroker));
|
||||
|
||||
// Destroy the font face
|
||||
if (m_face)
|
||||
FT_Done_Face(static_cast<FT_Face>(m_face));
|
||||
@ -459,6 +493,7 @@ void Font::cleanup()
|
||||
// Reset members
|
||||
m_library = NULL;
|
||||
m_face = NULL;
|
||||
m_stroker = NULL;
|
||||
m_streamRec = NULL;
|
||||
m_refCount = NULL;
|
||||
m_pages.clear();
|
||||
@ -467,7 +502,7 @@ void Font::cleanup()
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
Glyph Font::loadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold) const
|
||||
Glyph Font::loadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold, float outlineThickness) const
|
||||
{
|
||||
// The glyph to return
|
||||
Glyph glyph;
|
||||
@ -482,7 +517,10 @@ Glyph Font::loadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold) c
|
||||
return glyph;
|
||||
|
||||
// Load the glyph corresponding to the code point
|
||||
if (FT_Load_Char(face, codePoint, FT_LOAD_TARGET_NORMAL | FT_LOAD_FORCE_AUTOHINT) != 0)
|
||||
FT_Int32 flags = FT_LOAD_TARGET_NORMAL | FT_LOAD_FORCE_AUTOHINT;
|
||||
if (outlineThickness != 0)
|
||||
flags |= FT_LOAD_NO_BITMAP;
|
||||
if (FT_Load_Char(face, codePoint, flags) != 0)
|
||||
return glyph;
|
||||
|
||||
// Retrieve the glyph
|
||||
@ -490,13 +528,24 @@ Glyph Font::loadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold) c
|
||||
if (FT_Get_Glyph(face->glyph, &glyphDesc) != 0)
|
||||
return glyph;
|
||||
|
||||
// Apply bold if necessary -- first technique using outline (highest quality)
|
||||
// Apply bold and outline (there is no fallback for outline) if necessary -- first technique using outline (highest quality)
|
||||
FT_Pos weight = 1 << 6;
|
||||
bool outline = (glyphDesc->format == FT_GLYPH_FORMAT_OUTLINE);
|
||||
if (bold && outline)
|
||||
if (outline)
|
||||
{
|
||||
FT_OutlineGlyph outlineGlyph = (FT_OutlineGlyph)glyphDesc;
|
||||
FT_Outline_Embolden(&outlineGlyph->outline, weight);
|
||||
if (bold)
|
||||
{
|
||||
FT_OutlineGlyph outlineGlyph = (FT_OutlineGlyph)glyphDesc;
|
||||
FT_Outline_Embolden(&outlineGlyph->outline, weight);
|
||||
}
|
||||
|
||||
if (outlineThickness != 0)
|
||||
{
|
||||
FT_Stroker stroker = static_cast<FT_Stroker>(m_stroker);
|
||||
|
||||
FT_Stroker_Set(stroker, static_cast<FT_Fixed>(outlineThickness * static_cast<float>(1 << 6)), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
|
||||
FT_Glyph_Stroke(&glyphDesc, stroker, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the glyph to a bitmap (i.e. rasterize it)
|
||||
@ -504,9 +553,13 @@ Glyph Font::loadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold) c
|
||||
FT_Bitmap& bitmap = reinterpret_cast<FT_BitmapGlyph>(glyphDesc)->bitmap;
|
||||
|
||||
// Apply bold if necessary -- fallback technique using bitmap (lower quality)
|
||||
if (bold && !outline)
|
||||
if (!outline)
|
||||
{
|
||||
FT_Bitmap_Embolden(static_cast<FT_Library>(m_library), &bitmap, weight, weight);
|
||||
if (bold)
|
||||
FT_Bitmap_Embolden(static_cast<FT_Library>(m_library), &bitmap, weight, weight);
|
||||
|
||||
if (outlineThickness != 0)
|
||||
err() << "Failed to outline glyph (no fallback available)" << std::endl;
|
||||
}
|
||||
|
||||
// Compute the glyph's advance offset
|
||||
@ -537,10 +590,10 @@ Glyph Font::loadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold) c
|
||||
glyph.textureRect.height -= 2 * padding;
|
||||
|
||||
// Compute the glyph's bounding box
|
||||
glyph.bounds.left = static_cast<float>(face->glyph->metrics.horiBearingX) / static_cast<float>(1 << 6);
|
||||
glyph.bounds.left = static_cast<float>(face->glyph->metrics.horiBearingX) / static_cast<float>(1 << 6);
|
||||
glyph.bounds.top = -static_cast<float>(face->glyph->metrics.horiBearingY) / static_cast<float>(1 << 6);
|
||||
glyph.bounds.width = static_cast<float>(face->glyph->metrics.width) / static_cast<float>(1 << 6);
|
||||
glyph.bounds.height = static_cast<float>(face->glyph->metrics.height) / static_cast<float>(1 << 6);
|
||||
glyph.bounds.width = static_cast<float>(face->glyph->metrics.width) / static_cast<float>(1 << 6) + outlineThickness * 2;
|
||||
glyph.bounds.height = static_cast<float>(face->glyph->metrics.height) / static_cast<float>(1 << 6) + outlineThickness * 2;
|
||||
|
||||
// Extract the glyph's pixels from the bitmap
|
||||
m_pixelBuffer.resize(width * height * 4, 255);
|
||||
|
@ -31,6 +31,45 @@
|
||||
#include <cmath>
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
// Add an underline or strikethrough line to the vertex array
|
||||
void addLine(sf::VertexArray& vertices, float lineLength, float lineTop, const sf::Color& color, float offset, float thickness, float outlineThickness = 0)
|
||||
{
|
||||
float top = std::floor(lineTop + offset - (thickness / 2) + 0.5f);
|
||||
float bottom = top + std::floor(thickness + 0.5f);
|
||||
|
||||
vertices.append(sf::Vertex(sf::Vector2f(-outlineThickness, top - outlineThickness), color, sf::Vector2f(1, 1)));
|
||||
vertices.append(sf::Vertex(sf::Vector2f(lineLength + outlineThickness, top - outlineThickness), color, sf::Vector2f(1, 1)));
|
||||
vertices.append(sf::Vertex(sf::Vector2f(-outlineThickness, bottom + outlineThickness), color, sf::Vector2f(1, 1)));
|
||||
vertices.append(sf::Vertex(sf::Vector2f(-outlineThickness, bottom + outlineThickness), color, sf::Vector2f(1, 1)));
|
||||
vertices.append(sf::Vertex(sf::Vector2f(lineLength + outlineThickness, top - outlineThickness), color, sf::Vector2f(1, 1)));
|
||||
vertices.append(sf::Vertex(sf::Vector2f(lineLength + outlineThickness, bottom + outlineThickness), color, sf::Vector2f(1, 1)));
|
||||
}
|
||||
|
||||
// Add a glyph quad to the vertex array
|
||||
void addGlyphQuad(sf::VertexArray& vertices, sf::Vector2f position, const sf::Color& color, const sf::Glyph& glyph, float italic, float outlineThickness = 0)
|
||||
{
|
||||
float left = glyph.bounds.left;
|
||||
float top = glyph.bounds.top;
|
||||
float right = glyph.bounds.left + glyph.bounds.width;
|
||||
float bottom = glyph.bounds.top + glyph.bounds.height;
|
||||
|
||||
float u1 = static_cast<float>(glyph.textureRect.left);
|
||||
float v1 = static_cast<float>(glyph.textureRect.top);
|
||||
float u2 = static_cast<float>(glyph.textureRect.left + glyph.textureRect.width);
|
||||
float v2 = static_cast<float>(glyph.textureRect.top + glyph.textureRect.height);
|
||||
|
||||
vertices.append(sf::Vertex(sf::Vector2f(position.x + left - italic * top - outlineThickness, position.y + top - outlineThickness), color, sf::Vector2f(u1, v1)));
|
||||
vertices.append(sf::Vertex(sf::Vector2f(position.x + right - italic * top - outlineThickness, position.y + top - outlineThickness), color, sf::Vector2f(u2, v1)));
|
||||
vertices.append(sf::Vertex(sf::Vector2f(position.x + left - italic * bottom - outlineThickness, position.y + bottom - outlineThickness), color, sf::Vector2f(u1, v2)));
|
||||
vertices.append(sf::Vertex(sf::Vector2f(position.x + left - italic * bottom - outlineThickness, position.y + bottom - outlineThickness), color, sf::Vector2f(u1, v2)));
|
||||
vertices.append(sf::Vertex(sf::Vector2f(position.x + right - italic * top - outlineThickness, position.y + top - outlineThickness), color, sf::Vector2f(u2, v1)));
|
||||
vertices.append(sf::Vertex(sf::Vector2f(position.x + right - italic * bottom - outlineThickness, position.y + bottom - outlineThickness), color, sf::Vector2f(u2, v2)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace sf
|
||||
{
|
||||
////////////////////////////////////////////////////////////
|
||||
@ -39,8 +78,11 @@ m_string (),
|
||||
m_font (NULL),
|
||||
m_characterSize (30),
|
||||
m_style (Regular),
|
||||
m_color (255, 255, 255),
|
||||
m_fillColor (255, 255, 255),
|
||||
m_outlineColor (0, 0, 0),
|
||||
m_outlineThickness (0),
|
||||
m_vertices (Triangles),
|
||||
m_outlineVertices (Triangles),
|
||||
m_bounds (),
|
||||
m_geometryNeedUpdate(false)
|
||||
{
|
||||
@ -54,8 +96,11 @@ m_string (string),
|
||||
m_font (&font),
|
||||
m_characterSize (characterSize),
|
||||
m_style (Regular),
|
||||
m_color (255, 255, 255),
|
||||
m_fillColor (255, 255, 255),
|
||||
m_outlineColor (0, 0, 0),
|
||||
m_outlineThickness (0),
|
||||
m_vertices (Triangles),
|
||||
m_outlineVertices (Triangles),
|
||||
m_bounds (),
|
||||
m_geometryNeedUpdate(true)
|
||||
{
|
||||
@ -110,21 +155,57 @@ void Text::setStyle(Uint32 style)
|
||||
////////////////////////////////////////////////////////////
|
||||
void Text::setColor(const Color& color)
|
||||
{
|
||||
if (color != m_color)
|
||||
setFillColor(color);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
void Text::setFillColor(const Color& color)
|
||||
{
|
||||
if (color != m_fillColor)
|
||||
{
|
||||
m_color = color;
|
||||
m_fillColor = color;
|
||||
|
||||
// Change vertex colors directly, no need to update whole geometry
|
||||
// (if geometry is updated anyway, we can skip this step)
|
||||
if (!m_geometryNeedUpdate)
|
||||
{
|
||||
for (std::size_t i = 0; i < m_vertices.getVertexCount(); ++i)
|
||||
m_vertices[i].color = m_color;
|
||||
m_vertices[i].color = m_fillColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
void Text::setOutlineColor(const Color& color)
|
||||
{
|
||||
if (color != m_outlineColor)
|
||||
{
|
||||
m_outlineColor = color;
|
||||
|
||||
// Change vertex colors directly, no need to update whole geometry
|
||||
// (if geometry is updated anyway, we can skip this step)
|
||||
if (!m_geometryNeedUpdate)
|
||||
{
|
||||
for (std::size_t i = 0; i < m_outlineVertices.getVertexCount(); ++i)
|
||||
m_outlineVertices[i].color = m_outlineColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
void Text::setOutlineThickness(float thickness)
|
||||
{
|
||||
if (thickness != m_outlineThickness)
|
||||
{
|
||||
m_outlineThickness = thickness;
|
||||
m_geometryNeedUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
const String& Text::getString() const
|
||||
{
|
||||
@ -156,7 +237,28 @@ Uint32 Text::getStyle() const
|
||||
////////////////////////////////////////////////////////////
|
||||
const Color& Text::getColor() const
|
||||
{
|
||||
return m_color;
|
||||
return getFillColor();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
const Color& Text::getFillColor() const
|
||||
{
|
||||
return m_fillColor;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
const Color& Text::getOutlineColor() const
|
||||
{
|
||||
return m_outlineColor;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
float Text::getOutlineThickness() const
|
||||
{
|
||||
return m_outlineThickness;
|
||||
}
|
||||
|
||||
|
||||
@ -231,6 +333,11 @@ void Text::draw(RenderTarget& target, RenderStates states) const
|
||||
|
||||
states.transform *= getTransform();
|
||||
states.texture = &m_font->getTexture(m_characterSize);
|
||||
|
||||
// Only draw the outline if there is something to draw
|
||||
if (m_outlineThickness != 0)
|
||||
target.draw(m_outlineVertices, states);
|
||||
|
||||
target.draw(m_vertices, states);
|
||||
}
|
||||
}
|
||||
@ -248,14 +355,11 @@ void Text::ensureGeometryUpdate() const
|
||||
|
||||
// Clear the previous geometry
|
||||
m_vertices.clear();
|
||||
m_outlineVertices.clear();
|
||||
m_bounds = FloatRect();
|
||||
|
||||
// No font: nothing to draw
|
||||
if (!m_font)
|
||||
return;
|
||||
|
||||
// No text: nothing to draw
|
||||
if (m_string.isEmpty())
|
||||
// No font or text: nothing to draw
|
||||
if (!m_font || m_string.isEmpty())
|
||||
return;
|
||||
|
||||
// Compute values related to the text style
|
||||
@ -289,35 +393,25 @@ void Text::ensureGeometryUpdate() const
|
||||
Uint32 curChar = m_string[i];
|
||||
|
||||
// Apply the kerning offset
|
||||
x += static_cast<float>(m_font->getKerning(prevChar, curChar, m_characterSize));
|
||||
x += m_font->getKerning(prevChar, curChar, m_characterSize);
|
||||
prevChar = curChar;
|
||||
|
||||
// If we're using the underlined style and there's a new line, draw a line
|
||||
if (underlined && (curChar == L'\n'))
|
||||
{
|
||||
float top = std::floor(y + underlineOffset - (underlineThickness / 2) + 0.5f);
|
||||
float bottom = top + std::floor(underlineThickness + 0.5f);
|
||||
addLine(m_vertices, x, y, m_fillColor, underlineOffset, underlineThickness);
|
||||
|
||||
m_vertices.append(Vertex(Vector2f(0, top), m_color, Vector2f(1, 1)));
|
||||
m_vertices.append(Vertex(Vector2f(x, top), m_color, Vector2f(1, 1)));
|
||||
m_vertices.append(Vertex(Vector2f(0, bottom), m_color, Vector2f(1, 1)));
|
||||
m_vertices.append(Vertex(Vector2f(0, bottom), m_color, Vector2f(1, 1)));
|
||||
m_vertices.append(Vertex(Vector2f(x, top), m_color, Vector2f(1, 1)));
|
||||
m_vertices.append(Vertex(Vector2f(x, bottom), m_color, Vector2f(1, 1)));
|
||||
if (m_outlineThickness != 0)
|
||||
addLine(m_outlineVertices, x, y, m_outlineColor, underlineOffset, underlineThickness, m_outlineThickness);
|
||||
}
|
||||
|
||||
// If we're using the strike through style and there's a new line, draw a line across all characters
|
||||
if (strikeThrough && (curChar == L'\n'))
|
||||
{
|
||||
float top = std::floor(y + strikeThroughOffset - (underlineThickness / 2) + 0.5f);
|
||||
float bottom = top + std::floor(underlineThickness + 0.5f);
|
||||
addLine(m_vertices, x, y, m_fillColor, strikeThroughOffset, underlineThickness);
|
||||
|
||||
m_vertices.append(Vertex(Vector2f(0, top), m_color, Vector2f(1, 1)));
|
||||
m_vertices.append(Vertex(Vector2f(x, top), m_color, Vector2f(1, 1)));
|
||||
m_vertices.append(Vertex(Vector2f(0, bottom), m_color, Vector2f(1, 1)));
|
||||
m_vertices.append(Vertex(Vector2f(0, bottom), m_color, Vector2f(1, 1)));
|
||||
m_vertices.append(Vertex(Vector2f(x, top), m_color, Vector2f(1, 1)));
|
||||
m_vertices.append(Vertex(Vector2f(x, bottom), m_color, Vector2f(1, 1)));
|
||||
if (m_outlineThickness != 0)
|
||||
addLine(m_outlineVertices, x, y, m_outlineColor, strikeThroughOffset, underlineThickness, m_outlineThickness);
|
||||
}
|
||||
|
||||
// Handle special characters
|
||||
@ -342,63 +436,67 @@ void Text::ensureGeometryUpdate() const
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Apply the outline
|
||||
if (m_outlineThickness != 0)
|
||||
{
|
||||
const Glyph& glyph = m_font->getGlyph(curChar, m_characterSize, bold, m_outlineThickness);
|
||||
|
||||
float left = glyph.bounds.left;
|
||||
float top = glyph.bounds.top;
|
||||
float right = glyph.bounds.left + glyph.bounds.width;
|
||||
float bottom = glyph.bounds.top + glyph.bounds.height;
|
||||
|
||||
// Add the outline glyph to the vertices
|
||||
addGlyphQuad(m_outlineVertices, Vector2f(x, y), m_outlineColor, glyph, italic, m_outlineThickness);
|
||||
|
||||
// Update the current bounds with the outlined glyph bounds
|
||||
minX = std::min(minX, x + left - italic * bottom - m_outlineThickness);
|
||||
maxX = std::max(maxX, x + right - italic * top - m_outlineThickness);
|
||||
minY = std::min(minY, y + top - m_outlineThickness);
|
||||
maxY = std::max(maxY, y + bottom - m_outlineThickness);
|
||||
}
|
||||
|
||||
// Extract the current glyph's description
|
||||
const Glyph& glyph = m_font->getGlyph(curChar, m_characterSize, bold);
|
||||
|
||||
float left = glyph.bounds.left;
|
||||
float top = glyph.bounds.top;
|
||||
float right = glyph.bounds.left + glyph.bounds.width;
|
||||
float bottom = glyph.bounds.top + glyph.bounds.height;
|
||||
// Add the glyph to the vertices
|
||||
addGlyphQuad(m_vertices, Vector2f(x, y), m_fillColor, glyph, italic);
|
||||
|
||||
float u1 = static_cast<float>(glyph.textureRect.left);
|
||||
float v1 = static_cast<float>(glyph.textureRect.top);
|
||||
float u2 = static_cast<float>(glyph.textureRect.left + glyph.textureRect.width);
|
||||
float v2 = static_cast<float>(glyph.textureRect.top + glyph.textureRect.height);
|
||||
// Update the current bounds with the non outlined glyph bounds
|
||||
if (m_outlineThickness == 0)
|
||||
{
|
||||
float left = glyph.bounds.left;
|
||||
float top = glyph.bounds.top;
|
||||
float right = glyph.bounds.left + glyph.bounds.width;
|
||||
float bottom = glyph.bounds.top + glyph.bounds.height;
|
||||
|
||||
// Add a quad for the current character
|
||||
m_vertices.append(Vertex(Vector2f(x + left - italic * top, y + top), m_color, Vector2f(u1, v1)));
|
||||
m_vertices.append(Vertex(Vector2f(x + right - italic * top, y + top), m_color, Vector2f(u2, v1)));
|
||||
m_vertices.append(Vertex(Vector2f(x + left - italic * bottom, y + bottom), m_color, Vector2f(u1, v2)));
|
||||
m_vertices.append(Vertex(Vector2f(x + left - italic * bottom, y + bottom), m_color, Vector2f(u1, v2)));
|
||||
m_vertices.append(Vertex(Vector2f(x + right - italic * top, y + top), m_color, Vector2f(u2, v1)));
|
||||
m_vertices.append(Vertex(Vector2f(x + right - italic * bottom, y + bottom), m_color, Vector2f(u2, v2)));
|
||||
|
||||
// Update the current bounds
|
||||
minX = std::min(minX, x + left - italic * bottom);
|
||||
maxX = std::max(maxX, x + right - italic * top);
|
||||
minY = std::min(minY, y + top);
|
||||
maxY = std::max(maxY, y + bottom);
|
||||
minX = std::min(minX, x + left - italic * bottom);
|
||||
maxX = std::max(maxX, x + right - italic * top);
|
||||
minY = std::min(minY, y + top);
|
||||
maxY = std::max(maxY, y + bottom);
|
||||
}
|
||||
|
||||
// Advance to the next character
|
||||
x += glyph.advance;
|
||||
}
|
||||
|
||||
// If we're using the underlined style, add the last line
|
||||
if (underlined)
|
||||
if (underlined && (x > 0))
|
||||
{
|
||||
float top = std::floor(y + underlineOffset - (underlineThickness / 2) + 0.5f);
|
||||
float bottom = top + std::floor(underlineThickness + 0.5f);
|
||||
addLine(m_vertices, x, y, m_fillColor, underlineOffset, underlineThickness);
|
||||
|
||||
m_vertices.append(Vertex(Vector2f(0, top), m_color, Vector2f(1, 1)));
|
||||
m_vertices.append(Vertex(Vector2f(x, top), m_color, Vector2f(1, 1)));
|
||||
m_vertices.append(Vertex(Vector2f(0, bottom), m_color, Vector2f(1, 1)));
|
||||
m_vertices.append(Vertex(Vector2f(0, bottom), m_color, Vector2f(1, 1)));
|
||||
m_vertices.append(Vertex(Vector2f(x, top), m_color, Vector2f(1, 1)));
|
||||
m_vertices.append(Vertex(Vector2f(x, bottom), m_color, Vector2f(1, 1)));
|
||||
if (m_outlineThickness != 0)
|
||||
addLine(m_outlineVertices, x, y, m_outlineColor, underlineOffset, underlineThickness, m_outlineThickness);
|
||||
}
|
||||
|
||||
// If we're using the strike through style, add the last line across all characters
|
||||
if (strikeThrough)
|
||||
if (strikeThrough && (x > 0))
|
||||
{
|
||||
float top = std::floor(y + strikeThroughOffset - (underlineThickness / 2) + 0.5f);
|
||||
float bottom = top + std::floor(underlineThickness + 0.5f);
|
||||
addLine(m_vertices, x, y, m_fillColor, strikeThroughOffset, underlineThickness);
|
||||
|
||||
m_vertices.append(Vertex(Vector2f(0, top), m_color, Vector2f(1, 1)));
|
||||
m_vertices.append(Vertex(Vector2f(x, top), m_color, Vector2f(1, 1)));
|
||||
m_vertices.append(Vertex(Vector2f(0, bottom), m_color, Vector2f(1, 1)));
|
||||
m_vertices.append(Vertex(Vector2f(0, bottom), m_color, Vector2f(1, 1)));
|
||||
m_vertices.append(Vertex(Vector2f(x, top), m_color, Vector2f(1, 1)));
|
||||
m_vertices.append(Vertex(Vector2f(x, bottom), m_color, Vector2f(1, 1)));
|
||||
if (m_outlineThickness != 0)
|
||||
addLine(m_outlineVertices, x, y, m_outlineColor, strikeThroughOffset, underlineThickness, m_outlineThickness);
|
||||
}
|
||||
|
||||
// Update the bounding rectangle
|
||||
|
Loading…
Reference in New Issue
Block a user