X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Finternal%2Ftext%2Frendering%2Fatlas%2Ftext-atlas-renderer.cpp;h=7a765343495beafdd5794323603eadcbb2783ba7;hp=9e8a3fa5ca7e3e1537bf0b04b7ed20492606896b;hb=a2de9cf491172cd5da9dc9ed60b17683dab6d7bc;hpb=e4b455f06ccdfb92d91399cb5dd09dc5d73c1185 diff --git a/dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.cpp b/dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.cpp index 9e8a3fa..7a76534 100644 --- a/dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.cpp +++ b/dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.cpp @@ -19,16 +19,17 @@ #include // EXTERNAL INCLUDES -#include #include -#include +#include +#include +#include // INTERNAL INCLUDES -#include +#include +#include #include -#include -#include -#include +#include +#include using namespace Dali; using namespace Dali::Toolkit; @@ -40,17 +41,15 @@ namespace Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_RENDERING"); #endif - const float ZERO( 0.0f ); - const float HALF( 0.5f ); - const float ONE( 1.0f ); - const float TWO( 2.0f ); - const uint32_t DEFAULT_ATLAS_WIDTH = 512u; - const uint32_t DEFAULT_ATLAS_HEIGHT = 512u; +const float ZERO( 0.0f ); +const float HALF( 0.5f ); +const float ONE( 1.0f ); +const uint32_t DEFAULT_ATLAS_WIDTH = 512u; +const uint32_t DEFAULT_ATLAS_HEIGHT = 512u; } -struct AtlasRenderer::Impl : public ConnectionTracker +struct AtlasRenderer::Impl { - enum Style { STYLE_NORMAL, @@ -59,15 +58,32 @@ struct AtlasRenderer::Impl : public ConnectionTracker struct MeshRecord { - Vector4 mColor; + MeshRecord() + : mAtlasId( 0u ) + { + } + uint32_t mAtlasId; - MeshData mMeshData; + AtlasManager::Mesh2D mMesh; FrameBufferImage mBuffer; - bool mIsUnderline; }; + /** + * brief Struct used to generate the underline mesh. + * There is one Extent per line of text. + */ struct Extent { + Extent() + : mBaseLine( 0.0f ), + mLeft( 0.0f ), + mRight( 0.0f ), + mUnderlinePosition( 0.0f ), + mUnderlineThickness( 0.0f ), + mMeshRecordIndex( 0u ) + { + } + float mBaseLine; float mLeft; float mRight; @@ -76,49 +92,113 @@ struct AtlasRenderer::Impl : public ConnectionTracker uint32_t mMeshRecordIndex; }; - struct AtlasRecord + struct MaxBlockSize { - uint32_t mImageId; + MaxBlockSize() + : mFontId( 0 ), + mNeededBlockWidth( 0 ), + mNeededBlockHeight( 0 ) + { + } + + FontId mFontId; + uint32_t mNeededBlockWidth; + uint32_t mNeededBlockHeight; + }; + + struct CheckEntry + { + CheckEntry() + : mFontId( 0 ), + mIndex( 0 ) + { + } + + FontId mFontId; Text::GlyphIndex mIndex; }; - struct MaxBlockSize + struct TextCacheEntry { + TextCacheEntry() + : mFontId( 0 ), + mIndex( 0 ), + mImageId( 0 ) + { + } + FontId mFontId; - uint32_t mNeededBlockWidth; - uint32_t mNeededBlockHeight; + Text::GlyphIndex mIndex; + uint32_t mImageId; }; Impl() + : mDepth( 0 ) { mGlyphManager = AtlasGlyphManager::Get(); mFontClient = TextAbstraction::FontClient::Get(); - mBasicShader = BasicShader::New(); - mBgraShader = BgraShader::New(); - mBasicShadowShader = BasicShadowShader::New(); - mFace.reserve( 6u ); - mFace.push_back( 0 ); mFace.push_back( 2u ); mFace.push_back( 1u ); - mFace.push_back( 1u ); mFace.push_back( 2u ); mFace.push_back( 3u ); + mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2; + mQuadVertexFormat[ "aTexCoord" ] = Property::VECTOR2; + mQuadVertexFormat[ "aColor" ] = Property::VECTOR4; + mQuadIndexFormat[ "indices" ] = Property::INTEGER; } - void AddGlyphs( const std::vector& positions, + bool IsGlyphUnderlined( GlyphIndex index, + const Vector& underlineRuns ) + { + for( Vector::ConstIterator it = underlineRuns.Begin(), + endIt = underlineRuns.End(); + it != endIt; + ++it ) + { + const GlyphRun& run = *it; + + if( ( run.glyphIndex <= index ) && ( index < run.glyphIndex + run.numberOfGlyphs ) ) + { + return true; + } + } + + return false; + } + + void AddGlyphs( Text::ViewInterface& view, + const Vector& positions, const Vector& glyphs, - const Vector4& textColor, - const Vector2& shadowOffset, - const Vector4& shadowColor, - bool underlineEnabled, - const Vector4& underlineColor, - float underlineHeight ) + const Vector& colors, + int depth ) { AtlasManager::AtlasSlot slot; std::vector< MeshRecord > meshContainer; Vector< Extent > extents; + TextCacheEntry textCacheEntry; + mDepth = depth; + + const Vector2& actorSize( view.GetControlSize() ); + const Vector2& textSize( view.GetLayoutSize() ); + const Vector2 halfTextSize( textSize * 0.5f ); + const Vector2& shadowOffset( view.GetShadowOffset() ); + const Vector4& shadowColor( view.GetShadowColor() ); + const bool underlineEnabled( view.IsUnderlineEnabled() ); + const Vector4& underlineColor( view.GetUnderlineColor() ); + const float underlineHeight( view.GetUnderlineHeight() ); + + // Get the underline runs. + const Length numberOfUnderlineRuns = view.GetNumberOfUnderlineRuns(); + Vector underlineRuns; + underlineRuns.Resize( numberOfUnderlineRuns ); + view.GetUnderlineRuns( underlineRuns.Begin(), + 0u, + numberOfUnderlineRuns ); + + bool thereAreUnderlinedGlyphs = false; float currentUnderlinePosition = ZERO; float currentUnderlineThickness = underlineHeight; uint32_t currentBlockSize = 0; FontId lastFontId = 0; + FontId lastUnderlinedFontId = 0; Style style = STYLE_NORMAL; if ( fabsf( shadowOffset.x ) > Math::MACHINE_EPSILON_1 || fabsf( shadowOffset.y ) > Math::MACHINE_EPSILON_1 ) @@ -126,31 +206,34 @@ struct AtlasRenderer::Impl : public ConnectionTracker style = STYLE_DROP_SHADOW; } - if ( mImageIds.Size() ) - { - // Unreference any currently used glyphs - RemoveText(); - } - CalculateBlocksSize( glyphs ); - for ( uint32_t i = 0; i < glyphs.Size(); ++i ) + // Avoid emptying mTextCache (& removing references) until after incremented references for the new text + Vector< TextCacheEntry > newTextCache; + const GlyphInfo* const glyphsBuffer = glyphs.Begin(); + const Vector2* const positionsBuffer = positions.Begin(); + const Vector4* const colorsBuffer = colors.Begin(); + + for( uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i ) { - GlyphInfo glyph = glyphs[ i ]; + const GlyphInfo& glyph = *( glyphsBuffer + i ); + + const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined( i, underlineRuns ); + thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph; // No operation for white space - if ( glyph.width && glyph.height ) + if( glyph.width && glyph.height ) { // Are we still using the same fontId as previous - if ( glyph.fontId != lastFontId ) + if( underlineGlyph && ( glyph.fontId != lastUnderlinedFontId ) ) { // We need to fetch fresh font underline metrics FontMetrics fontMetrics; mFontClient.GetFontMetrics( glyph.fontId, fontMetrics ); currentUnderlinePosition = ceil( fabsf( fontMetrics.underlinePosition ) ); - float descender = ceil( fabsf( fontMetrics.descender ) ); + const float descender = ceil( fabsf( fontMetrics.descender ) ); - if ( underlineHeight == ZERO ) + if( fabsf( underlineHeight ) < Math::MACHINE_EPSILON_1000 ) { currentUnderlineThickness = fontMetrics.underlineThickness; @@ -166,136 +249,192 @@ struct AtlasRenderer::Impl : public ConnectionTracker } // Clamp the underline position at the font descender and check for ( as EFL describes it ) a broken font - if ( currentUnderlinePosition > descender ) + if( currentUnderlinePosition > descender ) { currentUnderlinePosition = descender; } - if ( ZERO == currentUnderlinePosition ) + + if( fabsf( currentUnderlinePosition ) < Math::MACHINE_EPSILON_1000 ) { // Move offset down by one ( EFL behavior ) currentUnderlinePosition = ONE; } - } - Vector2 position = positions[ i ]; - MeshData newMeshData; - mGlyphManager.Cached( glyph.fontId, glyph.index, slot ); + lastUnderlinedFontId = glyph.fontId; + } // underline - if ( slot.mImageId ) - { - // This glyph already exists so generate mesh data plugging in our supplied position - mGlyphManager.GenerateMeshData( slot.mImageId, position, newMeshData ); - mImageIds.PushBack( slot.mImageId ); - } - else + if( !mGlyphManager.IsCached( glyph.fontId, glyph.index, slot ) ) { - // Select correct size for new atlas if needed....? - if ( lastFontId != glyph.fontId ) + if( lastFontId != glyph.fontId ) { - for ( uint32_t j = 0; j < mBlockSizes.size(); ++j ) + uint32_t index = 0u; + for( std::vector::const_iterator it = mBlockSizes.begin(), + endIt = mBlockSizes.end(); + it != endIt; + ++it, ++index ) { - if ( mBlockSizes[ j ].mFontId == glyph.fontId ) + const MaxBlockSize& blockSize = *it; + if( blockSize.mFontId == glyph.fontId ) { - currentBlockSize = j; + currentBlockSize = index; mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_WIDTH, DEFAULT_ATLAS_HEIGHT, - mBlockSizes[ j ].mNeededBlockWidth, - mBlockSizes[ j ].mNeededBlockHeight ); + blockSize.mNeededBlockWidth, + blockSize.mNeededBlockHeight ); } } } // Create a new image for the glyph BufferImage bitmap = mFontClient.CreateBitmap( glyph.fontId, glyph.index ); - if ( bitmap ) + if( bitmap ) { + MaxBlockSize& blockSize = mBlockSizes[currentBlockSize]; + // Ensure that the next image will fit into the current block size bool setSize = false; - if ( bitmap.GetWidth() > mBlockSizes[ currentBlockSize ].mNeededBlockWidth ) + if( bitmap.GetWidth() > blockSize.mNeededBlockWidth ) { setSize = true; - mBlockSizes[ currentBlockSize ].mNeededBlockWidth = bitmap.GetWidth(); + blockSize.mNeededBlockWidth = bitmap.GetWidth(); } - if ( bitmap.GetHeight() > mBlockSizes[ currentBlockSize ].mNeededBlockHeight ) + if( bitmap.GetHeight() > blockSize.mNeededBlockHeight ) { setSize = true; - mBlockSizes[ currentBlockSize ].mNeededBlockHeight = bitmap.GetHeight(); + blockSize.mNeededBlockHeight = bitmap.GetHeight(); } - if ( setSize ) + if( setSize ) { mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_WIDTH, DEFAULT_ATLAS_HEIGHT, - mBlockSizes[ currentBlockSize ].mNeededBlockWidth, - mBlockSizes[ currentBlockSize ].mNeededBlockHeight ); + blockSize.mNeededBlockWidth, + blockSize.mNeededBlockHeight ); } // Locate a new slot for our glyph mGlyphManager.Add( glyph, bitmap, slot ); + } + } + else + { + // We have 2+ copies of the same glyph + mGlyphManager.AdjustReferenceCount( glyph.fontId, glyph.index, 1/*increment*/ ); + } - // Generate mesh data for this quad, plugging in our supplied position - if ( slot.mImageId ) - { - mGlyphManager.GenerateMeshData( slot.mImageId, position, newMeshData ); - mImageIds.PushBack( slot.mImageId ); - } + // Move the origin (0,0) of the mesh to the center of the actor + const Vector2 position = *( positionsBuffer + i ) - halfTextSize; + + // Generate mesh data for this quad, plugging in our supplied position + AtlasManager::Mesh2D newMesh; + mGlyphManager.GenerateMeshData( slot.mImageId, position, newMesh ); + textCacheEntry.mFontId = glyph.fontId; + textCacheEntry.mImageId = slot.mImageId; + textCacheEntry.mIndex = glyph.index; + newTextCache.PushBack( textCacheEntry ); + + AtlasManager::Vertex2D* verticesBuffer = newMesh.mVertices.Begin(); + + // Adjust the vertices if the fixed-size font should be down-scaled + if( glyph.scaleFactor > 0 ) + { + for( unsigned int index = 0u, size = newMesh.mVertices.Count(); + index < size; + ++index ) + { + AtlasManager::Vertex2D& vertex = *( verticesBuffer + index ); + + // Set the position of the vertex. + vertex.mPosition.x = position.x + ( ( vertex.mPosition.x - position.x ) * glyph.scaleFactor ); + vertex.mPosition.y = position.y + ( ( vertex.mPosition.y - position.y ) * glyph.scaleFactor ); } } + + // Get the color of the character. + const Vector4& color = *( colorsBuffer + i ); + + for( unsigned int index = 0u, size = newMesh.mVertices.Count(); + index < size; + ++index ) + { + AtlasManager::Vertex2D& vertex = *( verticesBuffer + index ); + + // Set the color of the vertex. + vertex.mColor = color; + } + // Find an existing mesh data object to attach to ( or create a new one, if we can't find one using the same atlas) StitchTextMesh( meshContainer, - newMeshData, + newMesh, extents, - textColor, position.y + glyph.yBearing, + underlineGlyph, currentUnderlinePosition, currentUnderlineThickness, slot ); lastFontId = glyph.fontId; } - } + } // glyphs - if ( underlineEnabled ) + // Now remove references for the old text + RemoveText(); + mTextCache.Swap( newTextCache ); + + if( thereAreUnderlinedGlyphs ) { // Check to see if any of the text needs an underline - GenerateUnderlines( meshContainer, extents, underlineColor, textColor ); + GenerateUnderlines( meshContainer, extents, underlineColor ); } // For each MeshData object, create a mesh actor and add to the renderable actor - if ( meshContainer.size() ) + if( !meshContainer.empty() ) { - for ( std::vector< MeshRecord >::iterator mIt = meshContainer.begin(); mIt != meshContainer.end(); ++mIt ) + for( std::vector< MeshRecord >::iterator it = meshContainer.begin(), + endIt = meshContainer.end(); + it != endIt; ++it ) { - MeshActor actor = MeshActor::New( Mesh::New( mIt->mMeshData ) ); - actor.SetColor( mIt->mColor ); + MeshRecord& meshRecord = *it; - // Ensure that text rendering is unfiltered - actor.SetFilterMode( FilterMode::NEAREST, FilterMode::NEAREST ); - if ( mIt->mIsUnderline ) - { - actor.SetColorMode( USE_OWN_COLOR ); - } - else - { - actor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR ); - } + Actor actor = CreateMeshActor( meshRecord, textSize ); - // Check to see what pixel format the shader should be - if ( mGlyphManager.GetPixelFormat( mIt->mAtlasId ) == Pixel::L8 ) + // Create an effect if necessary + if( style == STYLE_DROP_SHADOW ) { - // Create an effect if necessary - if ( style == STYLE_DROP_SHADOW ) + // Change the color of the vertices. + for( Vector::Iterator vIt = meshRecord.mMesh.mVertices.Begin(), + vEndIt = meshRecord.mMesh.mVertices.End(); + vIt != vEndIt; + ++vIt ) { - actor.Add( GenerateShadow( *mIt, shadowOffset, shadowColor ) ); + AtlasManager::Vertex2D& vertex = *vIt; + + vertex.mColor = shadowColor; + } + + // Create a container actor to act as a common parent for text and shadow, to avoid color inheritence issues. + Actor containerActor = Actor::New(); + containerActor.SetParentOrigin( ParentOrigin::CENTER ); + containerActor.SetSize( actorSize ); + + Actor shadowActor = CreateMeshActor( meshRecord, textSize ); +#if defined(DEBUG_ENABLED) + shadowActor.SetName( "Text Shadow renderable actor" ); +#endif + // Offset shadow in x and y + shadowActor.RegisterProperty("uOffset", shadowOffset ); + if( actor.GetRendererCount() ) + { + Dali::Renderer renderer( shadowActor.GetRendererAt( 0 ) ); + int depthIndex = renderer.GetProperty(Dali::Renderer::Property::DEPTH_INDEX); + renderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, depthIndex - 1 ); + containerActor.Add( shadowActor ); + containerActor.Add( actor ); + actor = containerActor; } - actor.SetShaderEffect( mBasicShader ); - } - else - { - actor.SetShaderEffect( mBgraShader ); } - if ( mActor ) + if( mActor ) { mActor.Add( actor ); } @@ -304,7 +443,6 @@ struct AtlasRenderer::Impl : public ConnectionTracker mActor = actor; } } - mActor.OffStageSignal().Connect( this, &AtlasRenderer::Impl::OffStageDisconnect ); } #if defined(DEBUG_ENABLED) Toolkit::AtlasGlyphManager::Metrics metrics = mGlyphManager.GetMetrics(); @@ -312,9 +450,12 @@ struct AtlasRenderer::Impl : public ConnectionTracker metrics.mGlyphCount, metrics.mAtlasMetrics.mAtlasCount, metrics.mAtlasMetrics.mTextureMemoryUsed / 1024 ); - for ( uint32_t i = 0; i < metrics.mAtlasMetrics.mAtlasCount; ++i ) + + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%s\n", metrics.mVerboseGlyphCounts.c_str() ); + + for( uint32_t i = 0; i < metrics.mAtlasMetrics.mAtlasCount; ++i ) { - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Atlas [%i] %sPixels: %s Size: %ix%i, BlockSize: %ix%i, BlocksUsed: %i/%i\n", + DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Atlas [%i] %sPixels: %s Size: %ix%i, BlockSize: %ix%i, BlocksUsed: %i/%i\n", i + 1, i > 8 ? "" : " ", metrics.mAtlasMetrics.mAtlasMetrics[ i ].mPixelFormat == Pixel::L8 ? "L8 " : "BGRA", metrics.mAtlasMetrics.mAtlasMetrics[ i ].mSize.mWidth, @@ -327,37 +468,85 @@ struct AtlasRenderer::Impl : public ConnectionTracker #endif } + void RemoveText() + { + for( Vector< TextCacheEntry >::Iterator oldTextIter = mTextCache.Begin(); oldTextIter != mTextCache.End(); ++oldTextIter ) + { + mGlyphManager.AdjustReferenceCount( oldTextIter->mFontId, oldTextIter->mIndex, -1/*decrement*/ ); + } + mTextCache.Resize( 0 ); + } + + Actor CreateMeshActor( const MeshRecord& meshRecord, const Vector2& actorSize ) + { + PropertyBuffer quadVertices = PropertyBuffer::New( mQuadVertexFormat ); + PropertyBuffer quadIndices = PropertyBuffer::New( mQuadIndexFormat ); + quadVertices.SetData( const_cast< AtlasManager::Vertex2D* >( &meshRecord.mMesh.mVertices[ 0 ] ), meshRecord.mMesh.mVertices.Size() ); + quadIndices.SetData( const_cast< unsigned int* >( &meshRecord.mMesh.mIndices[ 0 ] ), meshRecord.mMesh.mIndices.Size() ); + + Geometry quadGeometry = Geometry::New(); + quadGeometry.AddVertexBuffer( quadVertices ); + quadGeometry.SetIndexBuffer( quadIndices ); + + TextureSet textureSet( mGlyphManager.GetTextures( meshRecord.mAtlasId ) ); + Shader shader( mGlyphManager.GetShader( meshRecord.mAtlasId ) ); + Dali::Renderer renderer = Dali::Renderer::New( quadGeometry, shader ); + renderer.SetTextures( textureSet ); + renderer.SetProperty( Dali::Renderer::Property::BLENDING_MODE, BlendingMode::ON ); + renderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT + mDepth ); + Actor actor = Actor::New(); +#if defined(DEBUG_ENABLED) + actor.SetName( "Text renderable actor" ); +#endif + actor.AddRenderer( renderer ); + + // Keep all of the origins aligned + actor.SetParentOrigin( ParentOrigin::TOP_LEFT ); + actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + + actor.SetSize( actorSize ); + actor.RegisterProperty("uOffset", Vector2::ZERO ); + return actor; + } + void StitchTextMesh( std::vector< MeshRecord >& meshContainer, - MeshData& newMeshData, + AtlasManager::Mesh2D& newMesh, Vector< Extent >& extents, - const Vector4& color, float baseLine, + bool underlineGlyph, float underlinePosition, float underlineThickness, AtlasManager::AtlasSlot& slot ) { if ( slot.mImageId ) { - MeshData::VertexContainer verts = newMeshData.GetVertices(); - float left = verts[ 0 ].x; - float right = verts[ 1 ].x; + float left = newMesh.mVertices[ 0 ].mPosition.x; + float right = newMesh.mVertices[ 1 ].mPosition.x; // Check to see if there's a mesh data object that references the same atlas ? uint32_t index = 0; - for ( std::vector< MeshRecord >::iterator mIt = meshContainer.begin(); mIt != meshContainer.end(); ++mIt, ++index ) + for ( std::vector< MeshRecord >::iterator mIt = meshContainer.begin(), + mEndIt = meshContainer.end(); + mIt != mEndIt; + ++mIt, ++index ) { - if ( slot.mAtlasId == mIt->mAtlasId && color == mIt->mColor ) + if( slot.mAtlasId == mIt->mAtlasId ) { - // Stitch the mesh to the existing mesh and adjust any extents - mGlyphManager.StitchMesh( mIt->mMeshData, newMeshData ); - AdjustExtents( extents, - meshContainer, - index, - left, - right, - baseLine, - underlinePosition, - underlineThickness ); + // Append the mesh to the existing mesh and adjust any extents + Toolkit::Internal::AtlasMeshFactory::AppendMesh( mIt->mMesh, newMesh ); + + if( underlineGlyph ) + { + AdjustExtents( extents, + meshContainer, + index, + left, + right, + baseLine, + underlinePosition, + underlineThickness ); + } + return; } } @@ -365,21 +554,21 @@ struct AtlasRenderer::Impl : public ConnectionTracker // No mesh data object currently exists that references this atlas, so create a new one MeshRecord meshRecord; meshRecord.mAtlasId = slot.mAtlasId; - meshRecord.mMeshData = newMeshData; - meshRecord.mColor = color; - meshRecord.mIsUnderline = false; + meshRecord.mMesh = newMesh; meshContainer.push_back( meshRecord ); - // Adjust extents for this new meshrecord - AdjustExtents( extents, - meshContainer, - meshContainer.size() - 1u, - left, - right, - baseLine, - underlinePosition, - underlineThickness ); - + if( underlineGlyph ) + { + // Adjust extents for this new meshrecord + AdjustExtents( extents, + meshContainer, + meshContainer.size() - 1u, + left, + right, + baseLine, + underlinePosition, + underlineThickness ); + } } } @@ -393,7 +582,10 @@ struct AtlasRenderer::Impl : public ConnectionTracker float underlineThickness ) { bool foundExtent = false; - for ( Vector< Extent >::Iterator eIt = extents.Begin(); eIt != extents.End(); ++eIt ) + for ( Vector< Extent >::Iterator eIt = extents.Begin(), + eEndIt = extents.End(); + eIt != eEndIt; + ++eIt ) { if ( Equals( baseLine, eIt->mBaseLine ) ) { @@ -430,57 +622,55 @@ struct AtlasRenderer::Impl : public ConnectionTracker } } - // Unreference any glyphs that were used with this actor - void OffStageDisconnect( Dali::Actor actor ) - { - RemoveText(); - } - - void RemoveText() - { - for ( uint32_t i = 0; i < mImageIds.Size(); ++i ) - { - mGlyphManager.Remove( mImageIds[ i ] ); - } - mImageIds.Resize( 0 ); - } - void CalculateBlocksSize( const Vector& glyphs ) { - MaxBlockSize maxBlockSize; - for ( uint32_t i = 0; i < glyphs.Size(); ++i ) + for( Vector::ConstIterator glyphIt = glyphs.Begin(), + glyphEndIt = glyphs.End(); + glyphIt != glyphEndIt; + ++glyphIt ) { - FontId fontId = glyphs[ i ].fontId; + const FontId fontId = (*glyphIt).fontId; bool foundFont = false; - for ( uint32_t j = 0; j < mBlockSizes.size(); ++j ) + + for( std::vector< MaxBlockSize >::const_iterator blockIt = mBlockSizes.begin(), + blockEndIt = mBlockSizes.end(); + blockIt != blockEndIt; + ++blockIt ) { - if ( mBlockSizes[ j ].mFontId == fontId ) + if( (*blockIt).mFontId == fontId ) { foundFont = true; + break; } } + if ( !foundFont ) { FontMetrics fontMetrics; mFontClient.GetFontMetrics( fontId, fontMetrics ); + + MaxBlockSize maxBlockSize; maxBlockSize.mNeededBlockWidth = static_cast< uint32_t >( fontMetrics.height ); - maxBlockSize.mNeededBlockHeight = static_cast< uint32_t >( fontMetrics.height ); + maxBlockSize.mNeededBlockHeight = maxBlockSize.mNeededBlockWidth; maxBlockSize.mFontId = fontId; + mBlockSizes.push_back( maxBlockSize ); } } } - void GenerateUnderlines( std::vector< MeshRecord>& meshRecords, + void GenerateUnderlines( std::vector< MeshRecord >& meshRecords, Vector< Extent >& extents, - const Vector4& underlineColor, - const Vector4& textColor ) + const Vector4& underlineColor ) { - MeshData newMeshData; - for ( Vector< Extent >::ConstIterator eIt = extents.Begin(); eIt != extents.End(); ++eIt ) + AtlasManager::Mesh2D newMesh; + unsigned short faceIndex = 0; + for ( Vector< Extent >::ConstIterator eIt = extents.Begin(), + eEndIt = extents.End(); + eIt != eEndIt; + ++eIt ) { - MeshData::VertexContainer newVerts; - newVerts.reserve( 4u ); + AtlasManager::Vertex2D vert; uint32_t index = eIt->mMeshRecordIndex; Vector2 uv = mGlyphManager.GetAtlasSize( meshRecords[ index ].mAtlasId ); @@ -492,195 +682,51 @@ struct AtlasRenderer::Impl : public ConnectionTracker float tlx = eIt->mLeft; float brx = eIt->mRight; - newVerts.push_back( MeshData::Vertex( Vector3( tlx, baseLine, ZERO ), - Vector2::ZERO, - Vector3::ZERO ) ); - - newVerts.push_back( MeshData::Vertex( Vector3( brx, baseLine, ZERO ), - Vector2( u, ZERO ), - Vector3::ZERO ) ); - - newVerts.push_back( MeshData::Vertex( Vector3( tlx, baseLine + thickness, ZERO ), - Vector2( ZERO, v ), - Vector3::ZERO ) ); - - newVerts.push_back( MeshData::Vertex( Vector3( brx, baseLine + thickness, ZERO ), - Vector2( u, v ), - Vector3::ZERO ) ); - - newMeshData.SetVertices( newVerts ); - newMeshData.SetFaceIndices( mFace ); - - if ( underlineColor == textColor ) - { - mGlyphManager.StitchMesh( meshRecords[ index ].mMeshData, newMeshData ); - } - else - { - MeshRecord record; - newMeshData.SetMaterial( meshRecords[ index ].mMeshData.GetMaterial() ); - newMeshData.SetHasNormals( true ); - newMeshData.SetHasColor( false ); - newMeshData.SetHasTextureCoords( true ); - record.mMeshData = newMeshData; - record.mAtlasId = meshRecords[ index ].mAtlasId; - record.mColor = underlineColor; - record.mIsUnderline = true; - meshRecords.push_back( record ); - } + vert.mPosition.x = tlx; + vert.mPosition.y = baseLine; + vert.mTexCoords.x = ZERO; + vert.mTexCoords.y = ZERO; + newMesh.mVertices.PushBack( vert ); + + vert.mPosition.x = brx; + vert.mPosition.y = baseLine; + vert.mTexCoords.x = u; + newMesh.mVertices.PushBack( vert ); + + vert.mPosition.x = tlx; + vert.mPosition.y = baseLine + thickness; + vert.mTexCoords.x = ZERO; + vert.mTexCoords.y = v; + newMesh.mVertices.PushBack( vert ); + + vert.mPosition.x = brx; + vert.mPosition.y = baseLine + thickness; + vert.mTexCoords.x = u; + newMesh.mVertices.PushBack( vert ); + + // Six indices in counter clockwise winding + newMesh.mIndices.PushBack( faceIndex + 1u ); + newMesh.mIndices.PushBack( faceIndex ); + newMesh.mIndices.PushBack( faceIndex + 2u ); + newMesh.mIndices.PushBack( faceIndex + 2u ); + newMesh.mIndices.PushBack( faceIndex + 3u ); + newMesh.mIndices.PushBack( faceIndex + 1u ); + faceIndex += 4; + + vert.mColor = underlineColor; + + Toolkit::Internal::AtlasMeshFactory::AppendMesh( meshRecords[ index ].mMesh, newMesh ); } } - MeshActor GenerateShadow( MeshRecord& meshRecord, - const Vector2& shadowOffset, - const Vector4& shadowColor ) - { - // Scan vertex buffer to determine width and height of effect buffer needed - MeshData::VertexContainer verts = meshRecord.mMeshData.GetVertices(); - float tlx = verts[ 0 ].x; - float tly = verts[ 0 ].y; - float brx = ZERO; - float bry = ZERO; - - for ( uint32_t i = 0; i < verts.size(); ++i ) - { - if ( verts[ i ].x < tlx ) - { - tlx = verts[ i ].x; - } - if ( verts[ i ].y < tly ) - { - tly = verts[ i ].y; - } - if ( verts[ i ].x > brx ) - { - brx = verts[ i ].x; - } - if ( verts[ i ].y > bry ) - { - bry = verts[ i ].y; - } - } - - float width = brx - tlx; - float height = bry - tly; - float divWidth = TWO / width; - float divHeight = TWO / height; - - // Create a buffer to render to - meshRecord.mBuffer = FrameBufferImage::New( width, height ); - - // Create a mesh actor to contain the post-effect render - MeshData::VertexContainer vertices; - MeshData::FaceIndices face; - - vertices.push_back( MeshData::Vertex( Vector3( tlx + shadowOffset.x, tly + shadowOffset.y, ZERO ), - Vector2::ZERO, - Vector3::ZERO ) ); - - vertices.push_back( MeshData::Vertex( Vector3( brx + shadowOffset.x, tly + shadowOffset.y, ZERO ), - Vector2( ONE, ZERO ), - Vector3::ZERO ) ); - - vertices.push_back( MeshData::Vertex( Vector3( tlx + shadowOffset.x, bry + shadowOffset.y, ZERO ), - Vector2( ZERO, ONE ), - Vector3::ZERO ) ); - - vertices.push_back( MeshData::Vertex( Vector3( brx + shadowOffset.x, bry + shadowOffset.y, ZERO ), - Vector2::ONE, - Vector3::ZERO ) ); - - MeshData meshData; - Material newMaterial = Material::New("effect buffer"); - newMaterial.SetDiffuseTexture( meshRecord.mBuffer ); - meshData.SetMaterial( newMaterial ); - meshData.SetVertices( vertices ); - meshData.SetFaceIndices( mFace ); - meshData.SetHasNormals( true ); - meshData.SetHasColor( false ); - meshData.SetHasTextureCoords( true ); - MeshActor actor = MeshActor::New( Mesh::New( meshData ) ); - actor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR ); - actor.SetShaderEffect( mBgraShader ); - actor.SetFilterMode( FilterMode::LINEAR, FilterMode::LINEAR ); - actor.SetSortModifier( 0.1f ); // force behind main text - - // Create a sub actor to render once with normalized vertex positions - MeshData newMeshData; - MeshData::VertexContainer newVerts; - MeshData::FaceIndices newFaces; - MeshData::FaceIndices faces = meshRecord.mMeshData.GetFaces(); - for ( uint32_t i = 0; i < verts.size(); ++i ) - { - MeshData::Vertex vertex = verts[ i ]; - vertex.x = ( ( vertex.x - tlx ) * divWidth ) - ONE; - vertex.y = ( ( vertex.y - tly ) * divHeight ) - ONE; - newVerts.push_back( vertex ); - } - - // Reverse triangle winding order - uint32_t faceCount = faces.size() / 3; - for ( uint32_t i = 0; i < faceCount; ++i ) - { - uint32_t index = i * 3; - newFaces.push_back( faces[ index + 2 ] ); - newFaces.push_back( faces[ index + 1 ] ); - newFaces.push_back( faces[ index ] ); - } - - newMeshData.SetMaterial( meshRecord.mMeshData.GetMaterial() ); - newMeshData.SetVertices( newVerts ); - newMeshData.SetFaceIndices( newFaces ); - newMeshData.SetHasNormals( true ); - newMeshData.SetHasColor( false ); - newMeshData.SetHasTextureCoords( true ); - - MeshActor subActor = MeshActor::New( Mesh::New( newMeshData ) ); - subActor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR ); - subActor.SetColor( shadowColor ); - subActor.SetShaderEffect( mBasicShadowShader ); - subActor.SetFilterMode( FilterMode::NEAREST, FilterMode::NEAREST ); - - // Create a render task to render the effect - RenderTask task = Stage::GetCurrent().GetRenderTaskList().CreateTask(); - task.SetTargetFrameBuffer( meshRecord.mBuffer ); - task.SetSourceActor( subActor ); - task.SetClearEnabled( true ); - task.SetClearColor( Vector4::ZERO ); - task.SetExclusive( true ); - task.SetRefreshRate( RenderTask::REFRESH_ONCE ); - task.FinishedSignal().Connect( this, &AtlasRenderer::Impl::RenderComplete ); - actor.Add( subActor ); - return actor; - } - - void RenderComplete( RenderTask& renderTask ) - { - // Disconnect and remove this single shot render task - renderTask.FinishedSignal().Disconnect( this, &AtlasRenderer::Impl::RenderComplete ); - Stage::GetCurrent().GetRenderTaskList().RemoveTask( renderTask ); - - // Get the actor used for render to buffer and remove it from the parent - Actor renderActor = renderTask.GetSourceActor(); - if ( renderActor ) - { - Actor parent = renderActor.GetParent(); - if ( parent ) - { - parent.Remove( renderActor ); - } - } - } - - RenderableActor mActor; ///< The actor parent which renders the text + Actor mActor; ///< The actor parent which renders the text AtlasGlyphManager mGlyphManager; ///< Glyph Manager to handle upload and caching - Vector< uint32_t > mImageIds; ///< A list of imageIDs used by the renderer TextAbstraction::FontClient mFontClient; ///> The font client used to supply glyph information - ShaderEffect mBasicShader; ///> Shader used to render L8 glyphs - ShaderEffect mBgraShader; ///> Shader used to render BGRA glyphs - ShaderEffect mBasicShadowShader; ///> Shader used to render drop shadow into buffer std::vector< MaxBlockSize > mBlockSizes; ///> Maximum size needed to contain a glyph in a block within a new atlas - std::vector< MeshData::FaceIndex > mFace; ///> Face indices for a quad + Vector< TextCacheEntry > mTextCache; ///> Caches data from previous render + Property::Map mQuadVertexFormat; ///> Describes the vertex format for text + Property::Map mQuadIndexFormat; ///> Describes the index format for text + int mDepth; ///> DepthIndex passed by control when connect to stage }; Text::RendererPtr AtlasRenderer::New() @@ -690,32 +736,47 @@ Text::RendererPtr AtlasRenderer::New() return Text::RendererPtr( new AtlasRenderer() ); } -RenderableActor AtlasRenderer::Render( Text::ViewInterface& view ) +Actor AtlasRenderer::Render( Text::ViewInterface& view, int depth ) { - UnparentAndReset( mImpl->mActor ); - Text::Length numberOfGlyphs = view.GetNumberOfGlyphs(); + Length numberOfGlyphs = view.GetNumberOfGlyphs(); - if( numberOfGlyphs > 0 ) + if( numberOfGlyphs > 0u ) { Vector glyphs; glyphs.Resize( numberOfGlyphs ); - view.GetGlyphs( &glyphs[0], 0, numberOfGlyphs ); + Vector positions; + positions.Resize( numberOfGlyphs ); + + Vector colors; + colors.Resize( numberOfGlyphs, view.GetTextColor() ); + + numberOfGlyphs = view.GetGlyphs( glyphs.Begin(), + positions.Begin(), + colors.Begin(), + 0u, + numberOfGlyphs ); + + glyphs.Resize( numberOfGlyphs ); + positions.Resize( numberOfGlyphs ); + colors.Resize( numberOfGlyphs ); - std::vector positions; - positions.resize( numberOfGlyphs ); - view.GetGlyphPositions( &positions[0], 0, numberOfGlyphs ); - mImpl->AddGlyphs( positions, + mImpl->AddGlyphs( view, + positions, glyphs, - view.GetTextColor(), - view.GetShadowOffset(), - view.GetShadowColor(), - view.IsUnderlineEnabled(), - view.GetUnderlineColor(), - view.GetUnderlineHeight() ); + colors, + depth ); + + /* In the case where AddGlyphs does not create a renderable Actor for example when glyphs are all whitespace create a new Actor. */ + /* This renderable actor is used to position the text, other "decorations" can rely on there always being an Actor regardless of it is whitespace or regular text. */ + if ( !mImpl->mActor ) + { + mImpl->mActor = Actor::New(); + } } + return mImpl->mActor; } @@ -727,5 +788,6 @@ AtlasRenderer::AtlasRenderer() AtlasRenderer::~AtlasRenderer() { + mImpl->RemoveText(); delete mImpl; }