2 * Copyright (c) 2015 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/dali.h>
23 #include <dali/devel-api/object/property-buffer.h>
24 #include <dali/devel-api/rendering/geometry.h>
25 #include <dali/devel-api/rendering/renderer.h>
26 #include <dali/devel-api/rendering/sampler.h>
27 #include <dali/devel-api/rendering/shader.h>
28 #include <dali/devel-api/text-abstraction/font-client.h>
29 #include <dali/integration-api/debug.h>
31 #include <dali-toolkit/public-api/controls/control-depth-index-ranges.h>
32 #include <dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager.h>
35 using namespace Dali::Toolkit;
36 using namespace Dali::Toolkit::Text;
40 #if defined(DEBUG_ENABLED)
41 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_RENDERING");
44 const float ZERO( 0.0f );
45 const float HALF( 0.5f );
46 const float ONE( 1.0f );
47 const float TWO( 2.0f );
48 const uint32_t DEFAULT_ATLAS_WIDTH = 512u;
49 const uint32_t DEFAULT_ATLAS_HEIGHT = 512u;
51 struct AtlasRenderer::Impl : public ConnectionTracker
64 AtlasManager::Mesh2D mMesh;
65 FrameBufferImage mBuffer;
74 float mUnderlinePosition;
75 float mUnderlineThickness;
76 uint32_t mMeshRecordIndex;
82 uint32_t mNeededBlockWidth;
83 uint32_t mNeededBlockHeight;
89 Text::GlyphIndex mIndex;
95 Text::GlyphIndex mIndex;
102 mGlyphManager = AtlasGlyphManager::Get();
103 mFontClient = TextAbstraction::FontClient::Get();
105 mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2;
106 mQuadVertexFormat[ "aTexCoord" ] = Property::VECTOR2;
107 mQuadIndexFormat[ "indices" ] = Property::INTEGER;
110 void AddGlyphs( const std::vector<Vector2>& positions,
111 const Vector<GlyphInfo>& glyphs,
112 const Vector4& textColor,
113 const Vector2& shadowOffset,
114 const Vector4& shadowColor,
115 bool underlineEnabled,
116 const Vector4& underlineColor,
117 float underlineHeight,
120 AtlasManager::AtlasSlot slot;
121 std::vector< MeshRecord > meshContainer;
122 Vector< Extent > extents;
123 TextCacheEntry textCacheEntry;
126 float currentUnderlinePosition = ZERO;
127 float currentUnderlineThickness = underlineHeight;
128 uint32_t currentBlockSize = 0;
129 FontId lastFontId = 0;
130 Style style = STYLE_NORMAL;
132 if ( fabsf( shadowOffset.x ) > Math::MACHINE_EPSILON_1 || fabsf( shadowOffset.y ) > Math::MACHINE_EPSILON_1 )
134 style = STYLE_DROP_SHADOW;
137 CalculateBlocksSize( glyphs );
139 // Avoid emptying mTextCache (& removing references) until after incremented references for the new text
140 Vector< TextCacheEntry > newTextCache;
142 for( uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i )
144 const GlyphInfo& glyph = glyphs[ i ];
146 // No operation for white space
147 if ( glyph.width && glyph.height )
149 // Are we still using the same fontId as previous
150 if ( glyph.fontId != lastFontId )
152 // We need to fetch fresh font underline metrics
153 FontMetrics fontMetrics;
154 mFontClient.GetFontMetrics( glyph.fontId, fontMetrics );
155 currentUnderlinePosition = ceil( fabsf( fontMetrics.underlinePosition ) );
156 float descender = ceil( fabsf( fontMetrics.descender ) );
158 if ( underlineHeight == ZERO )
160 currentUnderlineThickness = fontMetrics.underlineThickness;
162 // Ensure underline will be at least a pixel high
163 if ( currentUnderlineThickness < ONE )
165 currentUnderlineThickness = ONE;
169 currentUnderlineThickness = ceil( currentUnderlineThickness );
173 // Clamp the underline position at the font descender and check for ( as EFL describes it ) a broken font
174 if ( currentUnderlinePosition > descender )
176 currentUnderlinePosition = descender;
178 if ( ZERO == currentUnderlinePosition )
180 // Move offset down by one ( EFL behavior )
181 currentUnderlinePosition = ONE;
185 const Vector2& position = positions[ i ];
186 AtlasManager::Mesh2D newMesh;
188 if ( !mGlyphManager.Cached( glyph.fontId, glyph.index, slot ) )
190 // Select correct size for new atlas if needed....?
191 if ( lastFontId != glyph.fontId )
193 for ( uint32_t j = 0; j < mBlockSizes.size(); ++j )
195 if ( mBlockSizes[ j ].mFontId == glyph.fontId )
197 currentBlockSize = j;
198 mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_WIDTH,
199 DEFAULT_ATLAS_HEIGHT,
200 mBlockSizes[ j ].mNeededBlockWidth,
201 mBlockSizes[ j ].mNeededBlockHeight );
206 // Create a new image for the glyph
207 BufferImage bitmap = mFontClient.CreateBitmap( glyph.fontId, glyph.index );
210 // Ensure that the next image will fit into the current block size
211 bool setSize = false;
212 if ( bitmap.GetWidth() > mBlockSizes[ currentBlockSize ].mNeededBlockWidth )
215 mBlockSizes[ currentBlockSize ].mNeededBlockWidth = bitmap.GetWidth();
217 if ( bitmap.GetHeight() > mBlockSizes[ currentBlockSize ].mNeededBlockHeight )
220 mBlockSizes[ currentBlockSize ].mNeededBlockHeight = bitmap.GetHeight();
225 mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_WIDTH,
226 DEFAULT_ATLAS_HEIGHT,
227 mBlockSizes[ currentBlockSize ].mNeededBlockWidth,
228 mBlockSizes[ currentBlockSize ].mNeededBlockHeight );
231 // Locate a new slot for our glyph
232 mGlyphManager.Add( glyph, bitmap, slot );
237 // We have 2+ copies of the same glyph
238 mGlyphManager.AdjustReferenceCount( glyph.fontId, glyph.index, 1/*increment*/ );
241 // Generate mesh data for this quad, plugging in our supplied position
242 mGlyphManager.GenerateMeshData( slot.mImageId, position, newMesh );
243 textCacheEntry.mFontId = glyph.fontId;
244 textCacheEntry.mImageId = slot.mImageId;
245 textCacheEntry.mIndex = glyph.index;
246 newTextCache.PushBack( textCacheEntry );
248 // Find an existing mesh data object to attach to ( or create a new one, if we can't find one using the same atlas)
249 StitchTextMesh( meshContainer,
253 position.y + glyph.yBearing,
254 currentUnderlinePosition,
255 currentUnderlineThickness,
257 lastFontId = glyph.fontId;
261 // Now remove references for the old text
263 mTextCache.Swap( newTextCache );
265 if ( underlineEnabled )
267 // Check to see if any of the text needs an underline
268 GenerateUnderlines( meshContainer, extents, underlineColor, textColor );
271 // For each MeshData object, create a mesh actor and add to the renderable actor
272 if ( meshContainer.size() )
274 for ( std::vector< MeshRecord >::iterator mIt = meshContainer.begin(); mIt != meshContainer.end(); ++mIt )
276 Actor actor = CreateMeshActor( *mIt );
278 // Create an effect if necessary
279 if ( style == STYLE_DROP_SHADOW )
281 actor.Add( GenerateShadow( *mIt, shadowOffset, shadowColor ) );
286 actor.SetParentOrigin( ParentOrigin::CENTER ); // Keep all of the origins aligned
295 #if defined(DEBUG_ENABLED)
296 Toolkit::AtlasGlyphManager::Metrics metrics = mGlyphManager.GetMetrics();
297 DALI_LOG_INFO( gLogFilter, Debug::General, "TextAtlasRenderer::GlyphManager::GlyphCount: %i, AtlasCount: %i, TextureMemoryUse: %iK\n",
299 metrics.mAtlasMetrics.mAtlasCount,
300 metrics.mAtlasMetrics.mTextureMemoryUsed / 1024 );
302 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%s\n", metrics.mVerboseGlyphCounts.c_str() );
304 for ( uint32_t i = 0; i < metrics.mAtlasMetrics.mAtlasCount; ++i )
306 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Atlas [%i] %sPixels: %s Size: %ix%i, BlockSize: %ix%i, BlocksUsed: %i/%i\n",
307 i + 1, i > 8 ? "" : " ",
308 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mPixelFormat == Pixel::L8 ? "L8 " : "BGRA",
309 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mSize.mWidth,
310 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mSize.mHeight,
311 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mSize.mBlockWidth,
312 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mSize.mBlockHeight,
313 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mBlocksUsed,
314 metrics.mAtlasMetrics.mAtlasMetrics[ i ].mTotalBlocks );
321 for ( Vector< TextCacheEntry >::Iterator oldTextIter = mTextCache.Begin(); oldTextIter != mTextCache.End(); ++oldTextIter )
323 mGlyphManager.AdjustReferenceCount( oldTextIter->mFontId, oldTextIter->mIndex, -1/*decrement*/ );
325 mTextCache.Resize( 0 );
328 Actor CreateMeshActor( const MeshRecord& meshRecord )
330 PropertyBuffer quadVertices = PropertyBuffer::New( mQuadVertexFormat, meshRecord.mMesh.mVertices.Size() );
331 PropertyBuffer quadIndices = PropertyBuffer::New( mQuadIndexFormat, meshRecord.mMesh.mIndices.Size() );
332 quadVertices.SetData( const_cast< AtlasManager::Vertex2D* >( &meshRecord.mMesh.mVertices[ 0 ] ) );
333 quadIndices.SetData( const_cast< unsigned int* >( &meshRecord.mMesh.mIndices[ 0 ] ) );
335 Geometry quadGeometry = Geometry::New();
336 quadGeometry.AddVertexBuffer( quadVertices );
337 quadGeometry.SetIndexBuffer( quadIndices );
339 Material material = mGlyphManager.GetMaterial( meshRecord.mAtlasId );
340 Dali::Renderer renderer = Dali::Renderer::New( quadGeometry, material );
341 renderer.SetDepthIndex( CONTENT_DEPTH_INDEX + mDepth );
342 Actor actor = Actor::New();
343 #if defined(DEBUG_ENABLED)
344 actor.SetName( "Text renderable actor" );
346 actor.AddRenderer( renderer );
347 actor.SetSize( 1.0f, 1.0f );
348 actor.SetColor( meshRecord.mColor );
350 if ( meshRecord.mIsUnderline )
352 actor.SetColorMode( USE_OWN_COLOR );
356 actor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
361 void StitchTextMesh( std::vector< MeshRecord >& meshContainer,
362 AtlasManager::Mesh2D& newMesh,
363 Vector< Extent >& extents,
364 const Vector4& color,
366 float underlinePosition,
367 float underlineThickness,
368 AtlasManager::AtlasSlot& slot )
372 float left = newMesh.mVertices[ 0 ].mPosition.x;
373 float right = newMesh.mVertices[ 1 ].mPosition.x;
375 // Check to see if there's a mesh data object that references the same atlas ?
377 for ( std::vector< MeshRecord >::iterator mIt = meshContainer.begin(); mIt != meshContainer.end(); ++mIt, ++index )
379 if ( slot.mAtlasId == mIt->mAtlasId && color == mIt->mColor )
381 // Stitch the mesh to the existing mesh and adjust any extents
382 mGlyphManager.StitchMesh( mIt->mMesh, newMesh );
383 AdjustExtents( extents,
390 underlineThickness );
395 // No mesh data object currently exists that references this atlas, so create a new one
396 MeshRecord meshRecord;
397 meshRecord.mAtlasId = slot.mAtlasId;
398 meshRecord.mMesh = newMesh;
399 meshRecord.mColor = color;
400 meshRecord.mIsUnderline = false;
401 meshContainer.push_back( meshRecord );
403 // Adjust extents for this new meshrecord
404 AdjustExtents( extents,
406 meshContainer.size() - 1u,
411 underlineThickness );
416 void AdjustExtents( Vector< Extent >& extents,
417 std::vector< MeshRecord>& meshRecords,
422 float underlinePosition,
423 float underlineThickness )
425 bool foundExtent = false;
426 for ( Vector< Extent >::Iterator eIt = extents.Begin(); eIt != extents.End(); ++eIt )
428 if ( Equals( baseLine, eIt->mBaseLine ) )
431 if ( left < eIt->mLeft )
435 if ( right > eIt->mRight )
440 if ( underlinePosition > eIt->mUnderlinePosition )
442 eIt->mUnderlinePosition = underlinePosition;
444 if ( underlineThickness > eIt->mUnderlineThickness )
446 eIt->mUnderlineThickness = underlineThickness;
454 extent.mRight = right;
455 extent.mBaseLine = baseLine;
456 extent.mUnderlinePosition = underlinePosition;
457 extent.mUnderlineThickness = underlineThickness;
458 extent.mMeshRecordIndex = index;
459 extents.PushBack( extent );
463 void CalculateBlocksSize( const Vector<GlyphInfo>& glyphs )
465 MaxBlockSize maxBlockSize;
466 for ( uint32_t i = 0; i < glyphs.Size(); ++i )
468 FontId fontId = glyphs[ i ].fontId;
469 bool foundFont = false;
470 for ( uint32_t j = 0; j < mBlockSizes.size(); ++j )
472 if ( mBlockSizes[ j ].mFontId == fontId )
479 FontMetrics fontMetrics;
480 mFontClient.GetFontMetrics( fontId, fontMetrics );
481 maxBlockSize.mNeededBlockWidth = static_cast< uint32_t >( fontMetrics.height );
482 maxBlockSize.mNeededBlockHeight = static_cast< uint32_t >( fontMetrics.height );
483 maxBlockSize.mFontId = fontId;
484 mBlockSizes.push_back( maxBlockSize );
489 void GenerateUnderlines( std::vector< MeshRecord>& meshRecords,
490 Vector< Extent >& extents,
491 const Vector4& underlineColor,
492 const Vector4& textColor )
494 AtlasManager::Mesh2D newMesh;
495 unsigned short faceIndex = 0;
496 for ( Vector< Extent >::ConstIterator eIt = extents.Begin(); eIt != extents.End(); ++eIt )
498 AtlasManager::Vertex2D vert;
499 uint32_t index = eIt->mMeshRecordIndex;
500 Vector2 uv = mGlyphManager.GetAtlasSize( meshRecords[ index ].mAtlasId );
502 // Make sure we don't hit texture edge for single pixel texture ( filled pixel is in top left of every atlas )
503 float u = HALF / uv.x;
504 float v = HALF / uv.y;
505 float thickness = eIt->mUnderlineThickness;
506 float baseLine = eIt->mBaseLine + eIt->mUnderlinePosition - ( thickness * HALF );
507 float tlx = eIt->mLeft;
508 float brx = eIt->mRight;
510 vert.mPosition.x = tlx;
511 vert.mPosition.y = baseLine;
512 vert.mTexCoords.x = ZERO;
513 vert.mTexCoords.y = ZERO;
514 newMesh.mVertices.PushBack( vert );
516 vert.mPosition.x = brx;
517 vert.mPosition.y = baseLine;
518 vert.mTexCoords.x = u;
519 newMesh.mVertices.PushBack( vert );
521 vert.mPosition.x = tlx;
522 vert.mPosition.y = baseLine + thickness;
523 vert.mTexCoords.x = ZERO;
524 vert.mTexCoords.y = v;
525 newMesh.mVertices.PushBack( vert );
527 vert.mPosition.x = brx;
528 vert.mPosition.y = baseLine + thickness;
529 vert.mTexCoords.x = u;
530 newMesh.mVertices.PushBack( vert );
532 // Six indices in counter clockwise winding
533 newMesh.mIndices.PushBack( faceIndex + 1u );
534 newMesh.mIndices.PushBack( faceIndex );
535 newMesh.mIndices.PushBack( faceIndex + 2u );
536 newMesh.mIndices.PushBack( faceIndex + 2u );
537 newMesh.mIndices.PushBack( faceIndex + 3u );
538 newMesh.mIndices.PushBack( faceIndex + 1u );
541 if ( underlineColor == textColor )
543 mGlyphManager.StitchMesh( meshRecords[ index ].mMesh, newMesh );
548 record.mMesh = newMesh;
549 record.mAtlasId = meshRecords[ index ].mAtlasId;
550 record.mColor = underlineColor;
551 record.mIsUnderline = true;
552 meshRecords.push_back( record );
557 Actor GenerateShadow( MeshRecord& meshRecord,
558 const Vector2& shadowOffset,
559 const Vector4& shadowColor )
561 // Scan vertex buffer to determine width and height of effect buffer needed
562 const Vector< AtlasManager::Vertex2D >& verts = meshRecord.mMesh.mVertices;
563 float tlx = verts[ 0 ].mPosition.x;
564 float tly = verts[ 0 ].mPosition.y;
568 for ( uint32_t i = 0; i < verts.Size(); ++i )
570 if ( verts[ i ].mPosition.x < tlx )
572 tlx = verts[ i ].mPosition.x;
574 if ( verts[ i ].mPosition.y < tly )
576 tly = verts[ i ].mPosition.y;
578 if ( verts[ i ].mPosition.x > brx )
580 brx = verts[ i ].mPosition.x;
582 if ( verts[ i ].mPosition.y > bry )
584 bry = verts[ i ].mPosition.y;
588 float width = brx - tlx;
589 float height = bry - tly;
590 float divWidth = TWO / width;
591 float divHeight = TWO / height;
593 // Create a buffer to render to
594 meshRecord.mBuffer = FrameBufferImage::New( width, height );
596 // We will render a quad into this buffer
597 unsigned int indices[ 6 ] = { 1, 0, 2, 2, 3, 1 };
598 PropertyBuffer quadVertices = PropertyBuffer::New( mQuadVertexFormat, 4u );
599 PropertyBuffer quadIndices = PropertyBuffer::New( mQuadIndexFormat, sizeof(indices)/sizeof(indices[0]) );
601 AtlasManager::Vertex2D vertices[ 4 ] = {
602 { Vector2( tlx + shadowOffset.x, tly + shadowOffset.y ), Vector2( ZERO, ZERO ) },
603 { Vector2( brx + shadowOffset.x, tly + shadowOffset.y ), Vector2( ONE, ZERO ) },
604 { Vector2( tlx + shadowOffset.x, bry + shadowOffset.y ), Vector2( ZERO, ONE ) },
605 { Vector2( brx + shadowOffset.x, bry + shadowOffset.y ), Vector2( ONE, ONE ) } };
607 quadVertices.SetData( vertices );
608 quadIndices.SetData( indices );
610 Geometry quadGeometry = Geometry::New();
611 quadGeometry.AddVertexBuffer( quadVertices );
612 quadGeometry.SetIndexBuffer( quadIndices );
614 Sampler sampler = Sampler::New( meshRecord.mBuffer, "sTexture" );
615 Material material = Material::New( mGlyphManager.GetEffectBufferShader() );
616 material.AddSampler( sampler );
618 Dali::Renderer renderer = Dali::Renderer::New( quadGeometry, material );
620 // Ensure shadow is behind the text...
621 renderer.SetDepthIndex( CONTENT_DEPTH_INDEX + mDepth - 1 );
622 Actor actor = Actor::New();
623 actor.AddRenderer( renderer );
624 actor.SetSize( 1.0f, 1.0f );
626 // Create a sub actor to render the source with normalized vertex positions
627 Vector< AtlasManager::Vertex2D > normVertexList;
628 for ( uint32_t i = 0; i < verts.Size(); ++i )
630 AtlasManager::Vertex2D vertex = verts[ i ];
631 vertex.mPosition.x = ( ( vertex.mPosition.x - tlx ) * divWidth ) - ONE;
632 vertex.mPosition.y = ( ( vertex.mPosition.y - tly ) * divHeight ) - ONE;
633 normVertexList.PushBack( vertex );
636 PropertyBuffer normVertices = PropertyBuffer::New( mQuadVertexFormat, normVertexList.Size() );
637 PropertyBuffer normIndices = PropertyBuffer::New( mQuadIndexFormat, meshRecord.mMesh.mIndices.Size() );
638 normVertices.SetData( const_cast< AtlasManager::Vertex2D* >( &normVertexList[ 0 ] ) );
639 normIndices.SetData( const_cast< unsigned int* >( &meshRecord.mMesh.mIndices[ 0 ] ) );
641 Geometry normGeometry = Geometry::New();
642 normGeometry.AddVertexBuffer( normVertices );
643 normGeometry.SetIndexBuffer( normIndices );
645 Material normMaterial = Material::New( mGlyphManager.GetGlyphShadowShader() );
646 Sampler normSampler = mGlyphManager.GetSampler( meshRecord.mAtlasId );
647 normMaterial.AddSampler( normSampler );
648 Dali::Renderer normRenderer = Dali::Renderer::New( normGeometry, normMaterial );
649 Actor subActor = Actor::New();
650 subActor.AddRenderer( normRenderer );
651 subActor.SetSize( 1.0f, 1.0f );
652 subActor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
653 subActor.SetColor( shadowColor );
655 // Create a render task to render the effect
656 RenderTask task = Stage::GetCurrent().GetRenderTaskList().CreateTask();
657 task.SetTargetFrameBuffer( meshRecord.mBuffer );
658 task.SetSourceActor( subActor );
659 task.SetClearEnabled( true );
660 task.SetClearColor( Vector4::ZERO );
661 task.SetExclusive( true );
662 task.SetRefreshRate( RenderTask::REFRESH_ONCE );
663 task.FinishedSignal().Connect( this, &AtlasRenderer::Impl::RenderComplete );
664 actor.Add( subActor );
669 void RenderComplete( RenderTask& renderTask )
671 // Disconnect and remove this single shot render task
672 renderTask.FinishedSignal().Disconnect( this, &AtlasRenderer::Impl::RenderComplete );
673 Stage::GetCurrent().GetRenderTaskList().RemoveTask( renderTask );
675 // Get the actor used for render to buffer and remove it from the parent
676 Actor renderActor = renderTask.GetSourceActor();
679 Actor parent = renderActor.GetParent();
682 parent.Remove( renderActor );
687 Actor mActor; ///< The actor parent which renders the text
688 AtlasGlyphManager mGlyphManager; ///< Glyph Manager to handle upload and caching
689 TextAbstraction::FontClient mFontClient; ///> The font client used to supply glyph information
690 std::vector< MaxBlockSize > mBlockSizes; ///> Maximum size needed to contain a glyph in a block within a new atlas
691 std::vector< uint32_t > mFace; ///> Face indices for a quad
692 Vector< TextCacheEntry > mTextCache;
693 Property::Map mQuadVertexFormat;
694 Property::Map mQuadIndexFormat;
698 Text::RendererPtr AtlasRenderer::New()
700 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Text::AtlasRenderer::New()\n" );
702 return Text::RendererPtr( new AtlasRenderer() );
705 Actor AtlasRenderer::Render( Text::ViewInterface& view, int depth )
707 UnparentAndReset( mImpl->mActor );
709 Length numberOfGlyphs = view.GetNumberOfGlyphs();
711 if( numberOfGlyphs > 0u )
713 Vector<GlyphInfo> glyphs;
714 glyphs.Resize( numberOfGlyphs );
716 std::vector<Vector2> positions;
717 positions.resize( numberOfGlyphs );
719 numberOfGlyphs = view.GetGlyphs( glyphs.Begin(),
723 glyphs.Resize( numberOfGlyphs );
724 positions.resize( numberOfGlyphs );
726 mImpl->AddGlyphs( positions,
729 view.GetShadowOffset(),
730 view.GetShadowColor(),
731 view.IsUnderlineEnabled(),
732 view.GetUnderlineColor(),
733 view.GetUnderlineHeight(),
737 return mImpl->mActor;
740 AtlasRenderer::AtlasRenderer()
746 AtlasRenderer::~AtlasRenderer()