a5ea934cfb7c195dcdbf310f67c548cca4311931
[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
25 // INTERNAL INCLUDES
26 #include <dali-toolkit/internal/atlas-manager/atlas-manager.h>
27 #include <dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager.h>
28 #include <dali-toolkit/internal/text/rendering/shaders/text-basic-shader.h>
29 #include <dali-toolkit/internal/text/rendering/shaders/text-bgra-shader.h>
30 #include <dali-toolkit/internal/text/rendering/shaders/text-basic-shadow-shader.h>
31 #if defined(DEBUG_ENABLED)
32 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_ATLAS_RENDERER");
33 #endif
34
35 using namespace Dali;
36 using namespace Dali::Toolkit;
37 using namespace Dali::Toolkit::Text;
38
39 namespace
40 {
41   const Vector2 DEFAULT_ATLAS_SIZE( 512.0f, 512.0f );
42   const Vector2 DEFAULT_BLOCK_SIZE( 16.0f, 16.0f );
43   const Vector2 PADDING( 4.0f, 4.0f ); // Allow for variation in font glyphs
44 }
45
46 struct AtlasRenderer::Impl : public ConnectionTracker
47 {
48
49   enum Style
50   {
51     STYLE_NORMAL,
52     STYLE_DROP_SHADOW
53   };
54
55   struct MeshRecord
56   {
57     uint32_t mAtlasId;
58     MeshData mMeshData;
59     FrameBufferImage mBuffer;
60   };
61
62   struct AtlasRecord
63   {
64     uint32_t mImageId;
65     Text::GlyphIndex mIndex;
66   };
67
68   struct MaxBlockSize
69   {
70     FontId mFontId;
71     Vector2 mNeededBlockSize;
72   };
73
74   Impl()
75   {
76     mGlyphManager = AtlasGlyphManager::Get();
77     mFontClient = TextAbstraction::FontClient::Get();
78     mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_SIZE, DEFAULT_BLOCK_SIZE );
79     mBasicShader = BasicShader::New();
80     mBgraShader = BgraShader::New();
81     mBasicShadowShader = BasicShadowShader::New();
82   }
83
84   void AddGlyphs( const std::vector<Vector2>& positions,
85                   const Vector<GlyphInfo>& glyphs,
86                   const Vector2& shadowOffset,
87                   const Vector4& shadowColor )
88   {
89     AtlasManager::AtlasSlot slot;
90     std::vector< MeshRecord > meshContainer;
91     FontId lastFontId = 0;
92     Style style = STYLE_NORMAL;
93
94     if ( shadowOffset.x > 0.0f || shadowOffset.y > 0.0f )
95     {
96       style = STYLE_DROP_SHADOW;
97     }
98
99     if ( mImageIds.Size() )
100     {
101       // Unreference any currently used glyphs
102       RemoveText();
103     }
104
105     CalculateBlocksSize( glyphs );
106
107     for ( uint32_t i = 0; i < glyphs.Size(); ++i )
108     {
109       GlyphInfo glyph = glyphs[ i ];
110
111       // No operation for white space
112       if ( glyph.width && glyph.height )
113       {
114         Vector2 position = positions[ i ];
115         MeshData newMeshData;
116         mGlyphManager.Cached( glyph.fontId, glyph.index, slot );
117
118         if ( slot.mImageId )
119         {
120           // This glyph already exists so generate mesh data plugging in our supplied position
121           mGlyphManager.GenerateMeshData( slot.mImageId, position, newMeshData );
122           mImageIds.PushBack( slot.mImageId );
123         }
124         else
125         {
126
127           // Select correct size for new atlas if needed....?
128           if ( lastFontId != glyph.fontId )
129           {
130             for ( uint32_t j = 0; j < mBlockSizes.size(); ++j )
131             {
132               if ( mBlockSizes[ j ].mFontId == glyph.fontId )
133               {
134                 mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_SIZE, mBlockSizes[ j ].mNeededBlockSize );
135               }
136             }
137             lastFontId = glyph.fontId;
138           }
139
140           // Glyph doesn't currently exist in atlas so upload
141           BufferImage bitmap = mFontClient.CreateBitmap( glyph.fontId, glyph.index );
142
143           // Locate a new slot for our glyph
144           mGlyphManager.Add( glyph, bitmap, slot );
145
146           // Generate mesh data for this quad, plugging in our supplied position
147           if ( slot.mImageId )
148           {
149             mGlyphManager.GenerateMeshData( slot.mImageId, position, newMeshData );
150             mImageIds.PushBack( slot.mImageId );
151           }
152         }
153         // Find an existing mesh data object to attach to ( or create a new one, if we can't find one using the same atlas)
154         StitchTextMesh( meshContainer, newMeshData, slot );
155       }
156     }
157
158     // For each MeshData object, create a mesh actor and add to the renderable actor
159     if ( meshContainer.size() )
160     {
161       for ( uint32_t i = 0; i < meshContainer.size(); ++i )
162       {
163         MeshActor actor = MeshActor::New( Mesh::New( meshContainer[ i ].mMeshData ) );
164         actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
165         actor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
166
167         // Check to see what pixel format the shader should be
168         if ( mGlyphManager.GetPixelFormat( meshContainer[ i ].mAtlasId ) == Pixel::L8 )
169         {
170           // Create an effect if necessary
171           if ( style == STYLE_DROP_SHADOW )
172           {
173             actor.Add( GenerateEffect( meshContainer[ i ], shadowOffset, shadowColor ) );
174           }
175           actor.SetShaderEffect( mBasicShader );
176         }
177         else
178         {
179           actor.SetShaderEffect( mBgraShader );
180         }
181
182         if ( i )
183         {
184           mActor.Add( actor );
185         }
186         else
187         {
188           mActor = actor;
189         }
190       }
191       mActor.OffStageSignal().Connect( this, &AtlasRenderer::Impl::OffStageDisconnect );
192     }
193 #if defined(DEBUG_ENABLED)
194     Toolkit::AtlasGlyphManager::Metrics metrics = mGlyphManager.GetMetrics();
195     DALI_LOG_INFO( gLogFilter, Debug::Concise, "TextAtlasRenderer::GlyphManager::GlyphCount: %i, AtlasCount: %i, TextureMemoryUse: %iK\n",
196                                                 metrics.mGlyphCount,
197                                                 metrics.mAtlasMetrics.mAtlasCount,
198                                                 metrics.mAtlasMetrics.mTextureMemoryUsed / 1024 );
199     for ( uint32_t i = 0; i < metrics.mAtlasMetrics.mAtlasCount; ++i )
200     {
201       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Atlas [%i] %sPixels: %s Size: %ix%i, BlockSize: %ix%i, BlocksUsed: %i/%i\n",
202                                                  i + 1, i > 8 ? "" : " ",
203                                                  metrics.mAtlasMetrics.mAtlasMetrics[ i ].mPixelFormat == Pixel::L8 ? "L8  " : "BGRA",
204                                                  metrics.mAtlasMetrics.mAtlasMetrics[ i ].mWidth,
205                                                  metrics.mAtlasMetrics.mAtlasMetrics[ i ].mHeight,
206                                                  metrics.mAtlasMetrics.mAtlasMetrics[ i ].mBlockWidth,
207                                                  metrics.mAtlasMetrics.mAtlasMetrics[ i ].mBlockHeight,
208                                                  metrics.mAtlasMetrics.mAtlasMetrics[ i ].mBlocksUsed,
209                                                  metrics.mAtlasMetrics.mAtlasMetrics[ i ].mTotalBlocks );
210     }
211 #endif
212   }
213
214   void StitchTextMesh( std::vector< MeshRecord >& meshContainer,
215                        MeshData& newMeshData,
216                        AtlasManager::AtlasSlot& slot )
217   {
218     if ( slot.mImageId )
219     {
220       // Check to see if there's a mesh data object that references the same atlas ?
221       for ( uint32_t i = 0; i < meshContainer.size(); ++i )
222       {
223         if ( slot.mAtlasId == meshContainer[ i ].mAtlasId )
224         {
225           // Stitch the mesh to the existing mesh
226           mGlyphManager.StitchMesh( meshContainer[ i ].mMeshData, newMeshData );
227           return;
228         }
229       }
230
231       // No mesh data object currently exists that references this atlas, so create a new one
232       MeshRecord meshRecord;
233       meshRecord.mAtlasId = slot.mAtlasId;
234       meshRecord.mMeshData = newMeshData;
235       meshContainer.push_back( meshRecord );
236     }
237   }
238
239   // Unreference any glyphs that were used with this actor
240   void OffStageDisconnect( Dali::Actor actor )
241   {
242     RemoveText();
243   }
244
245   void RemoveText()
246   {
247     for ( uint32_t i = 0; i < mImageIds.Size(); ++i )
248     {
249       mGlyphManager.Remove( mImageIds[ i ] );
250     }
251     mImageIds.Resize( 0 );
252   }
253
254   void CalculateBlocksSize( const Vector<GlyphInfo>& glyphs )
255   {
256     MaxBlockSize maxBlockSize;
257     for ( uint32_t i = 0; i < glyphs.Size(); ++i )
258     {
259       // Get the fontId of this glyph and check to see if a max size exists?
260       FontId fontId = glyphs[ i ].fontId;
261       float paddedWidth = glyphs[ i ].width + PADDING.x;
262       float paddedHeight = glyphs[ i ].height + PADDING.y;
263       bool foundFont = false;
264
265       for ( uint32_t j = 0; j < mBlockSizes.size(); ++j )
266       {
267         if ( mBlockSizes[ j ].mFontId == fontId )
268         {
269           foundFont = true;
270           if ( mBlockSizes[ j ].mNeededBlockSize.x < paddedWidth )
271           {
272             mBlockSizes[ j ].mNeededBlockSize.x = paddedWidth;
273           }
274           if ( mBlockSizes[ j ].mNeededBlockSize.y < paddedHeight )
275           {
276             mBlockSizes[ j ].mNeededBlockSize.y = paddedHeight;
277           }
278         }
279       }
280
281       if ( !foundFont )
282       {
283         maxBlockSize.mNeededBlockSize = Vector2( paddedWidth, paddedHeight );
284         maxBlockSize.mFontId = fontId;
285         mBlockSizes.push_back( maxBlockSize );
286       }
287     }
288   }
289
290   MeshActor GenerateEffect( MeshRecord& meshRecord,
291                             const Vector2& shadowOffset,
292                             const Vector4& shadowColor )
293   {
294     // Scan vertex buffer to determine width and height of effect buffer needed
295     MeshData::VertexContainer verts = meshRecord.mMeshData.GetVertices();
296     const float zero = 0.0f;
297     const float one = 1.0f;
298     float tlx = verts[ 0 ].x;
299     float tly = verts[ 0 ].y;
300     float brx = zero;
301     float bry = zero;
302
303     for ( uint32_t i = 0; i < verts.size(); ++i )
304     {
305       if ( verts[ i ].x < tlx )
306       {
307         tlx = verts[ i ].x;
308       }
309       if ( verts[ i ].y < tly )
310       {
311         tly = verts[ i ].y;
312       }
313       if ( verts[ i ].x > brx )
314       {
315         brx = verts[ i ].x;
316       }
317       if ( verts[ i ].y > bry )
318       {
319         bry = verts[ i ].y;
320       }
321     }
322
323     float width = brx - tlx;
324     float height = bry - tly;
325     float divWidth = 2.0f / width;
326     float divHeight = 2.0f / height;
327
328     // Create a buffer to render to
329     // TODO bloom style filter from this buffer
330     meshRecord.mBuffer = FrameBufferImage::New( width, height );
331
332     // Create a mesh actor to contain the post-effect render
333     MeshData::VertexContainer vertices;
334     MeshData::FaceIndices face;
335
336     vertices.push_back( MeshData::Vertex( Vector3( tlx + shadowOffset.x, tly + shadowOffset.y, zero ),
337                                           Vector2( zero, zero ),
338                                           Vector3( zero, zero, zero ) ) );
339
340     vertices.push_back( MeshData::Vertex( Vector3( brx + shadowOffset.x, tly + shadowOffset.y, zero ),
341                                           Vector2( one, zero ),
342                                           Vector3( zero, zero, zero ) ) );
343
344     vertices.push_back( MeshData::Vertex( Vector3( tlx + shadowOffset.x, bry + shadowOffset.y, zero ),
345                                           Vector2( zero, one ),
346                                           Vector3( zero, zero, zero ) ) );
347
348     vertices.push_back( MeshData::Vertex( Vector3( brx + shadowOffset.x, bry + shadowOffset.y, zero ),
349                                           Vector2( one, one ),
350                                           Vector3( zero, zero, zero ) ) );
351
352     face.push_back( 0 ); face.push_back( 2u ); face.push_back( 1u );
353     face.push_back( 1u ); face.push_back( 2u ); face.push_back( 3u );
354
355     MeshData meshData;
356     Material newMaterial = Material::New("effect buffer");
357     newMaterial.SetDiffuseTexture( meshRecord.mBuffer );
358     meshData.SetMaterial( newMaterial );
359     meshData.SetVertices( vertices );
360     meshData.SetFaceIndices( face );
361     meshData.SetHasNormals( true );
362     meshData.SetHasColor( false );
363     meshData.SetHasTextureCoords( true );
364     MeshActor actor = MeshActor::New( Mesh::New( meshData ) );
365     actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
366     actor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
367     actor.SetShaderEffect( mBgraShader );
368     actor.SetFilterMode( FilterMode::LINEAR, FilterMode::LINEAR );
369     actor.SetSortModifier( one ); // force behind main text
370
371     // Create a sub actor to render once with normalized vertex positions
372     MeshData newMeshData;
373     MeshData::VertexContainer newVerts;
374     MeshData::FaceIndices newFaces;
375     MeshData::FaceIndices faces = meshRecord.mMeshData.GetFaces();
376     for ( uint32_t i = 0; i < verts.size(); ++i )
377     {
378       MeshData::Vertex vertex = verts[ i ];
379       vertex.x = ( ( vertex.x - tlx ) * divWidth ) - one;
380       vertex.y = ( ( vertex.y - tly ) * divHeight ) - one;
381       newVerts.push_back( vertex );
382     }
383
384     // Reverse triangle winding order
385     uint32_t faceCount = faces.size() / 3;
386     for ( uint32_t i = 0; i < faceCount; ++i )
387     {
388       uint32_t index = i * 3;
389       newFaces.push_back( faces[ index + 2 ] );
390       newFaces.push_back( faces[ index + 1 ] );
391       newFaces.push_back( faces[ index ] );
392     }
393
394     newMeshData.SetMaterial( meshRecord.mMeshData.GetMaterial() );
395     newMeshData.SetVertices( newVerts );
396     newMeshData.SetFaceIndices( newFaces );
397     newMeshData.SetHasNormals( true );
398     newMeshData.SetHasColor( false );
399     newMeshData.SetHasTextureCoords( true );
400
401     MeshActor subActor = MeshActor::New( Mesh::New( newMeshData ) );
402     subActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
403     subActor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
404     subActor.SetColor( shadowColor );
405     subActor.SetShaderEffect( mBasicShadowShader );
406     subActor.SetFilterMode( FilterMode::NEAREST, FilterMode::NEAREST );
407
408     // Create a render task to render the effect
409     RenderTask task = Stage::GetCurrent().GetRenderTaskList().CreateTask();
410     task.SetTargetFrameBuffer( meshRecord.mBuffer );
411     task.SetSourceActor( subActor );
412     task.SetClearEnabled( true );
413     task.SetClearColor( Vector4::ZERO );
414     task.SetExclusive( true );
415     task.SetRefreshRate( RenderTask::REFRESH_ONCE );
416     task.FinishedSignal().Connect( this, &AtlasRenderer::Impl::RenderComplete );
417     actor.Add( subActor );
418     return actor;
419   }
420
421   void RenderComplete( RenderTask& renderTask )
422   {
423     // Disconnect and remove this single shot render task
424     renderTask.FinishedSignal().Disconnect( this, &AtlasRenderer::Impl::RenderComplete );
425     Stage::GetCurrent().GetRenderTaskList().RemoveTask( renderTask );
426
427     // Get the actor used for render to buffer and remove it from the parent
428     Actor renderActor = renderTask.GetSourceActor();
429     if ( renderActor )
430     {
431       Actor parent = renderActor.GetParent();
432       if ( parent )
433       {
434         parent.Remove( renderActor );
435       }
436     }
437   }
438
439   RenderableActor mActor;                             ///< The actor parent which renders the text
440   AtlasGlyphManager mGlyphManager;                    ///< Glyph Manager to handle upload and caching
441   Vector< uint32_t > mImageIds;                       ///< A list of imageIDs used by the renderer
442   TextAbstraction::FontClient mFontClient;            ///> The font client used to supply glyph information
443   ShaderEffect mBasicShader;                          ///> Shader used to render L8 glyphs
444   ShaderEffect mBgraShader;                           ///> Shader used to render BGRA glyphs
445   ShaderEffect mBasicShadowShader;                    ///> Shader used to render drop shadow into buffer
446   std::vector< MaxBlockSize > mBlockSizes;            ///> Maximum size needed to contain a glyph in a block within a new atlas
447 };
448
449 Text::RendererPtr AtlasRenderer::New()
450 {
451   return Text::RendererPtr( new AtlasRenderer() );
452 }
453
454 RenderableActor AtlasRenderer::Render( Text::ViewInterface& view )
455 {
456
457   UnparentAndReset( mImpl->mActor );
458
459   Text::Length numberOfGlyphs = view.GetNumberOfGlyphs();
460
461   if( numberOfGlyphs > 0 )
462   {
463     Vector<GlyphInfo> glyphs;
464     glyphs.Resize( numberOfGlyphs );
465
466     view.GetGlyphs( &glyphs[0], 0, numberOfGlyphs );
467
468     std::vector<Vector2> positions;
469     positions.resize( numberOfGlyphs );
470     view.GetGlyphPositions( &positions[0], 0, numberOfGlyphs );
471     mImpl->AddGlyphs( positions,
472                       glyphs,
473                       view.GetShadowOffset(),
474                       view.GetShadowColor() );
475   }
476   return mImpl->mActor;
477 }
478
479 AtlasRenderer::AtlasRenderer()
480 {
481   mImpl = new Impl();
482
483 }
484
485 AtlasRenderer::~AtlasRenderer()
486 {
487   delete mImpl;
488 }