Sfoglia il codice sorgente

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

Lasse Öörni 12 anni fa
parent
commit
ecc37d1f2e

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

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

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

@@ -31,6 +31,8 @@ namespace Urho3D
 class URHO3D_API AreaAllocator
 class URHO3D_API AreaAllocator
 {
 {
 public:
 public:
+    /// Default construct with empty size.
+    AreaAllocator();
     /// Construct with given width and height.
     /// Construct with given width and height.
     AreaAllocator(int width, int height);
     AreaAllocator(int width, int height);
     /// Construct with given width and height, and set the maximum it allows to grow.
     /// 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 "Precompiled.h"
-#include "AreaAllocator.h"
 #include "Context.h"
 #include "Context.h"
 #include "Deserializer.h"
 #include "Deserializer.h"
 #include "FileSystem.h"
 #include "FileSystem.h"
@@ -75,30 +74,53 @@ private:
 };
 };
 
 
 FontGlyph::FontGlyph() :
 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()
 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);
     HashMap<unsigned, unsigned>::ConstIterator i = glyphMapping_.Find(c);
     if (i != glyphMapping_.End())
     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;
         glyph.used_ = true;
         return &glyph;
         return &glyph;
     }
     }
     else
     else
-        return NULL;
+        return 0;
 }
 }
 
 
 short FontFace::GetKerning(unsigned c, unsigned d) const
 short FontFace::GetKerning(unsigned c, unsigned d) const
@@ -139,6 +161,160 @@ bool FontFace::IsDataLost() const
     return false;
     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) :
 Font::Font(Context* context) :
     Resource(context),
     Resource(context),
     fontDataSize_(0),
     fontDataSize_(0),
@@ -148,6 +324,9 @@ Font::Font(Context* context) :
 
 
 Font::~Font()
 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)
 void Font::RegisterObject(Context* context)
@@ -191,7 +370,7 @@ bool Font::Load(Deserializer& source)
 
 
 bool Font::SaveXML(Serializer& dest, int pointSize, bool usedGlyphs)
 bool Font::SaveXML(Serializer& dest, int pointSize, bool usedGlyphs)
 {
 {
-    const FontFace* fontFace = GetFace(pointSize);
+    FontFace* fontFace = GetFace(pointSize);
     if (!fontFace)
     if (!fontFace)
         return false;
         return false;
     
     
@@ -289,7 +468,7 @@ bool Font::SaveXML(Serializer& dest, int pointSize, bool usedGlyphs)
     return xml->Save(dest);
     return xml->Save(dest);
 }
 }
 
 
-const FontFace* Font::GetFace(int pointSize)
+FontFace* Font::GetFace(int pointSize)
 {
 {
     // In headless mode, always return null
     // In headless mode, always return null
     Graphics* graphics = GetSubsystem<Graphics>();
     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
     // Create & initialize FreeType library if it does not exist yet
     FreeTypeLibrary* freeType = GetSubsystem<FreeTypeLibrary>();
     FreeTypeLibrary* freeType = GetSubsystem<FreeTypeLibrary>();
     if (!freeType)
     if (!freeType)
         context_->RegisterSubsystem(freeType = new FreeTypeLibrary(context_));
         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_Face face;
     FT_Error error;
     FT_Error error;
     FT_Library library = freeType->GetLibrary();
     FT_Library library = freeType->GetLibrary();
@@ -366,7 +569,8 @@ const FontFace* Font::GetFaceTTF(int pointSize)
         return 0;
         return 0;
     }
     }
     
     
-    SharedPtr<FontFace> newFace(new FontFace());
+    SharedPtr<FontFace> newFace(new FontFace(this));
+    newFace->face_ = face;
     
     
     FT_GlyphSlot slot = face->glyph;
     FT_GlyphSlot slot = face->glyph;
     unsigned numGlyphs = 0;
     unsigned numGlyphs = 0;
@@ -437,153 +641,21 @@ const FontFace* Font::GetFaceTTF(int pointSize)
     newFace->pointSize_ = pointSize;
     newFace->pointSize_ = pointSize;
     newFace->rowHeight_ = Max((face->size->metrics.height + 63) >> 6, maxHeight);
     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;
     faces_[pointSize] = newFace;
     return newFace;
     return newFace;
 }
 }
 
 
-const FontFace* Font::GetFaceBitmap(int pointSize)
+FontFace* Font::GetFaceBitmap(int pointSize)
 {
 {
     SharedPtr<XMLFile> xmlReader(new XMLFile(context_));
     SharedPtr<XMLFile> xmlReader(new XMLFile(context_));
     MemoryBuffer memoryBuffer(fontData_, fontDataSize_);
     MemoryBuffer memoryBuffer(fontData_, fontDataSize_);
@@ -607,7 +679,7 @@ const FontFace* Font::GetFaceBitmap(int pointSize)
         return 0;
         return 0;
     }
     }
     
     
-    SharedPtr<FontFace> newFace(new FontFace());
+    SharedPtr<FontFace> newFace(new FontFace(this));
     
     
     XMLElement infoElem = root.GetChild("info");
     XMLElement infoElem = root.GetChild("info");
     if (!infoElem.IsNull())
     if (!infoElem.IsNull())
@@ -642,7 +714,7 @@ const FontFace* Font::GetFaceBitmap(int pointSize)
             LOGERROR("Failed to load font image file");
             LOGERROR("Failed to load font image file");
             return 0;
             return 0;
         }
         }
-        SharedPtr<Texture> texture = LoadFaceTexture(fontImage);
+        SharedPtr<Texture2D> texture = LoadFaceTexture(fontImage);
         if (!texture)
         if (!texture)
             return 0;
             return 0;
         newFace->textures_.Push(texture);
         newFace->textures_.Push(texture);
@@ -717,9 +789,10 @@ unsigned Font::ConvertFormatToNumComponents(unsigned format)
         return 1;
         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
     // Clone properties
     packedFontFace->pointSize_ = fontFace->pointSize_;
     packedFontFace->pointSize_ = fontFace->pointSize_;
@@ -809,7 +882,7 @@ SharedPtr<FontFace> Font::Pack(const FontFace* fontFace)
         }
         }
         
         
         // Finally load image into the texture
         // Finally load image into the texture
-        SharedPtr<Texture> texture = LoadFaceTexture(image);
+        SharedPtr<Texture2D> texture = LoadFaceTexture(image);
         if (!texture)
         if (!texture)
             return SharedPtr<FontFace>();
             return SharedPtr<FontFace>();
         packedFontFace->textures_.Push(texture);
         packedFontFace->textures_.Push(texture);
@@ -822,24 +895,7 @@ SharedPtr<FontFace> Font::Pack(const FontFace* fontFace)
     return packedFontFace;
     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* image = new Image(context_);
     image->SetSize(texture->GetWidth(), texture->GetHeight(), ConvertFormatToNumComponents(texture->GetFormat()));
     image->SetSize(texture->GetWidth(), texture->GetHeight(), ConvertFormatToNumComponents(texture->GetFormat()));
@@ -852,7 +908,7 @@ SharedPtr<Image> Font::SaveFaceTexture(Texture* texture)
     return SharedPtr<Image>(image);
     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);
     SharedPtr<Image> image = SaveFaceTexture(texture);
     return image ? image->SavePNG(fileName) : false;
     return image ? image->SavePNG(fileName) : false;

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

@@ -22,15 +22,18 @@
 
 
 #pragma once
 #pragma once
 
 
+#include "AreaAllocator.h"
 #include "ArrayPtr.h"
 #include "ArrayPtr.h"
 #include "Resource.h"
 #include "Resource.h"
 
 
 namespace Urho3D
 namespace Urho3D
 {
 {
 
 
+class Font;
+class FreeTypeLibrary;
 class Graphics;
 class Graphics;
 class Image;
 class Image;
-class Texture;
+class Texture2D;
 
 
 static const int FONT_TEXTURE_MIN_SIZE = 128;
 static const int FONT_TEXTURE_MIN_SIZE = 128;
 static const int FONT_TEXTURE_MAX_SIZE = 2048;
 static const int FONT_TEXTURE_MAX_SIZE = 2048;
@@ -56,12 +59,12 @@ struct FontGlyph
     short offsetY_;
     short offsetY_;
     /// Horizontal advance.
     /// Horizontal advance.
     short advanceX_;
     short advanceX_;
-    /// Page.
+    /// Texture page. M_MAX_UNSIGNED if not yet resident on any texture.
     unsigned page_;
     unsigned page_;
+    /// Used flag.
+    bool used_;
     /// Kerning information.
     /// Kerning information.
     HashMap<unsigned, unsigned> kerning_;
     HashMap<unsigned, unsigned> kerning_;
-    /// Used flag.
-    mutable bool used_;
 };
 };
 
 
 /// %Font file type.
 /// %Font file type.
@@ -76,31 +79,59 @@ enum FONT_TYPE
 /// %Font face description.
 /// %Font face description.
 class URHO3D_API FontFace : public RefCounted
 class URHO3D_API FontFace : public RefCounted
 {
 {
+    friend class Font;
+    
 public:
 public:
     /// Construct.
     /// Construct.
-    FontFace();
+    FontFace(Font* font);
     /// Destruct.
     /// Destruct.
     ~FontFace();
     ~FontFace();
     
     
     /// Return pointer to the glyph structure corresponding to a character. Return null if glyph not found.
     /// 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.
     /// Return the kerning for a character and the next character.
     short GetKerning(unsigned c, unsigned d) const;
     short GetKerning(unsigned c, unsigned d) const;
     /// Return true when one of the texture has a data loss.
     /// Return true when one of the texture has a data loss.
     bool IsDataLost() const;
     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.
     /// Glyphs.
     Vector<FontGlyph> glyphs_;
     Vector<FontGlyph> glyphs_;
+    /// Glyph index mapping.
+    HashMap<unsigned, unsigned> glyphMapping_;
     /// Point size.
     /// Point size.
     int pointSize_;
     int pointSize_;
     /// Row height.
     /// Row height.
     int rowHeight_;
     int rowHeight_;
-    /// Glyph index mapping.
-    HashMap<unsigned, unsigned> glyphMapping_;
     /// Kerning flag.
     /// Kerning flag.
     bool hasKerning_;
     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.
 /// %Font resource.
@@ -120,24 +151,29 @@ public:
     /// Save resource as a new bitmap font type in XML format. Return true if successful.
     /// Save resource as a new bitmap font type in XML format. Return true if successful.
     bool SaveXML(Serializer& dest, int pointSize, bool usedGlyphs = false);
     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.
     /// 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:
 private:
     /// Return True-type font face. Called internally. Return null on error.
     /// 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.
     /// 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.
     /// Convert graphics format to number of components.
     unsigned ConvertFormatToNumComponents(unsigned format);
     unsigned ConvertFormatToNumComponents(unsigned format);
     /// Pack used glyphs into smallest texture size and smallest number of texture.
     /// 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.
     /// Save font face texture as image resource.
-    SharedPtr<Image> SaveFaceTexture(Texture* texture);
+    SharedPtr<Image> SaveFaceTexture(Texture2D* texture);
     /// Save font face texture as image file.
     /// 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.
     /// Created faces.
     HashMap<int, SharedPtr<FontFace> > faces_;
     HashMap<int, SharedPtr<FontFace> > faces_;
     /// Font data.
     /// Font data.

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

@@ -156,14 +156,16 @@ void Text::GetBatches(PODVector<UIBatch>& batches, PODVector<float>& vertexData,
     // Text batch
     // Text batch
     if (font_)
     if (font_)
     {
     {
-        const FontFace* face = font_->GetFace(fontSize_);
+        FontFace* face = font_->GetFace(fontSize_);
         if (!face)
         if (!face)
             return;
             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
             // 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;
             unsigned rowIndex = 0;
             int x = GetRowStartPosition(rowIndex);
             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
                 // 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];
                 const PODVector<GlyphLocation>& pageGlyphLocation = pageGlyphLocations[n];
 
 
@@ -232,7 +234,7 @@ void Text::GetBatches(PODVector<UIBatch>& batches, PODVector<float>& vertexData,
             int x = GetRowStartPosition(0);
             int x = GetRowStartPosition(0);
             int y = 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_)
             switch (textEffect_)
             {
             {
@@ -416,11 +418,11 @@ void Text::UpdateText()
 
 
     if (font_)
     if (font_)
     {
     {
-        const FontFace* face = font_->GetFace(fontSize_);
+        FontFace* face = font_->GetFace(fontSize_);
         if (!face)
         if (!face)
             return;
             return;
 
 
-        rowHeight_ = face->rowHeight_;
+        rowHeight_ = face->GetRowHeight();
         int rowWidth = 0;
         int rowWidth = 0;
         int rowHeight = (int)(rowSpacing_ * rowHeight_);
         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 rowIndex = 0;
     unsigned startDataSize = batch.vertexData_->Size();
     unsigned startDataSize = batch.vertexData_->Size();

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

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