From e45e92e0ff294e6a1899b01b8cfdd8ab25d8dcdc Mon Sep 17 00:00:00 2001
From: Ekaitz Zarraga <ekaitz@elenq.tech>
Date: Tue, 13 Dec 2022 19:35:48 +0100
Subject: Make a better text rendering system and a simple example in
 titlestate

---
 src/graphics/text.cpp        | 79 +++++++++++++++++++++++++++++---------------
 src/graphics/text.h          | 48 +++++++++++++++++++++++++--
 src/graphics/textureFont.cpp | 48 +++++++++++++--------------
 src/graphics/textureFont.h   | 10 +++---
 4 files changed, 127 insertions(+), 58 deletions(-)

(limited to 'src/graphics')

diff --git a/src/graphics/text.cpp b/src/graphics/text.cpp
index 82b8811..43e8be1 100644
--- a/src/graphics/text.cpp
+++ b/src/graphics/text.cpp
@@ -2,36 +2,61 @@
 
 using namespace Graphics;
 
-Text::Text(TextureFont &font, const std::string &text)
-    : font_(font)
-    , text_(text)
-    , vertices_(sf::Quads, text.size()*4)
-{
-    unsigned int length = vertices_.getVertexCount();
-    sf::Vector2f glyphSize = font_.getGlyphSize();
-    // TODO: this is the simplest rendering way: everything in one line
-    //       we should improve this to make it handle newlines or max witdths
-    for(unsigned int i=0; i < length; i=i+4){
-        vertices_[i+0].position = sf::Vector2f(glyphSize.x * i/4,       0);
-        vertices_[i+1].position = sf::Vector2f(glyphSize.x * (i/4 + 1), 0);
-        vertices_[i+2].position = sf::Vector2f(glyphSize.x * (i/4 + 1), glyphSize.y);
-        vertices_[i+3].position = sf::Vector2f(glyphSize.x * i/4,       glyphSize.y);
+template <>
+void Text<TextureFont>::arrange(TextureFont &font){
+    // TODO: this is really awful rendering but works a little bit
+    unsigned int filledX = 0;
+    unsigned int row = 0;
+    unsigned int col = 0;
 
-        std::array<sf::Vector2f, 4> glyph = font_.getGlyphMapping(text_[i/4]);
+    for(unsigned int i=0; i < text_.length(); i++){
+        sf::Glyph glyph = font.getGlyph( text_[i] );
+        filledX += glyph.advance;
+        if (maxWidth_ != 0 && ( text_[i] == '\n' || filledX > maxWidth_)){
+            row++;
+            filledX = 0;
+            col = 0;
+            if(text_[i] == '\n') continue;
+        }
 
-        vertices_[i+0].texCoords = glyph[0];
-        vertices_[i+1].texCoords = glyph[1];
-        vertices_[i+2].texCoords = glyph[2];
-        vertices_[i+3].texCoords = glyph[3];
+        vertices_[4*i+0].position = sf::Vector2f(
+                col*glyph.advance + glyph.bounds.left,
+                (glyph.bounds.top) + row * glyph.bounds.height
+            );
+        vertices_[4*i+1].position = sf::Vector2f(
+                col*glyph.advance + glyph.bounds.left+glyph.bounds.width,
+                (glyph.bounds.top) + row * glyph.bounds.height
+            );
+        vertices_[4*i+2].position = sf::Vector2f(
+                col*glyph.advance + glyph.bounds.left+glyph.bounds.width,
+                (glyph.bounds.top+glyph.bounds.height) + row * glyph.bounds.height
+            );
+        vertices_[4*i+3].position = sf::Vector2f(
+                col*glyph.advance + glyph.bounds.left,
+                (glyph.bounds.top+glyph.bounds.height) + row * glyph.bounds.height
+            );
+        col++;
+
+        vertices_[4*i+0].texCoords = sf::Vector2f(
+                glyph.textureRect.left,
+                glyph.textureRect.top
+            );
+        vertices_[4*i+1].texCoords = sf::Vector2f(
+                glyph.textureRect.left+glyph.textureRect.width,
+                glyph.textureRect.top
+            );
+        vertices_[4*i+2].texCoords = sf::Vector2f(
+                glyph.textureRect.left+glyph.textureRect.width,
+                glyph.textureRect.top+glyph.textureRect.height
+            );
+        vertices_[4*i+3].texCoords = sf::Vector2f(
+                glyph.textureRect.left,
+                glyph.textureRect.top+glyph.textureRect.height
+            );
     }
 }
 
-void Text::draw(sf::RenderTarget& target, sf::RenderStates states) const
-{
-    // TODO: only repaint if needed: when repainting is needed
-    // vertices_.clear() and fun
-    states.transform *= getTransform();
-    states.texture = font_.getTexture();
-    // you may also override states.shader or states.blendMode if you want
-    target.draw(vertices_, states);
+template <>
+void Text<sf::Font>::arrange(sf::Font &font){
+    // TODO: Implement this function for a generic sf::Font
 }
diff --git a/src/graphics/text.h b/src/graphics/text.h
index 22179f5..f60980c 100644
--- a/src/graphics/text.h
+++ b/src/graphics/text.h
@@ -3,15 +3,59 @@
 #include"textureFont.h"
 
 namespace Graphics {
+
+    template<typename Font>
     class Text : public sf::Drawable, public sf::Transformable  {
         private:
-            TextureFont &font_;
+            Font &font_;
             const std::string text_;
             sf::VertexArray vertices_;
+            unsigned int size_, maxWidth_, maxHeight_;
+
+            void arrange(Font &font); // Specialize for sf::Font and TextureFont
+
         public:
-            Text(TextureFont &font, const std::string &text);
+            Text(Font &font, const std::string &text, unsigned int maxWidth=0,
+                    unsigned int maxHeight=0);
+
+            void setFont(Font &font);
+            void setText(const std::string &text);
+
             virtual void draw(sf::RenderTarget& target,
                     sf::RenderStates states) const override;
     };
+
+    template <typename Font>
+    Text<Font>::Text(Font &font, const std::string &text,
+            unsigned int maxWidth, unsigned int maxHeight)
+        : font_(font)
+        , text_(text)
+        , vertices_(sf::Quads, text.size()*4)
+        , size_(0)
+        , maxWidth_(maxWidth)
+        , maxHeight_(maxHeight)
+    {
+        arrange(font);
+    }
+
+
+    template <typename Font>
+    void Text<Font>::draw(sf::RenderTarget& target, sf::RenderStates states) const
+    {
+        // TODO: only repaint if needed: when repainting is needed
+        // vertices_.clear() and fun
+        states.transform *= getTransform();
+        states.texture = font_.getTexture(size_);
+        // you may also override states.shader or states.blendMode if you want
+        target.draw(vertices_, states);
+    }
+
+    template <typename Font>
+    void Text<Font>::setFont(Font &font){
+        font_ = font;
+        arrange(font);
+    }
 }
+
+
 #endif // GRAPHICS_TEXT_H
diff --git a/src/graphics/textureFont.cpp b/src/graphics/textureFont.cpp
index 29f9c71..3be2444 100644
--- a/src/graphics/textureFont.cpp
+++ b/src/graphics/textureFont.cpp
@@ -8,37 +8,35 @@ TextureFont::TextureFont(const sf::Texture &texture,
     , description_(description)
 {
     sf::Vector2u totalSize = tex_.getSize();
-    sf::Vector2f totalSize_f (totalSize.x, totalSize.y);
-    glyphSize_ = sf::Vector2f( totalSize.x / description_.numcols,
+    glyphSize_ = sf::Vector2i( totalSize.x / description_.numcols,
                                totalSize.y / description_.numrows );
-}
 
-std::array<sf::Vector2f, 4> TextureFont::getGlyphMapping(const char ch){
-    unsigned int pos = static_cast<unsigned int>( description_.mapping.find_first_of(ch));
-    if (pos == std::string::npos){
-        // TODO: Error handling!
-        pos = 0;
+    for(unsigned int i=0; i<description.mapping.size(); i++){
+        sf::Glyph glyph;
+        glyph.advance = glyphSize_.x;
+
+        unsigned int x = i % description_.numcols;
+        unsigned int y = (i - x) / description_.numcols;
+
+        glyph.textureRect = sf::IntRect(x*glyphSize_.x,
+                                        y*glyphSize_.y,
+                                        glyphSize_.x,
+                                        glyphSize_.y);
+
+        glyph.bounds = sf::FloatRect(0,
+                                     0,
+                                     glyphSize_.x,
+                                     glyphSize_.y);
+
+        glyphTable_.insert(std::make_pair(description.mapping[i], glyph));
     }
-    unsigned int x = pos % description_.numcols;
-    unsigned int y = (pos - x) / description_.numcols;
-
-    sf::Vector2f basePos(x*glyphSize_.x, y*glyphSize_.y);
-
-    // 1 --- 2
-    // |     |
-    // 4 --- 3
-    return std::array<sf::Vector2f,4> {
-        basePos,
-        basePos + sf::Vector2f( glyphSize_.x, 0 ),
-        basePos + glyphSize_,
-        basePos + sf::Vector2f( 0, glyphSize_.y ),
-    };
 }
 
-sf::Vector2f TextureFont::getGlyphSize(){
-    return glyphSize_;
+const sf::Glyph & TextureFont::getGlyph(char ch) const{
+    auto pair = glyphTable_.find(ch);
+    return pair->second;
 }
 
-const sf::Texture * TextureFont::getTexture(){
+const sf::Texture * TextureFont::getTexture(unsigned int _){
     return &tex_;
 }
diff --git a/src/graphics/textureFont.h b/src/graphics/textureFont.h
index 5e78f9e..f3f194a 100644
--- a/src/graphics/textureFont.h
+++ b/src/graphics/textureFont.h
@@ -13,14 +13,16 @@ namespace Graphics {
             unsigned int numrows, numcols;
         };
         private:
+            using GlyphTable = std::map<char, sf::Glyph>;
+            GlyphTable glyphTable_;
+
             const sf::Texture &tex_;
             const Descr description_;
-            sf::Vector2f glyphSize_;
+            sf::Vector2i glyphSize_;
         public:
             TextureFont(const sf::Texture &texture, Descr desc);
-            std::array<sf::Vector2f,4> getGlyphMapping(const char ch);
-            sf::Vector2f getGlyphSize();
-            const sf::Texture *getTexture();
+            const sf::Glyph &getGlyph(char ch) const;
+            const sf::Texture *getTexture(unsigned int _);
     };
 
 }
-- 
cgit v1.2.3