2 * Copyright (c) 2021 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/devel-api/text-abstraction/font-client.h>
23 #include <dali/integration-api/debug.h>
24 #include <dali/public-api/animation/constraints.h>
25 #include <dali/public-api/rendering/geometry.h>
26 #include <dali/public-api/rendering/renderer.h>
29 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
30 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.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 const float ZERO(0.0f);
47 const float HALF(0.5f);
48 const float ONE(1.0f);
49 const uint32_t DEFAULT_ATLAS_WIDTH = 512u;
50 const uint32_t DEFAULT_ATLAS_HEIGHT = 512u;
51 const uint32_t MAX_ATLAS_WIDTH = 1024u;
52 const uint32_t MAX_ATLAS_HEIGHT = 1024u;
53 const uint32_t DOUBLE_PIXEL_PADDING = 4u;//Padding will be added twice to Atlas
54 const uint16_t NO_OUTLINE = 0u;
57 struct AtlasRenderer::Impl
73 AtlasManager::Mesh2D mMesh;
77 * brief Struct used to generate the underline mesh.
78 * There is one Extent per line of text.
86 mUnderlinePosition(0.0f),
87 mUnderlineThickness(0.0f),
95 float mUnderlinePosition;
96 float mUnderlineThickness;
97 uint32_t mMeshRecordIndex;
104 mNeededBlockWidth(0),
105 mNeededBlockHeight(0)
110 uint32_t mNeededBlockWidth;
111 uint32_t mNeededBlockHeight;
123 Text::GlyphIndex mIndex;
126 struct TextCacheEntry
139 Text::GlyphIndex mIndex;
141 uint16_t mOutlineWidth;
149 mGlyphManager = AtlasGlyphManager::Get();
150 mFontClient = TextAbstraction::FontClient::Get();
152 mQuadVertexFormat["aPosition"] = Property::VECTOR2;
153 mQuadVertexFormat["aTexCoord"] = Property::VECTOR2;
154 mQuadVertexFormat["aColor"] = Property::VECTOR4;
157 bool IsGlyphUnderlined(GlyphIndex index,
158 const Vector<GlyphRun>& underlineRuns)
160 for(Vector<GlyphRun>::ConstIterator it = underlineRuns.Begin(),
161 endIt = underlineRuns.End();
165 const GlyphRun& run = *it;
167 if((run.glyphIndex <= index) && (index < run.glyphIndex + run.numberOfGlyphs))
176 void CacheGlyph(const GlyphInfo& glyph, FontId lastFontId, const AtlasGlyphManager::GlyphStyle& style, AtlasManager::AtlasSlot& slot)
178 const bool glyphNotCached = !mGlyphManager.IsCached(glyph.fontId, glyph.index, style, slot); // Check FontGlyphRecord vector for entry with glyph index and fontId
180 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "AddGlyphs fontID[%u] glyphIndex[%u] [%s]\n", glyph.fontId, glyph.index, (glyphNotCached) ? "not cached" : "cached");
184 MaxBlockSize& blockSize = mBlockSizes[0u];
186 if(lastFontId != glyph.fontId)
189 // 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.
190 // CalculateBlocksSize() above ensures a block size entry exists.
191 for(std::vector<MaxBlockSize>::const_iterator it = mBlockSizes.begin(),
192 endIt = mBlockSizes.end();
196 const MaxBlockSize& blockSizeEntry = *it;
197 if(blockSizeEntry.mFontId == glyph.fontId)
199 blockSize = mBlockSizes[index];
204 // Create a new image for the glyph
207 // Whether the glyph is an outline.
208 const bool isOutline = 0u != style.outline;
210 // Whether the current glyph is a color one.
211 const bool isColorGlyph = mFontClient.IsColorGlyph(glyph.fontId, glyph.index);
213 if(!isOutline || (isOutline && !isColorGlyph))
215 // Retrieve the emoji's bitmap.
216 TextAbstraction::FontClient::GlyphBufferData glyphBufferData;
217 glyphBufferData.width = isColorGlyph ? glyph.width : 0; // Desired width and height.
218 glyphBufferData.height = isColorGlyph ? glyph.height : 0;
220 mFontClient.CreateBitmap(glyph.fontId,
222 glyph.isItalicRequired,
223 glyph.isBoldRequired,
227 // Create the pixel data.
228 bitmap = PixelData::New(glyphBufferData.buffer,
229 glyphBufferData.width * glyphBufferData.height * GetBytesPerPixel(glyphBufferData.format),
230 glyphBufferData.width,
231 glyphBufferData.height,
232 glyphBufferData.format,
233 PixelData::DELETE_ARRAY);
237 // Ensure that the next image will fit into the current block size
238 if(bitmap.GetWidth() > blockSize.mNeededBlockWidth)
240 blockSize.mNeededBlockWidth = bitmap.GetWidth();
243 if(bitmap.GetHeight() > blockSize.mNeededBlockHeight)
245 blockSize.mNeededBlockHeight = bitmap.GetHeight();
248 // If CheckAtlas in AtlasManager::Add can't fit the bitmap in the current atlas it will create a new atlas
250 // Setting the block size and size of new atlas does not mean a new one will be created. An existing atlas may still surffice.
251 uint32_t default_width = DEFAULT_ATLAS_WIDTH;
252 uint32_t default_height = DEFAULT_ATLAS_HEIGHT;
255 (blockSize.mNeededBlockWidth >= (default_width - (DOUBLE_PIXEL_PADDING + 1u)) ||
256 blockSize.mNeededBlockHeight >= (default_height - (DOUBLE_PIXEL_PADDING + 1u)))
258 (default_width < MAX_ATLAS_WIDTH &&
259 default_height < MAX_ATLAS_HEIGHT))
261 default_width <<= 1u;
262 default_height <<= 1u;
264 mGlyphManager.SetNewAtlasSize(default_width,
266 blockSize.mNeededBlockWidth,
267 blockSize.mNeededBlockHeight);
269 // Locate a new slot for our glyph
270 mGlyphManager.Add(glyph, style, bitmap, slot); // slot will be 0 is glyph not added
276 // We have 2+ copies of the same glyph
277 mGlyphManager.AdjustReferenceCount(glyph.fontId, glyph.index, style, 1); //increment
281 void GenerateMesh(const GlyphInfo& glyph,
282 const Vector2& position,
283 const Vector4& color,
285 AtlasManager::AtlasSlot& slot,
287 float currentUnderlinePosition,
288 float currentUnderlineThickness,
289 std::vector<MeshRecord>& meshContainer,
290 Vector<TextCacheEntry>& newTextCache,
291 Vector<Extent>& extents)
293 // Generate mesh data for this quad, plugging in our supplied position
294 AtlasManager::Mesh2D newMesh;
295 mGlyphManager.GenerateMeshData(slot.mImageId, position, newMesh);
297 TextCacheEntry textCacheEntry;
298 textCacheEntry.mFontId = glyph.fontId;
299 textCacheEntry.mImageId = slot.mImageId;
300 textCacheEntry.mIndex = glyph.index;
301 textCacheEntry.mOutlineWidth = outline;
302 textCacheEntry.isItalic = glyph.isItalicRequired;
303 textCacheEntry.isBold = glyph.isBoldRequired;
305 newTextCache.PushBack(textCacheEntry);
307 AtlasManager::Vertex2D* verticesBuffer = newMesh.mVertices.Begin();
309 for(unsigned int index = 0u, size = newMesh.mVertices.Count();
313 AtlasManager::Vertex2D& vertex = *(verticesBuffer + index);
315 // Set the color of the vertex.
316 vertex.mColor = color;
319 // Find an existing mesh data object to attach to ( or create a new one, if we can't find one using the same atlas)
320 StitchTextMesh(meshContainer,
323 position.y + glyph.yBearing,
325 currentUnderlinePosition,
326 currentUnderlineThickness,
330 void CreateActors(const std::vector<MeshRecord>& meshContainer,
331 const Size& textSize,
332 const Vector4& color,
333 const Vector4& shadowColor,
334 const Vector2& shadowOffset,
336 Property::Index animatablePropertyIndex,
341 // Create a container actor to act as a common parent for text and shadow, to avoid color inheritence issues.
342 mActor = Actor::New();
343 mActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
344 mActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
345 mActor.SetProperty(Actor::Property::SIZE, textSize);
346 mActor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
349 for(std::vector<MeshRecord>::const_iterator it = meshContainer.begin(),
350 endIt = meshContainer.end();
354 const MeshRecord& meshRecord = *it;
356 Actor actor = CreateMeshActor(textControl, animatablePropertyIndex, color, meshRecord, textSize, STYLE_NORMAL);
358 // Whether the actor has renderers.
359 const bool hasRenderer = actor.GetRendererCount() > 0u;
361 // Create an effect if necessary
365 // Change the color of the vertices.
366 for(Vector<AtlasManager::Vertex2D>::Iterator vIt = meshRecord.mMesh.mVertices.Begin(),
367 vEndIt = meshRecord.mMesh.mVertices.End();
371 AtlasManager::Vertex2D& vertex = *vIt;
373 vertex.mColor = shadowColor;
376 Actor shadowActor = CreateMeshActor(textControl, animatablePropertyIndex, color, meshRecord, textSize, STYLE_DROP_SHADOW);
377 #if defined(DEBUG_ENABLED)
378 shadowActor.SetProperty(Dali::Actor::Property::NAME, "Text Shadow renderable actor");
380 // Offset shadow in x and y
381 shadowActor.RegisterProperty("uOffset", shadowOffset);
382 Dali::Renderer renderer(shadowActor.GetRendererAt(0));
383 int depthIndex = renderer.GetProperty<int>(Dali::Renderer::Property::DEPTH_INDEX);
384 renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, depthIndex - 1);
385 mActor.Add(shadowActor);
395 void AddGlyphs(Text::ViewInterface& view,
397 Property::Index animatablePropertyIndex,
398 const Vector<Vector2>& positions,
399 const Vector<GlyphInfo>& glyphs,
400 const Vector4& defaultColor,
401 const Vector4* const colorsBuffer,
402 const ColorIndex* const colorIndicesBuffer,
406 AtlasManager::AtlasSlot slot;
410 AtlasManager::AtlasSlot slotOutline;
411 slotOutline.mImageId = 0u;
412 slotOutline.mAtlasId = 0u;
414 std::vector<MeshRecord> meshContainer;
415 std::vector<MeshRecord> meshContainerOutline;
416 Vector<Extent> extents;
419 const Vector2& textSize(view.GetLayoutSize());
420 const Vector2 halfTextSize(textSize * 0.5f);
421 const Vector2& shadowOffset(view.GetShadowOffset());
422 const Vector4& shadowColor(view.GetShadowColor());
423 const bool underlineEnabled = view.IsUnderlineEnabled();
424 const Vector4& underlineColor(view.GetUnderlineColor());
425 const float underlineHeight = view.GetUnderlineHeight();
426 const uint16_t outlineWidth = view.GetOutlineWidth();
427 const Vector4& outlineColor(view.GetOutlineColor());
428 const bool isOutline = 0u != outlineWidth;
430 const bool useDefaultColor = (NULL == colorsBuffer);
432 // Get the underline runs.
433 const Length numberOfUnderlineRuns = view.GetNumberOfUnderlineRuns();
434 Vector<GlyphRun> underlineRuns;
435 underlineRuns.Resize(numberOfUnderlineRuns);
436 view.GetUnderlineRuns(underlineRuns.Begin(),
438 numberOfUnderlineRuns);
440 bool thereAreUnderlinedGlyphs = false;
442 float currentUnderlinePosition = ZERO;
443 float currentUnderlineThickness = underlineHeight;
444 FontId lastFontId = 0;
445 FontId lastUnderlinedFontId = 0;
446 Style style = STYLE_NORMAL;
448 if(fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1)
450 style = STYLE_DROP_SHADOW;
453 CalculateBlocksSize(glyphs);
455 // Avoid emptying mTextCache (& removing references) until after incremented references for the new text
456 Vector<TextCacheEntry> newTextCache;
457 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
458 const Vector2* const positionsBuffer = positions.Begin();
459 const Vector2 lineOffsetPosition(minLineOffset, 0.f);
461 for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
463 const GlyphInfo& glyph = *(glyphsBuffer + i);
464 const bool isGlyphUnderlined = underlineEnabled || IsGlyphUnderlined(i, underlineRuns);
465 thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || isGlyphUnderlined;
467 // No operation for white space
468 if(glyph.width && glyph.height)
470 // Are we still using the same fontId as previous
471 if(isGlyphUnderlined && (glyph.fontId != lastUnderlinedFontId))
473 // We need to fetch fresh font underline metrics
474 FontMetrics fontMetrics;
475 mFontClient.GetFontMetrics(glyph.fontId, fontMetrics);
476 currentUnderlinePosition = ceil(fabsf(fontMetrics.underlinePosition));
477 const float descender = ceil(fabsf(fontMetrics.descender));
479 if(fabsf(underlineHeight) < Math::MACHINE_EPSILON_1000)
481 currentUnderlineThickness = fontMetrics.underlineThickness;
483 // Ensure underline will be at least a pixel high
484 if(currentUnderlineThickness < ONE)
486 currentUnderlineThickness = ONE;
490 currentUnderlineThickness = ceil(currentUnderlineThickness);
494 // Clamp the underline position at the font descender and check for ( as EFL describes it ) a broken font
495 if(currentUnderlinePosition > descender)
497 currentUnderlinePosition = descender;
500 if(fabsf(currentUnderlinePosition) < Math::MACHINE_EPSILON_1000)
502 // Move offset down by one ( EFL behavior )
503 currentUnderlinePosition = ONE;
506 lastUnderlinedFontId = glyph.fontId;
509 AtlasGlyphManager::GlyphStyle style;
510 style.isItalic = glyph.isItalicRequired;
511 style.isBold = glyph.isBoldRequired;
513 // Retrieves and caches the glyph's bitmap.
514 CacheGlyph(glyph, lastFontId, style, slot);
516 // Retrieves and caches the outline glyph's bitmap.
519 style.outline = outlineWidth;
520 CacheGlyph(glyph, lastFontId, style, slotOutline);
523 // Move the origin (0,0) of the mesh to the center of the actor
524 const Vector2& temp = *(positionsBuffer + i);
525 const Vector2 position = Vector2(roundf(temp.x), temp.y) - halfTextSize - lineOffsetPosition; // roundf() avoids pixel alignment issues.
527 if(0u != slot.mImageId) // invalid slot id, glyph has failed to be added to atlas
529 Vector2 positionPlusOutlineOffset = position;
532 // Add an offset to the text.
533 const float outlineWidthOffset = static_cast<float>(outlineWidth);
534 positionPlusOutlineOffset += Vector2(outlineWidthOffset, outlineWidthOffset);
537 // Get the color of the character.
538 const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndicesBuffer + i);
539 const Vector4& color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + colorIndex - 1u);
542 positionPlusOutlineOffset,
547 currentUnderlinePosition,
548 currentUnderlineThickness,
553 lastFontId = glyph.fontId; // Prevents searching for existing blocksizes when string of the same fontId.
556 if(isOutline && (0u != slotOutline.mImageId)) // invalid slot id, glyph has failed to be added to atlas
564 currentUnderlinePosition,
565 currentUnderlineThickness,
566 meshContainerOutline,
573 // Now remove references for the old text
575 mTextCache.Swap(newTextCache);
577 if(thereAreUnderlinedGlyphs)
579 // Check to see if any of the text needs an underline
580 GenerateUnderlines(meshContainer, extents, underlineColor);
583 // For each MeshData object, create a mesh actor and add to the renderable actor
584 bool isShadowDrawn = false;
585 if(!meshContainerOutline.empty())
587 const bool drawShadow = STYLE_DROP_SHADOW == style;
588 CreateActors(meshContainerOutline,
594 animatablePropertyIndex,
597 isShadowDrawn = drawShadow;
600 // For each MeshData object, create a mesh actor and add to the renderable actor
601 if(!meshContainer.empty())
603 const bool drawShadow = !isShadowDrawn && (STYLE_DROP_SHADOW == style);
604 CreateActors(meshContainer,
610 animatablePropertyIndex,
614 #if defined(DEBUG_ENABLED)
615 Toolkit::AtlasGlyphManager::Metrics metrics = mGlyphManager.GetMetrics();
616 DALI_LOG_INFO(gLogFilter, Debug::General, "TextAtlasRenderer::GlyphManager::GlyphCount: %i, AtlasCount: %i, TextureMemoryUse: %iK\n", metrics.mGlyphCount, metrics.mAtlasMetrics.mAtlasCount, metrics.mAtlasMetrics.mTextureMemoryUsed / 1024);
618 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "%s\n", metrics.mVerboseGlyphCounts.c_str());
620 for(uint32_t i = 0; i < metrics.mAtlasMetrics.mAtlasCount; ++i)
622 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Atlas [%i] %sPixels: %s Size: %ix%i, BlockSize: %ix%i, BlocksUsed: %i/%i\n", i + 1, i > 8 ? "" : " ", metrics.mAtlasMetrics.mAtlasMetrics[i].mPixelFormat == Pixel::L8 ? "L8 " : "BGRA", metrics.mAtlasMetrics.mAtlasMetrics[i].mSize.mWidth, metrics.mAtlasMetrics.mAtlasMetrics[i].mSize.mHeight, metrics.mAtlasMetrics.mAtlasMetrics[i].mSize.mBlockWidth, metrics.mAtlasMetrics.mAtlasMetrics[i].mSize.mBlockHeight, metrics.mAtlasMetrics.mAtlasMetrics[i].mBlocksUsed, metrics.mAtlasMetrics.mAtlasMetrics[i].mTotalBlocks);
629 for(Vector<TextCacheEntry>::Iterator oldTextIter = mTextCache.Begin(); oldTextIter != mTextCache.End(); ++oldTextIter)
631 AtlasGlyphManager::GlyphStyle style;
632 style.outline = oldTextIter->mOutlineWidth;
633 style.isItalic = oldTextIter->isItalic;
634 style.isBold = oldTextIter->isBold;
635 mGlyphManager.AdjustReferenceCount(oldTextIter->mFontId, oldTextIter->mIndex, style, -1 /*decrement*/);
637 mTextCache.Resize(0);
640 Actor CreateMeshActor(Actor textControl, Property::Index animatablePropertyIndex, const Vector4& defaultColor, const MeshRecord& meshRecord, const Vector2& actorSize, Style style)
642 VertexBuffer quadVertices = VertexBuffer::New(mQuadVertexFormat);
643 quadVertices.SetData(const_cast<AtlasManager::Vertex2D*>(&meshRecord.mMesh.mVertices[0]), meshRecord.mMesh.mVertices.Size());
645 Geometry quadGeometry = Geometry::New();
646 quadGeometry.AddVertexBuffer(quadVertices);
647 quadGeometry.SetIndexBuffer(&meshRecord.mMesh.mIndices[0], meshRecord.mMesh.mIndices.Size());
649 TextureSet textureSet(mGlyphManager.GetTextures(meshRecord.mAtlasId));
651 // Choose the shader to use.
652 const bool isColorShader = (STYLE_DROP_SHADOW != style) && (Pixel::BGRA8888 == mGlyphManager.GetPixelFormat(meshRecord.mAtlasId));
656 // The glyph is an emoji and is not a shadow.
659 mShaderRgba = Shader::New(SHADER_TEXT_ATLAS_SHADER_VERT, SHADER_TEXT_ATLAS_RGBA_SHADER_FRAG);
661 shader = mShaderRgba;
665 // The glyph is text or a shadow.
668 mShaderL8 = Shader::New(SHADER_TEXT_ATLAS_SHADER_VERT, SHADER_TEXT_ATLAS_L8_SHADER_FRAG);
673 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "defaultColor[%f, %f, %f, %f ]\n", defaultColor.r, defaultColor.g, defaultColor.b, defaultColor.a);
675 Dali::Property::Index shaderTextColorIndex = shader.RegisterProperty("textColorAnimatable", defaultColor);
677 if(animatablePropertyIndex != Property::INVALID_INDEX)
679 // create constraint for the animatable text's color Property with textColorAnimatable in the shader.
680 if(shaderTextColorIndex)
682 Constraint constraint = Constraint::New<Vector4>(shader, shaderTextColorIndex, EqualToConstraint());
683 constraint.AddSource(Source(textControl, animatablePropertyIndex));
689 // If not animating the text colour then set to 1's so shader uses the current vertex color
690 shader.RegisterProperty("textColorAnimatable", Vector4(1.0, 1.0, 1.0, 1.0));
693 Dali::Renderer renderer = Dali::Renderer::New(quadGeometry, shader);
694 renderer.SetTextures(textureSet);
695 renderer.SetProperty(Dali::Renderer::Property::BLEND_MODE, BlendMode::ON);
696 renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT + mDepth);
698 Actor actor = Actor::New();
699 #if defined(DEBUG_ENABLED)
700 actor.SetProperty(Dali::Actor::Property::NAME, "Text renderable actor");
702 actor.AddRenderer(renderer);
703 // Keep all of the origins aligned
704 actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
705 actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
706 actor.SetProperty(Actor::Property::SIZE, actorSize);
707 actor.RegisterProperty("uOffset", Vector2::ZERO);
708 actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
713 void StitchTextMesh(std::vector<MeshRecord>& meshContainer,
714 AtlasManager::Mesh2D& newMesh,
715 Vector<Extent>& extents,
718 float underlinePosition,
719 float underlineThickness,
720 AtlasManager::AtlasSlot& slot)
724 float left = newMesh.mVertices[0].mPosition.x;
725 float right = newMesh.mVertices[1].mPosition.x;
727 // Check to see if there's a mesh data object that references the same atlas ?
729 for(std::vector<MeshRecord>::iterator mIt = meshContainer.begin(),
730 mEndIt = meshContainer.end();
734 if(slot.mAtlasId == mIt->mAtlasId)
736 // Append the mesh to the existing mesh and adjust any extents
737 Toolkit::Internal::AtlasMeshFactory::AppendMesh(mIt->mMesh, newMesh);
741 AdjustExtents(extents,
755 // No mesh data object currently exists that references this atlas, so create a new one
756 MeshRecord meshRecord;
757 meshRecord.mAtlasId = slot.mAtlasId;
758 meshRecord.mMesh = newMesh;
759 meshContainer.push_back(meshRecord);
763 // Adjust extents for this new meshrecord
764 AdjustExtents(extents,
766 meshContainer.size() - 1u,
776 void AdjustExtents(Vector<Extent>& extents,
777 std::vector<MeshRecord>& meshRecords,
782 float underlinePosition,
783 float underlineThickness)
785 bool foundExtent = false;
786 for(Vector<Extent>::Iterator eIt = extents.Begin(),
787 eEndIt = extents.End();
791 if(Equals(baseLine, eIt->mBaseLine))
794 if(left < eIt->mLeft)
798 if(right > eIt->mRight)
803 if(underlinePosition > eIt->mUnderlinePosition)
805 eIt->mUnderlinePosition = underlinePosition;
807 if(underlineThickness > eIt->mUnderlineThickness)
809 eIt->mUnderlineThickness = underlineThickness;
817 extent.mRight = right;
818 extent.mBaseLine = baseLine;
819 extent.mUnderlinePosition = underlinePosition;
820 extent.mUnderlineThickness = underlineThickness;
821 extent.mMeshRecordIndex = index;
822 extents.PushBack(extent);
826 void CalculateBlocksSize(const Vector<GlyphInfo>& glyphs)
828 for(Vector<GlyphInfo>::ConstIterator glyphIt = glyphs.Begin(),
829 glyphEndIt = glyphs.End();
830 glyphIt != glyphEndIt;
833 const FontId fontId = (*glyphIt).fontId;
834 bool foundFont = false;
836 for(std::vector<MaxBlockSize>::const_iterator blockIt = mBlockSizes.begin(),
837 blockEndIt = mBlockSizes.end();
838 blockIt != blockEndIt;
841 if((*blockIt).mFontId == fontId) // Different size fonts will have a different fontId
843 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Text::AtlasRenderer::CalculateBlocksSize match found fontID(%u) glyphIndex(%u)\n", fontId, (*glyphIt).index);
851 FontMetrics fontMetrics;
852 mFontClient.GetFontMetrics(fontId, fontMetrics);
854 MaxBlockSize maxBlockSize;
855 maxBlockSize.mNeededBlockWidth = static_cast<uint32_t>(fontMetrics.height);
856 maxBlockSize.mNeededBlockHeight = maxBlockSize.mNeededBlockWidth;
857 maxBlockSize.mFontId = fontId;
858 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Text::AtlasRenderer::CalculateBlocksSize New font with no matched blocksize, setting blocksize[%u]\n", maxBlockSize.mNeededBlockWidth);
859 mBlockSizes.push_back(maxBlockSize);
864 void GenerateUnderlines(std::vector<MeshRecord>& meshRecords,
865 Vector<Extent>& extents,
866 const Vector4& underlineColor)
868 AtlasManager::Mesh2D newMesh;
869 unsigned short faceIndex = 0;
870 for(Vector<Extent>::ConstIterator eIt = extents.Begin(),
871 eEndIt = extents.End();
875 AtlasManager::Vertex2D vert;
876 uint32_t index = eIt->mMeshRecordIndex;
877 Vector2 uv = mGlyphManager.GetAtlasSize(meshRecords[index].mAtlasId);
879 // Make sure we don't hit texture edge for single pixel texture ( filled pixel is in top left of every atlas )
880 float u = HALF / uv.x;
881 float v = HALF / uv.y;
882 float thickness = eIt->mUnderlineThickness;
883 float baseLine = eIt->mBaseLine + eIt->mUnderlinePosition - (thickness * HALF);
884 float tlx = eIt->mLeft;
885 float brx = eIt->mRight;
887 vert.mPosition.x = tlx;
888 vert.mPosition.y = baseLine;
889 vert.mTexCoords.x = ZERO;
890 vert.mTexCoords.y = ZERO;
891 vert.mColor = underlineColor;
892 newMesh.mVertices.PushBack(vert);
894 vert.mPosition.x = brx;
895 vert.mPosition.y = baseLine;
896 vert.mTexCoords.x = u;
897 vert.mColor = underlineColor;
898 newMesh.mVertices.PushBack(vert);
900 vert.mPosition.x = tlx;
901 vert.mPosition.y = baseLine + thickness;
902 vert.mTexCoords.x = ZERO;
903 vert.mTexCoords.y = v;
904 vert.mColor = underlineColor;
905 newMesh.mVertices.PushBack(vert);
907 vert.mPosition.x = brx;
908 vert.mPosition.y = baseLine + thickness;
909 vert.mTexCoords.x = u;
910 vert.mColor = underlineColor;
911 newMesh.mVertices.PushBack(vert);
913 // Six indices in counter clockwise winding
914 newMesh.mIndices.PushBack(faceIndex + 1u);
915 newMesh.mIndices.PushBack(faceIndex);
916 newMesh.mIndices.PushBack(faceIndex + 2u);
917 newMesh.mIndices.PushBack(faceIndex + 2u);
918 newMesh.mIndices.PushBack(faceIndex + 3u);
919 newMesh.mIndices.PushBack(faceIndex + 1u);
922 Toolkit::Internal::AtlasMeshFactory::AppendMesh(meshRecords[index].mMesh, newMesh);
926 Actor mActor; ///< The actor parent which renders the text
927 AtlasGlyphManager mGlyphManager; ///< Glyph Manager to handle upload and caching
928 TextAbstraction::FontClient mFontClient; ///< The font client used to supply glyph information
929 Shader mShaderL8; ///< The shader for glyphs and emoji's shadows.
930 Shader mShaderRgba; ///< The shader for emojis.
931 std::vector<MaxBlockSize> mBlockSizes; ///< Maximum size needed to contain a glyph in a block within a new atlas
932 Vector<TextCacheEntry> mTextCache; ///< Caches data from previous render
933 Property::Map mQuadVertexFormat; ///< Describes the vertex format for text
934 int mDepth; ///< DepthIndex passed by control when connect to stage
937 Text::RendererPtr AtlasRenderer::New()
939 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Text::AtlasRenderer::New()\n");
941 return Text::RendererPtr(new AtlasRenderer());
944 Actor AtlasRenderer::Render(Text::ViewInterface& view,
946 Property::Index animatablePropertyIndex,
947 float& alignmentOffset,
950 DALI_LOG_INFO(gLogFilter, Debug::General, "Text::AtlasRenderer::Render()\n");
952 UnparentAndReset(mImpl->mActor);
954 Length numberOfGlyphs = view.GetNumberOfGlyphs();
956 if(numberOfGlyphs > 0u)
958 Vector<GlyphInfo> glyphs;
959 glyphs.Resize(numberOfGlyphs);
961 Vector<Vector2> positions;
962 positions.Resize(numberOfGlyphs);
964 numberOfGlyphs = view.GetGlyphs(glyphs.Begin(),
970 glyphs.Resize(numberOfGlyphs);
971 positions.Resize(numberOfGlyphs);
973 const Vector4* const colorsBuffer = view.GetColors();
974 const ColorIndex* const colorIndicesBuffer = view.GetColorIndices();
975 const Vector4& defaultColor = view.GetTextColor();
977 mImpl->AddGlyphs(view,
979 animatablePropertyIndex,
988 /* In the case where AddGlyphs does not create a renderable Actor for example when glyphs are all whitespace create a new Actor. */
989 /* 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. */
992 mImpl->mActor = Actor::New();
996 return mImpl->mActor;
999 AtlasRenderer::AtlasRenderer()
1004 AtlasRenderer::~AtlasRenderer()
1006 mImpl->RemoveText();