Ver código fonte

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 anos atrás
pai
commit
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;
 };