Browse Source

Speed up TTF loading time. Tidied up code for loading/getting bitmap font.

Wei Tjong Yao 12 years ago
parent
commit
34a849e00e
6 changed files with 216 additions and 276 deletions
  1. 1 0
      Docs/ScriptAPI.dox
  2. 1 0
      Engine/Engine/UIAPI.cpp
  3. 57 98
      Engine/Math/AreaAllocator.cpp
  4. 10 5
      Engine/Math/AreaAllocator.h
  5. 132 156
      Engine/UI/Font.cpp
  6. 15 17
      Engine/UI/Font.h

+ 1 - 0
Docs/ScriptAPI.dox

@@ -3046,6 +3046,7 @@ Properties:<br>
 - String name
 - String name
 - uint memoryUse (readonly)
 - uint memoryUse (readonly)
 - uint useTimer (readonly)
 - uint useTimer (readonly)
+- String pathName (readonly)
 
 
 
 
 UIElement
 UIElement

+ 1 - 0
Engine/Engine/UIAPI.cpp

@@ -41,6 +41,7 @@ namespace Urho3D
 static void RegisterFont(asIScriptEngine* engine)
 static void RegisterFont(asIScriptEngine* engine)
 {
 {
     RegisterResource<Font>(engine, "Font");
     RegisterResource<Font>(engine, "Font");
+    engine->RegisterObjectMethod("Font", "const String&  get_pathName() const", asMETHOD(Font, GetPathName), asCALL_THISCALL);
 }
 }
 
 
 static void RegisterUIElement(asIScriptEngine* engine)
 static void RegisterUIElement(asIScriptEngine* engine)

+ 57 - 98
Engine/Math/AreaAllocator.cpp

@@ -26,7 +26,18 @@
 namespace Urho3D
 namespace Urho3D
 {
 {
 
 
-AreaAllocator::AreaAllocator(int width, int height)
+AreaAllocator::AreaAllocator(int width, int height) :
+    size_(width, height),
+    maxSize_(IntVector2::ZERO),
+    doubleWidth_(true)
+{
+    Reset(width, height);
+}
+
+AreaAllocator::AreaAllocator(int width, int height, int maxWidth, int maxHeight) :
+    size_(width, height),
+    maxSize_(maxWidth, maxHeight),
+    doubleWidth_(true)
 {
 {
     Reset(width, height);
     Reset(width, height);
 }
 }
@@ -49,117 +60,65 @@ bool AreaAllocator::Allocate(int width, int height, int& x, int& y)
     PODVector<IntRect>::Iterator best = freeAreas_.End();
     PODVector<IntRect>::Iterator best = freeAreas_.End();
     int bestFreeArea = M_MAX_INT;
     int bestFreeArea = M_MAX_INT;
     
     
-    for (PODVector<IntRect>::Iterator i = freeAreas_.Begin(); i != freeAreas_.End(); ++i)
+    for(;;)
     {
     {
-        int freeWidth = i->right_ - i->left_;
-        int freeHeight = i->bottom_ - i->top_;
-        
-        if (freeWidth >= width && freeHeight >= height)
+        for (PODVector<IntRect>::Iterator i = freeAreas_.Begin(); i != freeAreas_.End(); ++i)
         {
         {
-            // Calculate rank for free area. Lower is better
-            int freeArea = freeWidth * freeHeight;
+            int freeWidth = i->Width();
+            int freeHeight = i->Height();
             
             
-            if (best == freeAreas_.End() || freeArea < bestFreeArea)
+            if (freeWidth >= width && freeHeight >= height)
             {
             {
-                best = i;
-                bestFreeArea = freeArea;
+                // Calculate rank for free area. Lower is better
+                int freeArea = freeWidth * freeHeight;
+                
+                if (freeArea < bestFreeArea)
+                {
+                    best = i;
+                    bestFreeArea = freeArea;
+                }
             }
             }
         }
         }
+        
+        if (best == freeAreas_.End())
+        {
+            if (doubleWidth_ && size_.x_ < maxSize_.x_)
+            {
+                int oldWidth = size_.x_;
+                size_.x_ <<= 1;
+                IntRect newArea(oldWidth, 0, size_.x_, size_.y_);
+                freeAreas_.Push(newArea);
+            }
+            else if (!doubleWidth_ && size_.y_ < maxSize_.y_)
+            {
+                int oldHeight = size_.y_;
+                size_.y_ <<= 1;
+                IntRect newArea(0, oldHeight, size_.x_, size_.y_);
+                freeAreas_.Push(newArea);
+            }
+            else
+                return false;
+            
+            doubleWidth_ = !doubleWidth_;
+        }
+        else
+            break;
     }
     }
     
     
-    if (best == freeAreas_.End())
-        return false;
-    
-    IntRect reserved;
-    reserved.left_ = best->left_;
-    reserved.top_ = best->top_;
-    reserved.right_ = best->left_ + width;
-    reserved.bottom_ = best->top_ + height;
-    
+    IntRect reserved(best->left_, best->top_, best->left_ + width, best->top_ + height);
     x = best->left_;
     x = best->left_;
     y = best->top_;
     y = best->top_;
     
     
-    // Remove the reserved area from all free areas
-    for (unsigned i = 0; i < freeAreas_.Size();)
+    // Reserved the area by spliting up the remaining free area
+    best->left_ = reserved.right_;
+    if (best->Height() > 2 * height)
     {
     {
-        if (SplitRect(freeAreas_[i], reserved))
-            freeAreas_.Erase(i);
-        else
-            ++i;
+        IntRect splitArea(reserved.left_, reserved.bottom_, best->right_, best->bottom_);
+        best->bottom_ = reserved.bottom_;
+        freeAreas_.Push(splitArea);
     }
     }
-    
-    Cleanup();
-    return true;
-}
 
 
-bool AreaAllocator::SplitRect(IntRect original, const IntRect& reserve)
-{
-    if (reserve.right_ > original.left_ && reserve.left_ < original.right_ && reserve.bottom_ > original.top_ &&
-        reserve.top_ < original.bottom_)
-    {
-        // Check for splitting from the right
-        if (reserve.right_ < original.right_) 
-        {
-            IntRect newRect = original;
-            newRect.left_ = reserve.right_;
-            freeAreas_.Push(newRect);
-        }
-        // Check for splitting from the left
-        if (reserve.left_ > original.left_)
-        {
-            IntRect newRect = original;
-            newRect.right_ = reserve.left_;
-            freeAreas_.Push(newRect);
-        }
-        // Check for splitting from the bottom
-        if (reserve.bottom_ < original.bottom_)
-        {
-            IntRect newRect = original;
-            newRect.top_ = reserve.bottom_;
-            freeAreas_.Push(newRect);
-        }
-        // Check for splitting from the top
-        if (reserve.top_ > original.top_)
-        {
-            IntRect newRect = original;
-            newRect.bottom_ = reserve.top_;
-            freeAreas_.Push(newRect);
-        }
-        
-        return true;
-    }
-    
-    return false;
-}
-
-void AreaAllocator::Cleanup()
-{
-    // Remove rects which are contained within another rect
-    for (unsigned i = 0; i < freeAreas_.Size(); )
-    {
-        bool erased = false;
-        for (unsigned j = i + 1; j < freeAreas_.Size(); )
-        {
-            if ((freeAreas_[i].left_ >= freeAreas_[j].left_) &&
-                (freeAreas_[i].top_ >= freeAreas_[j].top_) &&
-                (freeAreas_[i].right_ <= freeAreas_[j].right_) &&
-                (freeAreas_[i].bottom_ <= freeAreas_[j].bottom_))
-            {
-                freeAreas_.Erase(i);
-                erased = true;
-                break;
-            }
-            if ((freeAreas_[j].left_ >= freeAreas_[i].left_) &&
-                (freeAreas_[j].top_ >= freeAreas_[i].top_) &&
-                (freeAreas_[j].right_ <= freeAreas_[i].right_) &&
-                (freeAreas_[j].bottom_ <= freeAreas_[i].bottom_))
-                freeAreas_.Erase(j);
-            else
-                ++j;
-        }
-        if (!erased)
-            ++i;
-    }
+    return true;
 }
 }
 
 
 }
 }

+ 10 - 5
Engine/Math/AreaAllocator.h

@@ -33,20 +33,25 @@ class AreaAllocator
 public:
 public:
     /// 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.
+    AreaAllocator(int width, int height, int maxWidth, int maxHeight);
     
     
     /// Reset to given width and height and remove all previous allocations.
     /// Reset to given width and height and remove all previous allocations.
     void Reset(int width, int height);
     void Reset(int width, int height);
     /// Try to allocate an area. Return true if successful, with x & y coordinates filled.
     /// Try to allocate an area. Return true if successful, with x & y coordinates filled.
     bool Allocate(int width, int height, int& x, int& y);
     bool Allocate(int width, int height, int& x, int& y);
+    /// Return the final area size (if it is allowed to grow).
+    IntVector2 GetSize() const { return size_; }
 
 
 private:
 private:
-    /// Cut the reserved area from a rectangle. Return true if the rectangle should be removed from the vector.
-    bool SplitRect(IntRect original, const IntRect& reserved);
-    /// Remove overlapping free rectangles.
-    void Cleanup();
-    
     /// Free rectangles.
     /// Free rectangles.
     PODVector<IntRect> freeAreas_;
     PODVector<IntRect> freeAreas_;
+    /// Current size.
+    IntVector2 size_;
+    /// Maximum size it allows to grow. It is zero when it is not allowed to grow.
+    IntVector2 maxSize_;
+    /// The dimension use for next growth. Use internally.
+    bool doubleWidth_;
 };
 };
 
 
 }
 }

+ 132 - 156
Engine/UI/Font.cpp

@@ -23,17 +23,16 @@
 #include "Precompiled.h"
 #include "Precompiled.h"
 #include "AreaAllocator.h"
 #include "AreaAllocator.h"
 #include "Context.h"
 #include "Context.h"
-#include "File.h"
+#include "Deserializer.h"
+#include "FileSystem.h"
 #include "Font.h"
 #include "Font.h"
 #include "Graphics.h"
 #include "Graphics.h"
-#include "Image.h"
 #include "Log.h"
 #include "Log.h"
+#include "MemoryBuffer.h"
 #include "Profiler.h"
 #include "Profiler.h"
+#include "ResourceCache.h"
 #include "Texture2D.h"
 #include "Texture2D.h"
 #include "XMLFile.h"
 #include "XMLFile.h"
-#include "ResourceCache.h"
-#include "FileSystem.h"
-#include "StringUtils.h"
 
 
 #include <ft2build.h>
 #include <ft2build.h>
 #include FT_FREETYPE_H
 #include FT_FREETYPE_H
@@ -119,11 +118,8 @@ OBJECTTYPESTATIC(Font);
 Font::Font(Context* context) :
 Font::Font(Context* context) :
     Resource(context),
     Resource(context),
     fontDataSize_(0),
     fontDataSize_(0),
-    fontType_(FONT_TTF)
+    fontType_(FONT_NONE)
 {
 {
-    // Create & initialize FreeType library if it does not exist yet
-    if (!GetSubsystem<FreeTypeLibrary>())
-        context_->RegisterSubsystem(new FreeTypeLibrary(context_));
 }
 }
 
 
 Font::~Font()
 Font::~Font()
@@ -139,22 +135,36 @@ bool Font::Load(Deserializer& source)
 {
 {
     PROFILE(LoadFont);
     PROFILE(LoadFont);
     
     
-    String ext = GetExtension(source.GetName()).ToLower();
-    if (ext == ".xml" || ext == ".fnt")
+    faces_.Clear();
+    pathName_.Clear();
+    
+    fontDataSize_ = source.GetSize();
+    if (fontDataSize_)
     {
     {
-        fontType_ = FONT_IMAGE;
-        return LoadImageFont(source);
+        fontData_ = new unsigned char[fontDataSize_];
+        if (source.Read(&fontData_[0], fontDataSize_) != fontDataSize_)
+            return false;
     }
     }
     else
     else
     {
     {
-        fontType_ = FONT_TTF;
-        return LoadTTFont(source);
+        fontData_.Reset();
+        return false;
     }
     }
+
+    pathName_ = source.GetName();
+    String ext = GetExtension(pathName_).ToLower();
+    if (ext == ".ttf")
+        fontType_ = FONT_TTF;
+    else if (ext == ".xml" || ext == ".fnt")
+        fontType_ = FONT_BITMAP;
+
+    SetMemoryUse(fontDataSize_);
+    return true;
 }
 }
 
 
-const FontFace* Font::GetFaceTTF(int pointSize)
+const FontFace* Font::GetFace( int pointSize )
 {
 {
-    HashMap<int, SharedPtr<FontFace> >::ConstIterator i = faces_.Find(pointSize);
+    HashMap<int, SharedPtr<FontFace> >::Iterator i = faces_.Find(pointSize);
     if (i != faces_.End())
     if (i != faces_.End())
     {
     {
         if (!i->second_->texture_->IsDataLost())
         if (!i->second_->texture_->IsDataLost())
@@ -162,15 +172,35 @@ const FontFace* Font::GetFaceTTF(int pointSize)
         else
         else
         {
         {
             // Erase and reload face if texture data lost (OpenGL mode only)
             // Erase and reload face if texture data lost (OpenGL mode only)
-            faces_.Erase(pointSize);
+            faces_.Erase(i);
         }
         }
     }
     }
     
     
     PROFILE(GetFontFace);
     PROFILE(GetFontFace);
     
     
+    switch (fontType_)
+    {
+    case FONT_TTF:
+        return GetFaceTTF(pointSize);
+
+    case FONT_BITMAP:
+        return GetFaceBitmap(pointSize);
+    
+    default:
+        return 0;
+    }
+}
+
+const 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_));
+
     FT_Face face;
     FT_Face face;
     FT_Error error;
     FT_Error error;
-    FT_Library library = GetSubsystem<FreeTypeLibrary>()->getLibrary();
+    FT_Library library = freeType->getLibrary();
     
     
     if (pointSize <= 0)
     if (pointSize <= 0)
     {
     {
@@ -272,69 +302,46 @@ const FontFace* Font::GetFaceTTF(int 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
     // Now try to pack into the smallest possible texture
-    int texWidth = FONT_TEXTURE_MIN_SIZE;
-    int texHeight = FONT_TEXTURE_MIN_SIZE;
-    bool doubleHorizontal = true;
+    int totalArea = 0;
+    for (unsigned i = 0; i < numGlyphs; ++i)
+        totalArea += (newFace->glyphs_[i].width_ + 1) * (newFace->glyphs_[i].height_ + 1);
     
     
-    for (;;)
+    bool success = true;
+    AreaAllocator allocator(FONT_TEXTURE_MIN_SIZE, FONT_TEXTURE_MIN_SIZE, FONT_TEXTURE_MAX_SIZE, FONT_TEXTURE_MAX_SIZE);
+    for (unsigned i = 0; i < numGlyphs; ++i)
     {
     {
-        bool success = true;
-        
-        // Check first for theoretical possible fit. If it fails, there is no need to try to fit
-        int totalArea = 0;
-        for (unsigned i = 0; i < numGlyphs; ++i)
-            totalArea += (newFace->glyphs_[i].width_ + 1) * (newFace->glyphs_[i].height_ + 1);
-        
-        if (totalArea > texWidth * texHeight)
-            success = false;
-        else
+        if (newFace->glyphs_[i].width_ && newFace->glyphs_[i].height_)
         {
         {
-            AreaAllocator allocator(texWidth, texHeight);
-            for (unsigned i = 0; i < numGlyphs; ++i)
+            int x, y;
+            // Reserve an empty border between glyphs for filtering
+            if (!allocator.Allocate(newFace->glyphs_[i].width_ + 1, newFace->glyphs_[i].height_ + 1, x, y))
             {
             {
-                if (newFace->glyphs_[i].width_ && newFace->glyphs_[i].height_)
-                {
-                    int x, y;
-                    // Reserve an empty border between glyphs for filtering
-                    if (!allocator.Allocate(newFace->glyphs_[i].width_ + 1, newFace->glyphs_[i].height_ + 1, x, y))
-                    {
-                        success = false;
-                        break;
-                    }
-                    else
-                    {
-                        newFace->glyphs_[i].x_ = x;
-                        newFace->glyphs_[i].y_ = y;
-                    }
-                }
-                else
-                {
-                    newFace->glyphs_[i].x_ = 0;
-                    newFace->glyphs_[i].y_ = 0;
-                }
+                success = false;
+                break;
             }
             }
-        }
-        
-        if (!success)
-        {
-            // Alternate between doubling the horizontal and the vertical dimension
-            if (doubleHorizontal)
-                texWidth <<= 1;
             else
             else
-                texHeight <<= 1;
-            
-            if (texWidth > FONT_TEXTURE_MAX_SIZE || texHeight > FONT_TEXTURE_MAX_SIZE)
             {
             {
-                FT_Done_Face(face);
-                LOGERROR("Font face could not be fit into the largest possible texture");
-                return 0;
+                newFace->glyphs_[i].x_ = x;
+                newFace->glyphs_[i].y_ = y;
             }
             }
-            doubleHorizontal = !doubleHorizontal;
         }
         }
         else
         else
-            break;
+        {
+            newFace->glyphs_[i].x_ = 0;
+            newFace->glyphs_[i].y_ = 0;
+        }
     }
     }
     
     
+    if (!success)
+    {
+        FT_Done_Face(face);
+        LOGERROR("Font face could not be fit into the largest possible texture");
+        return 0;
+    }
+
+    int texWidth = allocator.GetSize().x_;
+    int texHeight = allocator.GetSize().y_;
+
     // Create the image for rendering the fonts
     // Create the image for rendering the fonts
     SharedPtr<Image> image(new Image(context_));
     SharedPtr<Image> image(new Image(context_));
     image->SetSize(texWidth, texHeight, 1);
     image->SetSize(texWidth, texHeight, 1);
@@ -403,113 +410,87 @@ const FontFace* Font::GetFaceTTF(int pointSize)
     FT_Done_Face(face);
     FT_Done_Face(face);
     
     
     // Create the texture and load the image into it
     // Create the texture and load the image into it
-    SharedPtr<Texture2D> texture(new Texture2D(context_));
+    Texture2D* texture = new Texture2D(context_);
     texture->SetNumLevels(1); // No mipmaps
     texture->SetNumLevels(1); // No mipmaps
     texture->SetAddressMode(COORD_U, ADDRESS_BORDER);
     texture->SetAddressMode(COORD_U, ADDRESS_BORDER);
     texture->SetAddressMode(COORD_V, ADDRESS_BORDER),
     texture->SetAddressMode(COORD_V, ADDRESS_BORDER),
     texture->SetBorderColor(Color(0.0f, 0.0f, 0.0f, 0.0f));
     texture->SetBorderColor(Color(0.0f, 0.0f, 0.0f, 0.0f));
     if (!texture->SetSize(texWidth, texHeight, Graphics::GetAlphaFormat()) || !texture->Load(image, true))
     if (!texture->SetSize(texWidth, texHeight, Graphics::GetAlphaFormat()) || !texture->Load(image, true))
+    {
+        delete texture;
         return 0;
         return 0;
+    }
     
     
     SetMemoryUse(GetMemoryUse() + texWidth * texHeight);
     SetMemoryUse(GetMemoryUse() + texWidth * texHeight);
-    newFace->texture_ = texture;
+    newFace->texture_ = texture;    // Pass the texture ownership to FontFace
     faces_[pointSize] = newFace;
     faces_[pointSize] = newFace;
     return newFace;
     return newFace;
 }
 }
 
 
-const FontFace* Font::GetFace( int pointSize )
+const FontFace* Font::GetFaceBitmap(int pointSize)
 {
 {
-    switch(fontType_)
+    SharedPtr<XMLFile> xmlReader(new XMLFile(context_));
+    MemoryBuffer memoryBuffer(fontData_, fontDataSize_);
+    if (!xmlReader->Load(memoryBuffer))
     {
     {
-    case FONT_TTF:
-        return GetFaceTTF(pointSize);
-    case FONT_IMAGE:
-        return GetFaceImage(pointSize);
-    default:
+        LOGERROR("Could not load XML file");
         return 0;
         return 0;
     }
     }
-}
-
-bool Font::LoadTTFont( Deserializer& source )
-{
-    faces_.Clear();
-    
-    fontDataSize_ = source.GetSize();
-    if (fontDataSize_)
-    {
-        fontData_ = new unsigned char[fontDataSize_];
-        if (source.Read(&fontData_[0], fontDataSize_) != fontDataSize_)
-            return false;
-    }
-    else
-    {
-        fontData_.Reset();
-        return false;
-    }
-    
-    SetMemoryUse(fontDataSize_);
-    return true;
-}
-
-bool Font::LoadImageFont(Deserializer& source)
-{
-    SharedPtr<XMLFile>  xmlReader(new XMLFile(context_));
-    if (!xmlReader->Load(source))
-    {
-        LOGERROR("Can not load XML file");
-        return false;
-    }
     
     
     XMLElement root = xmlReader->GetRoot("font");
     XMLElement root = xmlReader->GetRoot("font");
     if (root.IsNull())
     if (root.IsNull())
     {
     {
-        LOGERROR("Can not find Font element");
-        return false;
+        LOGERROR("Could not find Font element");
+        return 0;
     }
     }
 
 
     XMLElement pagesElem = root.GetChild("pages");
     XMLElement pagesElem = root.GetChild("pages");
     if (pagesElem.IsNull())
     if (pagesElem.IsNull())
     {
     {
-        LOGERROR("Can not find Pages element");
-        return false;
+        LOGERROR("Could not find Pages element");
+        return 0;
     }
     }
     
     
     /// \todo Support multiple pages
     /// \todo Support multiple pages
     XMLElement pageElem = pagesElem.GetChild("page");
     XMLElement pageElem = pagesElem.GetChild("page");
     if (pageElem.IsNull())
     if (pageElem.IsNull())
     {
     {
-        LOGERROR("Can not find Page element");
-        return false;
+        LOGERROR("Could not find Page element");
+        return 0;
     }
     }
     
     
-    fontFace_ = new FontFace();
+    SharedPtr<FontFace> newFace(new FontFace());
+    
     XMLElement commonElem = root.GetChild("common");
     XMLElement commonElem = root.GetChild("common");
-    fontFace_->rowHeight_ = commonElem.GetInt("lineHeight");
+    newFace->rowHeight_ = commonElem.GetInt("lineHeight");
     
     
+    // \todo Does different point size always use the same bitmap file?
     String textureFile = pageElem.GetAttribute("file");
     String textureFile = pageElem.GetAttribute("file");
+
     // Assume the font image is in the same directory as the XML description
     // Assume the font image is in the same directory as the XML description
-    textureFile = GetPath(source.GetName()) + textureFile;
-    ResourceCache* resourceCache = GetSubsystem<ResourceCache>();
+    textureFile = GetPath(pathName_) + textureFile;
     
     
     // Load texture manually to allow controlling the alpha channel mode
     // Load texture manually to allow controlling the alpha channel mode
+    ResourceCache* resourceCache = GetSubsystem<ResourceCache>();
     SharedPtr<File> fontFile = resourceCache->GetFile(textureFile);
     SharedPtr<File> fontFile = resourceCache->GetFile(textureFile);
     SharedPtr<Image> fontImage(new Image(context_));
     SharedPtr<Image> fontImage(new Image(context_));
     if (!fontFile || !fontImage->Load(*fontFile))
     if (!fontFile || !fontImage->Load(*fontFile))
     {
     {
         LOGERROR("Failed to load font image file");
         LOGERROR("Failed to load font image file");
-        return false;
+        return 0;
     }
     }
-    fontFace_->texture_ = new Texture2D(context_);
-    if (!fontFace_->texture_->Load(fontImage, true))
+    Texture2D* texture = new Texture2D(context_);
+    if (!texture->Load(fontImage, true))
     {
     {
         LOGERROR("Failed to create font texture");
         LOGERROR("Failed to create font texture");
-        fontFace_->texture_.Reset();
-        return false;
+        delete texture;
+        return 0;
     }
     }
+    newFace->texture_ = texture;
     
     
     XMLElement charsElem = root.GetChild("chars");
     XMLElement charsElem = root.GetChild("chars");
     int count = charsElem.GetInt("count");
     int count = charsElem.GetInt("count");
-    fontFace_->glyphs_.Reserve(count);
+    newFace->glyphs_.Reserve(count);
     
     
     XMLElement charElem = charsElem.GetChild("char");
     XMLElement charElem = charsElem.GetChild("char");
     while(!charElem.IsNull())
     while(!charElem.IsNull())
@@ -523,44 +504,39 @@ bool Font::LoadImageFont(Deserializer& source)
         glyph.offsetX_ = charElem.GetInt("xoffset");
         glyph.offsetX_ = charElem.GetInt("xoffset");
         glyph.offsetY_ = charElem.GetInt("yoffset");
         glyph.offsetY_ = charElem.GetInt("yoffset");
         glyph.advanceX_ = charElem.GetInt("xadvance");
         glyph.advanceX_ = charElem.GetInt("xadvance");
-        unsigned index = fontFace_->glyphs_.Size();
-        fontFace_->glyphs_.Push(glyph);
-        fontFace_->glyphMapping_[id] = index;
+        unsigned index = newFace->glyphs_.Size();
+        newFace->glyphs_.Push(glyph);
+        newFace->glyphMapping_[id] = index;
+        
         charElem = charElem.GetNext("char");
         charElem = charElem.GetNext("char");
     }
     }
     
     
     XMLElement kerningsElem = root.GetChild("kernings");
     XMLElement kerningsElem = root.GetChild("kernings");
-    if(kerningsElem.IsNull())
-    {
-        fontFace_->hasKerning_ = false;
-        return true;
-    }
-    
-    XMLElement kerningElem = kerningsElem.GetChild("kerning");
-    while (!kerningElem.IsNull())
+    if (kerningsElem.IsNull())
+        newFace->hasKerning_ = false;
+    else
     {
     {
-        int first = kerningElem.GetInt("first");
-        int second = kerningElem.GetInt("second");
-        int amount = kerningElem.GetInt("amount");
-        
-        HashMap<unsigned, unsigned>::Iterator i = fontFace_->glyphMapping_.Find(first);
-        if(i == fontFace_->glyphMapping_.End())
-            continue;
-        
-        FontGlyph& fg = fontFace_->glyphs_[i->second_];
-        fg.kerning_[second] = amount;
-        
-        kerningElem = kerningElem.GetNext("kerning");
+        XMLElement kerningElem = kerningsElem.GetChild("kerning");
+        while (!kerningElem.IsNull())
+        {
+            int first = kerningElem.GetInt("first");
+            HashMap<unsigned, unsigned>::Iterator i = newFace->glyphMapping_.Find(first);
+            if (i != newFace->glyphMapping_.End())
+            {
+                int second = kerningElem.GetInt("second");
+                int amount = kerningElem.GetInt("amount");
+                
+                FontGlyph& fg = newFace->glyphs_[i->second_];
+                fg.kerning_[second] = amount;
+            }
+            
+            kerningElem = kerningElem.GetNext("kerning");
+        }
     }
     }
     
     
     SetMemoryUse(GetMemoryUse() + fontImage->GetWidth() * fontImage->GetHeight() * fontImage->GetComponents());
     SetMemoryUse(GetMemoryUse() + fontImage->GetWidth() * fontImage->GetHeight() * fontImage->GetComponents());
-    
-    return true;
-}
-
-const FontFace* Font::GetFaceImage(int pointSize)
-{
-    return fontFace_;
+    faces_[pointSize] = newFace;
+    return newFace;
 }
 }
 
 
 }
 }

+ 15 - 17
Engine/UI/Font.h

@@ -29,7 +29,7 @@ namespace Urho3D
 {
 {
 
 
 class Graphics;
 class Graphics;
-class Texture2D;
+class Texture;
 
 
 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;
@@ -59,9 +59,10 @@ struct FontGlyph
 /// %Font file type.
 /// %Font file type.
 enum FONT_TYPE
 enum FONT_TYPE
 {
 {
+    FONT_NONE = 0,
     FONT_TTF,
     FONT_TTF,
-    FONT_IMAGE,
-    FONT_NONE
+    FONT_BITMAP,
+    MAX_FONT_TYPES
 };
 };
 
 
 /// %Font face description.
 /// %Font face description.
@@ -79,7 +80,7 @@ public:
     short GetKerning(unsigned c, unsigned d) const;
     short GetKerning(unsigned c, unsigned d) const;
     
     
     /// Texture.
     /// Texture.
-    SharedPtr<Texture2D> texture_;
+    SharedPtr<Texture> texture_;
     /// Glyphs.
     /// Glyphs.
     Vector<FontGlyph> glyphs_;
     Vector<FontGlyph> glyphs_;
     /// Point size.
     /// Point size.
@@ -92,8 +93,6 @@ public:
     bool hasKerning_;
     bool hasKerning_;
 };
 };
 
 
-class Texture2D;
-
 /// %Font resource.
 /// %Font resource.
 class Font : public Resource
 class Font : public Resource
 {
 {
@@ -106,30 +105,29 @@ public:
     virtual ~Font();
     virtual ~Font();
     /// Register object factory.
     /// Register object factory.
     static void RegisterObject(Context* context);
     static void RegisterObject(Context* context);
-    
     /// Load resource. Return true if successful.
     /// Load resource. Return true if successful.
     virtual bool Load(Deserializer& source);
     virtual bool Load(Deserializer& source);
-    
+    /// Return font face. Pack and render to a texture if not rendered yet. Return null on error.
     const FontFace* GetFace(int pointSize);
     const FontFace* GetFace(int pointSize);
+    /// Return a fully qualified path name of font file.
+    const String& GetPathName() const { return pathName_; }
     
     
 private:
 private:
-    bool LoadTTFont(Deserializer& source);
-    bool LoadImageFont(Deserializer& source);
-
-    /// Return font face. Pack and render to a texture if not rendered yet. Return null on error.
+    /// Return True-type font face. Called internally. Return null on error.
     const FontFace* GetFaceTTF(int pointSize);
     const FontFace* GetFaceTTF(int pointSize);
-    const FontFace* GetFaceImage(int pointSize);
-private:
+    /// Return bitmap font face. Called internally. Return null on error.
+    const FontFace* GetFaceBitmap(int pointSize);
+    
     /// Created faces.
     /// Created faces.
     HashMap<int, SharedPtr<FontFace> > faces_;
     HashMap<int, SharedPtr<FontFace> > faces_;
     /// Font data.
     /// Font data.
     SharedArrayPtr<unsigned char> fontData_;
     SharedArrayPtr<unsigned char> fontData_;
     /// Size of font data.
     /// Size of font data.
     unsigned fontDataSize_;
     unsigned fontDataSize_;
-    /// Font type
+    /// Font type.
     FONT_TYPE fontType_;
     FONT_TYPE fontType_;
-    /// ImageFont Data
-    SharedPtr<FontFace> fontFace_;
+    /// FQPN of font file.
+    String pathName_;
 };
 };
 
 
 }
 }