Bläddra i källkod

Adapted Aster Jian's mutable glyph mechanism. Globally switchable on from the UI subsystem. Configurable font texture max size in UI subsystem.

Lasse Öörni 12 år sedan
förälder
incheckning
267523a738

+ 6 - 0
Docs/LuaScriptAPI.dox

@@ -4886,8 +4886,10 @@ Methods:
 - bool SaveLayout(Serializer& dest, UIElement* element)
 - void SetClipBoardText(const String text)
 - void SetDoubleClickInterval(float interval)
+- void SetMaxFontTextureSize(int size)
 - void SetNonFocusedMouseWheel(bool nonFocusedMouseWheel)
 - void SetUseSystemClipBoard(bool enable)
+- void SetUseMutableGlyphs(bool enable)
 - UIElement* GetRoot() const
 - UIElement* GetRootModalElement() const
 - Cursor* GetCursor() const
@@ -4898,8 +4900,10 @@ Methods:
 - IntVector2 GetCursorPosition() const
 - const String& GetClipBoardText() const
 - float GetDoubleClickInterval() const
+- int GetMaxFontTextureSize() const
 - bool IsNonFocusedMouseWheel() const
 - bool GetUseSystemClipBoard() const
+- bool GetUseMutableGlyphs() const
 - bool HasModalElement() const
 
 Properties:
@@ -4912,8 +4916,10 @@ Properties:
 - IntVector2 cursorPosition (readonly)
 - String& clipBoardText
 - float doubleClickInterval
+- int maxFontTextureSize
 - bool nonFocusedMouseWheel
 - bool useSystemClipBoard
+- bool useMutableGlyphs
 - bool modalElement (readonly)
 
 ### UIElement : Serializable

+ 2 - 0
Docs/ScriptAPI.dox

@@ -6468,8 +6468,10 @@ Properties:
 - UIElement@ modalRoot (readonly)
 - String clipBoardText
 - float doubleClickInterval
+- int maxFontTextureSize
 - bool nonFocusedMouseWheel
 - bool useSystemClipBoard
+- bool useMutableGlyphs
 
 
 ### Controls

+ 4 - 0
Source/Engine/Script/UIAPI.cpp

@@ -603,10 +603,14 @@ static void RegisterUI(asIScriptEngine* engine)
     engine->RegisterObjectMethod("UI", "const String& get_clipBoardText() const", asMETHOD(UI, GetClipBoardText), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "void set_doubleClickInterval(float)", asMETHOD(UI, SetDoubleClickInterval), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "float get_doubleClickInterval() const", asMETHOD(UI, GetDoubleClickInterval), asCALL_THISCALL);
+    engine->RegisterObjectMethod("UI", "void set_maxFontTextureSize(int)", asMETHOD(UI, SetMaxFontTextureSize), asCALL_THISCALL);
+    engine->RegisterObjectMethod("UI", "int get_maxFontTextureSize() const", asMETHOD(UI, GetMaxFontTextureSize), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "void set_nonFocusedMouseWheel(bool)", asMETHOD(UI, SetNonFocusedMouseWheel), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "bool get_nonFocusedMouseWheel() const", asMETHOD(UI, IsNonFocusedMouseWheel), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "void set_useSystemClipBoard(bool)", asMETHOD(UI, SetUseSystemClipBoard), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "bool get_useSystemClipBoard() const", asMETHOD(UI, GetUseSystemClipBoard), asCALL_THISCALL);
+    engine->RegisterObjectMethod("UI", "void set_useMutableGlyphs(bool)", asMETHOD(UI, SetUseMutableGlyphs), asCALL_THISCALL);
+    engine->RegisterObjectMethod("UI", "bool get_useMutableGlyphs() const", asMETHOD(UI, GetUseMutableGlyphs), asCALL_THISCALL);
     engine->RegisterGlobalFunction("UI@+ get_ui()", asFUNCTION(GetUI), asCALL_CDECL);
 }
 

+ 117 - 37
Source/Engine/UI/Font.cpp

@@ -32,6 +32,7 @@
 #include "ResourceCache.h"
 #include "StringUtils.h"
 #include "Texture2D.h"
+#include "UI.h"
 #include "XMLFile.h"
 
 #include <ft2build.h>
@@ -73,6 +74,11 @@ private:
     FT_Library library_;
 };
 
+MutableGlyph::MutableGlyph() :
+    glyphIndex_(M_MAX_UNSIGNED)
+{
+}
+
 FontGlyph::FontGlyph() :
     used_(false),
     page_(M_MAX_UNSIGNED)
@@ -103,6 +109,10 @@ FontFace::~FontFace()
             totalTextureSize += textures_[i]->GetWidth() * textures_[i]->GetHeight();
         font_->SetMemoryUse(font_->GetMemoryUse() - totalTextureSize);
     }
+    
+    for (List<MutableGlyph*>::Iterator i = mutableGlyphs_.Begin(); i != mutableGlyphs_.End(); ++i)
+        delete *i;
+    mutableGlyphs_.Clear();
 }
 
 const FontGlyph* FontFace::GetGlyph(unsigned c)
@@ -111,11 +121,18 @@ const FontGlyph* FontFace::GetGlyph(unsigned c)
     if (i != glyphMapping_.End())
     {
         FontGlyph& glyph = glyphs_[i->second_];
+        // Render glyph if not yet resident in a page texture
         if (glyph.page_ == M_MAX_UNSIGNED)
+            RenderGlyph(i->second_);
+        // If mutable glyphs in use, move to the front of the list
+        if (mutableGlyphs_.Size() && glyph.iterator_ != mutableGlyphs_.End())
         {
-            if (!RenderGlyph(i->second_))
-                return 0;
+            MutableGlyph* mutableGlyph = *glyph.iterator_;
+            mutableGlyphs_.Erase(glyph.iterator_);
+            mutableGlyphs_.PushFront(mutableGlyph);
+            glyph.iterator_ = mutableGlyphs_.Begin();
         }
+        
         glyph.used_ = true;
         return &glyph;
     }
@@ -161,13 +178,11 @@ bool FontFace::IsDataLost() const
     return false;
 }
 
-bool FontFace::RenderAllGlyphs()
+bool FontFace::RenderAllGlyphs(int maxWidth, int maxHeight)
 {
-    // RenderAllGlyphs() is valid only when no textures have yet been allocated
-    if (!font_ || !face_ || textures_.Size())
-        return false;
+    assert(font_ && face_ && textures_.Empty());
     
-    allocator_ = AreaAllocator(FONT_TEXTURE_MIN_SIZE, FONT_TEXTURE_MIN_SIZE, FONT_TEXTURE_MAX_SIZE, FONT_TEXTURE_MAX_SIZE);
+    allocator_ = AreaAllocator(FONT_TEXTURE_MIN_SIZE, FONT_TEXTURE_MIN_SIZE, maxWidth, maxHeight);
     
     for (unsigned i = 0; i < glyphs_.Size(); ++i)
     {
@@ -224,10 +239,9 @@ bool FontFace::RenderAllGlyphs()
     return true;
 }
 
-bool FontFace::RenderGlyph(unsigned index)
+void FontFace::RenderGlyph(unsigned index)
 {
-    if (!font_ || !face_ || !textures_.Size())
-        return false;
+    assert(font_ && face_);
     
     FontGlyph& glyph = glyphs_[index];
     
@@ -237,31 +251,57 @@ bool FontFace::RenderGlyph(unsigned index)
         glyph.x_ = 0;
         glyph.y_ = 0;
         glyph.page_ = textures_.Size() - 1;
-        return true;
+        return;
     }
     
-    // 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))
+    if (!mutableGlyphs_.Size())
     {
-        SetupNextTexture();
-        // This always succeeds, as it is the first allocation of an empty page
-        allocator_.Allocate(glyph.width_ + 1, glyph.height_ + 1, x, y);
+        // Not using mutable glyphs: 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(textures_[0]->GetWidth(), textures_[0]->GetHeight());
+            // 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());
     }
-    
-    glyph.x_ = x;
-    glyph.y_ = y;
-    glyph.page_ = textures_.Size() - 1;
-    
-    if (!bitmap_ || (int)bitmapSize_ < glyph.width_ * glyph.height_)
+    else
     {
-        bitmapSize_ = glyph.width_ * glyph.height_;
-        bitmap_ = new unsigned char[bitmapSize_];
+        // Using mutable glyphs: overwrite the least recently used glyph
+        List<MutableGlyph*>::Iterator it = --mutableGlyphs_.End();
+        MutableGlyph* mutableGlyph = *it;
+        if (mutableGlyph->glyphIndex_ != M_MAX_UNSIGNED)
+            glyphs_[mutableGlyph->glyphIndex_].page_ = M_MAX_UNSIGNED;
+        glyph.x_ = mutableGlyph->x_;
+        glyph.y_ = mutableGlyph->y_;
+        glyph.page_ = 0;
+        glyph.iterator_ = it;
+        mutableGlyph->glyphIndex_ = index;
+        
+        if (!bitmap_)
+        {
+            bitmapSize_ = cellWidth_ * cellHeight_;
+            bitmap_ = new unsigned char[bitmapSize_];
+        }
+        
+        // Clear the cell bitmap before rendering to ensure padding
+        memset(bitmap_.Get(), 0, cellWidth_ * cellHeight_);
+        RenderGlyphBitmap(index, bitmap_.Get(), cellWidth_);
+        textures_[0]->SetData(0, glyph.x_, glyph.y_, cellWidth_, cellHeight_, bitmap_.Get());
     }
-    
-    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)
@@ -299,17 +339,45 @@ void FontFace::RenderGlyphBitmap(unsigned index, unsigned char* dest, unsigned p
     }
 }
 
-void FontFace::SetupNextTexture()
+void FontFace::SetupNextTexture(int width, int height)
 {
     // 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);
+    allocator_ = AreaAllocator(width, height);
 
     SharedPtr<Texture2D> texture = font_->CreateFaceTexture();
-    texture->SetSize(FONT_TEXTURE_MAX_SIZE, FONT_TEXTURE_MAX_SIZE, Graphics::GetAlphaFormat());
+    texture->SetSize(width, height, Graphics::GetAlphaFormat());
+    SharedArrayPtr<unsigned char> emptyBitmap(new unsigned char[width * height]);
+    memset(emptyBitmap.Get(), 0, width * height);
+    texture->SetData(0, 0, 0, width, height, emptyBitmap.Get());
+    
     textures_.Push(texture);
-    font_->SetMemoryUse(font_->GetMemoryUse() + FONT_TEXTURE_MAX_SIZE * FONT_TEXTURE_MAX_SIZE);
+    font_->SetMemoryUse(font_->GetMemoryUse() + width * height);
     
-    LOGDEBUG(ToString("Font face %s (%dpt) is now using %d dynamic page textures", GetFileName(font_->GetName()).CString(), pointSize_, textures_.Size()));
+    LOGDEBUG(ToString("Font face %s (%dpt) is using %d dynamic page textures of size %dx%d", GetFileName(font_->GetName()).CString(),
+        pointSize_, textures_.Size(), width, height));
+}
+
+void FontFace::SetupMutableGlyphs(int textureWidth, int textureHeight, int maxWidth, int maxHeight)
+{
+    assert(mutableGlyphs_.Empty());
+    
+    SetupNextTexture(textureWidth, textureHeight);
+    
+    cellWidth_ = maxWidth + 1;
+    cellHeight_ = maxHeight + 1;
+    
+    // Allocate as many mutable glyphs as possible
+    int x, y;
+    while (allocator_.Allocate(cellWidth_, cellHeight_, x, y))
+    {
+        MutableGlyph* glyph = new MutableGlyph();
+        glyph->x_ = x;
+        glyph->y_ = y;
+        mutableGlyphs_.Push(glyph);
+    }
+    
+    LOGDEBUG(ToString("Font face %s (%dpt) is using %d mutable glyphs", GetFileName(font_->GetName()).CString(), pointSize_,
+        mutableGlyphs_.Size()));
 }
 
 Font::Font(Context* context) :
@@ -536,6 +604,9 @@ FontFace* Font::GetFaceTTF(int pointSize)
     // Ensure the FreeType library is kept alive as long as TTF font resources exist
     freeType_ = freeType;
     
+    UI* ui = GetSubsystem<UI>();
+    int maxTextureSize = ui->GetMaxFontTextureSize();
+    
     FT_Face face;
     FT_Error error;
     FT_Library library = freeType->GetLibrary();
@@ -585,6 +656,7 @@ FontFace* Font::GetFaceTTF(int pointSize)
     LOGDEBUG(ToString("Font face %s (%dpt) has %d glyphs", GetFileName(GetName()).CString(), pointSize, numGlyphs));
     
     // Load each of the glyphs to see the sizes & store other information
+    int maxWidth = 0;
     int maxHeight = 0;
     FT_Pos ascender = face->size->metrics.ascender;
     
@@ -604,6 +676,7 @@ FontFace* Font::GetFaceTTF(int pointSize)
             newGlyph.offsetY_ = (short)((ascender - slot->metrics.horiBearingY) >> 6);
             newGlyph.advanceX_ = (short)((slot->metrics.horiAdvance) >> 6);
             
+            maxWidth = Max(maxWidth, newGlyph.width_);
             maxHeight = Max(maxHeight, newGlyph.height_);
         }
         else
@@ -640,13 +713,18 @@ FontFace* Font::GetFaceTTF(int pointSize)
     
     // 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())
+    if (newFace->RenderAllGlyphs(maxTextureSize, maxTextureSize))
     {
         FT_Done_Face(face);
         newFace->face_ = 0;
     }
     else
-        newFace->SetupNextTexture();
+    {
+        if (ui->GetUseMutableGlyphs())
+            newFace->SetupMutableGlyphs(maxTextureSize, maxTextureSize, maxWidth, maxHeight);
+        else
+            newFace->SetupNextTexture(maxTextureSize, maxTextureSize);
+    }
     
     faces_[pointSize] = newFace;
     return newFace;
@@ -791,6 +869,8 @@ SharedPtr<FontFace> Font::Pack(FontFace* 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));
     
+    int maxTextureSize = GetSubsystem<UI>()->GetMaxFontTextureSize();
+    
     // Clone properties
     packedFontFace->pointSize_ = fontFace->pointSize_;
     packedFontFace->rowHeight_ = fontFace->rowHeight_;
@@ -812,7 +892,7 @@ SharedPtr<FontFace> Font::Pack(FontFace* fontFace)
     HashMap<unsigned, unsigned>::ConstIterator i;
     while (startIter != fontFace->glyphMapping_.End())
     {
-        AreaAllocator allocator(FONT_TEXTURE_MIN_SIZE, FONT_TEXTURE_MIN_SIZE, FONT_TEXTURE_MAX_SIZE, FONT_TEXTURE_MAX_SIZE);
+        AreaAllocator allocator(FONT_TEXTURE_MIN_SIZE, FONT_TEXTURE_MIN_SIZE, maxTextureSize, maxTextureSize);
         for (i = startIter; i != fontFace->glyphMapping_.End(); ++i)
         {
             FontGlyph glyph = fontFace->glyphs_[i->second_];

+ 32 - 8
Source/Engine/UI/Font.h

@@ -24,6 +24,7 @@
 
 #include "AreaAllocator.h"
 #include "ArrayPtr.h"
+#include "List.h"
 #include "Resource.h"
 
 namespace Urho3D
@@ -36,9 +37,22 @@ class Image;
 class Texture2D;
 
 static const int FONT_TEXTURE_MIN_SIZE = 128;
-static const int FONT_TEXTURE_MAX_SIZE = 2048;
 static const int FONT_DPI = 96;
 
+/// Mutable font glyph description.
+struct MutableGlyph
+{
+    /// Construct.
+    MutableGlyph();
+    
+    /// The actual glyph index that currently occupies this mutable slot. M_MAX_UNSIGNED if none.
+    unsigned glyphIndex_;
+    /// X position in texture.
+    short x_;
+    /// Y position in texture.
+    short y_;
+};
+
 /// %Font glyph description.
 struct FontGlyph
 {
@@ -65,6 +79,8 @@ struct FontGlyph
     bool used_;
     /// Kerning information.
     HashMap<unsigned, unsigned> kerning_;
+    /// Mutable glyph list iterator.
+    List<MutableGlyph*>::Iterator iterator_;
 };
 
 /// %Font file type.
@@ -101,14 +117,16 @@ public:
     const Vector<SharedPtr<Texture2D> >& GetTextures() const { return 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.
+    /// Render all glyphs of the face into a single texture. Return true if could fit them. Called by Font.
+    bool RenderAllGlyphs(int maxWidth, int maxHeight);
+    /// Render one glyph on demand into the current texture.
+    void RenderGlyph(unsigned index);
+    /// Render a glyph bitmap into a memory buffer.
     void RenderGlyphBitmap(unsigned index, unsigned char* dest, unsigned pitch);
-    /// Setup next texture for dynamic glyph rendering. Called internally.
-    void SetupNextTexture();
+    /// Setup next texture for dynamic glyph rendering.
+    void SetupNextTexture(int width, int height);
+    /// Setup mutable glyph rendering, in which glyphs form a regular-sized grid.
+    void SetupMutableGlyphs(int textureWidth, int textureHeight, int maxGlyphWidth, int maxGlyphHeight);
     
     /// Parent font.
     Font* font_;
@@ -120,10 +138,16 @@ private:
     HashMap<unsigned, unsigned> glyphMapping_;
     /// Glyph texture pages.
     Vector<SharedPtr<Texture2D> > textures_;
+    /// Mutable glyph list.
+    List<MutableGlyph*> mutableGlyphs_;
     /// Point size.
     int pointSize_;
     /// Row height.
     int rowHeight_;
+    /// Mutable glyph cell width, including 1 pixel padding.
+    int cellWidth_;
+    /// Mutable glyph cell height, including 1 pixel padding.
+    int cellHeight_;
     /// Kerning flag.
     bool hasKerning_;
     /// Glyph area allocator.

+ 3 - 1
Source/Engine/UI/Text.cpp

@@ -181,7 +181,9 @@ void Text::GetBatches(PODVector<UIBatch>& batches, PODVector<float>& vertexData,
                     if (!p)
                         continue;
 
-                    pageGlyphLocations[p->page_].Push(GlyphLocation(x, y, p));
+                    // Validate page because of possible glyph unallocations (should not happen, though)
+                    if (p->page_ < textures.Size())
+                        pageGlyphLocations[p->page_].Push(GlyphLocation(x, y, p));
 
                     x += p->advanceX_;
                     if (i < printText_.Size() - 1)

+ 14 - 0
Source/Engine/UI/UI.cpp

@@ -66,6 +66,7 @@ const ShortStringHash VAR_ORIGINAL_CHILD_INDEX("OriginalChildIndex");
 const ShortStringHash VAR_PARENT_CHANGED("ParentChanged");
 
 const float DEFAULT_DOUBLECLICK_INTERVAL = 0.5f;
+const int DEFAULT_FONT_TEXTURE_MAX_SIZE = 2048;
 
 const char* UI_CATEGORY = "UI";
 
@@ -75,6 +76,7 @@ UI::UI(Context* context) :
     rootModalElement_(new UIElement(context)),
     mouseButtons_(0),
     qualifiers_(0),
+    maxFontTextureSize_(DEFAULT_FONT_TEXTURE_MAX_SIZE),
     doubleClickInterval_(DEFAULT_DOUBLECLICK_INTERVAL),
     initialized_(false),
     usingTouchInput_(false),
@@ -84,6 +86,7 @@ UI::UI(Context* context) :
     nonFocusedMouseWheel_(true),     // Default Mac OS X and Linux behaviour
     #endif
     useSystemClipBoard_(false),
+    useMutableGlyphs_(false),
     nonModalBatchSize_(0)
 {
     rootElement_->SetTraversalMode(TM_DEPTH_FIRST);
@@ -466,6 +469,12 @@ void UI::SetDoubleClickInterval(float interval)
     doubleClickInterval_ = Max(interval, 0.0f);
 }
 
+void UI::SetMaxFontTextureSize(int size)
+{
+    if (IsPowerOfTwo(size) && size >= FONT_TEXTURE_MIN_SIZE)
+        maxFontTextureSize_ = size;
+}
+
 void UI::SetNonFocusedMouseWheel(bool nonFocusedMouseWheel)
 {
     nonFocusedMouseWheel_ = nonFocusedMouseWheel;
@@ -476,6 +485,11 @@ void UI::SetUseSystemClipBoard(bool enable)
     useSystemClipBoard_ = enable;
 }
 
+void UI::SetUseMutableGlyphs(bool enable)
+{
+    useMutableGlyphs_ = enable;
+}
+
 IntVector2 UI::GetCursorPosition() const
 {
     return cursor_ ? cursor_->GetPosition() : GetSubsystem<Input>()->GetMousePosition();

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

@@ -79,11 +79,15 @@ public:
     void SetClipBoardText(const String& text);
     /// Set UI element double click interval in seconds.
     void SetDoubleClickInterval(float interval);
+    /// Set maximum font face texture size. Must be a power of two. Default is 2048.
+    void SetMaxFontTextureSize(int size);
     /// Set whether mouse wheel can control also a non-focused element.
     void SetNonFocusedMouseWheel(bool nonFocusedMouseWheel);
     /// Set whether to use system clipboard. Default false.
     void SetUseSystemClipBoard(bool enable);
-
+    /// Set whether to use mutable (eraseable) glyphs to ensure a font face never expands to more than one texture. Default false.
+    void SetUseMutableGlyphs(bool enable);
+    
     /// Return root UI element.
     UIElement* GetRoot() const { return rootElement_; }
     /// Return root modal element.
@@ -104,10 +108,14 @@ public:
     const String& GetClipBoardText() const;
     /// Return UI element double click interval in seconds.
     float GetDoubleClickInterval() const { return doubleClickInterval_; }
+    /// Return font texture maximum size.
+    int GetMaxFontTextureSize() const { return maxFontTextureSize_; }
     /// Return whether mouse wheel can control also a non-focused element.
     bool IsNonFocusedMouseWheel() const { return nonFocusedMouseWheel_; }
     /// Return whether is using the system clipboard.
     bool GetUseSystemClipBoard() const { return useSystemClipBoard_; }
+    /// Return whether is using mutable (eraseable) glyphs for fonts.
+    bool GetUseMutableGlyphs() const { return useMutableGlyphs_; }
     /// Return true when UI has modal element(s).
     bool HasModalElement() const;
 
@@ -211,6 +219,8 @@ private:
     int mouseButtons_;
     /// Qualifier keys held down.
     int qualifiers_;
+    /// Font texture maximum size.
+    int maxFontTextureSize_;
     /// Initialized flag.
     bool initialized_;
     /// Touch used flag.
@@ -219,6 +229,8 @@ private:
     bool nonFocusedMouseWheel_;
     /// Flag for using operating system clipboard instead of internal.
     bool useSystemClipBoard_;
+    /// Flag for using mutable (eraseable) font glyphs.
+    bool useMutableGlyphs_;
     /// Non-modal batch size (used internally for rendering).
     unsigned nonModalBatchSize_;
     /// Timer used to trigger double click.

+ 10 - 4
Source/Extras/LuaScript/pkgs/UI/UI.pkg

@@ -11,29 +11,33 @@ class UI : public Object
     void Render();
     void DebugDraw(UIElement* element);
     bool SaveLayout(Serializer& dest, UIElement* element);
-    
+
     void SetClipBoardText(const String text);
 
     void SetDoubleClickInterval(float interval);
+    void SetMaxFontTextureSize(int size);
     void SetNonFocusedMouseWheel(bool nonFocusedMouseWheel);
     void SetUseSystemClipBoard(bool enable);
+    void SetUseMutableGlyphs(bool enable);
 
     UIElement* GetRoot() const;
     UIElement* GetRootModalElement() const;
     Cursor* GetCursor() const;
-    
+
     UIElement* GetElementAt(const IntVector2& position, bool enabledOnly = true);
     UIElement* GetElementAt(int x, int y, bool enabledOnly = true);
-    
+
     UIElement* GetFocusElement() const;
     UIElement* GetFrontElement() const;
     IntVector2 GetCursorPosition() const;
     const String& GetClipBoardText() const;
     float GetDoubleClickInterval() const;
+    int GetMaxFontTextureSize() const;
     bool IsNonFocusedMouseWheel() const;
     bool GetUseSystemClipBoard() const;
+    bool GetUseMutableGlyphs() const;
     bool HasModalElement() const;
-    
+
     tolua_readonly tolua_property__get_set UIElement* root;
     tolua_readonly tolua_property__get_set UIElement* rootModalElement;
     tolua_property__get_set Cursor* cursor;
@@ -42,7 +46,9 @@ class UI : public Object
     tolua_readonly tolua_property__get_set IntVector2 cursorPosition;
     tolua_property__get_set String& clipBoardText;
     tolua_property__get_set float doubleClickInterval;
+    tolua_property__get_set int maxFontTextureSize;
     tolua_property__is_set bool nonFocusedMouseWheel;
     tolua_property__get_set bool useSystemClipBoard;
+    tolua_property__get_set bool useMutableGlyphs;
     tolua_readonly tolua_property__has_set bool modalElement;
 };