X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dali-toolkit%2Finternal%2Ftext%2Frendering%2Fatlas%2Ftext-atlas-renderer.cpp;h=016284aac8a29a0399bff82cb8b879f8bc38e01e;hb=refs%2Fchanges%2F42%2F37942%2F5;hp=63deea2190306fb2f7953d25d90b849e507e438e;hpb=c78e3b4813003fff8c306a7ab6a4e0bfbfdfb158;p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git 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 63deea2..016284a 100644 --- a/dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.cpp +++ b/dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.cpp @@ -19,6 +19,7 @@ #include // EXTERNAL INCLUDES +#include #include #include @@ -38,6 +39,10 @@ using namespace Dali::Toolkit::Text; namespace { + const float ZERO( 0.0f ); + const float HALF( 0.5f ); + const float ONE( 1.0f ); + const float TWO( 2.0f ); 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 @@ -54,9 +59,21 @@ struct AtlasRenderer::Impl : public ConnectionTracker struct MeshRecord { + Vector4 mColor; uint32_t mAtlasId; MeshData mMeshData; FrameBufferImage mBuffer; + bool mIsUnderline; + }; + + struct Extent + { + float mBaseLine; + float mLeft; + float mRight; + float mUnderlinePosition; + float mUnderlineThickness; + uint32_t mMeshRecordIndex; }; struct AtlasRecord @@ -79,19 +96,32 @@ struct AtlasRenderer::Impl : public ConnectionTracker 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 ); } void AddGlyphs( const std::vector& positions, const Vector& glyphs, + const Vector4& textColor, const Vector2& shadowOffset, - const Vector4& shadowColor ) + const Vector4& shadowColor, + bool underlineEnabled, + const Vector4& underlineColor, + float underlineHeight ) { AtlasManager::AtlasSlot slot; std::vector< MeshRecord > meshContainer; + Vector< Extent > extents; + + float currentUnderlinePosition = ZERO; + float currentUnderlineThickness = underlineHeight; + FontId lastFontId = 0; Style style = STYLE_NORMAL; - if ( shadowOffset.x > 0.0f || shadowOffset.y > 0.0f ) + if ( fabsf( shadowOffset.x ) > Math::MACHINE_EPSILON_1 || fabsf( shadowOffset.y ) > Math::MACHINE_EPSILON_1 ) { style = STYLE_DROP_SHADOW; } @@ -111,6 +141,42 @@ struct AtlasRenderer::Impl : public ConnectionTracker // No operation for white space if ( glyph.width && glyph.height ) { + // Are we still using the same fontId as previous + if ( glyph.fontId != lastFontId ) + { + // We need to fetch fresh font underline metrics + FontMetrics fontMetrics; + mFontClient.GetFontMetrics( glyph.fontId, fontMetrics ); + currentUnderlinePosition = FontMetricsRoundUp( fabsf( fontMetrics.underlinePosition ) ); + float descender = FontMetricsRoundUp( 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 = FontMetricsRoundUp( 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; + } + } + Vector2 position = positions[ i ]; MeshData newMeshData; mGlyphManager.Cached( glyph.fontId, glyph.index, slot ); @@ -134,7 +200,6 @@ struct AtlasRenderer::Impl : public ConnectionTracker mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_SIZE, mBlockSizes[ j ].mNeededBlockSize ); } } - lastFontId = glyph.fontId; } // Glyph doesn't currently exist in atlas so upload @@ -151,25 +216,47 @@ struct AtlasRenderer::Impl : public ConnectionTracker } } // 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, + newMeshData, + extents, + textColor, + position.y + glyph.yBearing, + currentUnderlinePosition, + currentUnderlineThickness, + slot ); } + lastFontId = glyph.fontId; + } + + 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 ) { - MeshActor actor = MeshActor::New( Mesh::New( meshContainer[ i ].mMeshData ) ); - actor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR ); + MeshActor actor = MeshActor::New( Mesh::New( mIt->mMeshData ) ); + actor.SetColor( mIt->mColor ); + if ( mIt->mIsUnderline ) + { + actor.SetColorMode( USE_OWN_COLOR ); + } + else + { + actor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR ); + } // Check to see what pixel format the shader should be - if ( mGlyphManager.GetPixelFormat( meshContainer[ i ].mAtlasId ) == Pixel::L8 ) + if ( mGlyphManager.GetPixelFormat( mIt->mAtlasId ) == Pixel::L8 ) { // Create an effect if necessary if ( style == STYLE_DROP_SHADOW ) { - actor.Add( GenerateEffect( meshContainer[ i ], shadowOffset, shadowColor ) ); + actor.Add( GenerateShadow( *mIt, shadowOffset, shadowColor ) ); } actor.SetShaderEffect( mBasicShader ); } @@ -178,7 +265,7 @@ struct AtlasRenderer::Impl : public ConnectionTracker actor.SetShaderEffect( mBgraShader ); } - if ( i ) + if ( mActor ) { mActor.Add( actor ); } @@ -212,17 +299,35 @@ struct AtlasRenderer::Impl : public ConnectionTracker void StitchTextMesh( std::vector< MeshRecord >& meshContainer, MeshData& newMeshData, + Vector< Extent >& extents, + const Vector4& color, + float baseLine, + 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; + // 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(); mIt != meshContainer.end(); ++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->mMeshData, newMeshData ); + AdjustExtents( extents, + meshContainer, + index, + left, + right, + baseLine, + underlinePosition, + underlineThickness ); return; } } @@ -231,7 +336,67 @@ struct AtlasRenderer::Impl : public ConnectionTracker MeshRecord meshRecord; meshRecord.mAtlasId = slot.mAtlasId; meshRecord.mMeshData = newMeshData; + meshRecord.mColor = color; + meshRecord.mIsUnderline = false; meshContainer.push_back( meshRecord ); + + // Adjust extents for this new meshrecord + AdjustExtents( extents, + meshContainer, + meshContainer.size() - 1u, + left, + right, + baseLine, + underlinePosition, + underlineThickness ); + + } + } + + void AdjustExtents( Vector< Extent >& extents, + std::vector< MeshRecord>& meshRecords, + uint32_t index, + float left, + float right, + float baseLine, + float underlinePosition, + float underlineThickness ) + { + bool foundExtent = false; + for ( Vector< Extent >::Iterator eIt = extents.Begin(); eIt != extents.End(); ++eIt ) + { + if ( Equals( baseLine, eIt->mBaseLine ) ) + { + foundExtent = true; + if ( left < eIt->mLeft ) + { + eIt->mLeft = left; + } + if ( right > eIt->mRight ) + { + eIt->mRight = right; + } + + if ( underlinePosition > eIt->mUnderlinePosition ) + { + eIt->mUnderlinePosition = underlinePosition; + } + if ( underlineThickness > eIt->mUnderlineThickness ) + { + eIt->mUnderlineThickness = underlineThickness; + } + } + } + if ( !foundExtent ) + { + Extent extent; + extent.mLeft = left; + extent.mRight = right; + extent.mBaseLine = baseLine; + extent.mUnderlinePosition = underlinePosition; + extent.mUnderlineThickness = underlineThickness; + extent.mMeshRecordIndex = index; + extents.PushBack( extent ); } } @@ -286,18 +451,89 @@ struct AtlasRenderer::Impl : public ConnectionTracker } } - MeshActor GenerateEffect( MeshRecord& meshRecord, + float FontMetricsRoundUp( float value ) + { + double integral_part; + if ( modf( value, &integral_part ) ) + { + return ( static_cast< float >( integral_part ) + 1.0f ); + } + else + { + return static_cast< float >( integral_part ); + } + } + + void GenerateUnderlines( std::vector< MeshRecord>& meshRecords, + Vector< Extent >& extents, + const Vector4& underlineColor, + const Vector4& textColor ) + { + MeshData newMeshData; + for ( Vector< Extent >::ConstIterator eIt = extents.Begin(); eIt != extents.End(); ++eIt ) + { + MeshData::VertexContainer newVerts; + newVerts.reserve( 4u ); + 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; + + 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 ); + } + } + } + + 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(); - const float zero = 0.0f; - const float one = 1.0f; float tlx = verts[ 0 ].x; float tly = verts[ 0 ].y; - float brx = zero; - float bry = zero; + float brx = ZERO; + float bry = ZERO; for ( uint32_t i = 0; i < verts.size(); ++i ) { @@ -321,42 +557,38 @@ struct AtlasRenderer::Impl : public ConnectionTracker float width = brx - tlx; float height = bry - tly; - float divWidth = 2.0f / width; - float divHeight = 2.0f / height; + float divWidth = TWO / width; + float divHeight = TWO / height; // Create a buffer to render to - // TODO bloom style filter from this buffer 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, zero ), - Vector3( zero, zero, zero ) ) ); + 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, zero, 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, zero, 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, one ), - Vector3( zero, zero, zero ) ) ); - - face.push_back( 0 ); face.push_back( 2u ); face.push_back( 1u ); - face.push_back( 1u ); face.push_back( 2u ); face.push_back( 3u ); + 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( face ); + meshData.SetFaceIndices( mFace ); meshData.SetHasNormals( true ); meshData.SetHasColor( false ); meshData.SetHasTextureCoords( true ); @@ -364,7 +596,7 @@ struct AtlasRenderer::Impl : public ConnectionTracker actor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR ); actor.SetShaderEffect( mBgraShader ); actor.SetFilterMode( FilterMode::LINEAR, FilterMode::LINEAR ); - actor.SetSortModifier( one ); // force behind main text + actor.SetSortModifier( 0.1f ); // force behind main text // Create a sub actor to render once with normalized vertex positions MeshData newMeshData; @@ -374,8 +606,8 @@ struct AtlasRenderer::Impl : public ConnectionTracker 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; + vertex.x = ( ( vertex.x - tlx ) * divWidth ) - ONE; + vertex.y = ( ( vertex.y - tly ) * divHeight ) - ONE; newVerts.push_back( vertex ); } @@ -441,6 +673,7 @@ struct AtlasRenderer::Impl : public ConnectionTracker 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 }; Text::RendererPtr AtlasRenderer::New() @@ -467,8 +700,12 @@ RenderableActor AtlasRenderer::Render( Text::ViewInterface& view ) view.GetGlyphPositions( &positions[0], 0, numberOfGlyphs ); mImpl->AddGlyphs( positions, glyphs, + view.GetTextColor(), view.GetShadowOffset(), - view.GetShadowColor() ); + view.GetShadowColor(), + view.IsUnderlineEnabled(), + view.GetUnderlineColor(), + view.GetUnderlineHeight() ); } return mImpl->mActor; } @@ -482,4 +719,4 @@ AtlasRenderer::AtlasRenderer() AtlasRenderer::~AtlasRenderer() { delete mImpl; -} +} \ No newline at end of file