Skip to content

Commit

Permalink
Fixed incorrect rendering of outlines in RichTextLabel (there was a g…
Browse files Browse the repository at this point in the history
…ap between each piece + even if the gap didn't exist it would render outlines of the second piece on top of the first piece)
  • Loading branch information
texus committed Sep 1, 2024
1 parent 0167e73 commit 3efbfa1
Show file tree
Hide file tree
Showing 10 changed files with 109 additions and 10 deletions.
2 changes: 1 addition & 1 deletion changelog.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
TGUI 1.6 (TBD)
--------------

Fixed crash on exit when tool tip was visible
- Fixed crash on exit when tool tip was visible


TGUI 1.5 (25 August 2024)
Expand Down
26 changes: 26 additions & 0 deletions include/TGUI/Backend/Renderer/BackendRenderTarget.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,35 @@ TGUI_MODULE_EXPORT namespace tgui
///
/// @param states Render states to use for drawing
/// @param text Text to draw
///
/// This function does the same as calling both drawTextOutline and drawTextWithoutOutline.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
virtual void drawText(const RenderStates& states, const Text& text);

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @brief Draws only the outline of some text
///
/// @param states Render states to use for drawing
/// @param text Text to draw
///
/// This function should always be called prior to drawTextWithoutOutline. It exists to allow rendering the outline of
/// multiple texts before rendering all these texts, which is done by calling drawTextOutline for each text before
/// calling drawTextWithoutOutline on all the same text objects.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
virtual void drawTextOutline(const RenderStates& states, const Text& text);

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @brief Draws some text, but without rendering its outline
///
/// @param states Render states to use for drawing
/// @param text Text to draw
///
/// This function should always be called after drawTextOutline. It exists to allow rendering the outline of
/// multiple texts before rendering all these texts, which is done by calling drawTextOutline for each text before
/// calling drawTextWithoutOutline on all the same text objects.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
virtual void drawTextWithoutOutline(const RenderStates& states, const Text& text);

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @brief Draws a single triangles (using the color that is specified in the vertices)
///
Expand Down
4 changes: 3 additions & 1 deletion include/TGUI/Backend/Renderer/BackendText.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,11 @@ TGUI_MODULE_EXPORT namespace tgui

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @brief Returns the information that is needed to render this text
/// @param includeOutline Should the returned data include the text outline?
/// @param includeText Should the returned data include the text itself (i.e. everything except the outline)?
/// @return Data that contains the textures and vertices used by this text
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TGUI_NODISCARD TextVertexData getVertexData();
TGUI_NODISCARD TextVertexData getVertexData(bool includeOutline = true, bool includeText = true);

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
protected:
Expand Down
2 changes: 1 addition & 1 deletion include/TGUI/Widgets/Label.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ TGUI_MODULE_EXPORT namespace tgui
Color m_textColorCached;
Color m_borderColorCached;
Color m_backgroundColorCached;
Color m_textOutlineColorCached;
Color m_textOutlineColorCached = Color::Black;
float m_textOutlineThicknessCached = 0;

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down
40 changes: 40 additions & 0 deletions src/Backend/Renderer/BackendRenderTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,46 @@ namespace tgui

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void BackendRenderTarget::drawTextOutline(const RenderStates& states, const Text& text)
{
RenderStates transformedStates = states;
transformedStates.transform.translate(text.getPosition());

// Round the text to the nearest pixel to try to avoid blurry text
transformedStates.transform.roundPosition(m_pixelsPerPoint);

auto vertexData = text.getBackendText()->getVertexData(true, false);

for (const auto& data : vertexData)
{
const std::shared_ptr<BackendTexture>& texture = data.first;
const std::shared_ptr<std::vector<Vertex>>& vertices = data.second;
drawVertexArray(transformedStates, vertices->data(), vertices->size(), nullptr, 0, texture);
}
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void BackendRenderTarget::drawTextWithoutOutline(const RenderStates& states, const Text& text)
{
RenderStates transformedStates = states;
transformedStates.transform.translate(text.getPosition());

// Round the text to the nearest pixel to try to avoid blurry text
transformedStates.transform.roundPosition(m_pixelsPerPoint);

auto vertexData = text.getBackendText()->getVertexData(false, true);

for (const auto& data : vertexData)
{
const std::shared_ptr<BackendTexture>& texture = data.first;
const std::shared_ptr<std::vector<Vertex>>& vertices = data.second;
drawVertexArray(transformedStates, vertices->data(), vertices->size(), nullptr, 0, texture);
}
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void BackendRenderTarget::drawTriangle(const RenderStates& states, const Vertex& point1, const Vertex& point2, const Vertex& point3)
{
const std::array<Vertex, 3> vertices = {{ point1, point2, point3 }};
Expand Down
8 changes: 4 additions & 4 deletions src/Backend/Renderer/BackendText.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ namespace tgui

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

BackendText::TextVertexData BackendText::getVertexData()
BackendText::TextVertexData BackendText::getVertexData(bool includeOutline, bool includeText)
{
BackendText::TextVertexData data;

Expand All @@ -257,10 +257,10 @@ namespace tgui
texture = m_font->getTexture(m_characterSize, m_lastFontTextureVersion);
}

if (m_outlineVertices && !m_outlineVertices->empty())
if (includeOutline && m_outlineVertices && !m_outlineVertices->empty())
data.emplace_back(texture, m_outlineVertices);

if (m_vertices && !m_vertices->empty())
if (includeText && m_vertices && !m_vertices->empty())
data.emplace_back(texture, m_vertices);

return data;
Expand Down Expand Up @@ -416,7 +416,7 @@ namespace tgui
const float fontHeight = m_font->getFontHeight(m_characterSize);
const float height = std::max(fontHeight, lineSpacing) + (nrLines - 1) * lineSpacing;

m_size = {maxX + 2 * m_outlineThickness, height + 2 * m_outlineThickness};
m_size = {maxX + m_outlineThickness, height + 2 * m_outlineThickness};

// Normalize the texture coordinates
const Vector2u textureSize = m_font->getTextureSize(m_characterSize);
Expand Down
26 changes: 24 additions & 2 deletions src/Widgets/Label.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -847,10 +847,21 @@ namespace tgui
// Draw the text
if (m_autoSize)
{
// We need to draw all the outlines prior to start rendering the text itself,
// because otherwise the outline of one piece could render on top of a previously drawn piece.
if (m_textOutlineThicknessCached)
{
for (const auto& line : m_lines)
{
for (const auto& text : line)
target.drawTextOutline(states, text);
}
}

for (const auto& line : m_lines)
{
for (const auto& text : line)
target.drawText(states, text);
target.drawTextWithoutOutline(states, text);
}
}
else
Expand All @@ -863,10 +874,21 @@ namespace tgui
if (m_scrollbar->isVisible())
states.transform.translate({0, -static_cast<float>(m_scrollbar->getValue())});

// We need to draw all the outlines prior to start rendering the text itself,
// because otherwise the outline of one piece could render on top of a previously drawn piece.
if (m_textOutlineThicknessCached)
{
for (const auto& line : m_lines)
{
for (const auto& text : line)
target.drawTextOutline(states, text);
}
}

for (const auto& line : m_lines)
{
for (const auto& text : line)
target.drawText(states, text);
target.drawTextWithoutOutline(states, text);
}

target.removeClippingLayer();
Expand Down
2 changes: 1 addition & 1 deletion src/Widgets/RichTextLabel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ namespace tgui
textPiece.setPosition({pos.x + lineWidth, pos.y});

maxLineHeight = std::max(maxLineHeight, textPiece.getSize().y);
lineWidth += textPiece.getSize().x;
lineWidth += textPiece.getSize().x - (2 * m_textOutlineThicknessCached);

// Take kerning into account
if (j > 0 && !textPiecesLine[j-1].text.empty() && !textPiecesLine[j].text.empty())
Expand Down
9 changes: 9 additions & 0 deletions tests/Widgets/RichTextLabel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,5 +329,14 @@ TEST_CASE("[RichTextLabel]")
U"can be included when there are too many lines!\n\n\n\n\n\n\n\n");

TEST_DRAW("RichTextLabel.png")

SECTION("Outline")
{
// Outline of one piece should not be rendered on top of another piece, and there should be no additional gap between pieces
label->getRenderer()->setTextOutlineColor(tgui::Color::Green);
label->getRenderer()->setTextOutlineThickness(4);
label->setText("<color=blue>Hello</color><color=red>World</color>");
TEST_DRAW("RichTextLabel_Outline.png")
}
}
}
Binary file added tests/expected/RichTextLabel_Outline.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 3efbfa1

Please sign in to comment.