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
60 FrameBufferImage mBuffer;
69 float mUnderlinePosition;
70 float mUnderlineThickness;
71 uint32_t mMeshRecordIndex;
77 Text::GlyphIndex mIndex;
83 Vector2 mNeededBlockSize;
88 mGlyphManager = AtlasGlyphManager::Get();
89 mFontClient = TextAbstraction::FontClient::Get();
90 mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_SIZE, DEFAULT_BLOCK_SIZE );
91 mBasicShader = BasicShader::New();
92 mBgraShader = BgraShader::New();
93 mBasicShadowShader = BasicShadowShader::New();
96 mFace.push_back( 0 ); mFace.push_back( 2u ); mFace.push_back( 1u );
97 mFace.push_back( 1u ); mFace.push_back( 2u ); mFace.push_back( 3u );
100 void AddGlyphs( const std::vector<Vector2>& positions,
101 const Vector<GlyphInfo>& glyphs,
102 const Vector4& textColor,
103 const Vector2& shadowOffset,
104 const Vector4& shadowColor,
105 float underlineEnabled,
106 const Vector4& underlineColor )
108 AtlasManager::AtlasSlot slot;
109 std::vector< MeshRecord > meshContainer;
110 Vector< Extent > extents;
112 float currentUnderlinePosition = 0.0f;
113 float currentUnderlineThickness = 0.0f;
114 FontId lastFontId = 0;
115 Style style = STYLE_NORMAL;
117 if ( shadowOffset.x != 0.0f || shadowOffset.y != 0.0f )
119 style = STYLE_DROP_SHADOW;
122 if ( mImageIds.Size() )
124 // Unreference any currently used glyphs
128 CalculateBlocksSize( glyphs );
130 for ( uint32_t i = 0; i < glyphs.Size(); ++i )
132 GlyphInfo glyph = glyphs[ i ];
134 // No operation for white space
135 if ( glyph.width && glyph.height )
137 // Are we still using the same fontId as previous
138 if ( glyph.fontId != lastFontId )
140 // We need to fetch fresh font underline metrics
141 FontMetrics fontMetrics;
142 mFontClient.GetFontMetrics( glyph.fontId, fontMetrics );
143 currentUnderlinePosition = fontMetrics.underlinePosition;
144 currentUnderlineThickness = fontMetrics.underlineThickness;
146 // Ensure that an underline is at least 1 pixel high
147 if ( currentUnderlineThickness < 1.0f )
149 currentUnderlineThickness = 1.0f;
153 Vector2 position = positions[ i ];
154 MeshData newMeshData;
155 mGlyphManager.Cached( glyph.fontId, glyph.index, slot );
159 // This glyph already exists so generate mesh data plugging in our supplied position
160 mGlyphManager.GenerateMeshData( slot.mImageId, position, newMeshData );
161 mImageIds.PushBack( slot.mImageId );
166 // Select correct size for new atlas if needed....?
167 if ( lastFontId != glyph.fontId )
169 for ( uint32_t j = 0; j < mBlockSizes.size(); ++j )
171 if ( mBlockSizes[ j ].mFontId == glyph.fontId )
173 mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_SIZE, mBlockSizes[ j ].mNeededBlockSize );
176 lastFontId = glyph.fontId;
179 // Glyph doesn't currently exist in atlas so upload
180 BufferImage bitmap = mFontClient.CreateBitmap( glyph.fontId, glyph.index );
182 // Locate a new slot for our glyph
183 mGlyphManager.Add( glyph, bitmap, slot );
185 // Generate mesh data for this quad, plugging in our supplied position
188 mGlyphManager.GenerateMeshData( slot.mImageId, position, newMeshData );
189 mImageIds.PushBack( slot.mImageId );
192 // Find an existing mesh data object to attach to ( or create a new one, if we can't find one using the same atlas)
193 StitchTextMesh( meshContainer,
197 position.y + glyph.yBearing,
198 currentUnderlinePosition,
199 currentUnderlineThickness,
204 if ( underlineEnabled )
206 // Check to see if any of the text needs an underline
207 GenerateUnderlines( meshContainer, extents, underlineColor, textColor );
210 // For each MeshData object, create a mesh actor and add to the renderable actor
211 if ( meshContainer.size() )
213 for ( std::vector< MeshRecord >::iterator mIt = meshContainer.begin(); mIt != meshContainer.end(); ++mIt )
215 MeshActor actor = MeshActor::New( Mesh::New( mIt->mMeshData ) );
216 actor.SetColor( mIt->mColor );
217 if ( mIt->mIsUnderline )
219 actor.SetColorMode( USE_OWN_COLOR );
223 actor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
226 // Check to see what pixel format the shader should be
227 if ( mGlyphManager.GetPixelFormat( mIt->mAtlasId ) == Pixel::L8 )
229 // Create an effect if necessary
230 if ( style == STYLE_DROP_SHADOW )
232 actor.Add( GenerateShadow( *mIt, shadowOffset, shadowColor ) );
234 actor.SetShaderEffect( mBasicShader );
238 actor.SetShaderEffect( mBgraShader );
250 mActor.OffStageSignal().Connect( this, &AtlasRenderer::Impl::OffStageDisconnect );
252 #if defined(DEBUG_ENABLED)
253 Toolkit::AtlasGlyphManager::Metrics metrics = mGlyphManager.GetMetrics();
254 DALI_LOG_INFO( gLogFilter, Debug::Concise, "TextAtlasRenderer::GlyphManager::GlyphCount: %i, AtlasCount: %i, TextureMemoryUse: %iK\n",
256 metrics.mAtlasMetrics.mAtlasCount,
257 metrics.mAtlasMetrics.mTextureMemoryUsed / 1024 );
258 for ( uint32_t i = 0; i < metrics.mAtlasMetrics.mAtlasCount; ++i )
260 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Atlas [%i] %sPixels: %s Size: %ix%i, BlockSize: %ix%i, BlocksUsed: %i/%i\n",
261 i + 1, i > 8 ? "" : " ",
262 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mPixelFormat == Pixel::L8 ? "L8 " : "BGRA",
263 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mWidth,
264 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mHeight,
265 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mBlockWidth,
266 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mBlockHeight,
267 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mBlocksUsed,
268 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mTotalBlocks );
273 void StitchTextMesh( std::vector< MeshRecord >& meshContainer,
274 MeshData& newMeshData,
275 Vector< Extent >& extents,
276 const Vector4& color,
278 float underlinePosition,
279 float underlineThickness,
280 AtlasManager::AtlasSlot& slot )
284 MeshData::VertexContainer verts = newMeshData.GetVertices();
285 float left = verts[ 0 ].x;
286 float right = verts[ 1 ].x;
288 // Check to see if there's a mesh data object that references the same atlas ?
290 for ( std::vector< MeshRecord >::iterator mIt = meshContainer.begin(); mIt != meshContainer.end(); ++mIt, ++index )
292 if ( slot.mAtlasId == mIt->mAtlasId )
294 // Stitch the mesh to the existing mesh and adjust any extents
295 mGlyphManager.StitchMesh( mIt->mMeshData, newMeshData );
296 AdjustExtents( extents,
304 underlineThickness );
309 // No mesh data object currently exists that references this atlas, so create a new one
310 MeshRecord meshRecord;
311 meshRecord.mAtlasId = slot.mAtlasId;
312 meshRecord.mMeshData = newMeshData;
313 meshRecord.mColor = color;
314 meshRecord.mIsUnderline = false;
315 meshContainer.push_back( meshRecord );
317 // Adjust extents for this new meshrecord
318 AdjustExtents( extents,
320 meshContainer.size() - 1u,
326 underlineThickness );
331 void AdjustExtents( Vector< Extent >& extents,
332 std::vector< MeshRecord>& meshRecords,
334 const Vector4& color,
338 float underlinePosition,
339 float underlineThickness )
341 bool foundExtent = false;
342 for ( Vector< Extent >::Iterator eIt = extents.Begin(); eIt != extents.End(); ++eIt )
344 if ( Equals( baseLine, eIt->mBaseLine ) )
346 // If we've found an extent with the same color then we don't need to create a new extent
347 if ( color == meshRecords[ index ].mColor )
350 if ( left < eIt->mLeft )
354 if ( right > eIt->mRight )
359 // Font metrics use negative values for lower underline positions
360 if ( underlinePosition < eIt->mUnderlinePosition )
362 eIt->mUnderlinePosition = underlinePosition;
364 if ( underlineThickness > eIt->mUnderlineThickness )
366 eIt->mUnderlineThickness = underlineThickness;
374 extent.mRight = right;
375 extent.mBaseLine = baseLine;
376 extent.mUnderlinePosition = underlinePosition;
377 extent.mUnderlineThickness = underlineThickness;
378 extent.mMeshRecordIndex = index;
379 extents.PushBack( extent );
383 // Unreference any glyphs that were used with this actor
384 void OffStageDisconnect( Dali::Actor actor )
391 for ( uint32_t i = 0; i < mImageIds.Size(); ++i )
393 mGlyphManager.Remove( mImageIds[ i ] );
395 mImageIds.Resize( 0 );
398 void CalculateBlocksSize( const Vector<GlyphInfo>& glyphs )
400 MaxBlockSize maxBlockSize;
401 for ( uint32_t i = 0; i < glyphs.Size(); ++i )
403 // Get the fontId of this glyph and check to see if a max size exists?
404 FontId fontId = glyphs[ i ].fontId;
405 float paddedWidth = glyphs[ i ].width + PADDING.x;
406 float paddedHeight = glyphs[ i ].height + PADDING.y;
407 bool foundFont = false;
409 for ( uint32_t j = 0; j < mBlockSizes.size(); ++j )
411 if ( mBlockSizes[ j ].mFontId == fontId )
414 if ( mBlockSizes[ j ].mNeededBlockSize.x < paddedWidth )
416 mBlockSizes[ j ].mNeededBlockSize.x = paddedWidth;
418 if ( mBlockSizes[ j ].mNeededBlockSize.y < paddedHeight )
420 mBlockSizes[ j ].mNeededBlockSize.y = paddedHeight;
427 maxBlockSize.mNeededBlockSize = Vector2( paddedWidth, paddedHeight );
428 maxBlockSize.mFontId = fontId;
429 mBlockSizes.push_back( maxBlockSize );
434 void GenerateUnderlines( std::vector< MeshRecord>& meshRecords,
435 Vector< Extent >& extents,
436 const Vector4& underlineColor,
437 const Vector4& textColor )
439 MeshData newMeshData;
440 const float zero = 0.0f;
441 const float half = 0.5f;
443 for ( Vector< Extent >::ConstIterator eIt = extents.Begin(); eIt != extents.End(); ++eIt )
445 MeshData::VertexContainer newVerts;
446 newVerts.reserve( 4u );
447 uint32_t index = eIt->mMeshRecordIndex;
448 Vector2 uv = mGlyphManager.GetAtlasSize( meshRecords[ index ].mAtlasId );
450 // Make sure we don't hit texture edge for single pixel texture ( filled pixel is in top left of every atlas )
451 float u = half / uv.x;
452 float v = half / uv.y;
453 float thickness = eIt->mUnderlineThickness;
454 float baseLine = eIt->mBaseLine - eIt->mUnderlinePosition - ( thickness * 0.5f );
455 float tlx = eIt->mLeft;
456 float brx = eIt->mRight;
458 newVerts.push_back( MeshData::Vertex( Vector3( tlx, baseLine, zero ),
459 Vector2( zero, zero ),
460 Vector3( zero, zero, zero ) ) );
462 newVerts.push_back( MeshData::Vertex( Vector3( brx, baseLine, zero ),
464 Vector3( zero, zero, zero ) ) );
466 newVerts.push_back( MeshData::Vertex( Vector3( tlx, baseLine + thickness, zero ),
468 Vector3( zero, zero, zero ) ) );
470 newVerts.push_back( MeshData::Vertex( Vector3( brx, baseLine + thickness, zero ),
472 Vector3( zero, zero, zero ) ) );
474 newMeshData.SetVertices( newVerts );
475 newMeshData.SetFaceIndices( mFace );
477 if ( underlineColor == textColor )
479 mGlyphManager.StitchMesh( meshRecords[ index ].mMeshData, newMeshData );
484 newMeshData.SetMaterial( meshRecords[ index ].mMeshData.GetMaterial() );
485 newMeshData.SetHasNormals( true );
486 newMeshData.SetHasColor( false );
487 newMeshData.SetHasTextureCoords( true );
488 record.mMeshData = newMeshData;
489 record.mAtlasId = meshRecords[ index ].mAtlasId;
490 record.mColor = underlineColor;
491 record.mIsUnderline = true;
492 meshRecords.push_back( record );
497 MeshActor GenerateShadow( MeshRecord& meshRecord,
498 const Vector2& shadowOffset,
499 const Vector4& shadowColor )
501 // Scan vertex buffer to determine width and height of effect buffer needed
502 MeshData::VertexContainer verts = meshRecord.mMeshData.GetVertices();
503 const float one = 1.0f;
504 const float zero = 0.0f;
505 float tlx = verts[ 0 ].x;
506 float tly = verts[ 0 ].y;
510 for ( uint32_t i = 0; i < verts.size(); ++i )
512 if ( verts[ i ].x < tlx )
516 if ( verts[ i ].y < tly )
520 if ( verts[ i ].x > brx )
524 if ( verts[ i ].y > bry )
530 float width = brx - tlx;
531 float height = bry - tly;
532 float divWidth = 2.0f / width;
533 float divHeight = 2.0f / height;
535 // Create a buffer to render to
536 meshRecord.mBuffer = FrameBufferImage::New( width, height );
538 // Create a mesh actor to contain the post-effect render
539 MeshData::VertexContainer vertices;
540 MeshData::FaceIndices face;
542 vertices.push_back( MeshData::Vertex( Vector3( tlx + shadowOffset.x, tly + shadowOffset.y, zero ),
543 Vector2( zero, zero ),
544 Vector3( zero, zero, zero ) ) );
546 vertices.push_back( MeshData::Vertex( Vector3( brx + shadowOffset.x, tly + shadowOffset.y, zero ),
547 Vector2( one, zero ),
548 Vector3( zero, zero, zero ) ) );
550 vertices.push_back( MeshData::Vertex( Vector3( tlx + shadowOffset.x, bry + shadowOffset.y, zero ),
551 Vector2( zero, one ),
552 Vector3( zero, zero, zero ) ) );
554 vertices.push_back( MeshData::Vertex( Vector3( brx + shadowOffset.x, bry + shadowOffset.y, zero ),
556 Vector3( zero, zero, zero ) ) );
559 Material newMaterial = Material::New("effect buffer");
560 newMaterial.SetDiffuseTexture( meshRecord.mBuffer );
561 meshData.SetMaterial( newMaterial );
562 meshData.SetVertices( vertices );
563 meshData.SetFaceIndices( mFace );
564 meshData.SetHasNormals( true );
565 meshData.SetHasColor( false );
566 meshData.SetHasTextureCoords( true );
567 MeshActor actor = MeshActor::New( Mesh::New( meshData ) );
568 actor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
569 actor.SetShaderEffect( mBgraShader );
570 actor.SetFilterMode( FilterMode::LINEAR, FilterMode::LINEAR );
571 actor.SetSortModifier( 0.1f ); // force behind main text
573 // Create a sub actor to render once with normalized vertex positions
574 MeshData newMeshData;
575 MeshData::VertexContainer newVerts;
576 MeshData::FaceIndices newFaces;
577 MeshData::FaceIndices faces = meshRecord.mMeshData.GetFaces();
578 for ( uint32_t i = 0; i < verts.size(); ++i )
580 MeshData::Vertex vertex = verts[ i ];
581 vertex.x = ( ( vertex.x - tlx ) * divWidth ) - one;
582 vertex.y = ( ( vertex.y - tly ) * divHeight ) - one;
583 newVerts.push_back( vertex );
586 // Reverse triangle winding order
587 uint32_t faceCount = faces.size() / 3;
588 for ( uint32_t i = 0; i < faceCount; ++i )
590 uint32_t index = i * 3;
591 newFaces.push_back( faces[ index + 2 ] );
592 newFaces.push_back( faces[ index + 1 ] );
593 newFaces.push_back( faces[ index ] );
596 newMeshData.SetMaterial( meshRecord.mMeshData.GetMaterial() );
597 newMeshData.SetVertices( newVerts );
598 newMeshData.SetFaceIndices( newFaces );
599 newMeshData.SetHasNormals( true );
600 newMeshData.SetHasColor( false );
601 newMeshData.SetHasTextureCoords( true );
603 MeshActor subActor = MeshActor::New( Mesh::New( newMeshData ) );
604 subActor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
605 subActor.SetColor( shadowColor );
606 subActor.SetShaderEffect( mBasicShadowShader );
607 subActor.SetFilterMode( FilterMode::NEAREST, FilterMode::NEAREST );
609 // Create a render task to render the effect
610 RenderTask task = Stage::GetCurrent().GetRenderTaskList().CreateTask();
611 task.SetTargetFrameBuffer( meshRecord.mBuffer );
612 task.SetSourceActor( subActor );
613 task.SetClearEnabled( true );
614 task.SetClearColor( Vector4::ZERO );
615 task.SetExclusive( true );
616 task.SetRefreshRate( RenderTask::REFRESH_ONCE );
617 task.FinishedSignal().Connect( this, &AtlasRenderer::Impl::RenderComplete );
618 actor.Add( subActor );
622 void RenderComplete( RenderTask& renderTask )
624 // Disconnect and remove this single shot render task
625 renderTask.FinishedSignal().Disconnect( this, &AtlasRenderer::Impl::RenderComplete );
626 Stage::GetCurrent().GetRenderTaskList().RemoveTask( renderTask );
628 // Get the actor used for render to buffer and remove it from the parent
629 Actor renderActor = renderTask.GetSourceActor();
632 Actor parent = renderActor.GetParent();
635 parent.Remove( renderActor );
640 RenderableActor mActor; ///< The actor parent which renders the text
641 AtlasGlyphManager mGlyphManager; ///< Glyph Manager to handle upload and caching
642 Vector< uint32_t > mImageIds; ///< A list of imageIDs used by the renderer
643 TextAbstraction::FontClient mFontClient; ///> The font client used to supply glyph information
644 ShaderEffect mBasicShader; ///> Shader used to render L8 glyphs
645 ShaderEffect mBgraShader; ///> Shader used to render BGRA glyphs
646 ShaderEffect mBasicShadowShader; ///> Shader used to render drop shadow into buffer
647 std::vector< MaxBlockSize > mBlockSizes; ///> Maximum size needed to contain a glyph in a block within a new atlas
648 std::vector< MeshData::FaceIndex > mFace; ///> Face indices for a quad
651 Text::RendererPtr AtlasRenderer::New()
653 return Text::RendererPtr( new AtlasRenderer() );
656 RenderableActor AtlasRenderer::Render( Text::ViewInterface& view )
659 UnparentAndReset( mImpl->mActor );
661 Text::Length numberOfGlyphs = view.GetNumberOfGlyphs();
663 if( numberOfGlyphs > 0 )
665 Vector<GlyphInfo> glyphs;
666 glyphs.Resize( numberOfGlyphs );
668 view.GetGlyphs( &glyphs[0], 0, numberOfGlyphs );
670 std::vector<Vector2> positions;
671 positions.resize( numberOfGlyphs );
672 view.GetGlyphPositions( &positions[0], 0, numberOfGlyphs );
673 mImpl->AddGlyphs( positions,
676 view.GetShadowOffset(),
677 view.GetShadowColor(),
678 view.IsUnderlineEnabled(),
679 view.GetUnderlineColor() );
681 return mImpl->mActor;
684 AtlasRenderer::AtlasRenderer()
690 AtlasRenderer::~AtlasRenderer()