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.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
166 // Check to see what pixel format the shader should be
167 if ( mGlyphManager.GetPixelFormat( meshContainer[ i ].mAtlasId ) == Pixel::L8 )
169 // Create an effect if necessary
170 if ( style == STYLE_DROP_SHADOW )
172 actor.Add( GenerateEffect( meshContainer[ i ], shadowOffset, shadowColor ) );
174 actor.SetShaderEffect( mBasicShader );
178 actor.SetShaderEffect( mBgraShader );
190 mActor.OffStageSignal().Connect( this, &AtlasRenderer::Impl::OffStageDisconnect );
192 #if defined(DEBUG_ENABLED)
193 Toolkit::AtlasGlyphManager::Metrics metrics = mGlyphManager.GetMetrics();
194 DALI_LOG_INFO( gLogFilter, Debug::Concise, "TextAtlasRenderer::GlyphManager::GlyphCount: %i, AtlasCount: %i, TextureMemoryUse: %iK\n",
196 metrics.mAtlasMetrics.mAtlasCount,
197 metrics.mAtlasMetrics.mTextureMemoryUsed / 1024 );
198 for ( uint32_t i = 0; i < metrics.mAtlasMetrics.mAtlasCount; ++i )
200 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Atlas [%i] %sPixels: %s Size: %ix%i, BlockSize: %ix%i, BlocksUsed: %i/%i\n",
201 i + 1, i > 8 ? "" : " ",
202 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mPixelFormat == Pixel::L8 ? "L8 " : "BGRA",
203 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mWidth,
204 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mHeight,
205 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mBlockWidth,
206 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mBlockHeight,
207 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mBlocksUsed,
208 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mTotalBlocks );
213 void StitchTextMesh( std::vector< MeshRecord >& meshContainer,
214 MeshData& newMeshData,
215 AtlasManager::AtlasSlot& slot )
219 // Check to see if there's a mesh data object that references the same atlas ?
220 for ( uint32_t i = 0; i < meshContainer.size(); ++i )
222 if ( slot.mAtlasId == meshContainer[ i ].mAtlasId )
224 // Stitch the mesh to the existing mesh
225 mGlyphManager.StitchMesh( meshContainer[ i ].mMeshData, newMeshData );
230 // No mesh data object currently exists that references this atlas, so create a new one
231 MeshRecord meshRecord;
232 meshRecord.mAtlasId = slot.mAtlasId;
233 meshRecord.mMeshData = newMeshData;
234 meshContainer.push_back( meshRecord );
238 // Unreference any glyphs that were used with this actor
239 void OffStageDisconnect( Dali::Actor actor )
246 for ( uint32_t i = 0; i < mImageIds.Size(); ++i )
248 mGlyphManager.Remove( mImageIds[ i ] );
250 mImageIds.Resize( 0 );
253 void CalculateBlocksSize( const Vector<GlyphInfo>& glyphs )
255 MaxBlockSize maxBlockSize;
256 for ( uint32_t i = 0; i < glyphs.Size(); ++i )
258 // Get the fontId of this glyph and check to see if a max size exists?
259 FontId fontId = glyphs[ i ].fontId;
260 float paddedWidth = glyphs[ i ].width + PADDING.x;
261 float paddedHeight = glyphs[ i ].height + PADDING.y;
262 bool foundFont = false;
264 for ( uint32_t j = 0; j < mBlockSizes.size(); ++j )
266 if ( mBlockSizes[ j ].mFontId == fontId )
269 if ( mBlockSizes[ j ].mNeededBlockSize.x < paddedWidth )
271 mBlockSizes[ j ].mNeededBlockSize.x = paddedWidth;
273 if ( mBlockSizes[ j ].mNeededBlockSize.y < paddedHeight )
275 mBlockSizes[ j ].mNeededBlockSize.y = paddedHeight;
282 maxBlockSize.mNeededBlockSize = Vector2( paddedWidth, paddedHeight );
283 maxBlockSize.mFontId = fontId;
284 mBlockSizes.push_back( maxBlockSize );
289 MeshActor GenerateEffect( MeshRecord& meshRecord,
290 const Vector2& shadowOffset,
291 const Vector4& shadowColor )
293 // Scan vertex buffer to determine width and height of effect buffer needed
294 MeshData::VertexContainer verts = meshRecord.mMeshData.GetVertices();
295 const float zero = 0.0f;
296 const float one = 1.0f;
297 float tlx = verts[ 0 ].x;
298 float tly = verts[ 0 ].y;
302 for ( uint32_t i = 0; i < verts.size(); ++i )
304 if ( verts[ i ].x < tlx )
308 if ( verts[ i ].y < tly )
312 if ( verts[ i ].x > brx )
316 if ( verts[ i ].y > bry )
322 float width = brx - tlx;
323 float height = bry - tly;
324 float divWidth = 2.0f / width;
325 float divHeight = 2.0f / height;
327 // Create a buffer to render to
328 // TODO bloom style filter from this buffer
329 meshRecord.mBuffer = FrameBufferImage::New( width, height );
331 // Create a mesh actor to contain the post-effect render
332 MeshData::VertexContainer vertices;
333 MeshData::FaceIndices face;
335 vertices.push_back( MeshData::Vertex( Vector3( tlx + shadowOffset.x, tly + shadowOffset.y, zero ),
336 Vector2( zero, zero ),
337 Vector3( zero, zero, zero ) ) );
339 vertices.push_back( MeshData::Vertex( Vector3( brx + shadowOffset.x, tly + shadowOffset.y, zero ),
340 Vector2( one, zero ),
341 Vector3( zero, zero, zero ) ) );
343 vertices.push_back( MeshData::Vertex( Vector3( tlx + shadowOffset.x, bry + shadowOffset.y, zero ),
344 Vector2( zero, one ),
345 Vector3( zero, zero, zero ) ) );
347 vertices.push_back( MeshData::Vertex( Vector3( brx + shadowOffset.x, bry + shadowOffset.y, zero ),
349 Vector3( zero, zero, zero ) ) );
351 face.push_back( 0 ); face.push_back( 2u ); face.push_back( 1u );
352 face.push_back( 1u ); face.push_back( 2u ); face.push_back( 3u );
355 Material newMaterial = Material::New("effect buffer");
356 newMaterial.SetDiffuseTexture( meshRecord.mBuffer );
357 meshData.SetMaterial( newMaterial );
358 meshData.SetVertices( vertices );
359 meshData.SetFaceIndices( face );
360 meshData.SetHasNormals( true );
361 meshData.SetHasColor( false );
362 meshData.SetHasTextureCoords( true );
363 MeshActor actor = MeshActor::New( Mesh::New( meshData ) );
364 actor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
365 actor.SetShaderEffect( mBgraShader );
366 actor.SetFilterMode( FilterMode::LINEAR, FilterMode::LINEAR );
367 actor.SetSortModifier( one ); // force behind main text
369 // Create a sub actor to render once with normalized vertex positions
370 MeshData newMeshData;
371 MeshData::VertexContainer newVerts;
372 MeshData::FaceIndices newFaces;
373 MeshData::FaceIndices faces = meshRecord.mMeshData.GetFaces();
374 for ( uint32_t i = 0; i < verts.size(); ++i )
376 MeshData::Vertex vertex = verts[ i ];
377 vertex.x = ( ( vertex.x - tlx ) * divWidth ) - one;
378 vertex.y = ( ( vertex.y - tly ) * divHeight ) - one;
379 newVerts.push_back( vertex );
382 // Reverse triangle winding order
383 uint32_t faceCount = faces.size() / 3;
384 for ( uint32_t i = 0; i < faceCount; ++i )
386 uint32_t index = i * 3;
387 newFaces.push_back( faces[ index + 2 ] );
388 newFaces.push_back( faces[ index + 1 ] );
389 newFaces.push_back( faces[ index ] );
392 newMeshData.SetMaterial( meshRecord.mMeshData.GetMaterial() );
393 newMeshData.SetVertices( newVerts );
394 newMeshData.SetFaceIndices( newFaces );
395 newMeshData.SetHasNormals( true );
396 newMeshData.SetHasColor( false );
397 newMeshData.SetHasTextureCoords( true );
399 MeshActor subActor = MeshActor::New( Mesh::New( newMeshData ) );
400 subActor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
401 subActor.SetColor( shadowColor );
402 subActor.SetShaderEffect( mBasicShadowShader );
403 subActor.SetFilterMode( FilterMode::NEAREST, FilterMode::NEAREST );
405 // Create a render task to render the effect
406 RenderTask task = Stage::GetCurrent().GetRenderTaskList().CreateTask();
407 task.SetTargetFrameBuffer( meshRecord.mBuffer );
408 task.SetSourceActor( subActor );
409 task.SetClearEnabled( true );
410 task.SetClearColor( Vector4::ZERO );
411 task.SetExclusive( true );
412 task.SetRefreshRate( RenderTask::REFRESH_ONCE );
413 task.FinishedSignal().Connect( this, &AtlasRenderer::Impl::RenderComplete );
414 actor.Add( subActor );
418 void RenderComplete( RenderTask& renderTask )
420 // Disconnect and remove this single shot render task
421 renderTask.FinishedSignal().Disconnect( this, &AtlasRenderer::Impl::RenderComplete );
422 Stage::GetCurrent().GetRenderTaskList().RemoveTask( renderTask );
424 // Get the actor used for render to buffer and remove it from the parent
425 Actor renderActor = renderTask.GetSourceActor();
428 Actor parent = renderActor.GetParent();
431 parent.Remove( renderActor );
436 RenderableActor mActor; ///< The actor parent which renders the text
437 AtlasGlyphManager mGlyphManager; ///< Glyph Manager to handle upload and caching
438 Vector< uint32_t > mImageIds; ///< A list of imageIDs used by the renderer
439 TextAbstraction::FontClient mFontClient; ///> The font client used to supply glyph information
440 ShaderEffect mBasicShader; ///> Shader used to render L8 glyphs
441 ShaderEffect mBgraShader; ///> Shader used to render BGRA glyphs
442 ShaderEffect mBasicShadowShader; ///> Shader used to render drop shadow into buffer
443 std::vector< MaxBlockSize > mBlockSizes; ///> Maximum size needed to contain a glyph in a block within a new atlas
446 Text::RendererPtr AtlasRenderer::New()
448 return Text::RendererPtr( new AtlasRenderer() );
451 RenderableActor AtlasRenderer::Render( Text::ViewInterface& view )
454 UnparentAndReset( mImpl->mActor );
456 Text::Length numberOfGlyphs = view.GetNumberOfGlyphs();
458 if( numberOfGlyphs > 0 )
460 Vector<GlyphInfo> glyphs;
461 glyphs.Resize( numberOfGlyphs );
463 view.GetGlyphs( &glyphs[0], 0, numberOfGlyphs );
465 std::vector<Vector2> positions;
466 positions.resize( numberOfGlyphs );
467 view.GetGlyphPositions( &positions[0], 0, numberOfGlyphs );
468 mImpl->AddGlyphs( positions,
470 view.GetShadowOffset(),
471 view.GetShadowColor() );
473 return mImpl->mActor;
476 AtlasRenderer::AtlasRenderer()
482 AtlasRenderer::~AtlasRenderer()