2 * Copyright (c) 2016 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.h>
22 #include <dali/public-api/rendering/geometry.h>
23 #include <dali/public-api/rendering/renderer.h>
24 #include <dali/public-api/images/frame-buffer-image.h>
25 #include <dali/devel-api/text-abstraction/font-client.h>
26 #include <dali/integration-api/debug.h>
27 #include <dali/public-api/animation/constraints.h>
30 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
31 #include <dali-toolkit/internal/text/glyph-run.h>
32 #include <dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager.h>
33 #include <dali-toolkit/internal/text/rendering/atlas/atlas-mesh-factory.h>
34 #include <dali-toolkit/internal/text/text-view.h>
37 using namespace Dali::Toolkit;
38 using namespace Dali::Toolkit::Text;
42 #if defined(DEBUG_ENABLED)
43 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_RENDERING");
46 #define MAKE_SHADER(A)#A
48 const char* VERTEX_SHADER = MAKE_SHADER(
49 attribute mediump vec2 aPosition;
50 attribute mediump vec2 aTexCoord;
51 attribute mediump vec4 aColor;
52 uniform mediump vec2 uOffset;
53 uniform mediump mat4 uMvpMatrix;
54 varying mediump vec2 vTexCoord;
55 varying mediump vec4 vColor;
59 mediump vec4 position = vec4( aPosition.xy + uOffset, 0.0, 1.0 );
60 gl_Position = uMvpMatrix * position;
61 vTexCoord = aTexCoord;
66 const char* FRAGMENT_SHADER_L8 = MAKE_SHADER(
67 uniform lowp vec4 uColor;
68 uniform lowp vec4 textColorAnimatable;
69 uniform sampler2D sTexture;
70 varying mediump vec2 vTexCoord;
71 varying mediump vec4 vColor;
75 mediump vec4 color = texture2D( sTexture, vTexCoord );
76 gl_FragColor = vec4( vColor.rgb * uColor.rgb * textColorAnimatable.rgb, uColor.a * vColor.a * textColorAnimatable.a * color.r );
80 const char* FRAGMENT_SHADER_RGBA = MAKE_SHADER(
81 uniform lowp vec4 uColor;
82 uniform lowp vec4 textColorAnimatable;
83 uniform sampler2D sTexture;
84 varying mediump vec2 vTexCoord;
88 gl_FragColor = texture2D( sTexture, vTexCoord ) * uColor * textColorAnimatable;
92 const float ZERO( 0.0f );
93 const float HALF( 0.5f );
94 const float ONE( 1.0f );
95 const uint32_t DEFAULT_ATLAS_WIDTH = 512u;
96 const uint32_t DEFAULT_ATLAS_HEIGHT = 512u;
97 const int NO_OUTLINE( 0 );
100 struct AtlasRenderer::Impl
116 AtlasManager::Mesh2D mMesh;
117 FrameBufferImage mBuffer;
121 * brief Struct used to generate the underline mesh.
122 * There is one Extent per line of text.
130 mUnderlinePosition( 0.0f ),
131 mUnderlineThickness( 0.0f ),
132 mMeshRecordIndex( 0u )
139 float mUnderlinePosition;
140 float mUnderlineThickness;
141 uint32_t mMeshRecordIndex;
148 mNeededBlockWidth( 0 ),
149 mNeededBlockHeight( 0 )
154 uint32_t mNeededBlockWidth;
155 uint32_t mNeededBlockHeight;
167 Text::GlyphIndex mIndex;
170 struct TextCacheEntry
180 Text::GlyphIndex mIndex;
187 mGlyphManager = AtlasGlyphManager::Get();
188 mFontClient = TextAbstraction::FontClient::Get();
190 mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2;
191 mQuadVertexFormat[ "aTexCoord" ] = Property::VECTOR2;
192 mQuadVertexFormat[ "aColor" ] = Property::VECTOR4;
195 bool IsGlyphUnderlined( GlyphIndex index,
196 const Vector<GlyphRun>& underlineRuns )
198 for( Vector<GlyphRun>::ConstIterator it = underlineRuns.Begin(),
199 endIt = underlineRuns.End();
203 const GlyphRun& run = *it;
205 if( ( run.glyphIndex <= index ) && ( index < run.glyphIndex + run.numberOfGlyphs ) )
214 void AddGlyphs( Text::ViewInterface& view,
216 Property::Index animatablePropertyIndex,
217 const Vector<Vector2>& positions,
218 const Vector<GlyphInfo>& glyphs,
219 const Vector4& defaultColor,
220 const Vector4* const colorsBuffer,
221 const ColorIndex* const colorIndicesBuffer,
223 float minLineOffset )
225 AtlasManager::AtlasSlot slot;
226 std::vector< MeshRecord > meshContainer;
227 Vector< Extent > extents;
228 TextCacheEntry textCacheEntry;
231 const Vector2& textSize( view.GetLayoutSize() );
232 const Vector2 halfTextSize( textSize * 0.5f );
233 const Vector2& shadowOffset( view.GetShadowOffset() );
234 const Vector4& shadowColor( view.GetShadowColor() );
235 const bool underlineEnabled( view.IsUnderlineEnabled() );
236 const Vector4& underlineColor( view.GetUnderlineColor() );
237 const float underlineHeight( view.GetUnderlineHeight() );
239 const bool useDefaultColor = ( NULL == colorsBuffer );
241 // Get the underline runs.
242 const Length numberOfUnderlineRuns = view.GetNumberOfUnderlineRuns();
243 Vector<GlyphRun> underlineRuns;
244 underlineRuns.Resize( numberOfUnderlineRuns );
245 view.GetUnderlineRuns( underlineRuns.Begin(),
247 numberOfUnderlineRuns );
249 bool thereAreUnderlinedGlyphs = false;
251 float currentUnderlinePosition = ZERO;
252 float currentUnderlineThickness = underlineHeight;
253 uint32_t currentBlockSize = 0;
254 FontId lastFontId = 0;
255 FontId lastUnderlinedFontId = 0;
256 Style style = STYLE_NORMAL;
258 if ( fabsf( shadowOffset.x ) > Math::MACHINE_EPSILON_1 || fabsf( shadowOffset.y ) > Math::MACHINE_EPSILON_1 )
260 style = STYLE_DROP_SHADOW;
263 CalculateBlocksSize( glyphs );
265 // Avoid emptying mTextCache (& removing references) until after incremented references for the new text
266 Vector< TextCacheEntry > newTextCache;
267 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
268 const Vector2* const positionsBuffer = positions.Begin();
269 const Vector2 lineOffsetPosition( minLineOffset, 0.f );
271 for( uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i )
273 const GlyphInfo& glyph = *( glyphsBuffer + i );
274 const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined( i, underlineRuns );
275 thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph;
277 // No operation for white space
278 if( glyph.width && glyph.height )
280 // Are we still using the same fontId as previous
281 if( underlineGlyph && ( glyph.fontId != lastUnderlinedFontId ) )
283 // We need to fetch fresh font underline metrics
284 FontMetrics fontMetrics;
285 mFontClient.GetFontMetrics( glyph.fontId, fontMetrics );
286 currentUnderlinePosition = ceil( fabsf( fontMetrics.underlinePosition ) );
287 const float descender = ceil( fabsf( fontMetrics.descender ) );
289 if( fabsf( underlineHeight ) < Math::MACHINE_EPSILON_1000 )
291 currentUnderlineThickness = fontMetrics.underlineThickness;
293 // Ensure underline will be at least a pixel high
294 if ( currentUnderlineThickness < ONE )
296 currentUnderlineThickness = ONE;
300 currentUnderlineThickness = ceil( currentUnderlineThickness );
304 // Clamp the underline position at the font descender and check for ( as EFL describes it ) a broken font
305 if( currentUnderlinePosition > descender )
307 currentUnderlinePosition = descender;
310 if( fabsf( currentUnderlinePosition ) < Math::MACHINE_EPSILON_1000 )
312 // Move offset down by one ( EFL behavior )
313 currentUnderlinePosition = ONE;
316 lastUnderlinedFontId = glyph.fontId;
319 bool glyphNotCached = !mGlyphManager.IsCached( glyph.fontId, glyph.index, slot ); // Check FontGlyphRecord vector for entry with glyph index and fontId
321 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "AddGlyphs fontID[%u] glyphIndex[%u] [%s]\n", glyph.fontId, glyph.index, (glyphNotCached)?"not cached":"cached" );
325 MaxBlockSize& blockSize = mBlockSizes[currentBlockSize];
327 if ( lastFontId != glyph.fontId )
330 // 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.
331 // CalculateBlocksSize() above ensures a block size entry exists.
332 for( std::vector<MaxBlockSize>::const_iterator it = mBlockSizes.begin(),
333 endIt = mBlockSizes.end();
337 const MaxBlockSize& blockSizeEntry = *it;
338 if( blockSizeEntry.mFontId == glyph.fontId )
340 blockSize = mBlockSizes[index];
345 // Create a new image for the glyph
348 // Whether the current glyph is a color one.
349 const bool isColorGlyph = mFontClient.IsColorGlyph( glyph.fontId, glyph.index );
351 // Retrieve the emoji's bitmap.
352 TextAbstraction::FontClient::GlyphBufferData glyphBufferData;
353 glyphBufferData.width = isColorGlyph ? glyph.width : 0; // Desired width and height.
354 glyphBufferData.height = isColorGlyph ? glyph.height : 0;
356 mFontClient.CreateBitmap( glyph.fontId,
361 // Create the pixel data.
362 bitmap = PixelData::New( glyphBufferData.buffer,
363 glyph.width * glyph.height * GetBytesPerPixel( glyphBufferData.format ),
366 glyphBufferData.format,
367 PixelData::DELETE_ARRAY );
371 // Ensure that the next image will fit into the current block size
372 if( bitmap.GetWidth() > blockSize.mNeededBlockWidth )
374 blockSize.mNeededBlockWidth = bitmap.GetWidth();
377 if( bitmap.GetHeight() > blockSize.mNeededBlockHeight )
379 blockSize.mNeededBlockHeight = bitmap.GetHeight();
382 // If CheckAtlas in AtlasManager::Add can't fit the bitmap in the current atlas it will create a new atlas
384 // Setting the block size and size of new atlas does not mean a new one will be created. An existing atlas may still surffice.
385 mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_WIDTH,
386 DEFAULT_ATLAS_HEIGHT,
387 blockSize.mNeededBlockWidth,
388 blockSize.mNeededBlockHeight );
390 // Locate a new slot for our glyph
391 mGlyphManager.Add( glyph, bitmap, slot ); // slot will be 0 is glyph not added
396 // We have 2+ copies of the same glyph
397 mGlyphManager.AdjustReferenceCount( glyph.fontId, glyph.index, 1/*increment*/ );
400 // Move the origin (0,0) of the mesh to the center of the actor
401 const Vector2 position = *( positionsBuffer + i ) - halfTextSize - lineOffsetPosition;
403 if ( 0u != slot.mImageId ) // invalid slot id, glyph has failed to be added to atlas
405 // Generate mesh data for this quad, plugging in our supplied position
406 AtlasManager::Mesh2D newMesh;
407 mGlyphManager.GenerateMeshData( slot.mImageId, position, newMesh );
408 textCacheEntry.mFontId = glyph.fontId;
409 textCacheEntry.mImageId = slot.mImageId;
410 textCacheEntry.mIndex = glyph.index;
411 newTextCache.PushBack( textCacheEntry );
413 AtlasManager::Vertex2D* verticesBuffer = newMesh.mVertices.Begin();
415 // Get the color of the character.
416 const ColorIndex colorIndex = useDefaultColor ? 0u : *( colorIndicesBuffer + i );
417 const Vector4& color = ( useDefaultColor || ( 0u == colorIndex ) ) ? defaultColor : *( colorsBuffer + colorIndex - 1u );
419 for( unsigned int index = 0u, size = newMesh.mVertices.Count();
423 AtlasManager::Vertex2D& vertex = *( verticesBuffer + index );
425 // Set the color of the vertex.
426 vertex.mColor = color;
429 // Find an existing mesh data object to attach to ( or create a new one, if we can't find one using the same atlas)
430 StitchTextMesh( meshContainer,
433 position.y + glyph.yBearing,
435 currentUnderlinePosition,
436 currentUnderlineThickness,
439 lastFontId = glyph.fontId; // Prevents searching for existing blocksizes when string of the same fontId.
444 // Now remove references for the old text
446 mTextCache.Swap( newTextCache );
448 if( thereAreUnderlinedGlyphs )
450 // Check to see if any of the text needs an underline
451 GenerateUnderlines( meshContainer, extents, underlineColor );
454 // For each MeshData object, create a mesh actor and add to the renderable actor
455 if( !meshContainer.empty() )
459 // Create a container actor to act as a common parent for text and shadow, to avoid color inheritence issues.
460 mActor = Actor::New();
461 mActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
462 mActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
463 mActor.SetSize( textSize );
464 mActor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
467 for( std::vector< MeshRecord >::iterator it = meshContainer.begin(),
468 endIt = meshContainer.end();
471 MeshRecord& meshRecord = *it;
473 Actor actor = CreateMeshActor( textControl, animatablePropertyIndex, defaultColor, meshRecord, textSize, STYLE_NORMAL );
475 // Whether the actor has renderers.
476 const bool hasRenderer = actor.GetRendererCount() > 0u;
478 // Create an effect if necessary
480 ( style == STYLE_DROP_SHADOW ) )
482 // Change the color of the vertices.
483 for( Vector<AtlasManager::Vertex2D>::Iterator vIt = meshRecord.mMesh.mVertices.Begin(),
484 vEndIt = meshRecord.mMesh.mVertices.End();
488 AtlasManager::Vertex2D& vertex = *vIt;
490 vertex.mColor = shadowColor;
493 Actor shadowActor = CreateMeshActor(textControl, animatablePropertyIndex, defaultColor, meshRecord, textSize, STYLE_DROP_SHADOW );
494 #if defined(DEBUG_ENABLED)
495 shadowActor.SetName( "Text Shadow renderable actor" );
497 // Offset shadow in x and y
498 shadowActor.RegisterProperty("uOffset", shadowOffset );
499 Dali::Renderer renderer( shadowActor.GetRendererAt( 0 ) );
500 int depthIndex = renderer.GetProperty<int>(Dali::Renderer::Property::DEPTH_INDEX);
501 renderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, depthIndex - 1 );
502 mActor.Add( shadowActor );
511 #if defined(DEBUG_ENABLED)
512 Toolkit::AtlasGlyphManager::Metrics metrics = mGlyphManager.GetMetrics();
513 DALI_LOG_INFO( gLogFilter, Debug::General, "TextAtlasRenderer::GlyphManager::GlyphCount: %i, AtlasCount: %i, TextureMemoryUse: %iK\n",
515 metrics.mAtlasMetrics.mAtlasCount,
516 metrics.mAtlasMetrics.mTextureMemoryUsed / 1024 );
518 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%s\n", metrics.mVerboseGlyphCounts.c_str() );
520 for( uint32_t i = 0; i < metrics.mAtlasMetrics.mAtlasCount; ++i )
522 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Atlas [%i] %sPixels: %s Size: %ix%i, BlockSize: %ix%i, BlocksUsed: %i/%i\n",
523 i + 1, i > 8 ? "" : " ",
524 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mPixelFormat == Pixel::L8 ? "L8 " : "BGRA",
525 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mSize.mWidth,
526 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mSize.mHeight,
527 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mSize.mBlockWidth,
528 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mSize.mBlockHeight,
529 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mBlocksUsed,
530 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mTotalBlocks );
537 for( Vector< TextCacheEntry >::Iterator oldTextIter = mTextCache.Begin(); oldTextIter != mTextCache.End(); ++oldTextIter )
539 mGlyphManager.AdjustReferenceCount( oldTextIter->mFontId, oldTextIter->mIndex, -1/*decrement*/ );
541 mTextCache.Resize( 0 );
544 Actor CreateMeshActor( Actor textControl, Property::Index animatablePropertyIndex, const Vector4& defaultColor, const MeshRecord& meshRecord,
545 const Vector2& actorSize, Style style )
547 PropertyBuffer quadVertices = PropertyBuffer::New( mQuadVertexFormat );
548 quadVertices.SetData( const_cast< AtlasManager::Vertex2D* >( &meshRecord.mMesh.mVertices[ 0 ] ), meshRecord.mMesh.mVertices.Size() );
550 Geometry quadGeometry = Geometry::New();
551 quadGeometry.AddVertexBuffer( quadVertices );
552 quadGeometry.SetIndexBuffer( &meshRecord.mMesh.mIndices[0], meshRecord.mMesh.mIndices.Size() );
554 TextureSet textureSet( mGlyphManager.GetTextures( meshRecord.mAtlasId ) );
556 // Choose the shader to use.
557 const bool isColorShader = ( STYLE_DROP_SHADOW != style ) && ( Pixel::BGRA8888 == mGlyphManager.GetPixelFormat( meshRecord.mAtlasId ) );
561 // The glyph is an emoji and is not a shadow.
564 mShaderRgba = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_RGBA );
566 shader = mShaderRgba;
570 // The glyph is text or a shadow.
573 mShaderL8 = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_L8 );
578 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "defaultColor[%f, %f, %f, %f ]\n", defaultColor.r, defaultColor.g, defaultColor.b, defaultColor.a );
580 Dali::Property::Index shaderTextColorIndex = shader.RegisterProperty( "textColorAnimatable", defaultColor );
582 if ( animatablePropertyIndex != Property::INVALID_INDEX )
584 // create constraint for the animatable text's color Property with textColorAnimatable in the shader.
585 if( shaderTextColorIndex )
587 Constraint constraint = Constraint::New<Vector4>( shader, shaderTextColorIndex, EqualToConstraint() );
588 constraint.AddSource( Source( textControl, animatablePropertyIndex ) );
594 // If not animating the text colour then set to 1's so shader uses the current vertex color
595 shader.RegisterProperty( "textColorAnimatable", Vector4(1.0, 1.0, 1.0, 1.0 ) );
598 Dali::Renderer renderer = Dali::Renderer::New( quadGeometry, shader );
599 renderer.SetTextures( textureSet );
600 renderer.SetProperty( Dali::Renderer::Property::BLEND_MODE, BlendMode::ON );
601 renderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT + mDepth );
603 Actor actor = Actor::New();
604 #if defined(DEBUG_ENABLED)
605 actor.SetName( "Text renderable actor" );
607 actor.AddRenderer( renderer );
608 // Keep all of the origins aligned
609 actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
610 actor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
611 actor.SetSize( actorSize );
612 actor.RegisterProperty("uOffset", Vector2::ZERO );
613 actor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
617 void StitchTextMesh( std::vector< MeshRecord >& meshContainer,
618 AtlasManager::Mesh2D& newMesh,
619 Vector< Extent >& extents,
622 float underlinePosition,
623 float underlineThickness,
624 AtlasManager::AtlasSlot& slot )
628 float left = newMesh.mVertices[ 0 ].mPosition.x;
629 float right = newMesh.mVertices[ 1 ].mPosition.x;
631 // Check to see if there's a mesh data object that references the same atlas ?
633 for ( std::vector< MeshRecord >::iterator mIt = meshContainer.begin(),
634 mEndIt = meshContainer.end();
638 if( slot.mAtlasId == mIt->mAtlasId )
640 // Append the mesh to the existing mesh and adjust any extents
641 Toolkit::Internal::AtlasMeshFactory::AppendMesh( mIt->mMesh, newMesh );
645 AdjustExtents( extents,
652 underlineThickness );
659 // No mesh data object currently exists that references this atlas, so create a new one
660 MeshRecord meshRecord;
661 meshRecord.mAtlasId = slot.mAtlasId;
662 meshRecord.mMesh = newMesh;
663 meshContainer.push_back( meshRecord );
667 // Adjust extents for this new meshrecord
668 AdjustExtents( extents,
670 meshContainer.size() - 1u,
675 underlineThickness );
680 void AdjustExtents( Vector< Extent >& extents,
681 std::vector< MeshRecord>& meshRecords,
686 float underlinePosition,
687 float underlineThickness )
689 bool foundExtent = false;
690 for ( Vector< Extent >::Iterator eIt = extents.Begin(),
691 eEndIt = extents.End();
695 if ( Equals( baseLine, eIt->mBaseLine ) )
698 if ( left < eIt->mLeft )
702 if ( right > eIt->mRight )
707 if ( underlinePosition > eIt->mUnderlinePosition )
709 eIt->mUnderlinePosition = underlinePosition;
711 if ( underlineThickness > eIt->mUnderlineThickness )
713 eIt->mUnderlineThickness = underlineThickness;
721 extent.mRight = right;
722 extent.mBaseLine = baseLine;
723 extent.mUnderlinePosition = underlinePosition;
724 extent.mUnderlineThickness = underlineThickness;
725 extent.mMeshRecordIndex = index;
726 extents.PushBack( extent );
730 void CalculateBlocksSize( const Vector<GlyphInfo>& glyphs )
732 for( Vector<GlyphInfo>::ConstIterator glyphIt = glyphs.Begin(),
733 glyphEndIt = glyphs.End();
734 glyphIt != glyphEndIt;
737 const FontId fontId = (*glyphIt).fontId;
738 bool foundFont = false;
740 for( std::vector< MaxBlockSize >::const_iterator blockIt = mBlockSizes.begin(),
741 blockEndIt = mBlockSizes.end();
742 blockIt != blockEndIt;
745 if( (*blockIt).mFontId == fontId ) // Different size fonts will have a different fontId
747 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Text::AtlasRenderer::CalculateBlocksSize match found fontID(%u) glyphIndex(%u)\n", fontId, (*glyphIt).index );
755 FontMetrics fontMetrics;
756 mFontClient.GetFontMetrics( fontId, fontMetrics );
758 MaxBlockSize maxBlockSize;
759 maxBlockSize.mNeededBlockWidth = static_cast< uint32_t >( fontMetrics.height );
760 maxBlockSize.mNeededBlockHeight = maxBlockSize.mNeededBlockWidth;
761 maxBlockSize.mFontId = fontId;
762 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Text::AtlasRenderer::CalculateBlocksSize New font with no matched blocksize, setting blocksize[%u]\n", maxBlockSize.mNeededBlockWidth );
763 mBlockSizes.push_back( maxBlockSize );
768 void GenerateUnderlines( std::vector< MeshRecord >& meshRecords,
769 Vector< Extent >& extents,
770 const Vector4& underlineColor )
772 AtlasManager::Mesh2D newMesh;
773 unsigned short faceIndex = 0;
774 for ( Vector< Extent >::ConstIterator eIt = extents.Begin(),
775 eEndIt = extents.End();
779 AtlasManager::Vertex2D vert;
780 uint32_t index = eIt->mMeshRecordIndex;
781 Vector2 uv = mGlyphManager.GetAtlasSize( meshRecords[ index ].mAtlasId );
783 // Make sure we don't hit texture edge for single pixel texture ( filled pixel is in top left of every atlas )
784 float u = HALF / uv.x;
785 float v = HALF / uv.y;
786 float thickness = eIt->mUnderlineThickness;
787 float baseLine = eIt->mBaseLine + eIt->mUnderlinePosition - ( thickness * HALF );
788 float tlx = eIt->mLeft;
789 float brx = eIt->mRight;
791 vert.mPosition.x = tlx;
792 vert.mPosition.y = baseLine;
793 vert.mTexCoords.x = ZERO;
794 vert.mTexCoords.y = ZERO;
795 vert.mColor = underlineColor;
796 newMesh.mVertices.PushBack( vert );
798 vert.mPosition.x = brx;
799 vert.mPosition.y = baseLine;
800 vert.mTexCoords.x = u;
801 vert.mColor = underlineColor;
802 newMesh.mVertices.PushBack( vert );
804 vert.mPosition.x = tlx;
805 vert.mPosition.y = baseLine + thickness;
806 vert.mTexCoords.x = ZERO;
807 vert.mTexCoords.y = v;
808 vert.mColor = underlineColor;
809 newMesh.mVertices.PushBack( vert );
811 vert.mPosition.x = brx;
812 vert.mPosition.y = baseLine + thickness;
813 vert.mTexCoords.x = u;
814 vert.mColor = underlineColor;
815 newMesh.mVertices.PushBack( vert );
817 // Six indices in counter clockwise winding
818 newMesh.mIndices.PushBack( faceIndex + 1u );
819 newMesh.mIndices.PushBack( faceIndex );
820 newMesh.mIndices.PushBack( faceIndex + 2u );
821 newMesh.mIndices.PushBack( faceIndex + 2u );
822 newMesh.mIndices.PushBack( faceIndex + 3u );
823 newMesh.mIndices.PushBack( faceIndex + 1u );
826 Toolkit::Internal::AtlasMeshFactory::AppendMesh( meshRecords[ index ].mMesh, newMesh );
830 Actor mActor; ///< The actor parent which renders the text
831 AtlasGlyphManager mGlyphManager; ///< Glyph Manager to handle upload and caching
832 TextAbstraction::FontClient mFontClient; ///< The font client used to supply glyph information
833 Shader mShaderL8; ///< The shader for glyphs and emoji's shadows.
834 Shader mShaderRgba; ///< The shader for emojis.
835 std::vector< MaxBlockSize > mBlockSizes; ///< Maximum size needed to contain a glyph in a block within a new atlas
836 Vector< TextCacheEntry > mTextCache; ///< Caches data from previous render
837 Property::Map mQuadVertexFormat; ///< Describes the vertex format for text
838 int mDepth; ///< DepthIndex passed by control when connect to stage
841 Text::RendererPtr AtlasRenderer::New()
843 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Text::AtlasRenderer::New()\n" );
845 return Text::RendererPtr( new AtlasRenderer() );
848 Actor AtlasRenderer::Render( Text::ViewInterface& view,
850 Property::Index animatablePropertyIndex,
851 float& alignmentOffset,
854 DALI_LOG_INFO( gLogFilter, Debug::General, "Text::AtlasRenderer::Render()\n" );
856 UnparentAndReset( mImpl->mActor );
858 Length numberOfGlyphs = view.GetNumberOfGlyphs();
860 if( numberOfGlyphs > 0u )
862 Vector<GlyphInfo> glyphs;
863 glyphs.Resize( numberOfGlyphs );
865 Vector<Vector2> positions;
866 positions.Resize( numberOfGlyphs );
868 numberOfGlyphs = view.GetGlyphs( glyphs.Begin(),
874 glyphs.Resize( numberOfGlyphs );
875 positions.Resize( numberOfGlyphs );
877 const Vector4* const colorsBuffer = view.GetColors();
878 const ColorIndex* const colorIndicesBuffer = view.GetColorIndices();
879 const Vector4& defaultColor = view.GetTextColor();
881 mImpl->AddGlyphs( view,
883 animatablePropertyIndex,
892 /* In the case where AddGlyphs does not create a renderable Actor for example when glyphs are all whitespace create a new Actor. */
893 /* 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. */
894 if ( !mImpl->mActor )
896 mImpl->mActor = Actor::New();
900 return mImpl->mActor;
903 AtlasRenderer::AtlasRenderer()
909 AtlasRenderer::~AtlasRenderer()