Merge "Stop setting crazy Z value with controls (at the moment depth is ignored by...
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / rendering / atlas / text-atlas-renderer.cpp
index 41dfac9..2e94ce1 100644 (file)
 #include <dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.h>
 
 // EXTERNAL INCLUDES
-#include <dali/dali.h>
 #include <dali/integration-api/debug.h>
+#include <dali/public-api/common/stage.h>
+#include <dali/public-api/images/frame-buffer-image.h>
+#include <dali/public-api/render-tasks/render-task.h>
+#include <dali/public-api/render-tasks/render-task-list.h>
+#include <dali/devel-api/rendering/renderer.h>
+#include <dali/devel-api/rendering/geometry.h>
+#include <dali/devel-api/text-abstraction/font-client.h>
 
 // INTERNAL INCLUDES
-#include <dali-toolkit/internal/atlas-manager/atlas-manager.h>
+#include <dali-toolkit/public-api/controls/control-depth-index-ranges.h>
 #include <dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager.h>
-#include <dali-toolkit/internal/text/rendering/shaders/text-basic-shader.h>
-#include <dali-toolkit/internal/text/rendering/shaders/text-bgra-shader.h>
-
-#if defined(DEBUG_ENABLED)
-Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_ATLAS_RENDERER");
-#endif
+#include <dali-toolkit/internal/text/text-view.h>
 
 using namespace Dali;
 using namespace Dali::Toolkit;
@@ -38,76 +39,192 @@ using namespace Dali::Toolkit::Text;
 
 namespace
 {
-  const Vector2 DEFAULT_ATLAS_SIZE( 512.0f, 512.0f );
-  const Vector2 DEFAULT_BLOCK_SIZE( 16.0f, 16.0f );
-  const Vector2 PADDING( 4.0f, 4.0f ); // Allow for variation in font glyphs
-}
+#if defined(DEBUG_ENABLED)
+  Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_RENDERING");
+#endif
 
-struct AtlasRenderer::Impl
+const float ZERO( 0.0f );
+const float HALF( 0.5f );
+const float ONE( 1.0f );
+const float TWO( 2.0f );
+const uint32_t DEFAULT_ATLAS_WIDTH = 512u;
+const uint32_t DEFAULT_ATLAS_HEIGHT = 512u;
+}
+struct AtlasRenderer::Impl : public ConnectionTracker
 {
+  enum Style
+  {
+    STYLE_NORMAL,
+    STYLE_DROP_SHADOW
+  };
 
   struct MeshRecord
   {
+    MeshRecord()
+    : mColor( Color::BLACK ),
+      mAtlasId( 0 )
+    {
+    }
+
+    Vector4 mColor;
     uint32_t mAtlasId;
-    MeshData mMeshData;
+    AtlasManager::Mesh2D mMesh;
+    FrameBufferImage mBuffer;
   };
 
-  struct AtlasRecord
+  struct Extent
   {
-    uint32_t mImageId;
-    Text::GlyphIndex mIndex;
+    Extent()
+    : mBaseLine( 0.0f ),
+      mLeft( 0.0f ),
+      mRight( 0.0f ),
+      mUnderlinePosition( 0.0f ),
+      mUnderlineThickness( 0.0f ),
+      mMeshRecordIndex( 0 )
+    {
+    }
+
+    float mBaseLine;
+    float mLeft;
+    float mRight;
+    float mUnderlinePosition;
+    float mUnderlineThickness;
+    uint32_t mMeshRecordIndex;
   };
 
   struct MaxBlockSize
   {
+    MaxBlockSize()
+    : mFontId( 0 ),
+      mNeededBlockWidth( 0 ),
+      mNeededBlockHeight( 0 )
+    {
+    }
+
+    FontId mFontId;
+    uint32_t mNeededBlockWidth;
+    uint32_t mNeededBlockHeight;
+  };
+
+  struct CheckEntry
+  {
+    CheckEntry()
+    : mFontId( 0 ),
+      mIndex( 0 )
+    {
+    }
+
     FontId mFontId;
-    Vector2 mNeededBlockSize;
+    Text::GlyphIndex mIndex;
+  };
+
+  struct TextCacheEntry
+  {
+    TextCacheEntry()
+    : mFontId( 0 ),
+      mIndex( 0 ),
+      mImageId( 0 )
+    {
+    }
+
+    FontId mFontId;
+    Text::GlyphIndex mIndex;
+    uint32_t mImageId;
   };
 
   Impl()
-  : mSlotDelegate( this )
+  : mDepth( 0 )
   {
     mGlyphManager = AtlasGlyphManager::Get();
     mFontClient = TextAbstraction::FontClient::Get();
-    mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_SIZE, DEFAULT_BLOCK_SIZE );
-    mBasicShader = BasicShader::New();
-    mBGRAShader = BgraShader::New();
+
+    mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2;
+    mQuadVertexFormat[ "aTexCoord" ] = Property::VECTOR2;
+    mQuadIndexFormat[ "indices" ] = Property::INTEGER;
   }
 
-  void AddGlyphs( const std::vector<Vector2>& positions, const Vector<GlyphInfo>& glyphs )
+  void AddGlyphs( Text::ViewInterface& view,
+                  const std::vector<Vector2>& positions,
+                  const Vector<GlyphInfo>& glyphs,
+                  int depth )
   {
     AtlasManager::AtlasSlot slot;
     std::vector< MeshRecord > meshContainer;
+    Vector< Extent > extents;
+    TextCacheEntry textCacheEntry;
+    mDepth = depth;
+
+    const Vector2& actorSize( view.GetControlSize() );
+    Vector2 halfActorSize( actorSize * 0.5f );
+    const Vector4& textColor( view.GetTextColor() );
+    const Vector2& shadowOffset( view.GetShadowOffset() );
+    const Vector4& shadowColor( view.GetShadowColor() );
+    bool underlineEnabled( view.IsUnderlineEnabled() );
+    const Vector4& underlineColor( view.GetUnderlineColor() );
+    float underlineHeight( view.GetUnderlineHeight() );
+
+    float currentUnderlinePosition = ZERO;
+    float currentUnderlineThickness = underlineHeight;
+    uint32_t currentBlockSize = 0;
     FontId lastFontId = 0;
+    Style style = STYLE_NORMAL;
 
-    if (mImageIds.Size() )
+    if ( fabsf( shadowOffset.x ) > Math::MACHINE_EPSILON_1 || fabsf( shadowOffset.y ) > Math::MACHINE_EPSILON_1 )
     {
-      // Unreference any currently used glyphs
-      RemoveText();
+      style = STYLE_DROP_SHADOW;
     }
 
     CalculateBlocksSize( glyphs );
 
-    for ( uint32_t i = 0; i < glyphs.Size(); ++i )
+    // Avoid emptying mTextCache (& removing references) until after incremented references for the new text
+    Vector< TextCacheEntry > newTextCache;
+    const GlyphInfo* const glyphsBuffer = glyphs.Begin();
+
+    for( uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i )
     {
-      GlyphInfo glyph = glyphs[ i ];
+      const GlyphInfo& glyph = *( glyphsBuffer + i );
 
       // No operation for white space
       if ( glyph.width && glyph.height )
       {
-        Vector2 position = positions[ i ];
-        MeshData newMeshData;
-        mGlyphManager.Cached( glyph.fontId, glyph.index, slot );
-
-        if ( slot.mImageId )
+        // Are we still using the same fontId as previous
+        if ( glyph.fontId != lastFontId )
         {
-          // This glyph already exists so generate mesh data plugging in our supplied position
-          mGlyphManager.GenerateMeshData( slot.mImageId, position, newMeshData );
-          mImageIds.PushBack( slot.mImageId );
+          // We need to fetch fresh font underline metrics
+          FontMetrics fontMetrics;
+          mFontClient.GetFontMetrics( glyph.fontId, fontMetrics );
+          currentUnderlinePosition = ceil( fabsf( fontMetrics.underlinePosition ) );
+          const float descender = ceil( fabsf( fontMetrics.descender ) );
+
+          if ( underlineHeight == ZERO )
+          {
+            currentUnderlineThickness = fontMetrics.underlineThickness;
+
+            // Ensure underline will be at least a pixel high
+            if ( currentUnderlineThickness < ONE )
+            {
+              currentUnderlineThickness = ONE;
+            }
+            else
+            {
+              currentUnderlineThickness = ceil( currentUnderlineThickness );
+            }
+          }
+
+          // Clamp the underline position at the font descender and check for ( as EFL describes it ) a broken font
+          if ( currentUnderlinePosition > descender )
+          {
+            currentUnderlinePosition = descender;
+          }
+          if ( ZERO == currentUnderlinePosition )
+          {
+            // Move offset down by one ( EFL behavior )
+            currentUnderlinePosition = ONE;
+          }
         }
-        else
-        {
 
+        if ( !mGlyphManager.Cached( glyph.fontId, glyph.index, slot ) )
+        {
           // Select correct size for new atlas if needed....?
           if ( lastFontId != glyph.fontId )
           {
@@ -115,51 +232,98 @@ struct AtlasRenderer::Impl
             {
               if ( mBlockSizes[ j ].mFontId == glyph.fontId )
               {
-                mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_SIZE, mBlockSizes[ j ].mNeededBlockSize );
+                currentBlockSize = j;
+                mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_WIDTH,
+                                               DEFAULT_ATLAS_HEIGHT,
+                                               mBlockSizes[ j ].mNeededBlockWidth,
+                                               mBlockSizes[ j ].mNeededBlockHeight );
               }
             }
-            lastFontId = glyph.fontId;
           }
 
-          // Glyph doesn't currently exist in atlas so upload
+          // Create a new image for the glyph
           BufferImage bitmap = mFontClient.CreateBitmap( glyph.fontId, glyph.index );
+          if ( bitmap )
+          {
+            // Ensure that the next image will fit into the current block size
+            bool setSize = false;
+            if ( bitmap.GetWidth() > mBlockSizes[ currentBlockSize ].mNeededBlockWidth )
+            {
+              setSize = true;
+              mBlockSizes[ currentBlockSize ].mNeededBlockWidth = bitmap.GetWidth();
+            }
+            if ( bitmap.GetHeight() > mBlockSizes[ currentBlockSize ].mNeededBlockHeight )
+            {
+              setSize = true;
+              mBlockSizes[ currentBlockSize ].mNeededBlockHeight = bitmap.GetHeight();
+            }
 
-          // Locate a new slot for our glyph
-          mGlyphManager.Add( glyph, bitmap, slot );
+            if ( setSize )
+            {
+              mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_WIDTH,
+                                             DEFAULT_ATLAS_HEIGHT,
+                                             mBlockSizes[ currentBlockSize ].mNeededBlockWidth,
+                                             mBlockSizes[ currentBlockSize ].mNeededBlockHeight );
+            }
 
-          // Generate mesh data for this quad, plugging in our supplied position
-          if ( slot.mImageId )
-          {
-            mGlyphManager.GenerateMeshData( slot.mImageId, position, newMeshData );
-            mImageIds.PushBack( slot.mImageId );
+            // Locate a new slot for our glyph
+            mGlyphManager.Add( glyph, bitmap, slot );
           }
         }
+        else
+        {
+          // We have 2+ copies of the same glyph
+          mGlyphManager.AdjustReferenceCount( glyph.fontId, glyph.index, 1/*increment*/ );
+        }
+
+        // Move the origin (0,0) of the mesh to the center of the actor
+        Vector2 position = positions[ i ] - halfActorSize;
+
+        // Generate mesh data for this quad, plugging in our supplied position
+        AtlasManager::Mesh2D newMesh;
+        mGlyphManager.GenerateMeshData( slot.mImageId, position, newMesh );
+        textCacheEntry.mFontId = glyph.fontId;
+        textCacheEntry.mImageId = slot.mImageId;
+        textCacheEntry.mIndex = glyph.index;
+        newTextCache.PushBack( textCacheEntry );
+
         // Find an existing mesh data object to attach to ( or create a new one, if we can't find one using the same atlas)
-        StitchTextMesh( meshContainer, newMeshData, slot );
+        StitchTextMesh( meshContainer,
+                        newMesh,
+                        extents,
+                        textColor,
+                        position.y + glyph.yBearing,
+                        currentUnderlinePosition,
+                        currentUnderlineThickness,
+                        slot );
+        lastFontId = glyph.fontId;
       }
+    } // glyphs
+
+    // Now remove references for the old text
+    RemoveText();
+    mTextCache.Swap( newTextCache );
+
+    if ( underlineEnabled )
+    {
+      // Check to see if any of the text needs an underline
+      GenerateUnderlines( meshContainer, extents, underlineColor, textColor );
     }
 
     // For each MeshData object, create a mesh actor and add to the renderable actor
     if ( meshContainer.size() )
     {
-      for ( uint32_t i = 0; i < meshContainer.size(); ++i )
+      for ( std::vector< MeshRecord >::iterator mIt = meshContainer.begin(); mIt != meshContainer.end(); ++mIt )
       {
-        Mesh mesh = Mesh::New( meshContainer[ i ].mMeshData );
-        MeshActor actor = MeshActor::New( mesh );
-        actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
-        actor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );;
+        Actor actor = CreateMeshActor( *mIt, actorSize );
 
-        // Check to see what pixel format the shader should be
-        if ( mGlyphManager.GetPixelFormat( meshContainer[ i ].mAtlasId ) == Pixel::L8 )
+        // Create an effect if necessary
+        if ( style == STYLE_DROP_SHADOW )
         {
-          actor.SetShaderEffect( mBasicShader );
-        }
-        else
-        {
-          actor.SetShaderEffect( mBGRAShader );
+          actor.Add( GenerateShadow( *mIt, actorSize, shadowOffset, shadowColor ) );
         }
 
-        if ( i )
+        if( mActor )
         {
           mActor.Add( actor );
         }
@@ -168,42 +332,98 @@ struct AtlasRenderer::Impl
           mActor = actor;
         }
       }
-      mActor.OffStageSignal().Connect( mSlotDelegate, &AtlasRenderer::Impl::OffStageDisconnect );
     }
 #if defined(DEBUG_ENABLED)
     Toolkit::AtlasGlyphManager::Metrics metrics = mGlyphManager.GetMetrics();
-    DALI_LOG_INFO( gLogFilter, Debug::Concise, "TextAtlasRenderer::GlyphManager::GlyphCount: %i, AtlasCount: %i, TextureMemoryUse: %iK\n",
+    DALI_LOG_INFO( gLogFilter, Debug::General, "TextAtlasRenderer::GlyphManager::GlyphCount: %i, AtlasCount: %i, TextureMemoryUse: %iK\n",
                                                 metrics.mGlyphCount,
                                                 metrics.mAtlasMetrics.mAtlasCount,
                                                 metrics.mAtlasMetrics.mTextureMemoryUsed / 1024 );
+
+    DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%s\n", metrics.mVerboseGlyphCounts.c_str() );
+
     for ( uint32_t i = 0; i < metrics.mAtlasMetrics.mAtlasCount; ++i )
     {
-      DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Atlas [%i] %sPixels: %s Size: %ix%i, BlockSize: %ix%i, BlocksUsed: %i/%i\n",
+      DALI_LOG_INFO( gLogFilter, Debug::Verbose, "   Atlas [%i] %sPixels: %s Size: %ix%i, BlockSize: %ix%i, BlocksUsed: %i/%i\n",
                                                  i + 1, i > 8 ? "" : " ",
                                                  metrics.mAtlasMetrics.mAtlasMetrics[ i ].mPixelFormat == Pixel::L8 ? "L8  " : "BGRA",
-                                                 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mWidth,
-                                                 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mHeight,
-                                                 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mBlockWidth,
-                                                 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mBlockHeight,
+                                                 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mSize.mWidth,
+                                                 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mSize.mHeight,
+                                                 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mSize.mBlockWidth,
+                                                 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mSize.mBlockHeight,
                                                  metrics.mAtlasMetrics.mAtlasMetrics[ i ].mBlocksUsed,
                                                  metrics.mAtlasMetrics.mAtlasMetrics[ i ].mTotalBlocks );
     }
 #endif
   }
 
+  void RemoveText()
+  {
+    for ( Vector< TextCacheEntry >::Iterator oldTextIter = mTextCache.Begin(); oldTextIter != mTextCache.End(); ++oldTextIter )
+    {
+      mGlyphManager.AdjustReferenceCount( oldTextIter->mFontId, oldTextIter->mIndex, -1/*decrement*/ );
+    }
+    mTextCache.Resize( 0 );
+  }
+
+  Actor CreateMeshActor( const MeshRecord& meshRecord, const Vector2& actorSize )
+  {
+    PropertyBuffer quadVertices = PropertyBuffer::New( mQuadVertexFormat, meshRecord.mMesh.mVertices.Size() );
+    PropertyBuffer quadIndices = PropertyBuffer::New( mQuadIndexFormat, meshRecord.mMesh.mIndices.Size() );
+    quadVertices.SetData( const_cast< AtlasManager::Vertex2D* >( &meshRecord.mMesh.mVertices[ 0 ] ) );
+    quadIndices.SetData( const_cast< unsigned int* >( &meshRecord.mMesh.mIndices[ 0 ] ) );
+
+    Geometry quadGeometry = Geometry::New();
+    quadGeometry.AddVertexBuffer( quadVertices );
+    quadGeometry.SetIndexBuffer( quadIndices );
+
+    Material material = mGlyphManager.GetMaterial( meshRecord.mAtlasId );
+    Dali::Renderer renderer = Dali::Renderer::New( quadGeometry, material );
+    renderer.SetDepthIndex( CONTENT_DEPTH_INDEX + mDepth );
+    Actor actor = Actor::New();
+#if defined(DEBUG_ENABLED)
+    actor.SetName( "Text renderable actor" );
+#endif
+    actor.AddRenderer( renderer );
+    actor.SetParentOrigin( ParentOrigin::CENTER ); // Keep all of the origins aligned
+    actor.SetSize( actorSize );
+    actor.SetColor( meshRecord.mColor );
+    return actor;
+  }
+
   void StitchTextMesh( std::vector< MeshRecord >& meshContainer,
-                       MeshData& newMeshData,
+                       AtlasManager::Mesh2D& newMesh,
+                       Vector< Extent >& extents,
+                       const Vector4& color,
+                       float baseLine,
+                       float underlinePosition,
+                       float underlineThickness,
                        AtlasManager::AtlasSlot& slot )
   {
     if ( slot.mImageId )
     {
+      float left = newMesh.mVertices[ 0 ].mPosition.x;
+      float right = newMesh.mVertices[ 1 ].mPosition.x;
+
       // Check to see if there's a mesh data object that references the same atlas ?
-      for ( uint32_t i = 0; i < meshContainer.size(); ++i )
+      uint32_t index = 0;
+      for ( std::vector< MeshRecord >::iterator mIt = meshContainer.begin(),
+              mEndIt = meshContainer.end();
+            mIt != mEndIt;
+            ++mIt, ++index )
       {
-        if ( slot.mAtlasId == meshContainer[ i ].mAtlasId )
+        if ( slot.mAtlasId == mIt->mAtlasId && color == mIt->mColor )
         {
-          // Stitch the mesh to the existing mesh
-          mGlyphManager.StitchMesh( meshContainer[ i ].mMeshData, newMeshData );
+          // Stitch the mesh to the existing mesh and adjust any extents
+          mGlyphManager.StitchMesh( mIt->mMesh, newMesh );
+          AdjustExtents( extents,
+                         meshContainer,
+                         index,
+                         left,
+                         right,
+                         baseLine,
+                         underlinePosition,
+                         underlineThickness );
           return;
         }
       }
@@ -211,24 +431,71 @@ struct AtlasRenderer::Impl
       // No mesh data object currently exists that references this atlas, so create a new one
       MeshRecord meshRecord;
       meshRecord.mAtlasId = slot.mAtlasId;
-      meshRecord.mMeshData = newMeshData;
+      meshRecord.mMesh = newMesh;
+      meshRecord.mColor = color;
       meshContainer.push_back( meshRecord );
+
+      // Adjust extents for this new meshrecord
+      AdjustExtents( extents,
+                     meshContainer,
+                     meshContainer.size() - 1u,
+                     left,
+                     right,
+                     baseLine,
+                     underlinePosition,
+                     underlineThickness );
+
     }
   }
 
-  // Unreference any glyphs that were used with this actor
-  void OffStageDisconnect( Dali::Actor actor )
+  void AdjustExtents( Vector< Extent >& extents,
+                      std::vector< MeshRecord>& meshRecords,
+                      uint32_t index,
+                      float left,
+                      float right,
+                      float baseLine,
+                      float underlinePosition,
+                      float underlineThickness )
   {
-    RemoveText();
-  }
+    bool foundExtent = false;
+    for ( Vector< Extent >::Iterator eIt = extents.Begin(),
+            eEndIt = extents.End();
+          eIt != eEndIt;
+          ++eIt )
+    {
+      if ( Equals( baseLine, eIt->mBaseLine ) )
+      {
+        foundExtent = true;
+        if ( left < eIt->mLeft )
+        {
+          eIt->mLeft = left;
+        }
+        if ( right > eIt->mRight  )
+        {
+          eIt->mRight = right;
+        }
 
-  void RemoveText()
-  {
-    for ( uint32_t i = 0; i < mImageIds.Size(); ++i )
+        if ( underlinePosition > eIt->mUnderlinePosition )
+        {
+          eIt->mUnderlinePosition = underlinePosition;
+        }
+        if ( underlineThickness > eIt->mUnderlineThickness )
+        {
+          eIt->mUnderlineThickness = underlineThickness;
+        }
+      }
+    }
+    if ( !foundExtent )
     {
-      mGlyphManager.Remove( mImageIds[ i ] );
+      Extent extent;
+      extent.mLeft = left;
+      extent.mRight = right;
+      extent.mBaseLine = baseLine;
+      extent.mUnderlinePosition = underlinePosition;
+      extent.mUnderlineThickness = underlineThickness;
+      extent.mMeshRecordIndex = index;
+      extents.PushBack( extent );
     }
-    mImageIds.Resize( 0 );
   }
 
   void CalculateBlocksSize( const Vector<GlyphInfo>& glyphs )
@@ -236,71 +503,275 @@ struct AtlasRenderer::Impl
     MaxBlockSize maxBlockSize;
     for ( uint32_t i = 0; i < glyphs.Size(); ++i )
     {
-      // Get the fontId of this glyph and check to see if a max size exists?
       FontId fontId = glyphs[ i ].fontId;
-      float paddedWidth = glyphs[ i ].width + PADDING.x;
-      float paddedHeight = glyphs[ i ].height + PADDING.y;
       bool foundFont = false;
-
       for ( uint32_t j = 0; j < mBlockSizes.size(); ++j )
       {
         if ( mBlockSizes[ j ].mFontId == fontId )
         {
           foundFont = true;
-          if ( mBlockSizes[ j ].mNeededBlockSize.x < paddedWidth )
-          {
-            mBlockSizes[ j ].mNeededBlockSize.x = paddedWidth;
-          }
-          if ( mBlockSizes[ j ].mNeededBlockSize.y < paddedHeight )
-          {
-            mBlockSizes[ j ].mNeededBlockSize.y = paddedHeight;
-          }
         }
       }
-
       if ( !foundFont )
       {
-        maxBlockSize.mNeededBlockSize = Vector2( paddedWidth, paddedHeight );
+        FontMetrics fontMetrics;
+        mFontClient.GetFontMetrics( fontId, fontMetrics );
+        maxBlockSize.mNeededBlockWidth = static_cast< uint32_t >( fontMetrics.height );
+        maxBlockSize.mNeededBlockHeight = static_cast< uint32_t >( fontMetrics.height );
         maxBlockSize.mFontId = fontId;
         mBlockSizes.push_back( maxBlockSize );
       }
     }
   }
 
-  RenderableActor mActor;                             ///< The actor parent which renders the text
+  void GenerateUnderlines( std::vector< MeshRecord >& meshRecords,
+                           Vector< Extent >& extents,
+                           const Vector4& underlineColor,
+                           const Vector4& textColor )
+  {
+    AtlasManager::Mesh2D newMesh;
+    unsigned short faceIndex = 0;
+    for ( Vector< Extent >::ConstIterator eIt = extents.Begin(),
+            eEndIt = extents.End();
+          eIt != eEndIt;
+          ++eIt )
+    {
+      AtlasManager::Vertex2D vert;
+      uint32_t index = eIt->mMeshRecordIndex;
+      Vector2 uv = mGlyphManager.GetAtlasSize( meshRecords[ index ].mAtlasId );
+
+      // Make sure we don't hit texture edge for single pixel texture ( filled pixel is in top left of every atlas )
+      float u = HALF / uv.x;
+      float v = HALF / uv.y;
+      float thickness = eIt->mUnderlineThickness;
+      float baseLine = eIt->mBaseLine + eIt->mUnderlinePosition - ( thickness * HALF );
+      float tlx = eIt->mLeft;
+      float brx = eIt->mRight;
+
+      vert.mPosition.x = tlx;
+      vert.mPosition.y = baseLine;
+      vert.mTexCoords.x = ZERO;
+      vert.mTexCoords.y = ZERO;
+      newMesh.mVertices.PushBack( vert );
+
+      vert.mPosition.x = brx;
+      vert.mPosition.y = baseLine;
+      vert.mTexCoords.x = u;
+      newMesh.mVertices.PushBack( vert );
+
+      vert.mPosition.x = tlx;
+      vert.mPosition.y = baseLine + thickness;
+      vert.mTexCoords.x = ZERO;
+      vert.mTexCoords.y = v;
+      newMesh.mVertices.PushBack( vert );
+
+      vert.mPosition.x = brx;
+      vert.mPosition.y = baseLine + thickness;
+      vert.mTexCoords.x = u;
+      newMesh.mVertices.PushBack( vert );
+
+      // Six indices in counter clockwise winding
+      newMesh.mIndices.PushBack( faceIndex + 1u );
+      newMesh.mIndices.PushBack( faceIndex );
+      newMesh.mIndices.PushBack( faceIndex + 2u );
+      newMesh.mIndices.PushBack( faceIndex + 2u );
+      newMesh.mIndices.PushBack( faceIndex + 3u );
+      newMesh.mIndices.PushBack( faceIndex + 1u );
+      faceIndex += 4;
+
+      if ( underlineColor == textColor )
+      {
+        mGlyphManager.StitchMesh( meshRecords[ index ].mMesh, newMesh );
+      }
+      else
+      {
+        MeshRecord record;
+        record.mMesh = newMesh;
+        record.mAtlasId = meshRecords[ index ].mAtlasId;
+        record.mColor = underlineColor;
+        meshRecords.push_back( record );
+      }
+    }
+  }
+
+  Actor GenerateShadow( MeshRecord& meshRecord,
+                        const Vector2& actorSize,
+                        const Vector2& shadowOffset,
+                        const Vector4& shadowColor )
+  {
+    // Scan vertex buffer to determine width and height of effect buffer needed
+    const Vector< AtlasManager::Vertex2D >& verts = meshRecord.mMesh.mVertices;
+    float tlx = verts[ 0 ].mPosition.x;
+    float tly = verts[ 0 ].mPosition.y;
+    float brx = ZERO;
+    float bry = ZERO;
+
+    for ( uint32_t i = 0; i < verts.Size(); ++i )
+    {
+      if ( verts[ i ].mPosition.x < tlx )
+      {
+        tlx = verts[ i ].mPosition.x;
+      }
+      if ( verts[ i ].mPosition.y < tly )
+      {
+        tly = verts[ i ].mPosition.y;
+      }
+      if ( verts[ i ].mPosition.x > brx )
+      {
+        brx = verts[ i ].mPosition.x;
+      }
+      if ( verts[ i ].mPosition.y > bry )
+      {
+        bry = verts[ i ].mPosition.y;
+      }
+    }
+
+    float width = brx - tlx;
+    float height = bry - tly;
+    float divWidth = TWO / width;
+    float divHeight = TWO / height;
+
+    // Create a buffer to render to
+    meshRecord.mBuffer = FrameBufferImage::New( width, height );
+
+    // We will render a quad into this buffer
+    unsigned int indices[ 6 ] = { 1, 0, 2, 2, 3, 1 };
+    PropertyBuffer quadVertices = PropertyBuffer::New( mQuadVertexFormat, 4u );
+    PropertyBuffer quadIndices = PropertyBuffer::New( mQuadIndexFormat, sizeof(indices)/sizeof(indices[0]) );
+
+    AtlasManager::Vertex2D vertices[ 4 ] = {
+    { Vector2( tlx + shadowOffset.x, tly + shadowOffset.y ), Vector2( ZERO, ZERO ) },
+    { Vector2( brx + shadowOffset.x, tly + shadowOffset.y ), Vector2( ONE, ZERO ) },
+    { Vector2( tlx + shadowOffset.x, bry + shadowOffset.y ), Vector2( ZERO, ONE ) },
+    { Vector2( brx + shadowOffset.x, bry + shadowOffset.y ), Vector2( ONE, ONE ) } };
+
+    quadVertices.SetData( vertices );
+    quadIndices.SetData( indices );
+
+    Geometry quadGeometry = Geometry::New();
+    quadGeometry.AddVertexBuffer( quadVertices );
+    quadGeometry.SetIndexBuffer( quadIndices );
+
+    Sampler sampler = Sampler::New( meshRecord.mBuffer, "sTexture" );
+    Material material = Material::New( mGlyphManager.GetEffectBufferShader() );
+    material.AddSampler( sampler );
+
+    Dali::Renderer renderer = Dali::Renderer::New( quadGeometry, material );
+
+    // Ensure shadow is behind the text...
+    renderer.SetDepthIndex( CONTENT_DEPTH_INDEX + mDepth - 1 );
+    Actor actor = Actor::New();
+    actor.AddRenderer( renderer );
+    actor.SetParentOrigin( ParentOrigin::CENTER ); // Keep all of the origins aligned
+    actor.SetSize( actorSize );
+
+    // Create a sub actor to render the source with normalized vertex positions
+    Vector< AtlasManager::Vertex2D > normVertexList;
+    for ( uint32_t i = 0; i < verts.Size(); ++i )
+    {
+      AtlasManager::Vertex2D vertex = verts[ i ];
+      vertex.mPosition.x = ( ( vertex.mPosition.x - tlx ) * divWidth ) - ONE;
+      vertex.mPosition.y = ( ( vertex.mPosition.y - tly ) * divHeight ) - ONE;
+      normVertexList.PushBack( vertex );
+    }
+
+    PropertyBuffer normVertices = PropertyBuffer::New( mQuadVertexFormat, normVertexList.Size() );
+    PropertyBuffer normIndices = PropertyBuffer::New( mQuadIndexFormat, meshRecord.mMesh.mIndices.Size() );
+    normVertices.SetData( const_cast< AtlasManager::Vertex2D* >( &normVertexList[ 0 ] ) );
+    normIndices.SetData( const_cast< unsigned int* >( &meshRecord.mMesh.mIndices[ 0 ] ) );
+
+    Geometry normGeometry = Geometry::New();
+    normGeometry.AddVertexBuffer( normVertices );
+    normGeometry.SetIndexBuffer( normIndices );
+
+    Material normMaterial = Material::New( mGlyphManager.GetGlyphShadowShader() );
+    Sampler normSampler =  mGlyphManager.GetSampler( meshRecord.mAtlasId );
+    normMaterial.AddSampler( normSampler );
+    Dali::Renderer normRenderer = Dali::Renderer::New( normGeometry, normMaterial );
+    Actor subActor = Actor::New();
+    subActor.AddRenderer( normRenderer );
+    subActor.SetParentOrigin( ParentOrigin::CENTER ); // Keep all of the origins aligned
+    subActor.SetSize( actorSize );
+    subActor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
+    subActor.SetColor( shadowColor );
+
+    // Create a render task to render the effect
+    RenderTask task = Stage::GetCurrent().GetRenderTaskList().CreateTask();
+    task.SetTargetFrameBuffer( meshRecord.mBuffer );
+    task.SetSourceActor( subActor );
+    task.SetClearEnabled( true );
+    task.SetClearColor( Vector4::ZERO );
+    task.SetExclusive( true );
+    task.SetRefreshRate( RenderTask::REFRESH_ONCE );
+    task.FinishedSignal().Connect( this, &AtlasRenderer::Impl::RenderComplete );
+    actor.Add( subActor );
+
+    return actor;
+  }
+
+  void RenderComplete( RenderTask& renderTask )
+  {
+    // Disconnect and remove this single shot render task
+    renderTask.FinishedSignal().Disconnect( this, &AtlasRenderer::Impl::RenderComplete );
+    Stage::GetCurrent().GetRenderTaskList().RemoveTask( renderTask );
+
+    // Get the actor used for render to buffer and remove it from the parent
+    Actor renderActor = renderTask.GetSourceActor();
+    if ( renderActor )
+    {
+      Actor parent = renderActor.GetParent();
+      if ( parent )
+      {
+        parent.Remove( renderActor );
+      }
+    }
+  }
+
+  Actor mActor;                                       ///< The actor parent which renders the text
   AtlasGlyphManager mGlyphManager;                    ///< Glyph Manager to handle upload and caching
-  Vector< uint32_t > mImageIds;                       ///< A list of imageIDs used by the renderer
   TextAbstraction::FontClient mFontClient;            ///> The font client used to supply glyph information
-  SlotDelegate< AtlasRenderer::Impl > mSlotDelegate;  ///> Signal generated to unreference glyphs when renderable actor is removed
-  ShaderEffect mBasicShader;                          ///> Shader to render L8 glyphs
-  ShaderEffect mBGRAShader;                           ///> Shader to render BGRA glyphs
   std::vector< MaxBlockSize > mBlockSizes;            ///> Maximum size needed to contain a glyph in a block within a new atlas
+  std::vector< uint32_t > mFace;                      ///> Face indices for a quad
+  Vector< TextCacheEntry > mTextCache;
+  Property::Map mQuadVertexFormat;
+  Property::Map mQuadIndexFormat;
+  int mDepth;
 };
 
 Text::RendererPtr AtlasRenderer::New()
 {
+  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Text::AtlasRenderer::New()\n" );
+
   return Text::RendererPtr( new AtlasRenderer() );
 }
 
-RenderableActor AtlasRenderer::Render( Text::ViewInterface& view )
+Actor AtlasRenderer::Render( Text::ViewInterface& view, int depth )
 {
-
   UnparentAndReset( mImpl->mActor );
 
-  Text::Length numberOfGlyphs = view.GetNumberOfGlyphs();
+  Length numberOfGlyphs = view.GetNumberOfGlyphs();
 
-  if( numberOfGlyphs > 0 )
+  if( numberOfGlyphs > 0u )
   {
     Vector<GlyphInfo> glyphs;
     glyphs.Resize( numberOfGlyphs );
 
-    view.GetGlyphs( &glyphs[0], 0, numberOfGlyphs );
-
     std::vector<Vector2> positions;
     positions.resize( numberOfGlyphs );
-    view.GetGlyphPositions( &positions[0], 0, numberOfGlyphs );
-    mImpl->AddGlyphs( positions, glyphs );
+
+    numberOfGlyphs = view.GetGlyphs( glyphs.Begin(),
+                                     &positions[0],
+                                     0u,
+                                     numberOfGlyphs );
+    glyphs.Resize( numberOfGlyphs );
+    positions.resize( numberOfGlyphs );
+
+    mImpl->AddGlyphs( view,
+                      positions,
+                      glyphs,
+                      depth );
   }
+
   return mImpl->mActor;
 }
 
@@ -312,5 +783,6 @@ AtlasRenderer::AtlasRenderer()
 
 AtlasRenderer::~AtlasRenderer()
 {
+  mImpl->RemoveText();
   delete mImpl;
-}
\ No newline at end of file
+}