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=e06b958344d3065ff9421e66b0deb2abd582cdc4;hp=7c629fdc6e4aab2d7bef63b8a7d0f5a3b4ac4093;hb=ff0db3ef923086d96dc8c2eb8501607da9c3c21e;hpb=4c1f4bfb9eb5b956b64a6c9bf077217fc0ca9234 diff --git a/dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.cpp b/dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.cpp old mode 100644 new mode 100755 index 7c629fd..e06b958 --- a/dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.cpp +++ b/dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * Copyright (c) 2016 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,19 +19,17 @@ #include // EXTERNAL INCLUDES -#include -#include -#include -#include -#include -#include -#include +#include +#include #include +#include +#include // INTERNAL INCLUDES -#include +#include #include #include +#include #include using namespace Dali; @@ -41,17 +39,64 @@ using namespace Dali::Toolkit::Text; namespace { #if defined(DEBUG_ENABLED) - Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_RENDERING"); + Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_RENDERING"); #endif +#define MAKE_SHADER(A)#A + +const char* VERTEX_SHADER = MAKE_SHADER( +attribute mediump vec2 aPosition; +attribute mediump vec2 aTexCoord; +attribute mediump vec4 aColor; +uniform mediump vec2 uOffset; +uniform highp mat4 uMvpMatrix; +varying mediump vec2 vTexCoord; +varying mediump vec4 vColor; + +void main() +{ + mediump vec4 position = vec4( aPosition.xy + uOffset, 0.0, 1.0 ); + gl_Position = uMvpMatrix * position; + vTexCoord = aTexCoord; + vColor = aColor; +} +); + +const char* FRAGMENT_SHADER_L8 = MAKE_SHADER( +uniform lowp vec4 uColor; +uniform lowp vec4 textColorAnimatable; +uniform sampler2D sTexture; +varying mediump vec2 vTexCoord; +varying mediump vec4 vColor; + +void main() +{ + mediump vec4 color = texture2D( sTexture, vTexCoord ); + gl_FragColor = vec4( vColor.rgb * uColor.rgb * textColorAnimatable.rgb, uColor.a * vColor.a * textColorAnimatable.a * color.r ); +} +); + +const char* FRAGMENT_SHADER_RGBA = MAKE_SHADER( +uniform lowp vec4 uColor; +uniform lowp vec4 textColorAnimatable; +uniform sampler2D sTexture; +varying mediump vec2 vTexCoord; + +void main() +{ + gl_FragColor = texture2D( sTexture, vTexCoord ) * uColor * textColorAnimatable; +} +); + 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 uint32_t NO_OUTLINE = 0; } -struct AtlasRenderer::Impl : public ConnectionTracker + +struct AtlasRenderer::Impl { enum Style { @@ -62,15 +107,12 @@ struct AtlasRenderer::Impl : public ConnectionTracker struct MeshRecord { MeshRecord() - : mColor( Color::BLACK ), - mAtlasId( 0 ) + : mAtlasId( 0u ) { } - Vector4 mColor; uint32_t mAtlasId; AtlasManager::Mesh2D mMesh; - FrameBufferImage mBuffer; }; /** @@ -85,7 +127,7 @@ struct AtlasRenderer::Impl : public ConnectionTracker mRight( 0.0f ), mUnderlinePosition( 0.0f ), mUnderlineThickness( 0.0f ), - mMeshRecordIndex( 0 ) + mMeshRecordIndex( 0u ) { } @@ -128,12 +170,14 @@ struct AtlasRenderer::Impl : public ConnectionTracker TextCacheEntry() : mFontId( 0 ), mIndex( 0 ), + mOutlineWidth( 0 ), mImageId( 0 ) { } FontId mFontId; Text::GlyphIndex mIndex; + uint32_t mOutlineWidth; uint32_t mImageId; }; @@ -145,15 +189,12 @@ struct AtlasRenderer::Impl : public ConnectionTracker mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2; mQuadVertexFormat[ "aTexCoord" ] = Property::VECTOR2; - mQuadIndexFormat[ "indices" ] = Property::INTEGER; + mQuadVertexFormat[ "aColor" ] = Property::VECTOR4; } bool IsGlyphUnderlined( GlyphIndex index, const Vector& underlineRuns ) { - // TODO: At the moment it works because we always traverse the glyphs starting from the beginning - // and there is only one glyph run! If there are more they should be ordered. - for( Vector::ConstIterator it = underlineRuns.Begin(), endIt = underlineRuns.End(); it != endIt; @@ -170,25 +211,245 @@ struct AtlasRenderer::Impl : public ConnectionTracker return false; } + void CacheGlyph( const GlyphInfo& glyph, FontId lastFontId, uint32_t outline, AtlasManager::AtlasSlot& slot ) + { + const bool glyphNotCached = !mGlyphManager.IsCached( glyph.fontId, glyph.index, outline, slot ); // Check FontGlyphRecord vector for entry with glyph index and fontId + + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "AddGlyphs fontID[%u] glyphIndex[%u] [%s]\n", glyph.fontId, glyph.index, (glyphNotCached)?"not cached":"cached" ); + + if( glyphNotCached ) + { + MaxBlockSize& blockSize = mBlockSizes[0u]; + + if ( lastFontId != glyph.fontId ) + { + uint32_t index = 0u; + // Looks through all stored block sizes until finds the one which mataches required glyph font it. Ensures new atlas block size will match existing for same font id. + // CalculateBlocksSize() above ensures a block size entry exists. + for( std::vector::const_iterator it = mBlockSizes.begin(), + endIt = mBlockSizes.end(); + it != endIt; + ++it, ++index ) + { + const MaxBlockSize& blockSizeEntry = *it; + if( blockSizeEntry.mFontId == glyph.fontId ) + { + blockSize = mBlockSizes[index]; + } + } + } + + // Create a new image for the glyph + PixelData bitmap; + + // Whether the glyph is an outline. + const bool isOutline = 0u != outline; + + // Whether the current glyph is a color one. + const bool isColorGlyph = mFontClient.IsColorGlyph( glyph.fontId, glyph.index ); + + if( !isOutline || ( isOutline && !isColorGlyph) ) + { + // Retrieve the emoji's bitmap. + TextAbstraction::FontClient::GlyphBufferData glyphBufferData; + glyphBufferData.width = isColorGlyph ? glyph.width : 0; // Desired width and height. + glyphBufferData.height = isColorGlyph ? glyph.height : 0; + + mFontClient.CreateBitmap( glyph.fontId, + glyph.index, + glyph.softwareItalic, + glyph.softwareBold, + glyphBufferData, + outline ); + + // Create the pixel data. + bitmap = PixelData::New( glyphBufferData.buffer, + glyphBufferData.width * glyphBufferData.height * GetBytesPerPixel( glyphBufferData.format ), + glyphBufferData.width, + glyphBufferData.height, + glyphBufferData.format, + PixelData::DELETE_ARRAY ); + + if( bitmap ) + { + // Ensure that the next image will fit into the current block size + if( bitmap.GetWidth() > blockSize.mNeededBlockWidth ) + { + blockSize.mNeededBlockWidth = bitmap.GetWidth(); + } + + if( bitmap.GetHeight() > blockSize.mNeededBlockHeight ) + { + blockSize.mNeededBlockHeight = bitmap.GetHeight(); + } + + // If CheckAtlas in AtlasManager::Add can't fit the bitmap in the current atlas it will create a new atlas + + // Setting the block size and size of new atlas does not mean a new one will be created. An existing atlas may still surffice. + mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_WIDTH, + DEFAULT_ATLAS_HEIGHT, + blockSize.mNeededBlockWidth, + blockSize.mNeededBlockHeight ); + + // Locate a new slot for our glyph + mGlyphManager.Add( glyph, outline, bitmap, slot ); // slot will be 0 is glyph not added + } + } + } + else + { + // We have 2+ copies of the same glyph + mGlyphManager.AdjustReferenceCount( glyph.fontId, glyph.index, outline, 1 ); //increment + } + } + + void GenerateMesh( const GlyphInfo& glyph, + const Vector2& position, + const Vector4& color, + uint32_t outline, + AtlasManager::AtlasSlot& slot, + bool underlineGlyph, + float currentUnderlinePosition, + float currentUnderlineThickness, + std::vector& meshContainer, + Vector& newTextCache, + Vector& extents ) + { + // Generate mesh data for this quad, plugging in our supplied position + AtlasManager::Mesh2D newMesh; + mGlyphManager.GenerateMeshData( slot.mImageId, position, newMesh ); + + TextCacheEntry textCacheEntry; + textCacheEntry.mFontId = glyph.fontId; + textCacheEntry.mImageId = slot.mImageId; + textCacheEntry.mIndex = glyph.index; + textCacheEntry.mOutlineWidth = outline; + + newTextCache.PushBack( textCacheEntry ); + + AtlasManager::Vertex2D* verticesBuffer = newMesh.mVertices.Begin(); + + 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, + newMesh, + extents, + position.y + glyph.yBearing, + underlineGlyph, + currentUnderlinePosition, + currentUnderlineThickness, + slot ); + } + + void CreateActors( const std::vector& meshContainer, + const Size& textSize, + const Vector4& color, + const Vector4& shadowColor, + const Vector2& shadowOffset, + Actor textControl, + Property::Index animatablePropertyIndex, + bool drawShadow ) + { + if( !mActor ) + { + // Create a container actor to act as a common parent for text and shadow, to avoid color inheritence issues. + mActor = Actor::New(); + mActor.SetParentOrigin( ParentOrigin::TOP_LEFT ); + mActor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + mActor.SetSize( textSize ); + mActor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR ); + } + + for( std::vector< MeshRecord >::const_iterator it = meshContainer.begin(), + endIt = meshContainer.end(); + it != endIt; ++it ) + { + const MeshRecord& meshRecord = *it; + + Actor actor = CreateMeshActor( textControl, animatablePropertyIndex, color, meshRecord, textSize, STYLE_NORMAL ); + + // Whether the actor has renderers. + const bool hasRenderer = actor.GetRendererCount() > 0u; + + // Create an effect if necessary + if( hasRenderer && + drawShadow ) + { + // Change the color of the vertices. + for( Vector::Iterator vIt = meshRecord.mMesh.mVertices.Begin(), + vEndIt = meshRecord.mMesh.mVertices.End(); + vIt != vEndIt; + ++vIt ) + { + AtlasManager::Vertex2D& vertex = *vIt; + + vertex.mColor = shadowColor; + } + + Actor shadowActor = CreateMeshActor(textControl, animatablePropertyIndex, color, meshRecord, textSize, STYLE_DROP_SHADOW ); +#if defined(DEBUG_ENABLED) + shadowActor.SetName( "Text Shadow renderable actor" ); +#endif + // Offset shadow in x and y + shadowActor.RegisterProperty("uOffset", shadowOffset ); + Dali::Renderer renderer( shadowActor.GetRendererAt( 0 ) ); + int depthIndex = renderer.GetProperty(Dali::Renderer::Property::DEPTH_INDEX); + renderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, depthIndex - 1 ); + mActor.Add( shadowActor ); + } + + if( hasRenderer ) + { + mActor.Add( actor ); + } + } + } + void AddGlyphs( Text::ViewInterface& view, + Actor textControl, + Property::Index animatablePropertyIndex, const Vector& positions, const Vector& glyphs, - int depth ) + const Vector4& defaultColor, + const Vector4* const colorsBuffer, + const ColorIndex* const colorIndicesBuffer, + int depth, + float minLineOffset ) { AtlasManager::AtlasSlot slot; + slot.mImageId = 0u; + slot.mAtlasId = 0u; + + AtlasManager::AtlasSlot slotOutline; + slotOutline.mImageId = 0u; + slotOutline.mAtlasId = 0u; + std::vector< MeshRecord > meshContainer; + std::vector< MeshRecord > meshContainerOutline; Vector< Extent > extents; - TextCacheEntry textCacheEntry; mDepth = depth; - const Vector2& actorSize( view.GetControlSize() ); - const Vector2 halfActorSize( actorSize * 0.5f ); - const Vector4& textColor( view.GetTextColor() ); + 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 bool underlineEnabled = view.IsUnderlineEnabled(); const Vector4& underlineColor( view.GetUnderlineColor() ); - const float underlineHeight( view.GetUnderlineHeight() ); + const float underlineHeight = view.GetUnderlineHeight(); + const unsigned int outlineWidth = view.GetOutlineWidth(); + const Vector4& outlineColor( view.GetOutlineColor() ); + const bool isOutline = 0u != outlineWidth; + + const bool useDefaultColor = ( NULL == colorsBuffer ); // Get the underline runs. const Length numberOfUnderlineRuns = view.GetNumberOfUnderlineRuns(); @@ -202,7 +463,6 @@ struct AtlasRenderer::Impl : public ConnectionTracker float currentUnderlinePosition = ZERO; float currentUnderlineThickness = underlineHeight; - uint32_t currentBlockSize = 0; FontId lastFontId = 0; FontId lastUnderlinedFontId = 0; Style style = STYLE_NORMAL; @@ -218,19 +478,19 @@ struct AtlasRenderer::Impl : public ConnectionTracker Vector< TextCacheEntry > newTextCache; const GlyphInfo* const glyphsBuffer = glyphs.Begin(); const Vector2* const positionsBuffer = positions.Begin(); + const Vector2 lineOffsetPosition( minLineOffset, 0.f ); for( uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i ) { const GlyphInfo& glyph = *( glyphsBuffer + i ); - - const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined( i, underlineRuns ); - thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph; + const bool isGlyphUnderlined = underlineEnabled || IsGlyphUnderlined( i, underlineRuns ); + thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || isGlyphUnderlined; // 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 ( underlineGlyph && ( glyph.fontId != lastUnderlinedFontId ) ) + if( isGlyphUnderlined && ( glyph.fontId != lastUnderlinedFontId ) ) { // We need to fetch fresh font underline metrics FontMetrics fontMetrics; @@ -238,7 +498,7 @@ struct AtlasRenderer::Impl : public ConnectionTracker currentUnderlinePosition = ceil( fabsf( fontMetrics.underlinePosition ) ); const float descender = ceil( fabsf( fontMetrics.descender ) ); - if ( underlineHeight == ZERO ) + if( fabsf( underlineHeight ) < Math::MACHINE_EPSILON_1000 ) { currentUnderlineThickness = fontMetrics.underlineThickness; @@ -254,11 +514,12 @@ 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; @@ -267,130 +528,105 @@ struct AtlasRenderer::Impl : public ConnectionTracker lastUnderlinedFontId = glyph.fontId; } // underline - if ( !mGlyphManager.Cached( glyph.fontId, glyph.index, slot ) ) - { - // Select correct size for new atlas if needed....? - if ( lastFontId != glyph.fontId ) - { - for ( uint32_t j = 0; j < mBlockSizes.size(); ++j ) - { - if ( mBlockSizes[ j ].mFontId == glyph.fontId ) - { - currentBlockSize = j; - mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_WIDTH, - DEFAULT_ATLAS_HEIGHT, - mBlockSizes[ j ].mNeededBlockWidth, - mBlockSizes[ j ].mNeededBlockHeight ); - } - } - } + // Retrieves and caches the glyph's bitmap. + CacheGlyph( glyph, lastFontId, NO_OUTLINE, slot ); - // 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(); - } - - if ( setSize ) - { - mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_WIDTH, - DEFAULT_ATLAS_HEIGHT, - mBlockSizes[ currentBlockSize ].mNeededBlockWidth, - mBlockSizes[ currentBlockSize ].mNeededBlockHeight ); - } - - // Locate a new slot for our glyph - mGlyphManager.Add( glyph, bitmap, slot ); - } - } - else + // Retrieves and caches the outline glyph's bitmap. + if( isOutline ) { - // We have 2+ copies of the same glyph - mGlyphManager.AdjustReferenceCount( glyph.fontId, glyph.index, 1/*increment*/ ); + CacheGlyph( glyph, lastFontId, outlineWidth, slotOutline ); } // Move the origin (0,0) of the mesh to the center of the actor - Vector2 position = *( positionsBuffer + 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 ); - - // Adjust the vertices if the fixed-size font should be down-scaled - if( glyph.scaleFactor > 0 ) + const Vector2 position = *( positionsBuffer + i ) - halfTextSize - lineOffsetPosition; + + if ( 0u != slot.mImageId ) // invalid slot id, glyph has failed to be added to atlas { - for( unsigned int i=0; i( outlineWidth ); + positionPlusOutlineOffset += Vector2( outlineWidthOffset, outlineWidthOffset ); } + + // Get the color of the character. + const ColorIndex colorIndex = useDefaultColor ? 0u : *( colorIndicesBuffer + i ); + const Vector4& color = ( useDefaultColor || ( 0u == colorIndex ) ) ? defaultColor : *( colorsBuffer + colorIndex - 1u ); + + GenerateMesh( glyph, + positionPlusOutlineOffset, + color, + NO_OUTLINE, + slot, + isGlyphUnderlined, + currentUnderlinePosition, + currentUnderlineThickness, + meshContainer, + newTextCache, + extents); + + lastFontId = glyph.fontId; // Prevents searching for existing blocksizes when string of the same fontId. } - // 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, - newMesh, - extents, - textColor, - position.y + glyph.yBearing, - underlineGlyph, + if( isOutline && ( 0u != slotOutline.mImageId ) ) // invalid slot id, glyph has failed to be added to atlas + { + GenerateMesh( glyph, + position, + outlineColor, + outlineWidth, + slotOutline, + false, currentUnderlinePosition, currentUnderlineThickness, - slot ); - lastFontId = glyph.fontId; + meshContainerOutline, + newTextCache, + extents); + } } } // glyphs // Now remove references for the old text RemoveText(); mTextCache.Swap( newTextCache ); - RemoveAllShadowRenderTasks(); 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() ) + bool isShadowDrawn = false; + if( !meshContainerOutline.empty() ) { - for ( std::vector< MeshRecord >::iterator mIt = meshContainer.begin(); mIt != meshContainer.end(); ++mIt ) - { - Actor actor = CreateMeshActor( *mIt, actorSize ); - - // Create an effect if necessary - if ( style == STYLE_DROP_SHADOW ) - { - Actor shadowActor = GenerateShadow( *mIt, actorSize, shadowOffset, shadowColor ); - shadowActor.Add( actor ); - actor = shadowActor; - } + const bool drawShadow = STYLE_DROP_SHADOW == style; + CreateActors( meshContainerOutline, + textSize, + outlineColor, + shadowColor, + shadowOffset, + textControl, + animatablePropertyIndex, + drawShadow ); + + isShadowDrawn = drawShadow; + } - if( mActor ) - { - mActor.Add( actor ); - } - else - { - mActor = actor; - } - } + // For each MeshData object, create a mesh actor and add to the renderable actor + if( !meshContainer.empty() ) + { + const bool drawShadow = !isShadowDrawn && ( STYLE_DROP_SHADOW == style ); + CreateActors( meshContainer, + textSize, + defaultColor, + shadowColor, + shadowOffset, + textControl, + animatablePropertyIndex, + drawShadow ); } + #if defined(DEBUG_ENABLED) Toolkit::AtlasGlyphManager::Metrics metrics = mGlyphManager.GetMetrics(); DALI_LOG_INFO( gLogFilter, Debug::General, "TextAtlasRenderer::GlyphManager::GlyphCount: %i, AtlasCount: %i, TextureMemoryUse: %iK\n", @@ -400,7 +636,7 @@ struct AtlasRenderer::Impl : public ConnectionTracker DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%s\n", metrics.mVerboseGlyphCounts.c_str() ); - for ( uint32_t i = 0; i < metrics.mAtlasMetrics.mAtlasCount; ++i ) + 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", i + 1, i > 8 ? "" : " ", @@ -417,42 +653,90 @@ struct AtlasRenderer::Impl : public ConnectionTracker void RemoveText() { - for ( Vector< TextCacheEntry >::Iterator oldTextIter = mTextCache.Begin(); oldTextIter != mTextCache.End(); ++oldTextIter ) + for( Vector< TextCacheEntry >::Iterator oldTextIter = mTextCache.Begin(); oldTextIter != mTextCache.End(); ++oldTextIter ) { - mGlyphManager.AdjustReferenceCount( oldTextIter->mFontId, oldTextIter->mIndex, -1/*decrement*/ ); + mGlyphManager.AdjustReferenceCount( oldTextIter->mFontId, oldTextIter->mIndex, oldTextIter->mOutlineWidth, -1/*decrement*/ ); } mTextCache.Resize( 0 ); } - Actor CreateMeshActor( const MeshRecord& meshRecord, const Vector2& actorSize ) + Actor CreateMeshActor( Actor textControl, Property::Index animatablePropertyIndex, const Vector4& defaultColor, const MeshRecord& meshRecord, + const Vector2& actorSize, Style style ) { - 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 ] ) ); + PropertyBuffer quadVertices = PropertyBuffer::New( mQuadVertexFormat ); + quadVertices.SetData( const_cast< AtlasManager::Vertex2D* >( &meshRecord.mMesh.mVertices[ 0 ] ), meshRecord.mMesh.mVertices.Size() ); Geometry quadGeometry = Geometry::New(); quadGeometry.AddVertexBuffer( quadVertices ); - quadGeometry.SetIndexBuffer( quadIndices ); + quadGeometry.SetIndexBuffer( &meshRecord.mMesh.mIndices[0], meshRecord.mMesh.mIndices.Size() ); + + TextureSet textureSet( mGlyphManager.GetTextures( meshRecord.mAtlasId ) ); + + // Choose the shader to use. + const bool isColorShader = ( STYLE_DROP_SHADOW != style ) && ( Pixel::BGRA8888 == mGlyphManager.GetPixelFormat( meshRecord.mAtlasId ) ); + Shader shader; + if( isColorShader ) + { + // The glyph is an emoji and is not a shadow. + if( !mShaderRgba ) + { + mShaderRgba = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_RGBA ); + } + shader = mShaderRgba; + } + else + { + // The glyph is text or a shadow. + if( !mShaderL8 ) + { + mShaderL8 = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_L8 ); + } + shader = mShaderL8; + } + + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "defaultColor[%f, %f, %f, %f ]\n", defaultColor.r, defaultColor.g, defaultColor.b, defaultColor.a ); + + Dali::Property::Index shaderTextColorIndex = shader.RegisterProperty( "textColorAnimatable", defaultColor ); + + if ( animatablePropertyIndex != Property::INVALID_INDEX ) + { + // create constraint for the animatable text's color Property with textColorAnimatable in the shader. + if( shaderTextColorIndex ) + { + Constraint constraint = Constraint::New( shader, shaderTextColorIndex, EqualToConstraint() ); + constraint.AddSource( Source( textControl, animatablePropertyIndex ) ); + constraint.Apply(); + } + } + else + { + // If not animating the text colour then set to 1's so shader uses the current vertex color + shader.RegisterProperty( "textColorAnimatable", Vector4(1.0, 1.0, 1.0, 1.0 ) ); + } + + Dali::Renderer renderer = Dali::Renderer::New( quadGeometry, shader ); + renderer.SetTextures( textureSet ); + renderer.SetProperty( Dali::Renderer::Property::BLEND_MODE, BlendMode::ON ); + renderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT + mDepth ); - 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 + // Keep all of the origins aligned + actor.SetParentOrigin( ParentOrigin::TOP_LEFT ); + actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); actor.SetSize( actorSize ); - actor.SetColor( meshRecord.mColor ); + actor.RegisterProperty("uOffset", Vector2::ZERO ); + actor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR ); + return actor; } void StitchTextMesh( std::vector< MeshRecord >& meshContainer, AtlasManager::Mesh2D& newMesh, Vector< Extent >& extents, - const Vector4& color, float baseLine, bool underlineGlyph, float underlinePosition, @@ -471,10 +755,10 @@ struct AtlasRenderer::Impl : public ConnectionTracker 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->mMesh, newMesh ); + // Append the mesh to the existing mesh and adjust any extents + Toolkit::Internal::AtlasMeshFactory::AppendMesh( mIt->mMesh, newMesh ); if( underlineGlyph ) { @@ -496,7 +780,6 @@ struct AtlasRenderer::Impl : public ConnectionTracker MeshRecord meshRecord; meshRecord.mAtlasId = slot.mAtlasId; meshRecord.mMesh = newMesh; - meshRecord.mColor = color; meshContainer.push_back( meshRecord ); if( underlineGlyph ) @@ -566,25 +849,37 @@ struct AtlasRenderer::Impl : public ConnectionTracker 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 ) // Different size fonts will have a different fontId { + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Text::AtlasRenderer::CalculateBlocksSize match found fontID(%u) glyphIndex(%u)\n", fontId, (*glyphIt).index ); 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; + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Text::AtlasRenderer::CalculateBlocksSize New font with no matched blocksize, setting blocksize[%u]\n", maxBlockSize.mNeededBlockWidth ); mBlockSizes.push_back( maxBlockSize ); } } @@ -592,8 +887,7 @@ struct AtlasRenderer::Impl : public ConnectionTracker void GenerateUnderlines( std::vector< MeshRecord >& meshRecords, Vector< Extent >& extents, - const Vector4& underlineColor, - const Vector4& textColor ) + const Vector4& underlineColor ) { AtlasManager::Mesh2D newMesh; unsigned short faceIndex = 0; @@ -618,22 +912,26 @@ struct AtlasRenderer::Impl : public ConnectionTracker vert.mPosition.y = baseLine; vert.mTexCoords.x = ZERO; vert.mTexCoords.y = ZERO; + vert.mColor = underlineColor; newMesh.mVertices.PushBack( vert ); vert.mPosition.x = brx; vert.mPosition.y = baseLine; vert.mTexCoords.x = u; + vert.mColor = underlineColor; newMesh.mVertices.PushBack( vert ); vert.mPosition.x = tlx; vert.mPosition.y = baseLine + thickness; vert.mTexCoords.x = ZERO; vert.mTexCoords.y = v; + vert.mColor = underlineColor; newMesh.mVertices.PushBack( vert ); vert.mPosition.x = brx; vert.mPosition.y = baseLine + thickness; vert.mTexCoords.x = u; + vert.mColor = underlineColor; newMesh.mVertices.PushBack( vert ); // Six indices in counter clockwise winding @@ -645,186 +943,19 @@ struct AtlasRenderer::Impl : public ConnectionTracker 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 ); - - // Set depth index to -1.0 to make sure shadow is rendered first in 3D layers - renderer.SetDepthIndex( -1.0f ); - 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.SetColor( shadowColor ); - - // Create a render task to render the effect - RenderTask shadowTask = Stage::GetCurrent().GetRenderTaskList().CreateTask(); - shadowTask.SetTargetFrameBuffer( meshRecord.mBuffer ); - shadowTask.SetSourceActor( subActor ); - shadowTask.SetClearEnabled( true ); - shadowTask.SetClearColor( Vector4::ZERO ); - shadowTask.SetExclusive( true ); - shadowTask.SetRefreshRate( RenderTask::REFRESH_ONCE ); - shadowTask.FinishedSignal().Connect( this, &AtlasRenderer::Impl::RenderComplete ); - mShadowTasks.push_back( shadowTask ); - actor.Add( subActor ); - - return actor; - } - - void RemoveShadowRenderTask( RenderTask renderTask ) - { - if( renderTask ) - { - renderTask.FinishedSignal().Disconnect( this, &AtlasRenderer::Impl::RenderComplete ); - - // Guard to prevent accessing Stage after dali-core destruction - if( Stage::IsInstalled() ) - { - Stage::GetCurrent().GetRenderTaskList().RemoveTask( renderTask ); - } - renderTask.Reset(); - } - } - - void RenderComplete( RenderTask& 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 ); - } - } - - RemoveShadowRenderTask( renderTask ); - } - - void RemoveAllShadowRenderTasks() - { - for ( std::vector< RenderTask >::iterator shadowIterator = mShadowTasks.begin(); - shadowIterator != mShadowTasks.end(); ++shadowIterator ) - { - RemoveShadowRenderTask( *shadowIterator ); + Toolkit::Internal::AtlasMeshFactory::AppendMesh( meshRecords[ index ].mMesh, newMesh ); } } Actor mActor; ///< The actor parent which renders the text AtlasGlyphManager mGlyphManager; ///< Glyph Manager to handle upload and caching - std::vector< RenderTask > mShadowTasks; ///< Used to render shadows - TextAbstraction::FontClient mFontClient; ///> The font client used to supply glyph information - 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; + TextAbstraction::FontClient mFontClient; ///< The font client used to supply glyph information + Shader mShaderL8; ///< The shader for glyphs and emoji's shadows. + Shader mShaderRgba; ///< The shader for emojis. + std::vector< MaxBlockSize > mBlockSizes; ///< Maximum size needed to contain a glyph in a block within a new atlas + Vector< TextCacheEntry > mTextCache; ///< Caches data from previous render + Property::Map mQuadVertexFormat; ///< Describes the vertex format for text + int mDepth; ///< DepthIndex passed by control when connect to stage }; Text::RendererPtr AtlasRenderer::New() @@ -834,8 +965,14 @@ Text::RendererPtr AtlasRenderer::New() return Text::RendererPtr( new AtlasRenderer() ); } -Actor AtlasRenderer::Render( Text::ViewInterface& view, int depth ) +Actor AtlasRenderer::Render( Text::ViewInterface& view, + Actor textControl, + Property::Index animatablePropertyIndex, + float& alignmentOffset, + int depth ) { + DALI_LOG_INFO( gLogFilter, Debug::General, "Text::AtlasRenderer::Render()\n" ); + UnparentAndReset( mImpl->mActor ); Length numberOfGlyphs = view.GetNumberOfGlyphs(); @@ -850,15 +987,34 @@ Actor AtlasRenderer::Render( Text::ViewInterface& view, int depth ) numberOfGlyphs = view.GetGlyphs( glyphs.Begin(), positions.Begin(), + alignmentOffset, 0u, numberOfGlyphs ); + glyphs.Resize( numberOfGlyphs ); positions.Resize( numberOfGlyphs ); + const Vector4* const colorsBuffer = view.GetColors(); + const ColorIndex* const colorIndicesBuffer = view.GetColorIndices(); + const Vector4& defaultColor = view.GetTextColor(); + mImpl->AddGlyphs( view, + textControl, + animatablePropertyIndex, positions, glyphs, - depth ); + defaultColor, + colorsBuffer, + colorIndicesBuffer, + depth, + alignmentOffset ); + + /* 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; @@ -872,8 +1028,6 @@ AtlasRenderer::AtlasRenderer() AtlasRenderer::~AtlasRenderer() { - mImpl->RemoveAllShadowRenderTasks(); - mImpl->RemoveText(); delete mImpl; }