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=2e94ce13fdc88fe22c5f9b132278f14ba9f6a0ea;hp=41dfac9aa961ac2961c2b192f63cbcf947dce772;hb=5b3cf0e6742934674bdf62bbe15af00e39eae566;hpb=43810310547ea62237f91305e8b9724fd017492a 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 41dfac9..2e94ce1 100644 --- a/dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.cpp +++ b/dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.cpp @@ -19,18 +19,19 @@ #include // EXTERNAL INCLUDES -#include #include +#include +#include +#include +#include +#include +#include +#include // INTERNAL INCLUDES -#include +#include #include -#include -#include - -#if defined(DEBUG_ENABLED) -Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_ATLAS_RENDERER"); -#endif +#include using namespace Dali; using namespace Dali::Toolkit; @@ -38,76 +39,192 @@ using namespace Dali::Toolkit::Text; namespace { - const Vector2 DEFAULT_ATLAS_SIZE( 512.0f, 512.0f ); - const Vector2 DEFAULT_BLOCK_SIZE( 16.0f, 16.0f ); - const Vector2 PADDING( 4.0f, 4.0f ); // Allow for variation in font glyphs -} +#if defined(DEBUG_ENABLED) + Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_RENDERING"); +#endif -struct AtlasRenderer::Impl +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; +} +struct AtlasRenderer::Impl : public ConnectionTracker { + enum Style + { + STYLE_NORMAL, + STYLE_DROP_SHADOW + }; struct MeshRecord { + MeshRecord() + : mColor( Color::BLACK ), + mAtlasId( 0 ) + { + } + + Vector4 mColor; uint32_t mAtlasId; - MeshData mMeshData; + AtlasManager::Mesh2D mMesh; + FrameBufferImage mBuffer; }; - struct AtlasRecord + struct Extent { - uint32_t mImageId; - Text::GlyphIndex mIndex; + Extent() + : mBaseLine( 0.0f ), + mLeft( 0.0f ), + mRight( 0.0f ), + mUnderlinePosition( 0.0f ), + mUnderlineThickness( 0.0f ), + mMeshRecordIndex( 0 ) + { + } + + float mBaseLine; + float mLeft; + float mRight; + float mUnderlinePosition; + float mUnderlineThickness; + uint32_t mMeshRecordIndex; }; struct MaxBlockSize { + MaxBlockSize() + : mFontId( 0 ), + mNeededBlockWidth( 0 ), + mNeededBlockHeight( 0 ) + { + } + + FontId mFontId; + uint32_t mNeededBlockWidth; + uint32_t mNeededBlockHeight; + }; + + struct CheckEntry + { + CheckEntry() + : mFontId( 0 ), + mIndex( 0 ) + { + } + FontId mFontId; - Vector2 mNeededBlockSize; + Text::GlyphIndex mIndex; + }; + + struct TextCacheEntry + { + TextCacheEntry() + : mFontId( 0 ), + mIndex( 0 ), + mImageId( 0 ) + { + } + + FontId mFontId; + Text::GlyphIndex mIndex; + uint32_t mImageId; }; Impl() - : mSlotDelegate( this ) + : mDepth( 0 ) { mGlyphManager = AtlasGlyphManager::Get(); mFontClient = TextAbstraction::FontClient::Get(); - mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_SIZE, DEFAULT_BLOCK_SIZE ); - mBasicShader = BasicShader::New(); - mBGRAShader = BgraShader::New(); + + mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2; + mQuadVertexFormat[ "aTexCoord" ] = Property::VECTOR2; + mQuadIndexFormat[ "indices" ] = Property::INTEGER; } - void AddGlyphs( const std::vector& positions, const Vector& glyphs ) + void AddGlyphs( Text::ViewInterface& view, + const std::vector& positions, + const Vector& glyphs, + int depth ) { AtlasManager::AtlasSlot slot; std::vector< MeshRecord > meshContainer; + Vector< Extent > extents; + TextCacheEntry textCacheEntry; + mDepth = depth; + + const Vector2& actorSize( view.GetControlSize() ); + Vector2 halfActorSize( actorSize * 0.5f ); + const Vector4& textColor( view.GetTextColor() ); + const Vector2& shadowOffset( view.GetShadowOffset() ); + const Vector4& shadowColor( view.GetShadowColor() ); + bool underlineEnabled( view.IsUnderlineEnabled() ); + const Vector4& underlineColor( view.GetUnderlineColor() ); + float underlineHeight( view.GetUnderlineHeight() ); + + float currentUnderlinePosition = ZERO; + float currentUnderlineThickness = underlineHeight; + uint32_t currentBlockSize = 0; FontId lastFontId = 0; + Style style = STYLE_NORMAL; - if (mImageIds.Size() ) + if ( fabsf( shadowOffset.x ) > Math::MACHINE_EPSILON_1 || fabsf( shadowOffset.y ) > Math::MACHINE_EPSILON_1 ) { - // Unreference any currently used glyphs - RemoveText(); + style = STYLE_DROP_SHADOW; } 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(); + + for( uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i ) { - GlyphInfo glyph = glyphs[ i ]; + const GlyphInfo& glyph = *( glyphsBuffer + i ); // No operation for white space if ( glyph.width && glyph.height ) { - Vector2 position = positions[ i ]; - MeshData newMeshData; - mGlyphManager.Cached( glyph.fontId, glyph.index, slot ); - - if ( slot.mImageId ) + // Are we still using the same fontId as previous + if ( glyph.fontId != lastFontId ) { - // This glyph already exists so generate mesh data plugging in our supplied position - mGlyphManager.GenerateMeshData( slot.mImageId, position, newMeshData ); - mImageIds.PushBack( slot.mImageId ); + // We need to fetch fresh font underline metrics + FontMetrics fontMetrics; + mFontClient.GetFontMetrics( glyph.fontId, fontMetrics ); + currentUnderlinePosition = ceil( fabsf( fontMetrics.underlinePosition ) ); + const float descender = ceil( fabsf( fontMetrics.descender ) ); + + if ( underlineHeight == ZERO ) + { + currentUnderlineThickness = fontMetrics.underlineThickness; + + // Ensure underline will be at least a pixel high + if ( currentUnderlineThickness < ONE ) + { + currentUnderlineThickness = ONE; + } + else + { + currentUnderlineThickness = ceil( currentUnderlineThickness ); + } + } + + // Clamp the underline position at the font descender and check for ( as EFL describes it ) a broken font + if ( currentUnderlinePosition > descender ) + { + currentUnderlinePosition = descender; + } + if ( ZERO == currentUnderlinePosition ) + { + // Move offset down by one ( EFL behavior ) + currentUnderlinePosition = ONE; + } } - else - { + if ( !mGlyphManager.Cached( glyph.fontId, glyph.index, slot ) ) + { // Select correct size for new atlas if needed....? if ( lastFontId != glyph.fontId ) { @@ -115,51 +232,98 @@ struct AtlasRenderer::Impl { if ( mBlockSizes[ j ].mFontId == glyph.fontId ) { - mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_SIZE, mBlockSizes[ j ].mNeededBlockSize ); + currentBlockSize = j; + mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_WIDTH, + DEFAULT_ATLAS_HEIGHT, + mBlockSizes[ j ].mNeededBlockWidth, + mBlockSizes[ j ].mNeededBlockHeight ); } } - lastFontId = glyph.fontId; } - // Glyph doesn't currently exist in atlas so upload + // Create a new image for the glyph BufferImage bitmap = mFontClient.CreateBitmap( glyph.fontId, glyph.index ); + if ( bitmap ) + { + // Ensure that the next image will fit into the current block size + bool setSize = false; + if ( bitmap.GetWidth() > mBlockSizes[ currentBlockSize ].mNeededBlockWidth ) + { + setSize = true; + mBlockSizes[ currentBlockSize ].mNeededBlockWidth = bitmap.GetWidth(); + } + if ( bitmap.GetHeight() > mBlockSizes[ currentBlockSize ].mNeededBlockHeight ) + { + setSize = true; + mBlockSizes[ currentBlockSize ].mNeededBlockHeight = bitmap.GetHeight(); + } - // Locate a new slot for our glyph - mGlyphManager.Add( glyph, bitmap, slot ); + if ( setSize ) + { + mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_WIDTH, + DEFAULT_ATLAS_HEIGHT, + mBlockSizes[ currentBlockSize ].mNeededBlockWidth, + mBlockSizes[ currentBlockSize ].mNeededBlockHeight ); + } - // Generate mesh data for this quad, plugging in our supplied position - if ( slot.mImageId ) - { - mGlyphManager.GenerateMeshData( slot.mImageId, position, newMeshData ); - mImageIds.PushBack( slot.mImageId ); + // 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*/ ); + } + + // Move the origin (0,0) of the mesh to the center of the actor + Vector2 position = positions[ i ] - halfActorSize; + + // 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 ); + // 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, slot ); + StitchTextMesh( meshContainer, + newMesh, + extents, + textColor, + position.y + glyph.yBearing, + currentUnderlinePosition, + currentUnderlineThickness, + slot ); + lastFontId = glyph.fontId; } + } // glyphs + + // Now remove references for the old text + RemoveText(); + mTextCache.Swap( newTextCache ); + + if ( underlineEnabled ) + { + // Check to see if any of the text needs an underline + GenerateUnderlines( meshContainer, extents, underlineColor, textColor ); } // For each MeshData object, create a mesh actor and add to the renderable actor if ( meshContainer.size() ) { - for ( uint32_t i = 0; i < meshContainer.size(); ++i ) + for ( std::vector< MeshRecord >::iterator mIt = meshContainer.begin(); mIt != meshContainer.end(); ++mIt ) { - Mesh mesh = Mesh::New( meshContainer[ i ].mMeshData ); - MeshActor actor = MeshActor::New( mesh ); - actor.SetParentOrigin( ParentOrigin::TOP_LEFT ); - actor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );; + Actor actor = CreateMeshActor( *mIt, actorSize ); - // Check to see what pixel format the shader should be - if ( mGlyphManager.GetPixelFormat( meshContainer[ i ].mAtlasId ) == Pixel::L8 ) + // Create an effect if necessary + if ( style == STYLE_DROP_SHADOW ) { - actor.SetShaderEffect( mBasicShader ); - } - else - { - actor.SetShaderEffect( mBGRAShader ); + actor.Add( GenerateShadow( *mIt, actorSize, shadowOffset, shadowColor ) ); } - if ( i ) + if( mActor ) { mActor.Add( actor ); } @@ -168,42 +332,98 @@ struct AtlasRenderer::Impl mActor = actor; } } - mActor.OffStageSignal().Connect( mSlotDelegate, &AtlasRenderer::Impl::OffStageDisconnect ); } #if defined(DEBUG_ENABLED) Toolkit::AtlasGlyphManager::Metrics metrics = mGlyphManager.GetMetrics(); - DALI_LOG_INFO( gLogFilter, Debug::Concise, "TextAtlasRenderer::GlyphManager::GlyphCount: %i, AtlasCount: %i, TextureMemoryUse: %iK\n", + DALI_LOG_INFO( gLogFilter, Debug::General, "TextAtlasRenderer::GlyphManager::GlyphCount: %i, AtlasCount: %i, TextureMemoryUse: %iK\n", metrics.mGlyphCount, metrics.mAtlasMetrics.mAtlasCount, metrics.mAtlasMetrics.mTextureMemoryUsed / 1024 ); + + 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 ].mWidth, - metrics.mAtlasMetrics.mAtlasMetrics[ i ].mHeight, - metrics.mAtlasMetrics.mAtlasMetrics[ i ].mBlockWidth, - metrics.mAtlasMetrics.mAtlasMetrics[ i ].mBlockHeight, + metrics.mAtlasMetrics.mAtlasMetrics[ i ].mSize.mWidth, + metrics.mAtlasMetrics.mAtlasMetrics[ i ].mSize.mHeight, + metrics.mAtlasMetrics.mAtlasMetrics[ i ].mSize.mBlockWidth, + metrics.mAtlasMetrics.mAtlasMetrics[ i ].mSize.mBlockHeight, metrics.mAtlasMetrics.mAtlasMetrics[ i ].mBlocksUsed, metrics.mAtlasMetrics.mAtlasMetrics[ i ].mTotalBlocks ); } #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, meshRecord.mMesh.mVertices.Size() ); + PropertyBuffer quadIndices = PropertyBuffer::New( mQuadIndexFormat, meshRecord.mMesh.mIndices.Size() ); + quadVertices.SetData( const_cast< AtlasManager::Vertex2D* >( &meshRecord.mMesh.mVertices[ 0 ] ) ); + quadIndices.SetData( const_cast< unsigned int* >( &meshRecord.mMesh.mIndices[ 0 ] ) ); + + Geometry quadGeometry = Geometry::New(); + quadGeometry.AddVertexBuffer( quadVertices ); + quadGeometry.SetIndexBuffer( quadIndices ); + + Material material = mGlyphManager.GetMaterial( meshRecord.mAtlasId ); + Dali::Renderer renderer = Dali::Renderer::New( quadGeometry, material ); + renderer.SetDepthIndex( CONTENT_DEPTH_INDEX + mDepth ); + Actor actor = Actor::New(); +#if defined(DEBUG_ENABLED) + actor.SetName( "Text renderable actor" ); +#endif + actor.AddRenderer( renderer ); + actor.SetParentOrigin( ParentOrigin::CENTER ); // Keep all of the origins aligned + actor.SetSize( actorSize ); + actor.SetColor( meshRecord.mColor ); + return actor; + } + void StitchTextMesh( std::vector< MeshRecord >& meshContainer, - MeshData& newMeshData, + AtlasManager::Mesh2D& newMesh, + Vector< Extent >& extents, + const Vector4& color, + float baseLine, + float underlinePosition, + float underlineThickness, AtlasManager::AtlasSlot& slot ) { if ( slot.mImageId ) { + 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 ? - for ( uint32_t i = 0; i < meshContainer.size(); ++i ) + uint32_t index = 0; + for ( std::vector< MeshRecord >::iterator mIt = meshContainer.begin(), + mEndIt = meshContainer.end(); + mIt != mEndIt; + ++mIt, ++index ) { - if ( slot.mAtlasId == meshContainer[ i ].mAtlasId ) + if ( slot.mAtlasId == mIt->mAtlasId && color == mIt->mColor ) { - // Stitch the mesh to the existing mesh - mGlyphManager.StitchMesh( meshContainer[ i ].mMeshData, newMeshData ); + // Stitch the mesh to the existing mesh and adjust any extents + mGlyphManager.StitchMesh( mIt->mMesh, newMesh ); + AdjustExtents( extents, + meshContainer, + index, + left, + right, + baseLine, + underlinePosition, + underlineThickness ); return; } } @@ -211,24 +431,71 @@ struct AtlasRenderer::Impl // 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.mMesh = newMesh; + meshRecord.mColor = color; meshContainer.push_back( meshRecord ); + + // Adjust extents for this new meshrecord + AdjustExtents( extents, + meshContainer, + meshContainer.size() - 1u, + left, + right, + baseLine, + underlinePosition, + underlineThickness ); + } } - // Unreference any glyphs that were used with this actor - void OffStageDisconnect( Dali::Actor actor ) + void AdjustExtents( Vector< Extent >& extents, + std::vector< MeshRecord>& meshRecords, + uint32_t index, + float left, + float right, + float baseLine, + float underlinePosition, + float underlineThickness ) { - RemoveText(); - } + bool foundExtent = false; + for ( Vector< Extent >::Iterator eIt = extents.Begin(), + eEndIt = extents.End(); + eIt != eEndIt; + ++eIt ) + { + if ( Equals( baseLine, eIt->mBaseLine ) ) + { + foundExtent = true; + if ( left < eIt->mLeft ) + { + eIt->mLeft = left; + } + if ( right > eIt->mRight ) + { + eIt->mRight = right; + } - void RemoveText() - { - for ( uint32_t i = 0; i < mImageIds.Size(); ++i ) + if ( underlinePosition > eIt->mUnderlinePosition ) + { + eIt->mUnderlinePosition = underlinePosition; + } + if ( underlineThickness > eIt->mUnderlineThickness ) + { + eIt->mUnderlineThickness = underlineThickness; + } + } + } + if ( !foundExtent ) { - mGlyphManager.Remove( mImageIds[ i ] ); + Extent extent; + extent.mLeft = left; + extent.mRight = right; + extent.mBaseLine = baseLine; + extent.mUnderlinePosition = underlinePosition; + extent.mUnderlineThickness = underlineThickness; + extent.mMeshRecordIndex = index; + extents.PushBack( extent ); } - mImageIds.Resize( 0 ); } void CalculateBlocksSize( const Vector& glyphs ) @@ -236,71 +503,275 @@ struct AtlasRenderer::Impl MaxBlockSize maxBlockSize; for ( uint32_t i = 0; i < glyphs.Size(); ++i ) { - // Get the fontId of this glyph and check to see if a max size exists? FontId fontId = glyphs[ i ].fontId; - float paddedWidth = glyphs[ i ].width + PADDING.x; - float paddedHeight = glyphs[ i ].height + PADDING.y; bool foundFont = false; - for ( uint32_t j = 0; j < mBlockSizes.size(); ++j ) { if ( mBlockSizes[ j ].mFontId == fontId ) { foundFont = true; - if ( mBlockSizes[ j ].mNeededBlockSize.x < paddedWidth ) - { - mBlockSizes[ j ].mNeededBlockSize.x = paddedWidth; - } - if ( mBlockSizes[ j ].mNeededBlockSize.y < paddedHeight ) - { - mBlockSizes[ j ].mNeededBlockSize.y = paddedHeight; - } } } - if ( !foundFont ) { - maxBlockSize.mNeededBlockSize = Vector2( paddedWidth, paddedHeight ); + FontMetrics fontMetrics; + mFontClient.GetFontMetrics( fontId, fontMetrics ); + maxBlockSize.mNeededBlockWidth = static_cast< uint32_t >( fontMetrics.height ); + maxBlockSize.mNeededBlockHeight = static_cast< uint32_t >( fontMetrics.height ); maxBlockSize.mFontId = fontId; mBlockSizes.push_back( maxBlockSize ); } } } - RenderableActor mActor; ///< The actor parent which renders the text + void GenerateUnderlines( std::vector< MeshRecord >& meshRecords, + Vector< Extent >& extents, + const Vector4& underlineColor, + const Vector4& textColor ) + { + AtlasManager::Mesh2D newMesh; + unsigned short faceIndex = 0; + for ( Vector< Extent >::ConstIterator eIt = extents.Begin(), + eEndIt = extents.End(); + eIt != eEndIt; + ++eIt ) + { + AtlasManager::Vertex2D vert; + uint32_t index = eIt->mMeshRecordIndex; + Vector2 uv = mGlyphManager.GetAtlasSize( meshRecords[ index ].mAtlasId ); + + // Make sure we don't hit texture edge for single pixel texture ( filled pixel is in top left of every atlas ) + float u = HALF / uv.x; + float v = HALF / uv.y; + float thickness = eIt->mUnderlineThickness; + float baseLine = eIt->mBaseLine + eIt->mUnderlinePosition - ( thickness * HALF ); + float tlx = eIt->mLeft; + float brx = eIt->mRight; + + 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; + + if ( underlineColor == textColor ) + { + mGlyphManager.StitchMesh( meshRecords[ index ].mMesh, newMesh ); + } + else + { + MeshRecord record; + record.mMesh = newMesh; + record.mAtlasId = meshRecords[ index ].mAtlasId; + record.mColor = underlineColor; + meshRecords.push_back( record ); + } + } + } + + Actor GenerateShadow( MeshRecord& meshRecord, + const Vector2& actorSize, + const Vector2& shadowOffset, + const Vector4& shadowColor ) + { + // Scan vertex buffer to determine width and height of effect buffer needed + const Vector< AtlasManager::Vertex2D >& verts = meshRecord.mMesh.mVertices; + float tlx = verts[ 0 ].mPosition.x; + float tly = verts[ 0 ].mPosition.y; + float brx = ZERO; + float bry = ZERO; + + for ( uint32_t i = 0; i < verts.Size(); ++i ) + { + if ( verts[ i ].mPosition.x < tlx ) + { + tlx = verts[ i ].mPosition.x; + } + if ( verts[ i ].mPosition.y < tly ) + { + tly = verts[ i ].mPosition.y; + } + if ( verts[ i ].mPosition.x > brx ) + { + brx = verts[ i ].mPosition.x; + } + if ( verts[ i ].mPosition.y > bry ) + { + bry = verts[ i ].mPosition.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 ); + + // We will render a quad into this buffer + unsigned int indices[ 6 ] = { 1, 0, 2, 2, 3, 1 }; + PropertyBuffer quadVertices = PropertyBuffer::New( mQuadVertexFormat, 4u ); + PropertyBuffer quadIndices = PropertyBuffer::New( mQuadIndexFormat, sizeof(indices)/sizeof(indices[0]) ); + + AtlasManager::Vertex2D vertices[ 4 ] = { + { Vector2( tlx + shadowOffset.x, tly + shadowOffset.y ), Vector2( ZERO, ZERO ) }, + { Vector2( brx + shadowOffset.x, tly + shadowOffset.y ), Vector2( ONE, ZERO ) }, + { Vector2( tlx + shadowOffset.x, bry + shadowOffset.y ), Vector2( ZERO, ONE ) }, + { Vector2( brx + shadowOffset.x, bry + shadowOffset.y ), Vector2( ONE, ONE ) } }; + + quadVertices.SetData( vertices ); + quadIndices.SetData( indices ); + + Geometry quadGeometry = Geometry::New(); + quadGeometry.AddVertexBuffer( quadVertices ); + quadGeometry.SetIndexBuffer( quadIndices ); + + Sampler sampler = Sampler::New( meshRecord.mBuffer, "sTexture" ); + Material material = Material::New( mGlyphManager.GetEffectBufferShader() ); + material.AddSampler( sampler ); + + Dali::Renderer renderer = Dali::Renderer::New( quadGeometry, material ); + + // Ensure shadow is behind the text... + renderer.SetDepthIndex( CONTENT_DEPTH_INDEX + mDepth - 1 ); + Actor actor = Actor::New(); + actor.AddRenderer( renderer ); + actor.SetParentOrigin( ParentOrigin::CENTER ); // Keep all of the origins aligned + actor.SetSize( actorSize ); + + // Create a sub actor to render the source with normalized vertex positions + Vector< AtlasManager::Vertex2D > normVertexList; + for ( uint32_t i = 0; i < verts.Size(); ++i ) + { + AtlasManager::Vertex2D vertex = verts[ i ]; + vertex.mPosition.x = ( ( vertex.mPosition.x - tlx ) * divWidth ) - ONE; + vertex.mPosition.y = ( ( vertex.mPosition.y - tly ) * divHeight ) - ONE; + normVertexList.PushBack( vertex ); + } + + PropertyBuffer normVertices = PropertyBuffer::New( mQuadVertexFormat, normVertexList.Size() ); + PropertyBuffer normIndices = PropertyBuffer::New( mQuadIndexFormat, meshRecord.mMesh.mIndices.Size() ); + normVertices.SetData( const_cast< AtlasManager::Vertex2D* >( &normVertexList[ 0 ] ) ); + normIndices.SetData( const_cast< unsigned int* >( &meshRecord.mMesh.mIndices[ 0 ] ) ); + + Geometry normGeometry = Geometry::New(); + normGeometry.AddVertexBuffer( normVertices ); + normGeometry.SetIndexBuffer( normIndices ); + + Material normMaterial = Material::New( mGlyphManager.GetGlyphShadowShader() ); + Sampler normSampler = mGlyphManager.GetSampler( meshRecord.mAtlasId ); + normMaterial.AddSampler( normSampler ); + Dali::Renderer normRenderer = Dali::Renderer::New( normGeometry, normMaterial ); + Actor subActor = Actor::New(); + subActor.AddRenderer( normRenderer ); + subActor.SetParentOrigin( ParentOrigin::CENTER ); // Keep all of the origins aligned + subActor.SetSize( actorSize ); + subActor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR ); + subActor.SetColor( shadowColor ); + + // 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 ); + } + } + } + + 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 - SlotDelegate< AtlasRenderer::Impl > mSlotDelegate; ///> Signal generated to unreference glyphs when renderable actor is removed - ShaderEffect mBasicShader; ///> Shader to render L8 glyphs - ShaderEffect mBGRAShader; ///> Shader to render BGRA glyphs std::vector< MaxBlockSize > mBlockSizes; ///> Maximum size needed to contain a glyph in a block within a new atlas + std::vector< uint32_t > mFace; ///> Face indices for a quad + Vector< TextCacheEntry > mTextCache; + Property::Map mQuadVertexFormat; + Property::Map mQuadIndexFormat; + int mDepth; }; Text::RendererPtr AtlasRenderer::New() { + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Text::AtlasRenderer::New()\n" ); + 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 ); - std::vector positions; positions.resize( numberOfGlyphs ); - view.GetGlyphPositions( &positions[0], 0, numberOfGlyphs ); - mImpl->AddGlyphs( positions, glyphs ); + + numberOfGlyphs = view.GetGlyphs( glyphs.Begin(), + &positions[0], + 0u, + numberOfGlyphs ); + glyphs.Resize( numberOfGlyphs ); + positions.resize( numberOfGlyphs ); + + mImpl->AddGlyphs( view, + positions, + glyphs, + depth ); } + return mImpl->mActor; } @@ -312,5 +783,6 @@ AtlasRenderer::AtlasRenderer() AtlasRenderer::~AtlasRenderer() { + mImpl->RemoveText(); delete mImpl; -} \ No newline at end of file +}