2 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.h>
22 #include <dali/dali.h>
23 #include <dali/integration-api/debug.h>
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");
36 using namespace Dali::Toolkit;
37 using namespace Dali::Toolkit::Text;
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
46 struct AtlasRenderer::Impl : public ConnectionTracker
59 FrameBufferImage mBuffer;
65 Text::GlyphIndex mIndex;
71 Vector2 mNeededBlockSize;
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();
84 void AddGlyphs( const std::vector<Vector2>& positions,
85 const Vector<GlyphInfo>& glyphs,
86 const Vector2& shadowOffset,
87 const Vector4& shadowColor )
89 AtlasManager::AtlasSlot slot;
90 std::vector< MeshRecord > meshContainer;
91 FontId lastFontId = 0;
92 Style style = STYLE_NORMAL;
94 if ( shadowOffset.x > 0.0f || shadowOffset.y > 0.0f )
96 style = STYLE_DROP_SHADOW;
99 if ( mImageIds.Size() )
101 // Unreference any currently used glyphs
105 CalculateBlocksSize( glyphs );
107 for ( uint32_t i = 0; i < glyphs.Size(); ++i )
109 GlyphInfo glyph = glyphs[ i ];
111 // No operation for white space
112 if ( glyph.width && glyph.height )
114 Vector2 position = positions[ i ];
115 MeshData newMeshData;
116 mGlyphManager.Cached( glyph.fontId, glyph.index, slot );
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 );
127 // Select correct size for new atlas if needed....?
128 if ( lastFontId != glyph.fontId )
130 for ( uint32_t j = 0; j < mBlockSizes.size(); ++j )
132 if ( mBlockSizes[ j ].mFontId == glyph.fontId )
134 mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_SIZE, mBlockSizes[ j ].mNeededBlockSize );
137 lastFontId = glyph.fontId;
140 // Glyph doesn't currently exist in atlas so upload
141 BufferImage bitmap = mFontClient.CreateBitmap( glyph.fontId, glyph.index );
143 // Locate a new slot for our glyph
144 mGlyphManager.Add( glyph, bitmap, slot );
146 // Generate mesh data for this quad, plugging in our supplied position
149 mGlyphManager.GenerateMeshData( slot.mImageId, position, newMeshData );
150 mImageIds.PushBack( slot.mImageId );
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 );
158 // For each MeshData object, create a mesh actor and add to the renderable actor
159 if ( meshContainer.size() )
161 for ( uint32_t i = 0; i < meshContainer.size(); ++i )
163 MeshActor actor = MeshActor::New( Mesh::New( meshContainer[ i ].mMeshData ) );
164 actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
165 actor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
167 // Check to see what pixel format the shader should be
168 if ( mGlyphManager.GetPixelFormat( meshContainer[ i ].mAtlasId ) == Pixel::L8 )
170 // Create an effect if necessary
171 if ( style == STYLE_DROP_SHADOW )
173 actor.Add( GenerateEffect( meshContainer[ i ], shadowOffset, shadowColor ) );
175 actor.SetShaderEffect( mBasicShader );
179 actor.SetShaderEffect( mBgraShader );
191 mActor.OffStageSignal().Connect( this, &AtlasRenderer::Impl::OffStageDisconnect );
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",
197 metrics.mAtlasMetrics.mAtlasCount,
198 metrics.mAtlasMetrics.mTextureMemoryUsed / 1024 );
199 for ( uint32_t i = 0; i < metrics.mAtlasMetrics.mAtlasCount; ++i )
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 );
214 void StitchTextMesh( std::vector< MeshRecord >& meshContainer,
215 MeshData& newMeshData,
216 AtlasManager::AtlasSlot& slot )
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 )
223 if ( slot.mAtlasId == meshContainer[ i ].mAtlasId )
225 // Stitch the mesh to the existing mesh
226 mGlyphManager.StitchMesh( meshContainer[ i ].mMeshData, newMeshData );
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 );
239 // Unreference any glyphs that were used with this actor
240 void OffStageDisconnect( Dali::Actor actor )
247 for ( uint32_t i = 0; i < mImageIds.Size(); ++i )
249 mGlyphManager.Remove( mImageIds[ i ] );
251 mImageIds.Resize( 0 );
254 void CalculateBlocksSize( const Vector<GlyphInfo>& glyphs )
256 MaxBlockSize maxBlockSize;
257 for ( uint32_t i = 0; i < glyphs.Size(); ++i )
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;
265 for ( uint32_t j = 0; j < mBlockSizes.size(); ++j )
267 if ( mBlockSizes[ j ].mFontId == fontId )
270 if ( mBlockSizes[ j ].mNeededBlockSize.x < paddedWidth )
272 mBlockSizes[ j ].mNeededBlockSize.x = paddedWidth;
274 if ( mBlockSizes[ j ].mNeededBlockSize.y < paddedHeight )
276 mBlockSizes[ j ].mNeededBlockSize.y = paddedHeight;
283 maxBlockSize.mNeededBlockSize = Vector2( paddedWidth, paddedHeight );
284 maxBlockSize.mFontId = fontId;
285 mBlockSizes.push_back( maxBlockSize );
290 MeshActor GenerateEffect( MeshRecord& meshRecord,
291 const Vector2& shadowOffset,
292 const Vector4& shadowColor )
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;
303 for ( uint32_t i = 0; i < verts.size(); ++i )
305 if ( verts[ i ].x < tlx )
309 if ( verts[ i ].y < tly )
313 if ( verts[ i ].x > brx )
317 if ( verts[ i ].y > bry )
323 float width = brx - tlx;
324 float height = bry - tly;
325 float divWidth = 2.0f / width;
326 float divHeight = 2.0f / height;
328 // Create a buffer to render to
329 // TODO bloom style filter from this buffer
330 meshRecord.mBuffer = FrameBufferImage::New( width, height );
332 // Create a mesh actor to contain the post-effect render
333 MeshData::VertexContainer vertices;
334 MeshData::FaceIndices face;
336 vertices.push_back( MeshData::Vertex( Vector3( tlx + shadowOffset.x, tly + shadowOffset.y, zero ),
337 Vector2( zero, zero ),
338 Vector3( zero, zero, zero ) ) );
340 vertices.push_back( MeshData::Vertex( Vector3( brx + shadowOffset.x, tly + shadowOffset.y, zero ),
341 Vector2( one, zero ),
342 Vector3( zero, zero, zero ) ) );
344 vertices.push_back( MeshData::Vertex( Vector3( tlx + shadowOffset.x, bry + shadowOffset.y, zero ),
345 Vector2( zero, one ),
346 Vector3( zero, zero, zero ) ) );
348 vertices.push_back( MeshData::Vertex( Vector3( brx + shadowOffset.x, bry + shadowOffset.y, zero ),
350 Vector3( zero, zero, zero ) ) );
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 );
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
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 )
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 );
384 // Reverse triangle winding order
385 uint32_t faceCount = faces.size() / 3;
386 for ( uint32_t i = 0; i < faceCount; ++i )
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 ] );
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 );
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 );
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 );
421 void RenderComplete( RenderTask& renderTask )
423 // Get the actor used for render to buffer and remove it from the parent
424 Actor renderActor = renderTask.GetSourceActor();
427 Actor parent = renderActor.GetParent();
430 parent.Remove( renderActor );
435 RenderableActor mActor; ///< The actor parent which renders the text
436 AtlasGlyphManager mGlyphManager; ///< Glyph Manager to handle upload and caching
437 Vector< uint32_t > mImageIds; ///< A list of imageIDs used by the renderer
438 TextAbstraction::FontClient mFontClient; ///> The font client used to supply glyph information
439 ShaderEffect mBasicShader; ///> Shader used to render L8 glyphs
440 ShaderEffect mBgraShader; ///> Shader used to render BGRA glyphs
441 ShaderEffect mBasicShadowShader; ///> Shader used to render drop shadow into buffer
442 std::vector< MaxBlockSize > mBlockSizes; ///> Maximum size needed to contain a glyph in a block within a new atlas
445 Text::RendererPtr AtlasRenderer::New()
447 return Text::RendererPtr( new AtlasRenderer() );
450 RenderableActor AtlasRenderer::Render( Text::ViewInterface& view )
453 UnparentAndReset( mImpl->mActor );
455 Text::Length numberOfGlyphs = view.GetNumberOfGlyphs();
457 if( numberOfGlyphs > 0 )
459 Vector<GlyphInfo> glyphs;
460 glyphs.Resize( numberOfGlyphs );
462 view.GetGlyphs( &glyphs[0], 0, numberOfGlyphs );
464 std::vector<Vector2> positions;
465 positions.resize( numberOfGlyphs );
466 view.GetGlyphPositions( &positions[0], 0, numberOfGlyphs );
467 mImpl->AddGlyphs( positions,
469 view.GetShadowOffset(),
470 view.GetShadowColor() );
472 return mImpl->mActor;
475 AtlasRenderer::AtlasRenderer()
481 AtlasRenderer::~AtlasRenderer()