diff --git a/source/graphics/Font.h b/source/graphics/Font.h index 2a44c7c128..3a739e766e 100644 --- a/source/graphics/Font.h +++ b/source/graphics/Font.h @@ -74,6 +74,13 @@ public: int GetHeight() const { return m_Height; } int GetCharacterWidth(wchar_t c) const; void CalculateStringSize(const wchar_t* string, int& w, int& h) const; + void GetGlyphBounds(float& x0, float& y0, float& x1, float& y1) const + { + x0 = m_BoundsX0; + y0 = m_BoundsY0; + x1 = m_BoundsX1; + y1 = m_BoundsY1; + } const GlyphMap& GetGlyphs() const { return m_Glyphs; } CTexturePtr GetTexture() const { return m_Texture; } @@ -86,6 +93,12 @@ private: int m_LineSpacing; int m_Height; // height of a capital letter, roughly + + // Bounding box of all glyphs + float m_BoundsX0; + float m_BoundsY0; + float m_BoundsX1; + float m_BoundsY1; }; #endif // INCLUDED_FONT diff --git a/source/graphics/FontManager.cpp b/source/graphics/FontManager.cpp index 6f9722bce6..4bffa209b8 100644 --- a/source/graphics/FontManager.cpp +++ b/source/graphics/FontManager.cpp @@ -88,6 +88,11 @@ bool CFontManager::ReadFont(CFont* font, const CStrW& fontName) FNTStream >> font->m_LineSpacing; FNTStream >> font->m_Height; + font->m_BoundsX0 = FLT_MAX; + font->m_BoundsY0 = FLT_MAX; + font->m_BoundsX1 = -FLT_MAX; + font->m_BoundsY1 = -FLT_MAX; + for (int i = 0; i < NumGlyphs; ++i) { int Codepoint, TextureX, TextureY, Width, Height, OffsetX, OffsetY, Advance; @@ -106,6 +111,11 @@ bool CFontManager::ReadFont(CFont* font, const CStrW& fontName) CFont::GlyphData g = { u, -v, u+w, -v+h, (i16)OffsetX, (i16)-OffsetY, (i16)(OffsetX+Width), (i16)(-OffsetY+Height), (i16)Advance }; font->m_Glyphs.set((u16)Codepoint, g); + + font->m_BoundsX0 = std::min(font->m_BoundsX0, (float)g.x0); + font->m_BoundsY0 = std::min(font->m_BoundsY0, (float)g.y0); + font->m_BoundsX1 = std::max(font->m_BoundsX1, (float)g.x1); + font->m_BoundsY1 = std::max(font->m_BoundsY1, (float)g.y1); } ENSURE(font->m_Height); // Ensure the height has been found (which should always happen if the font includes an 'I') diff --git a/source/graphics/TextRenderer.cpp b/source/graphics/TextRenderer.cpp index 01a6320400..c150cf81ae 100644 --- a/source/graphics/TextRenderer.cpp +++ b/source/graphics/TextRenderer.cpp @@ -66,6 +66,11 @@ void CTextRenderer::Translate(float x, float y, float z) m_Dirty = true; } +void CTextRenderer::SetClippingRect(const CRect& rect) +{ + m_Clipping = rect; +} + void CTextRenderer::Color(const CColor& color) { if (m_Color != color) @@ -151,6 +156,16 @@ void CTextRenderer::PutString(float x, float y, const std::wstring* buf, bool ow if (!m_Font) return; // invalid font; can't render + if (m_Clipping != CRect()) + { + float x0, y0, x1, y1; + m_Font->GetGlyphBounds(x0, y0, x1, y1); + if (y + y1 < m_Clipping.top) + return; + if (y + y0 > m_Clipping.bottom) + return; + } + // If any state has changed since the last batch, start a new batch if (m_Dirty) { diff --git a/source/graphics/TextRenderer.h b/source/graphics/TextRenderer.h index de6276986d..199fe206a0 100644 --- a/source/graphics/TextRenderer.h +++ b/source/graphics/TextRenderer.h @@ -41,6 +41,14 @@ public: void Translate(float x, float y, float z); + /** + * Set clipping rectangle, in pre-transform coordinates (i.e. text is clipped against + * this rect based purely on the x,y values passed into Put()). Text fully outside the + * clipping rectangle may not be rendered. Should be used in conjunction with glScissor + * for precise clipping - this is just an optimisation. + */ + void SetClippingRect(const CRect& rect); + /** * Set the color for subsequent print calls. */ @@ -149,6 +157,7 @@ private: CShaderProgramPtr m_Shader; CMatrix3D m_Transform; + CRect m_Clipping; CColor m_Color; CStrW m_FontName; diff --git a/source/gui/CGUI.cpp b/source/gui/CGUI.cpp index 59f818a4f3..0781873735 100644 --- a/source/gui/CGUI.cpp +++ b/source/gui/CGUI.cpp @@ -945,13 +945,15 @@ void CGUI::DrawText(SGUIText &Text, const CColor &DefaultColor, tech->BeginPass(); - if (clipping != CRect()) + bool isClipped = (clipping != CRect()); + if (isClipped) { glEnable(GL_SCISSOR_TEST); glScissor(clipping.left, g_yres - clipping.bottom, clipping.GetWidth(), clipping.GetHeight()); } CTextRenderer textRenderer(tech->GetShader()); + textRenderer.SetClippingRect(clipping); textRenderer.Translate(0.0f, 0.0f, z); for (std::vector::const_iterator it = Text.m_TextCalls.begin(); @@ -978,7 +980,7 @@ void CGUI::DrawText(SGUIText &Text, const CColor &DefaultColor, DrawSprite(it->m_Sprite, it->m_CellID, z, it->m_Area + pos); } - if (clipping != CRect()) + if (isClipped) glDisable(GL_SCISSOR_TEST); tech->EndPass();