Browse Source

If all font's glyph do not fit into one page, begin filling texture pages dynamically as glyphs are used.

Lasse Öörni 12 years ago
parent
commit
ecc37d1f2e

+ 8 - 0
Source/Engine/Math/AreaAllocator.cpp

@@ -26,6 +26,14 @@
 namespace Urho3D
 {
 
+AreaAllocator::AreaAllocator() :
+    size_(IntVector2::ZERO),
+    maxSize_(IntVector2::ZERO),
+    doubleWidth_(true)
+{
+    Reset(0, 0);
+}
+
 AreaAllocator::AreaAllocator(int width, int height) :
     size_(width, height),
     maxSize_(IntVector2::ZERO),

+ 2 - 0
Source/Engine/Math/AreaAllocator.h

@@ -31,6 +31,8 @@ namespace Urho3D
 class URHO3D_API AreaAllocator
 {
 public:
+    /// Default construct with empty size.
+    AreaAllocator();
     /// Construct with given width and height.
     AreaAllocator(int width, int height);
     /// Construct with given width and height, and set the maximum it allows to grow.

+ 232 - 176
Source/Engine/UI/Font.cpp

@@ -21,7 +21,6 @@
 //
 
 #include "Precompiled.h"
-#include "AreaAllocator.h"
 #include "Context.h"
 #include "Deserializer.h"
 #include "FileSystem.h"
@@ -75,30 +74,53 @@ private:
 };
 
 FontGlyph::FontGlyph() :
-    used_(false)
+    used_(false),
+    page_(M_MAX_UNSIGNED)
 {
 }
 
-FontFace::FontFace() :
-    hasKerning_(false)
+FontFace::FontFace(Font* font) :
+    font_(font),
+    face_(0),
+    hasKerning_(false),
+    bitmapSize_(0)
 {
 }
 
 FontFace::~FontFace()
 {
+    if (face_)
+    {
+        FT_Done_Face((FT_Face)face_);
+        face_ = 0;
+    }
+    
+    if (font_)
+    {
+        // When a face is unloaded, deduct the used texture data size from the parent font
+        unsigned totalTextureSize = 0;
+        for (unsigned i = 0; i < textures_.Size(); ++i)
+            totalTextureSize += textures_[i]->GetWidth() * textures_[i]->GetHeight();
+        font_->SetMemoryUse(font_->GetMemoryUse() - totalTextureSize);
+    }
 }
 
-const FontGlyph* FontFace::GetGlyph(unsigned c) const
+const FontGlyph* FontFace::GetGlyph(unsigned c)
 {
     HashMap<unsigned, unsigned>::ConstIterator i = glyphMapping_.Find(c);
     if (i != glyphMapping_.End())
     {
-        const FontGlyph& glyph = glyphs_[i->second_];
+        FontGlyph& glyph = glyphs_[i->second_];
+        if (glyph.page_ == M_MAX_UNSIGNED)
+        {
+            if (!RenderGlyph(i->second_))
+                return 0;
+        }
         glyph.used_ = true;
         return &glyph;
     }
     else
-        return NULL;
+        return 0;
 }
 
 short FontFace::GetKerning(unsigned c, unsigned d) const
@@ -139,6 +161,160 @@ bool FontFace::IsDataLost() const
     return false;
 }
 
+bool FontFace::RenderAllGlyphs()
+{
+    // RenderAllGlyphs() is valid only when no textures have yet been allocated
+    if (!font_ || !face_ || textures_.Size())
+        return false;
+    
+    allocator_ = AreaAllocator(FONT_TEXTURE_MIN_SIZE, FONT_TEXTURE_MIN_SIZE, FONT_TEXTURE_MAX_SIZE, FONT_TEXTURE_MAX_SIZE);
+    
+    for (unsigned i = 0; i < glyphs_.Size(); ++i)
+    {
+        if (glyphs_[i].width_ && glyphs_[i].height_)
+        {
+            int x, y;
+            // Reserve an empty border between glyphs for filtering
+            if (allocator_.Allocate(glyphs_[i].width_ + 1, glyphs_[i].height_ + 1, x, y))
+            {
+                glyphs_[i].x_ = x;
+                glyphs_[i].y_ = y;
+                glyphs_[i].page_ = 0;
+            }
+            else
+            {
+                // When allocation fails, reset the page of all glyphs allocated so far
+                for (unsigned j = 0; j <= i; ++j)
+                    glyphs_[j].page_ = M_MAX_UNSIGNED;
+                return false;
+            }
+        }
+        else
+        {
+            glyphs_[i].x_ = 0;
+            glyphs_[i].y_ = 0;
+            glyphs_[i].page_ = 0;
+        }
+    }
+    
+    // Create image for rendering all the glyphs, clear to black
+    SharedPtr<Image> image(new Image(font_->GetContext()));
+    image->SetSize(allocator_.GetWidth(), allocator_.GetHeight(), 1);
+    unsigned char* imageData = image->GetData();
+    memset(imageData, 0, image->GetWidth() * image->GetHeight());
+    
+    // Render glyphs
+    for (unsigned i = 0; i < glyphs_.Size(); ++i)
+        RenderGlyphBitmap(i, imageData + glyphs_[i].y_ * image->GetWidth() + glyphs_[i].x_, image->GetWidth());
+    
+    // Load image into a texture, increment memory usage of the parent font
+    SharedPtr<Texture2D> texture = font_->LoadFaceTexture(image);
+    if (!texture)
+    {
+        for (unsigned i = 0; i < glyphs_.Size(); ++i)
+            glyphs_[i].page_ = M_MAX_UNSIGNED;
+        return false;
+    }
+    
+    textures_.Push(texture);
+    font_->SetMemoryUse(font_->GetMemoryUse() + image->GetWidth() * image->GetHeight());
+    
+    LOGDEBUG(ToString("Font face %s (%dpt) uses a static page texture of size %dx%d", GetFileName(font_->GetName()).CString(), pointSize_, 
+        texture->GetWidth(), texture->GetHeight()));
+    return true;
+}
+
+bool FontFace::RenderGlyph(unsigned index)
+{
+    if (!font_ || !face_ || !textures_.Size())
+        return false;
+    
+    FontGlyph& glyph = glyphs_[index];
+    
+    // If glyph is empty, just set the current page
+    if (!glyph.width_ || !glyph.height_)
+    {
+        glyph.x_ = 0;
+        glyph.y_ = 0;
+        glyph.page_ = textures_.Size() - 1;
+        return true;
+    }
+    
+    // Try to allocate from current page, reserve next page if fails
+    int x, y;
+    if (!allocator_.Allocate(glyph.width_ + 1, glyph.height_ + 1, x, y))
+    {
+        SetupNextTexture();
+        // This always succeeds, as it is the first allocation of an empty page
+        allocator_.Allocate(glyph.width_ + 1, glyph.height_ + 1, x, y);
+    }
+    
+    glyph.x_ = x;
+    glyph.y_ = y;
+    glyph.page_ = textures_.Size() - 1;
+    
+    if (!bitmap_ || (int)bitmapSize_ < glyph.width_ * glyph.height_)
+    {
+        bitmapSize_ = glyph.width_ * glyph.height_;
+        bitmap_ = new unsigned char[bitmapSize_];
+    }
+    
+    RenderGlyphBitmap(index, bitmap_.Get(), glyph.width_);
+    textures_.Back()->SetData(0, glyph.x_, glyph.y_, glyph.width_, glyph.height_, bitmap_.Get());
+    return true;
+}
+
+void FontFace::RenderGlyphBitmap(unsigned index, unsigned char* dest, unsigned pitch)
+{
+    const FontGlyph& glyph = glyphs_[index];
+    if (!glyph.width_ || !glyph.height_)
+        return;
+    
+    FT_Face face = (FT_Face)face_;
+    FT_GlyphSlot slot = face->glyph;
+    FT_Load_Glyph(face, index, FT_LOAD_DEFAULT);
+    FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL);
+
+    if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO)
+    {
+        for (int y = 0; y < glyph.height_; ++y)
+        {
+            unsigned char* src = slot->bitmap.buffer + slot->bitmap.pitch * y;
+            unsigned char* rowDest = dest + y * pitch;
+
+            for (int x = 0; x < glyph.width_; ++x)
+                rowDest[x] = (src[x >> 3] & (0x80 >> (x & 7))) ? 255 : 0;
+        }
+    }
+    else
+    {
+        for (int y = 0; y < glyph.height_; ++y)
+        {
+            unsigned char* src = slot->bitmap.buffer + slot->bitmap.pitch * y;
+            unsigned char* rowDest = dest + y * pitch;
+
+            for (int x = 0; x < glyph.width_; ++x)
+                rowDest[x] = src[x];
+        }
+    }
+}
+
+void FontFace::SetupNextTexture()
+{
+    if (!font_)
+        return;
+    
+    // If several dynamic textures are needed, use the maximum size to pack as many as possible to one texture
+    allocator_ = AreaAllocator(FONT_TEXTURE_MAX_SIZE, FONT_TEXTURE_MAX_SIZE);
+
+    SharedPtr<Texture2D> texture = font_->CreateFaceTexture();
+    texture->SetSize(FONT_TEXTURE_MAX_SIZE, FONT_TEXTURE_MAX_SIZE, Graphics::GetAlphaFormat());
+    textures_.Push(texture);
+    font_->SetMemoryUse(font_->GetMemoryUse() + FONT_TEXTURE_MAX_SIZE * FONT_TEXTURE_MAX_SIZE);
+    
+    LOGDEBUG(ToString("Font face %s (%dpt) is now using %d dynamic page textures", GetFileName(font_->GetName()).CString(), pointSize_, textures_.Size()));
+}
+
 Font::Font(Context* context) :
     Resource(context),
     fontDataSize_(0),
@@ -148,6 +324,9 @@ Font::Font(Context* context) :
 
 Font::~Font()
 {
+    // To ensure FreeType deallocates properly, first clear all faces, then release the raw font data
+    faces_.Clear();
+    fontData_.Reset();
 }
 
 void Font::RegisterObject(Context* context)
@@ -191,7 +370,7 @@ bool Font::Load(Deserializer& source)
 
 bool Font::SaveXML(Serializer& dest, int pointSize, bool usedGlyphs)
 {
-    const FontFace* fontFace = GetFace(pointSize);
+    FontFace* fontFace = GetFace(pointSize);
     if (!fontFace)
         return false;
     
@@ -289,7 +468,7 @@ bool Font::SaveXML(Serializer& dest, int pointSize, bool usedGlyphs)
     return xml->Save(dest);
 }
 
-const FontFace* Font::GetFace(int pointSize)
+FontFace* Font::GetFace(int pointSize)
 {
     // In headless mode, always return null
     Graphics* graphics = GetSubsystem<Graphics>();
@@ -329,13 +508,37 @@ const FontFace* Font::GetFace(int pointSize)
     }
 }
 
-const FontFace* Font::GetFaceTTF(int pointSize)
+SharedPtr<Texture2D> Font::CreateFaceTexture()
+{
+    SharedPtr<Texture2D> texture(new Texture2D(context_));
+    texture->SetMipsToSkip(QUALITY_LOW, 0); // No quality reduction
+    texture->SetNumLevels(1); // No mipmaps
+    texture->SetAddressMode(COORD_U, ADDRESS_BORDER);
+    texture->SetAddressMode(COORD_V, ADDRESS_BORDER),
+    texture->SetBorderColor(Color(0.0f, 0.0f, 0.0f, 0.0f));
+    return texture;
+}
+
+SharedPtr<Texture2D> Font::LoadFaceTexture(SharedPtr<Image> image)
+{
+    SharedPtr<Texture2D> texture = CreateFaceTexture();
+    if (!texture->Load(image, true))
+    {
+        LOGERROR("Could not load texture from image resource");
+        return SharedPtr<Texture2D>();
+    }
+    return texture;
+}
+
+FontFace* Font::GetFaceTTF(int pointSize)
 {
     // Create & initialize FreeType library if it does not exist yet
     FreeTypeLibrary* freeType = GetSubsystem<FreeTypeLibrary>();
     if (!freeType)
         context_->RegisterSubsystem(freeType = new FreeTypeLibrary(context_));
-
+    // Ensure the FreeType library is kept alive as long as TTF font resources exist
+    freeType_ = freeType;
+    
     FT_Face face;
     FT_Error error;
     FT_Library library = freeType->GetLibrary();
@@ -366,7 +569,8 @@ const FontFace* Font::GetFaceTTF(int pointSize)
         return 0;
     }
     
-    SharedPtr<FontFace> newFace(new FontFace());
+    SharedPtr<FontFace> newFace(new FontFace(this));
+    newFace->face_ = face;
     
     FT_GlyphSlot slot = face->glyph;
     unsigned numGlyphs = 0;
@@ -437,153 +641,21 @@ const FontFace* Font::GetFaceTTF(int pointSize)
     newFace->pointSize_ = pointSize;
     newFace->rowHeight_ = Max((face->size->metrics.height + 63) >> 6, maxHeight);
     
-    // Now try to pack into the smallest possible texture(s)
-    Vector<SharedPtr<Image> > images;
-    unsigned totalTextureSize = 0;
-    unsigned page = 0;
-    unsigned startIndex = 0;
-    unsigned index;
-    unsigned sumMaxOpacity = 0;
-    unsigned samples = 0;
-    
-    while (startIndex < numGlyphs)
+    // Now try to pack into the smallest possible texture. If face does not fit into one texture, enable dynamic mode where
+    // glyphs are only created as necessary
+    if (newFace->RenderAllGlyphs())
     {
-        AreaAllocator allocator(FONT_TEXTURE_MIN_SIZE, FONT_TEXTURE_MIN_SIZE, FONT_TEXTURE_MAX_SIZE, FONT_TEXTURE_MAX_SIZE);
-        for (index = startIndex; index < numGlyphs; ++index)
-        {
-            if (newFace->glyphs_[index].width_ && newFace->glyphs_[index].height_)
-            {
-                int x, y;
-                // Reserve an empty border between glyphs for filtering
-                if (allocator.Allocate(newFace->glyphs_[index].width_ + 1, newFace->glyphs_[index].height_ + 1, x, y))
-                {
-                    newFace->glyphs_[index].x_ = x;
-                    newFace->glyphs_[index].y_ = y;
-                    newFace->glyphs_[index].page_ = page;
-                }
-                else
-                    break;
-            }
-            else
-            {
-                newFace->glyphs_[index].x_ = 0;
-                newFace->glyphs_[index].y_ = 0;
-                newFace->glyphs_[index].page_ = 0;
-            }
-        }
-        
-        int texWidth = allocator.GetWidth();
-        int texHeight = allocator.GetHeight();
-
-        // Create the image for rendering the fonts
-        SharedPtr<Image> image(new Image(context_));
-        images.Push(image);
-        image->SetSize(texWidth, texHeight, 1);
-        
-        // First clear the whole image
-        unsigned char* imageData = image->GetData();
-        for (int y = 0; y < texHeight; ++y)
-        {
-            unsigned char* dest = imageData + texWidth * y;
-            memset(dest, 0, texWidth);
-        }
-        
-        // Render glyphs into texture, and find out a scaling value in case font uses less than full opacity (thin outlines)
-        for (unsigned i = startIndex; i < index; ++i)
-        {
-            if (!newFace->glyphs_[i].width_ || !newFace->glyphs_[i].height_)
-                continue;
-            
-            FT_Load_Glyph(face, i, FT_LOAD_DEFAULT);
-            FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL);
-
-            unsigned char glyphOpacity = 0;
-            
-            if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO)
-            {
-                for (int y = 0; y < newFace->glyphs_[i].height_; ++y)
-                {
-                    unsigned char* src = slot->bitmap.buffer + slot->bitmap.pitch * y;
-                    unsigned char* dest = imageData + texWidth * (y + newFace->glyphs_[i].y_) + newFace->glyphs_[i].x_;
-
-                    for (int x = 0; x < newFace->glyphs_[i].width_; ++x)
-                    {
-                        dest[x] = (src[x >> 3] & (0x80 >> (x & 7))) ? 255 : 0;
-                        glyphOpacity = 255;
-                    }
-                }
-            }
-            else
-            {
-                for (int y = 0; y < newFace->glyphs_[i].height_; ++y)
-                {
-                    unsigned char* src = slot->bitmap.buffer + slot->bitmap.pitch * y;
-                    unsigned char* dest = imageData + texWidth * (y + newFace->glyphs_[i].y_) + newFace->glyphs_[i].x_;
-
-                    for (int x = 0; x < newFace->glyphs_[i].width_; ++x)
-                    {
-                        dest[x] = src[x];
-                        glyphOpacity = Max(glyphOpacity, src[x]);
-                    }
-                }
-            }
-            if (glyphOpacity)
-            {
-                sumMaxOpacity += glyphOpacity;
-                ++samples;
-            }
-        }
-        
-        ++page;
-        startIndex = index;
-    }
-    
-    // Clamp the minimum possible value to avoid overbrightening
-    unsigned char avgMaxOpacity = 255;
-    if (samples)
-        avgMaxOpacity = Max(sumMaxOpacity / samples, 128);
-    
-    if (avgMaxOpacity < 255)
-    {
-        // Apply the scaling value if necessary
-        float scale = 255.0f / avgMaxOpacity;
-        for (unsigned i = 0; i < numGlyphs; ++i)
-        {
-            Image* image = images[newFace->glyphs_[i].page_];
-            unsigned char* imageData = image->GetData();
-            for (int y = 0; y < newFace->glyphs_[i].height_; ++y)
-            {
-                unsigned char* dest = imageData + image->GetWidth() * (y + newFace->glyphs_[i].y_) + newFace->glyphs_[i].x_;
-                for (int x = 0; x < newFace->glyphs_[i].width_; ++x)
-                {
-                    int pixel = dest[x];
-                    dest[x] = Min((int)(pixel * scale), 255);
-                }
-            }
-        }
-    }
-    
-    // Create the texture and load the image into it
-    for (Vector<SharedPtr<Image> >::ConstIterator i = images.Begin(); i != images.End(); ++i)
-    {
-        int texWidth = i->Get()->GetWidth();
-        int texHeight = i->Get()->GetHeight();
-        
-        SharedPtr<Texture> texture = LoadFaceTexture(*i);
-        if (!texture)
-            return 0;
-        newFace->textures_.Push(texture);
-        totalTextureSize += texWidth * texHeight;
+        FT_Done_Face(face);
+        newFace->face_ = 0;
     }
+    else
+        newFace->SetupNextTexture();
     
-    FT_Done_Face(face);
-    
-    SetMemoryUse(GetMemoryUse() + totalTextureSize);
     faces_[pointSize] = newFace;
     return newFace;
 }
 
-const FontFace* Font::GetFaceBitmap(int pointSize)
+FontFace* Font::GetFaceBitmap(int pointSize)
 {
     SharedPtr<XMLFile> xmlReader(new XMLFile(context_));
     MemoryBuffer memoryBuffer(fontData_, fontDataSize_);
@@ -607,7 +679,7 @@ const FontFace* Font::GetFaceBitmap(int pointSize)
         return 0;
     }
     
-    SharedPtr<FontFace> newFace(new FontFace());
+    SharedPtr<FontFace> newFace(new FontFace(this));
     
     XMLElement infoElem = root.GetChild("info");
     if (!infoElem.IsNull())
@@ -642,7 +714,7 @@ const FontFace* Font::GetFaceBitmap(int pointSize)
             LOGERROR("Failed to load font image file");
             return 0;
         }
-        SharedPtr<Texture> texture = LoadFaceTexture(fontImage);
+        SharedPtr<Texture2D> texture = LoadFaceTexture(fontImage);
         if (!texture)
             return 0;
         newFace->textures_.Push(texture);
@@ -717,9 +789,10 @@ unsigned Font::ConvertFormatToNumComponents(unsigned format)
         return 1;
 }
 
-SharedPtr<FontFace> Font::Pack(const FontFace* fontFace)
+SharedPtr<FontFace> Font::Pack(FontFace* fontFace)
 {
-    SharedPtr<FontFace> packedFontFace(new FontFace());
+    // Set parent font as null for the packed face so that it does not attempt to manage the font's total memory use
+    SharedPtr<FontFace> packedFontFace(new FontFace((Font*)0));
     
     // Clone properties
     packedFontFace->pointSize_ = fontFace->pointSize_;
@@ -809,7 +882,7 @@ SharedPtr<FontFace> Font::Pack(const FontFace* fontFace)
         }
         
         // Finally load image into the texture
-        SharedPtr<Texture> texture = LoadFaceTexture(image);
+        SharedPtr<Texture2D> texture = LoadFaceTexture(image);
         if (!texture)
             return SharedPtr<FontFace>();
         packedFontFace->textures_.Push(texture);
@@ -822,24 +895,7 @@ SharedPtr<FontFace> Font::Pack(const FontFace* fontFace)
     return packedFontFace;
 }
 
-SharedPtr<Texture> Font::LoadFaceTexture(SharedPtr<Image> image)
-{
-    Texture2D* texture = new Texture2D(context_);
-    texture->SetMipsToSkip(QUALITY_LOW, 0); // No quality reduction
-    texture->SetNumLevels(1); // No mipmaps
-    texture->SetAddressMode(COORD_U, ADDRESS_BORDER);
-    texture->SetAddressMode(COORD_V, ADDRESS_BORDER),
-    texture->SetBorderColor(Color(0.0f, 0.0f, 0.0f, 0.0f));
-    if (!texture->Load(image, true))
-    {
-        delete texture;
-        LOGERROR("Could not load texture from image resource");
-        return SharedPtr<Texture>();
-    }
-    return SharedPtr<Texture>(texture);
-}
-
-SharedPtr<Image> Font::SaveFaceTexture(Texture* texture)
+SharedPtr<Image> Font::SaveFaceTexture(Texture2D* texture)
 {
     Image* image = new Image(context_);
     image->SetSize(texture->GetWidth(), texture->GetHeight(), ConvertFormatToNumComponents(texture->GetFormat()));
@@ -852,7 +908,7 @@ SharedPtr<Image> Font::SaveFaceTexture(Texture* texture)
     return SharedPtr<Image>(image);
 }
 
-bool Font::SaveFaceTexture(Texture* texture, const String& fileName)
+bool Font::SaveFaceTexture(Texture2D* texture, const String& fileName)
 {
     SharedPtr<Image> image = SaveFaceTexture(texture);
     return image ? image->SavePNG(fileName) : false;

+ 54 - 18
Source/Engine/UI/Font.h

@@ -22,15 +22,18 @@
 
 #pragma once
 
+#include "AreaAllocator.h"
 #include "ArrayPtr.h"
 #include "Resource.h"
 
 namespace Urho3D
 {
 
+class Font;
+class FreeTypeLibrary;
 class Graphics;
 class Image;
-class Texture;
+class Texture2D;
 
 static const int FONT_TEXTURE_MIN_SIZE = 128;
 static const int FONT_TEXTURE_MAX_SIZE = 2048;
@@ -56,12 +59,12 @@ struct FontGlyph
     short offsetY_;
     /// Horizontal advance.
     short advanceX_;
-    /// Page.
+    /// Texture page. M_MAX_UNSIGNED if not yet resident on any texture.
     unsigned page_;
+    /// Used flag.
+    bool used_;
     /// Kerning information.
     HashMap<unsigned, unsigned> kerning_;
-    /// Used flag.
-    mutable bool used_;
 };
 
 /// %Font file type.
@@ -76,31 +79,59 @@ enum FONT_TYPE
 /// %Font face description.
 class URHO3D_API FontFace : public RefCounted
 {
+    friend class Font;
+    
 public:
     /// Construct.
-    FontFace();
+    FontFace(Font* font);
     /// Destruct.
     ~FontFace();
     
     /// Return pointer to the glyph structure corresponding to a character. Return null if glyph not found.
-    const FontGlyph* GetGlyph(unsigned c) const;
+    const FontGlyph* GetGlyph(unsigned c);
     /// Return the kerning for a character and the next character.
     short GetKerning(unsigned c, unsigned d) const;
     /// Return true when one of the texture has a data loss.
     bool IsDataLost() const;
+    /// Return point size.
+    int GetPointSize() const { return pointSize_; }
+    /// Return row height.
+    int GetRowHeight() const { return rowHeight_; }
+    /// Return textures.
+    const Vector<SharedPtr<Texture2D> >& GetTextures() const { return textures_; }
     
-    /// Texture.
-    Vector<SharedPtr<Texture> > textures_;
+private:
+    /// Render all glyphs of the face into a single texture. Return true if could fit them. Called internally.
+    bool RenderAllGlyphs();
+    /// Render one glyph on demand into the current texture. Return true if successful. Called internally.
+    bool RenderGlyph(unsigned index);
+    /// Render a glyph bitmap into a memory buffer. Called internally.
+    void RenderGlyphBitmap(unsigned index, unsigned char* dest, unsigned pitch);
+    /// Setup next texture for dynamic glyph rendering. Called internally.
+    void SetupNextTexture();
+    
+    /// Parent font.
+    Font* font_;
+    /// FreeType face in dynamic mode.
+    void* face_;
+    /// Glyph texture pages.
+    Vector<SharedPtr<Texture2D> > textures_;
     /// Glyphs.
     Vector<FontGlyph> glyphs_;
+    /// Glyph index mapping.
+    HashMap<unsigned, unsigned> glyphMapping_;
     /// Point size.
     int pointSize_;
     /// Row height.
     int rowHeight_;
-    /// Glyph index mapping.
-    HashMap<unsigned, unsigned> glyphMapping_;
     /// Kerning flag.
     bool hasKerning_;
+    /// AreaAllocator in dynamic mode.
+    AreaAllocator allocator_;
+    /// Rendering bitmap in dynamic mode glyphs.
+    SharedArrayPtr<unsigned char> bitmap_;
+    /// Dynamic mode bitmap byte size.
+    unsigned bitmapSize_;
 };
 
 /// %Font resource.
@@ -120,24 +151,29 @@ public:
     /// Save resource as a new bitmap font type in XML format. Return true if successful.
     bool SaveXML(Serializer& dest, int pointSize, bool usedGlyphs = false);
     /// Return font face. Pack and render to a texture if not rendered yet. Return null on error.
-    const FontFace* GetFace(int pointSize);
+    FontFace* GetFace(int pointSize);
+    
+    /// Create a texture for font rendering.
+    SharedPtr<Texture2D> CreateFaceTexture();
+    /// Load font face texture from image resource.
+    SharedPtr<Texture2D> LoadFaceTexture(SharedPtr<Image> image);
     
 private:
     /// Return True-type font face. Called internally. Return null on error.
-    const FontFace* GetFaceTTF(int pointSize);
+    FontFace* GetFaceTTF(int pointSize);
     /// Return bitmap font face. Called internally. Return null on error.
-    const FontFace* GetFaceBitmap(int pointSize);
+    FontFace* GetFaceBitmap(int pointSize);
     /// Convert graphics format to number of components.
     unsigned ConvertFormatToNumComponents(unsigned format);
     /// Pack used glyphs into smallest texture size and smallest number of texture.
-    SharedPtr<FontFace> Pack(const FontFace* fontFace);
-    /// Load font face texture from image resource.
-    SharedPtr<Texture> LoadFaceTexture(SharedPtr<Image> image);
+    SharedPtr<FontFace> Pack(FontFace* fontFace);
     /// Save font face texture as image resource.
-    SharedPtr<Image> SaveFaceTexture(Texture* texture);
+    SharedPtr<Image> SaveFaceTexture(Texture2D* texture);
     /// Save font face texture as image file.
-    bool SaveFaceTexture(Texture* texture, const String& fileName);
+    bool SaveFaceTexture(Texture2D* texture, const String& fileName);
     
+    /// FreeType library.
+    SharedPtr<FreeTypeLibrary> freeType_;
     /// Created faces.
     HashMap<int, SharedPtr<FontFace> > faces_;
     /// Font data.

+ 11 - 9
Source/Engine/UI/Text.cpp

@@ -156,14 +156,16 @@ void Text::GetBatches(PODVector<UIBatch>& batches, PODVector<float>& vertexData,
     // Text batch
     if (font_)
     {
-        const FontFace* face = font_->GetFace(fontSize_);
+        FontFace* face = font_->GetFace(fontSize_);
         if (!face)
             return;
 
-        if (face->textures_.Size() > 1)
+        const Vector<SharedPtr<Texture2D> >& textures = face->GetTextures();
+        
+        if (textures.Size() > 1)
         {
             // Only traversing thru the printText once regardless of number of textures/pages in the font
-            Vector<PODVector<GlyphLocation> > pageGlyphLocations(face->textures_.Size());
+            Vector<PODVector<GlyphLocation> > pageGlyphLocations(textures.Size());
 
             unsigned rowIndex = 0;
             int x = GetRowStartPosition(rowIndex);
@@ -192,10 +194,10 @@ void Text::GetBatches(PODVector<UIBatch>& batches, PODVector<float>& vertexData,
                 }
             }
 
-            for (unsigned n = 0; n < face->textures_.Size(); ++n)
+            for (unsigned n = 0; n < textures.Size(); ++n)
             {
                 // One batch per texture/page
-                UIBatch pageBatch(this, BLEND_ALPHA, currentScissor, face->textures_[n], &vertexData);
+                UIBatch pageBatch(this, BLEND_ALPHA, currentScissor, textures[n], &vertexData);
 
                 const PODVector<GlyphLocation>& pageGlyphLocation = pageGlyphLocations[n];
 
@@ -232,7 +234,7 @@ void Text::GetBatches(PODVector<UIBatch>& batches, PODVector<float>& vertexData,
             int x = GetRowStartPosition(0);
             int y = 0;
 
-            UIBatch batch(this, BLEND_ALPHA, currentScissor, face->textures_[0], &vertexData);
+            UIBatch batch(this, BLEND_ALPHA, currentScissor, textures[0], &vertexData);
 
             switch (textEffect_)
             {
@@ -416,11 +418,11 @@ void Text::UpdateText()
 
     if (font_)
     {
-        const FontFace* face = font_->GetFace(fontSize_);
+        FontFace* face = font_->GetFace(fontSize_);
         if (!face)
             return;
 
-        rowHeight_ = face->rowHeight_;
+        rowHeight_ = face->GetRowHeight();
         int rowWidth = 0;
         int rowHeight = (int)(rowSpacing_ * rowHeight_);
 
@@ -672,7 +674,7 @@ void Text::ConstructBatch(UIBatch& pageBatch, const PODVector<GlyphLocation>& pa
     }
 }
 
-void Text::ConstructBatch(UIBatch& batch, const FontFace* face, int x, int y, int dx, int dy, Color* color, float depthBias)
+void Text::ConstructBatch(UIBatch& batch, FontFace* face, int x, int y, int dx, int dy, Color* color, float depthBias)
 {
     unsigned rowIndex = 0;
     unsigned startDataSize = batch.vertexData_->Size();

+ 1 - 1
Source/Engine/UI/Text.h

@@ -159,7 +159,7 @@ protected:
     /// Contruct batch.
     void ConstructBatch(UIBatch& pageBatch, const PODVector<GlyphLocation>& pageGlyphLocation, int dx = 0, int dy = 0, Color* color = 0, float depthBias = 0.0f);
     /// Contruct batch.
-    void ConstructBatch(UIBatch& batch, const FontFace* face, int x, int y, int dx = 0, int dy = 0, Color* color = 0, float depthBias = 0.0f);
+    void ConstructBatch(UIBatch& batch, FontFace* face, int x, int y, int dx = 0, int dy = 0, Color* color = 0, float depthBias = 0.0f);
 
     /// Font.
     SharedPtr<Font> font_;