Merge "Added metrics logging to TextAtlasRenderer" into new_text
[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
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
47 {
48
49   struct MeshRecord
50   {
51     uint32_t mAtlasId;
52     MeshData mMeshData;
53   };
54
55   struct AtlasRecord
56   {
57     uint32_t mImageId;
58     Text::GlyphIndex mIndex;
59   };
60
61   struct MaxBlockSize
62   {
63     FontId mFontId;
64     Vector2 mNeededBlockSize;
65   };
66
67   Impl()
68   : mSlotDelegate( this )
69   {
70     mGlyphManager = AtlasGlyphManager::Get();
71     mFontClient = TextAbstraction::FontClient::Get();
72     mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_SIZE, DEFAULT_BLOCK_SIZE );
73     mBasicShader = BasicShader::New();
74     mBGRAShader = BgraShader::New();
75   }
76
77   void AddGlyphs( const std::vector<Vector2>& positions, const Vector<GlyphInfo>& glyphs )
78   {
79     AtlasManager::AtlasSlot slot;
80     std::vector< MeshRecord > meshContainer;
81     FontId lastFontId = 0;
82
83     if (mImageIds.Size() )
84     {
85       // Unreference any currently used glyphs
86       RemoveText();
87     }
88
89     CalculateBlocksSize( glyphs );
90
91     for ( uint32_t i = 0; i < glyphs.Size(); ++i )
92     {
93       GlyphInfo glyph = glyphs[ i ];
94
95       // No operation for white space
96       if ( glyph.width && glyph.height )
97       {
98         Vector2 position = positions[ i ];
99         MeshData newMeshData;
100         mGlyphManager.Cached( glyph.fontId, glyph.index, slot );
101
102         if ( slot.mImageId )
103         {
104           // This glyph already exists so generate mesh data plugging in our supplied position
105           mGlyphManager.GenerateMeshData( slot.mImageId, position, newMeshData );
106           mImageIds.PushBack( slot.mImageId );
107         }
108         else
109         {
110
111           // Select correct size for new atlas if needed....?
112           if ( lastFontId != glyph.fontId )
113           {
114             for ( uint32_t j = 0; j < mBlockSizes.size(); ++j )
115             {
116               if ( mBlockSizes[ j ].mFontId == glyph.fontId )
117               {
118                 mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_SIZE, mBlockSizes[ j ].mNeededBlockSize );
119               }
120             }
121             lastFontId = glyph.fontId;
122           }
123
124           // Glyph doesn't currently exist in atlas so upload
125           BufferImage bitmap = mFontClient.CreateBitmap( glyph.fontId, glyph.index );
126
127           // Locate a new slot for our glyph
128           mGlyphManager.Add( glyph, bitmap, slot );
129
130           // Generate mesh data for this quad, plugging in our supplied position
131           if ( slot.mImageId )
132           {
133             mGlyphManager.GenerateMeshData( slot.mImageId, position, newMeshData );
134             mImageIds.PushBack( slot.mImageId );
135           }
136         }
137         // Find an existing mesh data object to attach to ( or create a new one, if we can't find one using the same atlas)
138         StitchTextMesh( meshContainer, newMeshData, slot );
139       }
140     }
141
142     // For each MeshData object, create a mesh actor and add to the renderable actor
143     if ( meshContainer.size() )
144     {
145       for ( uint32_t i = 0; i < meshContainer.size(); ++i )
146       {
147         Mesh mesh = Mesh::New( meshContainer[ i ].mMeshData );
148         MeshActor actor = MeshActor::New( mesh );
149         actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
150         actor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );;
151
152         // Check to see what pixel format the shader should be
153         if ( mGlyphManager.GetPixelFormat( meshContainer[ i ].mAtlasId ) == Pixel::L8 )
154         {
155           actor.SetShaderEffect( mBasicShader );
156         }
157         else
158         {
159           actor.SetShaderEffect( mBGRAShader );
160         }
161
162         if ( i )
163         {
164           mActor.Add( actor );
165         }
166         else
167         {
168           mActor = actor;
169         }
170       }
171       mActor.OffStageSignal().Connect( mSlotDelegate, &AtlasRenderer::Impl::OffStageDisconnect );
172     }
173 #if defined(DEBUG_ENABLED)
174     Toolkit::AtlasGlyphManager::Metrics metrics = mGlyphManager.GetMetrics();
175     DALI_LOG_INFO( gLogFilter, Debug::Concise, "TextAtlasRenderer::GlyphManager::GlyphCount: %i, AtlasCount: %i, TextureMemoryUse: %iK\n",
176                                                 metrics.mGlyphCount,
177                                                 metrics.mAtlasMetrics.mAtlasCount,
178                                                 metrics.mAtlasMetrics.mTextureMemoryUsed / 1024 );
179     for ( uint32_t i = 0; i < metrics.mAtlasMetrics.mAtlasCount; ++i )
180     {
181       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Atlas [%i] %sPixels: %s Size: %ix%i, BlockSize: %ix%i, BlocksUsed: %i/%i\n",
182                                                  i + 1, i > 8 ? "" : " ",
183                                                  metrics.mAtlasMetrics.mAtlasMetrics[ i ].mPixelFormat == Pixel::L8 ? "L8  " : "BGRA",
184                                                  metrics.mAtlasMetrics.mAtlasMetrics[ i ].mWidth,
185                                                  metrics.mAtlasMetrics.mAtlasMetrics[ i ].mHeight,
186                                                  metrics.mAtlasMetrics.mAtlasMetrics[ i ].mBlockWidth,
187                                                  metrics.mAtlasMetrics.mAtlasMetrics[ i ].mBlockHeight,
188                                                  metrics.mAtlasMetrics.mAtlasMetrics[ i ].mBlocksUsed,
189                                                  metrics.mAtlasMetrics.mAtlasMetrics[ i ].mTotalBlocks );
190     }
191 #endif
192   }
193
194   void StitchTextMesh( std::vector< MeshRecord >& meshContainer,
195                        MeshData& newMeshData,
196                        AtlasManager::AtlasSlot& slot )
197   {
198     if ( slot.mImageId )
199     {
200       // Check to see if there's a mesh data object that references the same atlas ?
201       for ( uint32_t i = 0; i < meshContainer.size(); ++i )
202       {
203         if ( slot.mAtlasId == meshContainer[ i ].mAtlasId )
204         {
205           // Stitch the mesh to the existing mesh
206           mGlyphManager.StitchMesh( meshContainer[ i ].mMeshData, newMeshData );
207           return;
208         }
209       }
210
211       // No mesh data object currently exists that references this atlas, so create a new one
212       MeshRecord meshRecord;
213       meshRecord.mAtlasId = slot.mAtlasId;
214       meshRecord.mMeshData = newMeshData;
215       meshContainer.push_back( meshRecord );
216     }
217   }
218
219   // Unreference any glyphs that were used with this actor
220   void OffStageDisconnect( Dali::Actor actor )
221   {
222     RemoveText();
223   }
224
225   void RemoveText()
226   {
227     for ( uint32_t i = 0; i < mImageIds.Size(); ++i )
228     {
229       mGlyphManager.Remove( mImageIds[ i ] );
230     }
231     mImageIds.Resize( 0 );
232   }
233
234   void CalculateBlocksSize( const Vector<GlyphInfo>& glyphs )
235   {
236     MaxBlockSize maxBlockSize;
237     for ( uint32_t i = 0; i < glyphs.Size(); ++i )
238     {
239       // Get the fontId of this glyph and check to see if a max size exists?
240       FontId fontId = glyphs[ i ].fontId;
241       float paddedWidth = glyphs[ i ].width + PADDING.x;
242       float paddedHeight = glyphs[ i ].height + PADDING.y;
243       bool foundFont = false;
244
245       for ( uint32_t j = 0; j < mBlockSizes.size(); ++j )
246       {
247         if ( mBlockSizes[ j ].mFontId == fontId )
248         {
249           foundFont = true;
250           if ( mBlockSizes[ j ].mNeededBlockSize.x < paddedWidth )
251           {
252             mBlockSizes[ j ].mNeededBlockSize.x = paddedWidth;
253           }
254           if ( mBlockSizes[ j ].mNeededBlockSize.y < paddedHeight )
255           {
256             mBlockSizes[ j ].mNeededBlockSize.y = paddedHeight;
257           }
258         }
259       }
260
261       if ( !foundFont )
262       {
263         maxBlockSize.mNeededBlockSize = Vector2( paddedWidth, paddedHeight );
264         maxBlockSize.mFontId = fontId;
265         mBlockSizes.push_back( maxBlockSize );
266       }
267     }
268   }
269
270   RenderableActor mActor;                             ///< The actor parent which renders the text
271   AtlasGlyphManager mGlyphManager;                    ///< Glyph Manager to handle upload and caching
272   Vector< uint32_t > mImageIds;                       ///< A list of imageIDs used by the renderer
273   TextAbstraction::FontClient mFontClient;            ///> The font client used to supply glyph information
274   SlotDelegate< AtlasRenderer::Impl > mSlotDelegate;  ///> Signal generated to unreference glyphs when renderable actor is removed
275   ShaderEffect mBasicShader;                          ///> Shader to render L8 glyphs
276   ShaderEffect mBGRAShader;                           ///> Shader to render BGRA glyphs
277   std::vector< MaxBlockSize > mBlockSizes;            ///> Maximum size needed to contain a glyph in a block within a new atlas
278 };
279
280 Text::RendererPtr AtlasRenderer::New()
281 {
282   return Text::RendererPtr( new AtlasRenderer() );
283 }
284
285 RenderableActor AtlasRenderer::Render( Text::ViewInterface& view )
286 {
287
288   UnparentAndReset( mImpl->mActor );
289
290   Text::Length numberOfGlyphs = view.GetNumberOfGlyphs();
291
292   if( numberOfGlyphs > 0 )
293   {
294     Vector<GlyphInfo> glyphs;
295     glyphs.Resize( numberOfGlyphs );
296
297     view.GetGlyphs( &glyphs[0], 0, numberOfGlyphs );
298
299     std::vector<Vector2> positions;
300     positions.resize( numberOfGlyphs );
301     view.GetGlyphPositions( &positions[0], 0, numberOfGlyphs );
302     mImpl->AddGlyphs( positions, glyphs );
303   }
304   return mImpl->mActor;
305 }
306
307 AtlasRenderer::AtlasRenderer()
308 {
309   mImpl = new Impl();
310
311 }
312
313 AtlasRenderer::~AtlasRenderer()
314 {
315   delete mImpl;
316 }