More Text related logging & clean-up
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / rendering / atlas / text-atlas-renderer.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/dali.h>
23 #include <dali/integration-api/debug.h>
24 #include <dali/public-api/text-abstraction/text-abstraction.h>
25
26 // INTERNAL INCLUDES
27 #include <dali-toolkit/internal/atlas-manager/atlas-manager.h>
28 #include <dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager.h>
29 #include <dali-toolkit/internal/text/rendering/shaders/text-basic-shader.h>
30 #include <dali-toolkit/internal/text/rendering/shaders/text-bgra-shader.h>
31 #include <dali-toolkit/internal/text/rendering/shaders/text-basic-shadow-shader.h>
32
33 using namespace Dali;
34 using namespace Dali::Toolkit;
35 using namespace Dali::Toolkit::Text;
36
37 namespace
38 {
39 #if defined(DEBUG_ENABLED)
40   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_RENDERING");
41 #endif
42
43   const float ZERO( 0.0f );
44   const float HALF( 0.5f );
45   const float ONE( 1.0f );
46   const float TWO( 2.0f );
47   const uint32_t DEFAULT_ATLAS_WIDTH = 512u;
48   const uint32_t DEFAULT_ATLAS_HEIGHT = 512u;
49 }
50
51 struct AtlasRenderer::Impl : public ConnectionTracker
52 {
53
54   enum Style
55   {
56     STYLE_NORMAL,
57     STYLE_DROP_SHADOW
58   };
59
60   struct MeshRecord
61   {
62     Vector4 mColor;
63     uint32_t mAtlasId;
64     MeshData mMeshData;
65     FrameBufferImage mBuffer;
66     bool mIsUnderline;
67   };
68
69   struct Extent
70   {
71     float mBaseLine;
72     float mLeft;
73     float mRight;
74     float mUnderlinePosition;
75     float mUnderlineThickness;
76     uint32_t mMeshRecordIndex;
77   };
78
79   struct AtlasRecord
80   {
81     uint32_t mImageId;
82     Text::GlyphIndex mIndex;
83   };
84
85   struct MaxBlockSize
86   {
87     FontId mFontId;
88     uint32_t mNeededBlockWidth;
89     uint32_t mNeededBlockHeight;
90   };
91
92   Impl()
93   {
94     mGlyphManager = AtlasGlyphManager::Get();
95     mFontClient = TextAbstraction::FontClient::Get();
96     mBasicShader = BasicShader::New();
97     mBgraShader = BgraShader::New();
98     mBasicShadowShader = BasicShadowShader::New();
99
100     mFace.reserve( 6u );
101     mFace.push_back( 0 ); mFace.push_back( 2u ); mFace.push_back( 1u );
102     mFace.push_back( 1u ); mFace.push_back( 2u ); mFace.push_back( 3u );
103   }
104
105   void AddGlyphs( const std::vector<Vector2>& positions,
106                   const Vector<GlyphInfo>& glyphs,
107                   const Vector4& textColor,
108                   const Vector2& shadowOffset,
109                   const Vector4& shadowColor,
110                   bool underlineEnabled,
111                   const Vector4& underlineColor,
112                   float underlineHeight )
113   {
114     AtlasManager::AtlasSlot slot;
115     std::vector< MeshRecord > meshContainer;
116     Vector< Extent > extents;
117
118     float currentUnderlinePosition = ZERO;
119     float currentUnderlineThickness = underlineHeight;
120     uint32_t currentBlockSize = 0;
121     FontId lastFontId = 0;
122     Style style = STYLE_NORMAL;
123
124     if ( fabsf( shadowOffset.x ) > Math::MACHINE_EPSILON_1 || fabsf( shadowOffset.y ) > Math::MACHINE_EPSILON_1 )
125     {
126       style = STYLE_DROP_SHADOW;
127     }
128
129     if ( mImageIds.Size() )
130     {
131       // Unreference any currently used glyphs
132       RemoveText();
133     }
134
135     CalculateBlocksSize( glyphs );
136
137     for ( uint32_t i = 0; i < glyphs.Size(); ++i )
138     {
139       GlyphInfo glyph = glyphs[ i ];
140
141       // No operation for white space
142       if ( glyph.width && glyph.height )
143       {
144         // Are we still using the same fontId as previous
145         if ( glyph.fontId != lastFontId )
146         {
147           // We need to fetch fresh font underline metrics
148           FontMetrics fontMetrics;
149           mFontClient.GetFontMetrics( glyph.fontId, fontMetrics );
150           currentUnderlinePosition = ceil( fabsf( fontMetrics.underlinePosition ) );
151           float descender = ceil( fabsf( fontMetrics.descender ) );
152
153           if ( underlineHeight == ZERO )
154           {
155             currentUnderlineThickness = fontMetrics.underlineThickness;
156
157             // Ensure underline will be at least a pixel high
158             if ( currentUnderlineThickness < ONE )
159             {
160               currentUnderlineThickness = ONE;
161             }
162             else
163             {
164               currentUnderlineThickness = ceil( currentUnderlineThickness );
165             }
166           }
167
168           // Clamp the underline position at the font descender and check for ( as EFL describes it ) a broken font
169           if ( currentUnderlinePosition > descender )
170           {
171             currentUnderlinePosition = descender;
172           }
173           if ( ZERO == currentUnderlinePosition )
174           {
175             // Move offset down by one ( EFL behavior )
176             currentUnderlinePosition = ONE;
177           }
178         }
179
180         Vector2 position = positions[ i ];
181         MeshData newMeshData;
182         mGlyphManager.Cached( glyph.fontId, glyph.index, slot );
183
184         if ( slot.mImageId )
185         {
186           // This glyph already exists so generate mesh data plugging in our supplied position
187           mGlyphManager.GenerateMeshData( slot.mImageId, position, newMeshData );
188           mImageIds.PushBack( slot.mImageId );
189         }
190         else
191         {
192
193           // Select correct size for new atlas if needed....?
194           if ( lastFontId != glyph.fontId )
195           {
196             for ( uint32_t j = 0; j < mBlockSizes.size(); ++j )
197             {
198               if ( mBlockSizes[ j ].mFontId == glyph.fontId )
199               {
200                 currentBlockSize = j;
201                 mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_WIDTH,
202                                                DEFAULT_ATLAS_HEIGHT,
203                                                mBlockSizes[ j ].mNeededBlockWidth,
204                                                mBlockSizes[ j ].mNeededBlockHeight );
205               }
206             }
207           }
208
209           // Create a new image for the glyph
210           BufferImage bitmap = mFontClient.CreateBitmap( glyph.fontId, glyph.index );
211           if ( bitmap )
212           {
213             // Ensure that the next image will fit into the current block size
214             bool setSize = false;
215             if ( bitmap.GetWidth() > mBlockSizes[ currentBlockSize ].mNeededBlockWidth )
216             {
217               setSize = true;
218               mBlockSizes[ currentBlockSize ].mNeededBlockWidth = bitmap.GetWidth();
219             }
220             if ( bitmap.GetHeight() > mBlockSizes[ currentBlockSize ].mNeededBlockHeight )
221             {
222               setSize = true;
223               mBlockSizes[ currentBlockSize ].mNeededBlockHeight = bitmap.GetHeight();
224             }
225
226             if ( setSize )
227             {
228               mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_WIDTH,
229                                              DEFAULT_ATLAS_HEIGHT,
230                                              mBlockSizes[ currentBlockSize ].mNeededBlockWidth,
231                                              mBlockSizes[ currentBlockSize ].mNeededBlockHeight );
232             }
233
234             // Locate a new slot for our glyph
235             mGlyphManager.Add( glyph, bitmap, slot );
236
237             // Generate mesh data for this quad, plugging in our supplied position
238             if ( slot.mImageId )
239             {
240               mGlyphManager.GenerateMeshData( slot.mImageId, position, newMeshData );
241               mImageIds.PushBack( slot.mImageId );
242             }
243           }
244         }
245         // Find an existing mesh data object to attach to ( or create a new one, if we can't find one using the same atlas)
246         StitchTextMesh( meshContainer,
247                         newMeshData,
248                         extents,
249                         textColor,
250                         position.y + glyph.yBearing,
251                         currentUnderlinePosition,
252                         currentUnderlineThickness,
253                         slot );
254         lastFontId = glyph.fontId;
255       }
256     }
257
258     if ( underlineEnabled )
259     {
260       // Check to see if any of the text needs an underline
261       GenerateUnderlines( meshContainer, extents, underlineColor, textColor );
262     }
263
264     // For each MeshData object, create a mesh actor and add to the renderable actor
265     if ( meshContainer.size() )
266     {
267       for ( std::vector< MeshRecord >::iterator mIt = meshContainer.begin(); mIt != meshContainer.end(); ++mIt )
268       {
269         MeshActor actor = MeshActor::New( Mesh::New( mIt->mMeshData ) );
270         actor.SetColor( mIt->mColor );
271
272         // Ensure that text rendering is unfiltered
273         actor.SetFilterMode( FilterMode::NEAREST, FilterMode::NEAREST );
274         if ( mIt->mIsUnderline )
275         {
276           actor.SetColorMode( USE_OWN_COLOR );
277         }
278         else
279         {
280           actor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
281         }
282
283         // Check to see what pixel format the shader should be
284         if ( mGlyphManager.GetPixelFormat( mIt->mAtlasId ) == Pixel::L8 )
285         {
286           // Create an effect if necessary
287           if ( style == STYLE_DROP_SHADOW )
288           {
289             actor.Add( GenerateShadow( *mIt, shadowOffset, shadowColor ) );
290           }
291           actor.SetShaderEffect( mBasicShader );
292         }
293         else
294         {
295           actor.SetShaderEffect( mBgraShader );
296         }
297
298         if ( mActor )
299         {
300           mActor.Add( actor );
301         }
302         else
303         {
304           mActor = actor;
305         }
306       }
307       mActor.OffStageSignal().Connect( this, &AtlasRenderer::Impl::OffStageDisconnect );
308     }
309 #if defined(DEBUG_ENABLED)
310     Toolkit::AtlasGlyphManager::Metrics metrics = mGlyphManager.GetMetrics();
311     DALI_LOG_INFO( gLogFilter, Debug::General, "TextAtlasRenderer::GlyphManager::GlyphCount: %i, AtlasCount: %i, TextureMemoryUse: %iK\n",
312                                                 metrics.mGlyphCount,
313                                                 metrics.mAtlasMetrics.mAtlasCount,
314                                                 metrics.mAtlasMetrics.mTextureMemoryUsed / 1024 );
315     for ( uint32_t i = 0; i < metrics.mAtlasMetrics.mAtlasCount; ++i )
316     {
317       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Atlas [%i] %sPixels: %s Size: %ix%i, BlockSize: %ix%i, BlocksUsed: %i/%i\n",
318                                                  i + 1, i > 8 ? "" : " ",
319                                                  metrics.mAtlasMetrics.mAtlasMetrics[ i ].mPixelFormat == Pixel::L8 ? "L8  " : "BGRA",
320                                                  metrics.mAtlasMetrics.mAtlasMetrics[ i ].mSize.mWidth,
321                                                  metrics.mAtlasMetrics.mAtlasMetrics[ i ].mSize.mHeight,
322                                                  metrics.mAtlasMetrics.mAtlasMetrics[ i ].mSize.mBlockWidth,
323                                                  metrics.mAtlasMetrics.mAtlasMetrics[ i ].mSize.mBlockHeight,
324                                                  metrics.mAtlasMetrics.mAtlasMetrics[ i ].mBlocksUsed,
325                                                  metrics.mAtlasMetrics.mAtlasMetrics[ i ].mTotalBlocks );
326     }
327 #endif
328   }
329
330   void StitchTextMesh( std::vector< MeshRecord >& meshContainer,
331                        MeshData& newMeshData,
332                        Vector< Extent >& extents,
333                        const Vector4& color,
334                        float baseLine,
335                        float underlinePosition,
336                        float underlineThickness,
337                        AtlasManager::AtlasSlot& slot )
338   {
339     if ( slot.mImageId )
340     {
341       MeshData::VertexContainer verts = newMeshData.GetVertices();
342       float left = verts[ 0 ].x;
343       float right = verts[ 1 ].x;
344
345       // Check to see if there's a mesh data object that references the same atlas ?
346       uint32_t index = 0;
347       for ( std::vector< MeshRecord >::iterator mIt = meshContainer.begin(); mIt != meshContainer.end(); ++mIt, ++index )
348       {
349         if ( slot.mAtlasId == mIt->mAtlasId && color == mIt->mColor )
350         {
351           // Stitch the mesh to the existing mesh and adjust any extents
352           mGlyphManager.StitchMesh( mIt->mMeshData, newMeshData );
353           AdjustExtents( extents,
354                          meshContainer,
355                          index,
356                          left,
357                          right,
358                          baseLine,
359                          underlinePosition,
360                          underlineThickness );
361           return;
362         }
363       }
364
365       // No mesh data object currently exists that references this atlas, so create a new one
366       MeshRecord meshRecord;
367       meshRecord.mAtlasId = slot.mAtlasId;
368       meshRecord.mMeshData = newMeshData;
369       meshRecord.mColor = color;
370       meshRecord.mIsUnderline = false;
371       meshContainer.push_back( meshRecord );
372
373       // Adjust extents for this new meshrecord
374       AdjustExtents( extents,
375                      meshContainer,
376                      meshContainer.size() - 1u,
377                      left,
378                      right,
379                      baseLine,
380                      underlinePosition,
381                      underlineThickness );
382
383     }
384   }
385
386   void AdjustExtents( Vector< Extent >& extents,
387                       std::vector< MeshRecord>& meshRecords,
388                       uint32_t index,
389                       float left,
390                       float right,
391                       float baseLine,
392                       float underlinePosition,
393                       float underlineThickness )
394   {
395     bool foundExtent = false;
396     for ( Vector< Extent >::Iterator eIt = extents.Begin(); eIt != extents.End(); ++eIt )
397     {
398       if ( Equals( baseLine, eIt->mBaseLine ) )
399       {
400         foundExtent = true;
401         if ( left < eIt->mLeft )
402         {
403           eIt->mLeft = left;
404         }
405         if ( right > eIt->mRight  )
406         {
407           eIt->mRight = right;
408         }
409
410         if ( underlinePosition > eIt->mUnderlinePosition )
411         {
412           eIt->mUnderlinePosition = underlinePosition;
413         }
414         if ( underlineThickness > eIt->mUnderlineThickness )
415         {
416           eIt->mUnderlineThickness = underlineThickness;
417         }
418       }
419     }
420     if ( !foundExtent )
421     {
422       Extent extent;
423       extent.mLeft = left;
424       extent.mRight = right;
425       extent.mBaseLine = baseLine;
426       extent.mUnderlinePosition = underlinePosition;
427       extent.mUnderlineThickness = underlineThickness;
428       extent.mMeshRecordIndex = index;
429       extents.PushBack( extent );
430     }
431   }
432
433   // Unreference any glyphs that were used with this actor
434   void OffStageDisconnect( Dali::Actor actor )
435   {
436     RemoveText();
437   }
438
439   void RemoveText()
440   {
441     for ( uint32_t i = 0; i < mImageIds.Size(); ++i )
442     {
443       mGlyphManager.Remove( mImageIds[ i ] );
444     }
445     mImageIds.Resize( 0 );
446   }
447
448   void CalculateBlocksSize( const Vector<GlyphInfo>& glyphs )
449   {
450     MaxBlockSize maxBlockSize;
451     for ( uint32_t i = 0; i < glyphs.Size(); ++i )
452     {
453       FontId fontId = glyphs[ i ].fontId;
454       bool foundFont = false;
455       for ( uint32_t j = 0; j < mBlockSizes.size(); ++j )
456       {
457         if ( mBlockSizes[ j ].mFontId == fontId )
458         {
459           foundFont = true;
460         }
461       }
462       if ( !foundFont )
463       {
464         FontMetrics fontMetrics;
465         mFontClient.GetFontMetrics( fontId, fontMetrics );
466         maxBlockSize.mNeededBlockWidth = static_cast< uint32_t >( fontMetrics.height );
467         maxBlockSize.mNeededBlockHeight = static_cast< uint32_t >( fontMetrics.height );
468         maxBlockSize.mFontId = fontId;
469         mBlockSizes.push_back( maxBlockSize );
470       }
471     }
472   }
473
474   void GenerateUnderlines( std::vector< MeshRecord>& meshRecords,
475                            Vector< Extent >& extents,
476                            const Vector4& underlineColor,
477                            const Vector4& textColor )
478   {
479     MeshData newMeshData;
480     for ( Vector< Extent >::ConstIterator eIt = extents.Begin(); eIt != extents.End(); ++eIt )
481     {
482       MeshData::VertexContainer newVerts;
483       newVerts.reserve( 4u );
484       uint32_t index = eIt->mMeshRecordIndex;
485       Vector2 uv = mGlyphManager.GetAtlasSize( meshRecords[ index ].mAtlasId );
486
487       // Make sure we don't hit texture edge for single pixel texture ( filled pixel is in top left of every atlas )
488       float u = HALF / uv.x;
489       float v = HALF / uv.y;
490       float thickness = eIt->mUnderlineThickness;
491       float baseLine = eIt->mBaseLine + eIt->mUnderlinePosition - ( thickness * HALF );
492       float tlx = eIt->mLeft;
493       float brx = eIt->mRight;
494
495       newVerts.push_back( MeshData::Vertex( Vector3( tlx, baseLine, ZERO ),
496                                             Vector2::ZERO,
497                                             Vector3::ZERO ) );
498
499       newVerts.push_back( MeshData::Vertex( Vector3( brx, baseLine, ZERO ),
500                                             Vector2( u, ZERO ),
501                                             Vector3::ZERO ) );
502
503       newVerts.push_back( MeshData::Vertex( Vector3( tlx, baseLine + thickness, ZERO ),
504                                             Vector2( ZERO, v ),
505                                             Vector3::ZERO ) );
506
507       newVerts.push_back( MeshData::Vertex( Vector3( brx, baseLine + thickness, ZERO ),
508                                             Vector2( u, v ),
509                                             Vector3::ZERO ) );
510
511       newMeshData.SetVertices( newVerts );
512       newMeshData.SetFaceIndices( mFace );
513
514       if ( underlineColor == textColor )
515       {
516         mGlyphManager.StitchMesh( meshRecords[ index ].mMeshData, newMeshData );
517       }
518       else
519       {
520         MeshRecord record;
521         newMeshData.SetMaterial( meshRecords[ index ].mMeshData.GetMaterial() );
522         newMeshData.SetHasNormals( true );
523         newMeshData.SetHasColor( false );
524         newMeshData.SetHasTextureCoords( true );
525         record.mMeshData = newMeshData;
526         record.mAtlasId = meshRecords[ index ].mAtlasId;
527         record.mColor = underlineColor;
528         record.mIsUnderline = true;
529         meshRecords.push_back( record );
530       }
531     }
532   }
533
534   MeshActor GenerateShadow( MeshRecord& meshRecord,
535                             const Vector2& shadowOffset,
536                             const Vector4& shadowColor )
537   {
538     // Scan vertex buffer to determine width and height of effect buffer needed
539     MeshData::VertexContainer verts = meshRecord.mMeshData.GetVertices();
540     float tlx = verts[ 0 ].x;
541     float tly = verts[ 0 ].y;
542     float brx = ZERO;
543     float bry = ZERO;
544
545     for ( uint32_t i = 0; i < verts.size(); ++i )
546     {
547       if ( verts[ i ].x < tlx )
548       {
549         tlx = verts[ i ].x;
550       }
551       if ( verts[ i ].y < tly )
552       {
553         tly = verts[ i ].y;
554       }
555       if ( verts[ i ].x > brx )
556       {
557         brx = verts[ i ].x;
558       }
559       if ( verts[ i ].y > bry )
560       {
561         bry = verts[ i ].y;
562       }
563     }
564
565     float width = brx - tlx;
566     float height = bry - tly;
567     float divWidth = TWO / width;
568     float divHeight = TWO / height;
569
570     // Create a buffer to render to
571     meshRecord.mBuffer = FrameBufferImage::New( width, height );
572
573     // Create a mesh actor to contain the post-effect render
574     MeshData::VertexContainer vertices;
575     MeshData::FaceIndices face;
576
577     vertices.push_back( MeshData::Vertex( Vector3( tlx + shadowOffset.x, tly + shadowOffset.y, ZERO ),
578                                           Vector2::ZERO,
579                                           Vector3::ZERO ) );
580
581     vertices.push_back( MeshData::Vertex( Vector3( brx + shadowOffset.x, tly + shadowOffset.y, ZERO ),
582                                           Vector2( ONE, ZERO ),
583                                           Vector3::ZERO ) );
584
585     vertices.push_back( MeshData::Vertex( Vector3( tlx + shadowOffset.x, bry + shadowOffset.y, ZERO ),
586                                           Vector2( ZERO, ONE ),
587                                           Vector3::ZERO ) );
588
589     vertices.push_back( MeshData::Vertex( Vector3( brx + shadowOffset.x, bry + shadowOffset.y, ZERO ),
590                                           Vector2::ONE,
591                                           Vector3::ZERO ) );
592
593     MeshData meshData;
594     Material newMaterial = Material::New("effect buffer");
595     newMaterial.SetDiffuseTexture( meshRecord.mBuffer );
596     meshData.SetMaterial( newMaterial );
597     meshData.SetVertices( vertices );
598     meshData.SetFaceIndices( mFace );
599     meshData.SetHasNormals( true );
600     meshData.SetHasColor( false );
601     meshData.SetHasTextureCoords( true );
602     MeshActor actor = MeshActor::New( Mesh::New( meshData ) );
603     actor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
604     actor.SetShaderEffect( mBgraShader );
605     actor.SetFilterMode( FilterMode::LINEAR, FilterMode::LINEAR );
606     actor.SetSortModifier( 0.1f ); // force behind main text
607
608     // Create a sub actor to render once with normalized vertex positions
609     MeshData newMeshData;
610     MeshData::VertexContainer newVerts;
611     MeshData::FaceIndices newFaces;
612     MeshData::FaceIndices faces = meshRecord.mMeshData.GetFaces();
613     for ( uint32_t i = 0; i < verts.size(); ++i )
614     {
615       MeshData::Vertex vertex = verts[ i ];
616       vertex.x = ( ( vertex.x - tlx ) * divWidth ) - ONE;
617       vertex.y = ( ( vertex.y - tly ) * divHeight ) - ONE;
618       newVerts.push_back( vertex );
619     }
620
621     // Reverse triangle winding order
622     uint32_t faceCount = faces.size() / 3;
623     for ( uint32_t i = 0; i < faceCount; ++i )
624     {
625       uint32_t index = i * 3;
626       newFaces.push_back( faces[ index + 2 ] );
627       newFaces.push_back( faces[ index + 1 ] );
628       newFaces.push_back( faces[ index ] );
629     }
630
631     newMeshData.SetMaterial( meshRecord.mMeshData.GetMaterial() );
632     newMeshData.SetVertices( newVerts );
633     newMeshData.SetFaceIndices( newFaces );
634     newMeshData.SetHasNormals( true );
635     newMeshData.SetHasColor( false );
636     newMeshData.SetHasTextureCoords( true );
637
638     MeshActor subActor = MeshActor::New( Mesh::New( newMeshData ) );
639     subActor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
640     subActor.SetColor( shadowColor );
641     subActor.SetShaderEffect( mBasicShadowShader );
642     subActor.SetFilterMode( FilterMode::NEAREST, FilterMode::NEAREST );
643
644     // Create a render task to render the effect
645     RenderTask task = Stage::GetCurrent().GetRenderTaskList().CreateTask();
646     task.SetTargetFrameBuffer( meshRecord.mBuffer );
647     task.SetSourceActor( subActor );
648     task.SetClearEnabled( true );
649     task.SetClearColor( Vector4::ZERO );
650     task.SetExclusive( true );
651     task.SetRefreshRate( RenderTask::REFRESH_ONCE );
652     task.FinishedSignal().Connect( this, &AtlasRenderer::Impl::RenderComplete );
653     actor.Add( subActor );
654     return actor;
655   }
656
657   void RenderComplete( RenderTask& renderTask )
658   {
659     // Disconnect and remove this single shot render task
660     renderTask.FinishedSignal().Disconnect( this, &AtlasRenderer::Impl::RenderComplete );
661     Stage::GetCurrent().GetRenderTaskList().RemoveTask( renderTask );
662
663     // Get the actor used for render to buffer and remove it from the parent
664     Actor renderActor = renderTask.GetSourceActor();
665     if ( renderActor )
666     {
667       Actor parent = renderActor.GetParent();
668       if ( parent )
669       {
670         parent.Remove( renderActor );
671       }
672     }
673   }
674
675   RenderableActor mActor;                             ///< The actor parent which renders the text
676   AtlasGlyphManager mGlyphManager;                    ///< Glyph Manager to handle upload and caching
677   Vector< uint32_t > mImageIds;                       ///< A list of imageIDs used by the renderer
678   TextAbstraction::FontClient mFontClient;            ///> The font client used to supply glyph information
679   ShaderEffect mBasicShader;                          ///> Shader used to render L8 glyphs
680   ShaderEffect mBgraShader;                           ///> Shader used to render BGRA glyphs
681   ShaderEffect mBasicShadowShader;                    ///> Shader used to render drop shadow into buffer
682   std::vector< MaxBlockSize > mBlockSizes;            ///> Maximum size needed to contain a glyph in a block within a new atlas
683   std::vector< MeshData::FaceIndex > mFace;           ///> Face indices for a quad
684 };
685
686 Text::RendererPtr AtlasRenderer::New()
687 {
688   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Text::AtlasRenderer::New()\n" );
689
690   return Text::RendererPtr( new AtlasRenderer() );
691 }
692
693 RenderableActor AtlasRenderer::Render( Text::ViewInterface& view )
694 {
695
696   UnparentAndReset( mImpl->mActor );
697
698   Text::Length numberOfGlyphs = view.GetNumberOfGlyphs();
699
700   if( numberOfGlyphs > 0 )
701   {
702     Vector<GlyphInfo> glyphs;
703     glyphs.Resize( numberOfGlyphs );
704
705     view.GetGlyphs( &glyphs[0], 0, numberOfGlyphs );
706
707     std::vector<Vector2> positions;
708     positions.resize( numberOfGlyphs );
709     view.GetGlyphPositions( &positions[0], 0, numberOfGlyphs );
710     mImpl->AddGlyphs( positions,
711                       glyphs,
712                       view.GetTextColor(),
713                       view.GetShadowOffset(),
714                       view.GetShadowColor(),
715                       view.IsUnderlineEnabled(),
716                       view.GetUnderlineColor(),
717                       view.GetUnderlineHeight() );
718   }
719   return mImpl->mActor;
720 }
721
722 AtlasRenderer::AtlasRenderer()
723 {
724   mImpl = new Impl();
725
726 }
727
728 AtlasRenderer::~AtlasRenderer()
729 {
730   delete mImpl;
731 }