2 * Copyright (c) 2024 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/math/math-utils.h>
26 #include <dali/public-api/rendering/geometry.h>
27 #include <dali/public-api/rendering/renderer.h>
31 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
32 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
33 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
34 #include <dali-toolkit/internal/text/glyph-run.h>
35 #include <dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager.h>
36 #include <dali-toolkit/internal/text/rendering/atlas/atlas-mesh-factory.h>
37 #include <dali-toolkit/internal/text/rendering/styles/strikethrough-helper-functions.h>
38 #include <dali-toolkit/internal/text/rendering/styles/underline-helper-functions.h>
39 #include <dali-toolkit/internal/text/text-view.h>
42 using namespace Dali::Toolkit;
43 using namespace Dali::Toolkit::Text;
47 #if defined(DEBUG_ENABLED)
48 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_RENDERING");
51 const float ZERO(0.0f);
52 const float HALF(0.5f);
53 const float ONE(1.0f);
54 const float ONE_AND_A_HALF(1.5f);
55 const uint32_t DOUBLE_PIXEL_PADDING = 4u; //Padding will be added twice to Atlas
56 const uint16_t NO_OUTLINE = 0u;
59 struct AtlasRenderer::Impl
75 AtlasManager::Mesh2D mMesh;
79 * brief Struct used to generate the underline/striketthrough mesh.
80 * There is one Extent per line of text.
88 mUnderlinePosition(0.0f),
91 mUnderlineChunkId(0u),
92 mStrikethroughPosition(0.0f),
93 mStrikethroughChunkId(0u)
100 float mUnderlinePosition;
101 float mLineThickness;
102 uint32_t mMeshRecordIndex;
103 uint32_t mUnderlineChunkId;
104 float mStrikethroughPosition;
105 uint32_t mStrikethroughChunkId;
112 mNeededBlockWidth(0),
113 mNeededBlockHeight(0)
118 uint32_t mNeededBlockWidth;
119 uint32_t mNeededBlockHeight;
131 Text::GlyphIndex mIndex;
134 struct TextCacheEntry
147 Text::GlyphIndex mIndex;
149 uint16_t mOutlineWidth;
157 mGlyphManager = AtlasGlyphManager::Get();
158 mFontClient = TextAbstraction::FontClient::Get();
160 mQuadVertexFormat["aPosition"] = Property::VECTOR2;
161 mQuadVertexFormat["aTexCoord"] = Property::VECTOR2;
162 mQuadVertexFormat["aColor"] = Property::VECTOR4;
165 void CacheGlyph(const GlyphInfo& glyph, FontId lastFontId, const AtlasGlyphManager::GlyphStyle& style, AtlasManager::AtlasSlot& slot)
167 const Size& defaultTextAtlasSize = mFontClient.GetDefaultTextAtlasSize(); //Retrieve default size of text-atlas-block from font-client.
168 const Size& maximumTextAtlasSize = mFontClient.GetMaximumTextAtlasSize(); //Retrieve maximum size of text-atlas-block from font-client.
170 const bool glyphNotCached = !mGlyphManager.IsCached(glyph.fontId, glyph.index, style, slot); // Check FontGlyphRecord vector for entry with glyph index and fontId
172 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "AddGlyphs fontID[%u] glyphIndex[%u] [%s]\n", glyph.fontId, glyph.index, (glyphNotCached) ? "not cached" : "cached");
176 MaxBlockSize& blockSize = mBlockSizes[0u];
178 if(lastFontId != glyph.fontId)
181 // 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.
182 // CalculateBlocksSize() above ensures a block size entry exists.
183 for(std::vector<MaxBlockSize>::const_iterator it = mBlockSizes.begin(),
184 endIt = mBlockSizes.end();
188 const MaxBlockSize& blockSizeEntry = *it;
189 if(blockSizeEntry.mFontId == glyph.fontId)
191 blockSize = mBlockSizes[index];
196 // Create a new image for the glyph
199 // Whether the glyph is an outline.
200 const bool isOutline = 0u != style.outline;
202 // Whether the current glyph is a color one.
203 const bool isColorGlyph = mFontClient.IsColorGlyph(glyph.fontId, glyph.index);
205 if(!isOutline || (isOutline && !isColorGlyph))
207 // Retrieve the emoji's bitmap.
208 TextAbstraction::GlyphBufferData glyphBufferData;
209 glyphBufferData.width = isColorGlyph ? glyph.width : 0; // Desired width and height.
210 glyphBufferData.height = isColorGlyph ? glyph.height : 0;
212 mFontClient.CreateBitmap(glyph.fontId,
214 glyph.isItalicRequired,
215 glyph.isBoldRequired,
219 uint32_t glyphBufferSize = glyphBufferData.width * glyphBufferData.height * Pixel::GetBytesPerPixel(glyphBufferData.format);
220 // If glyph buffer data don't have ownership, Or if we need to decompress, create new memory and replace ownership.
221 if(!glyphBufferData.isBufferOwned || glyphBufferData.compressionType != TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION)
223 uint8_t* newBuffer = (uint8_t*)malloc(glyphBufferSize);
224 if(DALI_LIKELY(newBuffer != nullptr))
226 TextAbstraction::GlyphBufferData::Decompress(glyphBufferData, newBuffer);
227 if(glyphBufferData.isBufferOwned)
229 // Release previous buffer
230 free(glyphBufferData.buffer);
232 glyphBufferData.isBufferOwned = true;
233 glyphBufferData.buffer = newBuffer;
234 glyphBufferData.compressionType = TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION;
238 // Create the pixel data.
239 bitmap = PixelData::New(glyphBufferData.buffer,
241 glyphBufferData.width,
242 glyphBufferData.height,
243 glyphBufferData.format,
246 // Change buffer ownership.
247 glyphBufferData.isBufferOwned = false;
251 // Ensure that the next image will fit into the current block size
252 if(bitmap.GetWidth() > blockSize.mNeededBlockWidth)
254 blockSize.mNeededBlockWidth = bitmap.GetWidth();
257 if(bitmap.GetHeight() > blockSize.mNeededBlockHeight)
259 blockSize.mNeededBlockHeight = bitmap.GetHeight();
262 // If CheckAtlas in AtlasManager::Add can't fit the bitmap in the current atlas it will create a new atlas
264 // Setting the block size and size of new atlas does not mean a new one will be created. An existing atlas may still surffice.
265 uint32_t default_width = defaultTextAtlasSize.width;
266 uint32_t default_height = defaultTextAtlasSize.height;
269 (blockSize.mNeededBlockWidth >= (default_width - (DOUBLE_PIXEL_PADDING + 1u)) ||
270 blockSize.mNeededBlockHeight >= (default_height - (DOUBLE_PIXEL_PADDING + 1u))) &&
271 (default_width < maximumTextAtlasSize.width &&
272 default_height < maximumTextAtlasSize.height))
274 default_width <<= 1u;
275 default_height <<= 1u;
277 mGlyphManager.SetNewAtlasSize(default_width,
279 blockSize.mNeededBlockWidth,
280 blockSize.mNeededBlockHeight);
282 // Locate a new slot for our glyph
283 mGlyphManager.Add(glyph, style, bitmap, slot); // slot will be 0 is glyph not added
289 // We have 2+ copies of the same glyph
290 mGlyphManager.AdjustReferenceCount(glyph.fontId, glyph.index, style, 1); //increment
294 void GenerateMesh(const GlyphInfo& glyph,
295 const Vector2& position,
296 const Vector4& color,
298 AtlasManager::AtlasSlot& slot,
299 bool decorationlineGlyph,
300 float currentUnderlinePosition,
301 float currentlineThickness,
302 std::vector<MeshRecord>& meshContainer,
303 Vector<TextCacheEntry>& newTextCache,
304 Vector<Extent>& extents,
305 uint32_t underlineChunkId,
307 uint32_t strikethroughChunkId)
309 // Generate mesh data for this quad, plugging in our supplied position
310 AtlasManager::Mesh2D newMesh;
311 mGlyphManager.GenerateMeshData(slot.mImageId, position, newMesh);
315 TextCacheEntry textCacheEntry;
316 textCacheEntry.mFontId = glyph.fontId;
317 textCacheEntry.mImageId = slot.mImageId;
318 textCacheEntry.mIndex = glyph.index;
319 textCacheEntry.mOutlineWidth = outline;
320 textCacheEntry.isItalic = glyph.isItalicRequired;
321 textCacheEntry.isBold = glyph.isBoldRequired;
323 newTextCache.PushBack(textCacheEntry);
326 AtlasManager::Vertex2D* verticesBuffer = newMesh.mVertices.Begin();
328 for(unsigned int index = 0u, size = newMesh.mVertices.Count();
332 AtlasManager::Vertex2D& vertex = *(verticesBuffer + index);
334 // Set the color of the vertex.
335 vertex.mColor = color;
338 // Since Free Type font doesn't contain the strikethrough-position property,
339 // strikethrough position will be calculated by moving the underline position upwards by half the value of the line height.
340 float strikethroughStartingYPosition = (position.y + glyph.yBearing + currentUnderlinePosition) - ((glyph.height) * HALF);
342 // Find an existing mesh data object to attach to ( or create a new one, if we can't find one using the same atlas)
343 StitchTextMesh(meshContainer,
346 position.y + glyph.yBearing,
348 currentUnderlinePosition,
349 currentlineThickness,
352 strikethroughStartingYPosition,
353 strikethroughChunkId);
356 void CreateActors(const std::vector<MeshRecord>& meshContainer,
357 const Size& textSize,
358 const Vector4& color,
359 const Vector4& shadowColor,
360 const Vector2& shadowOffset,
362 Property::Index animatablePropertyIndex,
367 // Create a container actor to act as a common parent for text and shadow, to avoid color inheritence issues.
368 mActor = Actor::New();
369 mActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
370 mActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
371 mActor.SetProperty(Actor::Property::SIZE, textSize);
372 mActor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
375 for(std::vector<MeshRecord>::const_iterator it = meshContainer.begin(),
376 endIt = meshContainer.end();
380 const MeshRecord& meshRecord = *it;
382 Actor actor = CreateMeshActor(textControl, animatablePropertyIndex, color, meshRecord, textSize, STYLE_NORMAL);
384 // Whether the actor has renderers.
385 const bool hasRenderer = actor.GetRendererCount() > 0u;
387 // Create an effect if necessary
391 // Change the color of the vertices.
392 for(Vector<AtlasManager::Vertex2D>::Iterator vIt = meshRecord.mMesh.mVertices.Begin(),
393 vEndIt = meshRecord.mMesh.mVertices.End();
397 AtlasManager::Vertex2D& vertex = *vIt;
399 vertex.mColor = shadowColor;
402 Actor shadowActor = CreateMeshActor(textControl, animatablePropertyIndex, color, meshRecord, textSize, STYLE_DROP_SHADOW);
403 #if defined(DEBUG_ENABLED)
404 shadowActor.SetProperty(Dali::Actor::Property::NAME, "Text Shadow renderable actor");
406 // Offset shadow in x and y
407 shadowActor.RegisterProperty("uOffset", shadowOffset);
408 Dali::Renderer renderer(shadowActor.GetRendererAt(0));
409 int depthIndex = renderer.GetProperty<int>(Dali::Renderer::Property::DEPTH_INDEX);
410 renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, depthIndex - 1);
411 mActor.Add(shadowActor);
421 void AddGlyphs(Text::ViewInterface& view,
423 Property::Index animatablePropertyIndex,
424 const Vector<Vector2>& positions,
425 const Vector<GlyphInfo>& glyphs,
426 const Vector4& defaultColor,
427 const Vector4* const colorsBuffer,
428 const ColorIndex* const colorIndicesBuffer,
432 AtlasManager::AtlasSlot slot;
436 AtlasManager::AtlasSlot slotOutline;
437 slotOutline.mImageId = 0u;
438 slotOutline.mAtlasId = 0u;
440 std::vector<MeshRecord> meshContainer;
441 std::vector<MeshRecord> meshContainerOutline;
442 Vector<Extent> extents;
443 Vector<Extent> strikethroughExtents;
446 const Vector2& textSize(view.GetLayoutSize());
447 const Vector2 halfTextSize(textSize * 0.5f);
448 const Vector2& shadowOffset(view.GetShadowOffset());
449 const Vector4& shadowColor(view.GetShadowColor());
450 const bool underlineEnabled = view.IsUnderlineEnabled();
451 const uint16_t outlineWidth = view.GetOutlineWidth();
452 const Vector4& outlineColor(view.GetOutlineColor());
453 const Vector2& outlineOffset(view.GetOutlineOffset());
454 const bool isOutline = 0u != outlineWidth;
455 const GlyphInfo* hyphens = view.GetHyphens();
456 const Length* hyphenIndices = view.GetHyphenIndices();
457 const Length hyphensCount = view.GetHyphensCount();
458 const bool strikethroughEnabled = view.IsStrikethroughEnabled();
459 const float characterSpacing(view.GetCharacterSpacing());
461 // Elided text info. Indices according to elided text.
462 const auto startIndexOfGlyphs = view.GetStartIndexOfElidedGlyphs();
463 const auto firstMiddleIndexOfElidedGlyphs = view.GetFirstMiddleIndexOfElidedGlyphs();
464 const auto secondMiddleIndexOfElidedGlyphs = view.GetSecondMiddleIndexOfElidedGlyphs();
466 const bool useDefaultColor = (NULL == colorsBuffer);
468 // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
469 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
471 // Get the underline runs.
472 const Length numberOfUnderlineRuns = view.GetNumberOfUnderlineRuns();
473 Vector<UnderlinedGlyphRun> underlineRuns;
474 underlineRuns.Resize(numberOfUnderlineRuns);
475 view.GetUnderlineRuns(underlineRuns.Begin(),
477 numberOfUnderlineRuns);
479 // Aggregate underline-style-properties from view
480 const UnderlineStyleProperties viewUnderlineProperties{view.GetUnderlineType(),
481 view.GetUnderlineColor(),
482 view.GetUnderlineHeight(),
483 view.GetDashedUnderlineGap(),
484 view.GetDashedUnderlineWidth(),
491 float maxUnderlineHeight = viewUnderlineProperties.height;
493 // Get the strikethrough runs.
494 const Length numberOfStrikethroughRuns = view.GetNumberOfStrikethroughRuns();
495 Vector<StrikethroughGlyphRun> strikethroughRuns;
496 strikethroughRuns.Resize(numberOfStrikethroughRuns);
497 view.GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
499 const StrikethroughStyleProperties viewStrikethroughProperties{view.GetStrikethroughColor(),
500 view.GetStrikethroughHeight(),
504 float maxStrikethroughHeight = viewStrikethroughProperties.height;
506 FontId lastFontId = 0;
507 Style style = STYLE_NORMAL;
508 float currentUnderlinePosition = ZERO;
509 bool thereAreUnderlinedGlyphs = false;
510 bool thereAreStrikethroughGlyphs = false;
512 if(fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1)
514 style = STYLE_DROP_SHADOW;
517 CalculateBlocksSize(glyphs);
519 // Avoid emptying mTextCache (& removing references) until after incremented references for the new text
520 Vector<TextCacheEntry> newTextCache;
521 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
522 const Vector2* const positionsBuffer = positions.Begin();
523 const Vector2 lineOffsetPosition(minLineOffset, 0.f);
524 uint32_t hyphenIndex = 0;
526 //For septated underlined chunks. (this is for Markup case)
527 uint32_t underlineChunkId = 0u; // give id for each chunk.
528 bool isPreUnderlined = false; // status of underlined for previous glyph.
529 std::map<uint32_t, UnderlineStyleProperties> mapUnderlineChunkIdWithProperties; // mapping underlineChunkId with UnderlineStyleProperties to get properties of underlined chunk
530 UnderlineStyleProperties preUnderlineProperties = viewUnderlineProperties; // the previous UnderlineStyleProperties
532 //For septated strikethrough chunks. (this is for Markup case)
533 uint32_t strikethroughChunkId = 0u; // give id for each chunk.
534 bool isPreStrikethrough = false; // status of strikethrough for previous glyph.
535 std::map<uint32_t, StrikethroughStyleProperties> mapStrikethroughChunkIdWithProperties; // mapping strikethroughChunkId with StrikethroughStyleProperties to get properties of strikethrough chunk
536 StrikethroughStyleProperties preStrikethroughProperties = viewStrikethroughProperties; // the previous StrikethroughStyleProperties
538 const Character* textBuffer = view.GetTextBuffer();
539 float calculatedAdvance = 0.f;
540 const Vector<CharacterIndex>& glyphToCharacterMap = view.GetGlyphsToCharacters();
541 const CharacterIndex* glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
543 //Skip hyphenIndices less than startIndexOfGlyphs or between two middle of elided text
546 while((hyphenIndex < hyphensCount) && (hyphenIndices[hyphenIndex] < startIndexOfGlyphs ||
547 (hyphenIndices[hyphenIndex] > firstMiddleIndexOfElidedGlyphs && hyphenIndices[hyphenIndex] < secondMiddleIndexOfElidedGlyphs)))
553 //To keep the last fontMetrics of lastDecorativeLinesFontId
554 FontId lastDecorativeLinesFontId = 0; // DecorativeLines like Undeline and Strikethrough
555 FontMetrics lastDecorativeLinesFontMetrics;
556 fontClient.GetFontMetrics(lastDecorativeLinesFontId, lastDecorativeLinesFontMetrics);
558 // Iteration on glyphs
559 for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
562 bool addHyphen = ((hyphenIndex < hyphensCount) && hyphenIndices && ((i + startIndexOfGlyphs) == hyphenIndices[hyphenIndex]));
563 // TODO : Shouldn't we have to control here when i == 0 cases?
564 if(addHyphen && hyphens && i > 0u)
566 glyph = hyphens[hyphenIndex];
571 glyph = *(glyphsBuffer + i);
574 Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
575 const bool isGlyphUnderlined = underlineEnabled || IsGlyphUnderlined(i, underlineRuns, currentUnderlinedGlyphRunIt);
576 const UnderlineStyleProperties currentUnderlineProperties = GetCurrentUnderlineProperties(i, isGlyphUnderlined, underlineRuns, currentUnderlinedGlyphRunIt, viewUnderlineProperties);
577 float currentUnderlineHeight = currentUnderlineProperties.height;
578 thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || isGlyphUnderlined;
580 Vector<StrikethroughGlyphRun>::ConstIterator currentStrikethroughGlyphRunIt = strikethroughRuns.End();
581 const bool isGlyphStrikethrough = strikethroughEnabled || IsGlyphStrikethrough(i, strikethroughRuns, currentStrikethroughGlyphRunIt);
582 const StrikethroughStyleProperties currentStrikethroughProperties = GetCurrentStrikethroughProperties(i, isGlyphStrikethrough, strikethroughRuns, currentStrikethroughGlyphRunIt, viewStrikethroughProperties);
583 float currentStrikethroughHeight = currentStrikethroughProperties.height;
584 thereAreStrikethroughGlyphs = thereAreStrikethroughGlyphs || isGlyphStrikethrough;
586 // No operation for white space
587 if(!Dali::EqualsZero(glyph.width) && !Dali::EqualsZero(glyph.height))
589 // Check and update decorative-lines informations
590 if(isGlyphUnderlined || isGlyphStrikethrough)
592 bool isDecorativeLinesFontIdUpdated = false;
593 // Are we still using the same fontId as previous
594 if(glyph.fontId != lastDecorativeLinesFontId)
596 // We need to fetch fresh font metrics
597 lastDecorativeLinesFontId = glyph.fontId;
598 isDecorativeLinesFontIdUpdated = true;
599 fontClient.GetFontMetrics(lastDecorativeLinesFontId, lastDecorativeLinesFontMetrics);
601 if(isGlyphStrikethrough || isGlyphUnderlined)
603 //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
604 currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(lastDecorativeLinesFontMetrics);
608 if(isGlyphUnderlined && (isDecorativeLinesFontIdUpdated || !(currentUnderlineProperties.IsHeightEqualTo(preUnderlineProperties))))
610 //If the Underline Height is changed then we need to recalculate height.
611 if(!(currentUnderlineProperties.IsHeightEqualTo(preUnderlineProperties)))
613 maxUnderlineHeight = currentUnderlineHeight;
616 CalcualteUnderlineHeight(lastDecorativeLinesFontMetrics, currentUnderlineHeight, maxUnderlineHeight);
619 if(isGlyphStrikethrough && (isDecorativeLinesFontIdUpdated || !(currentStrikethroughProperties.IsHeightEqualTo(preStrikethroughProperties))))
621 //If the Strikethrough Height is changed then we need to recalculate height.
622 if(!(currentStrikethroughProperties.IsHeightEqualTo(preStrikethroughProperties)))
624 maxStrikethroughHeight = currentStrikethroughHeight;
627 CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
629 } // decorative-lines
631 AtlasGlyphManager::GlyphStyle style;
632 style.isItalic = glyph.isItalicRequired;
633 style.isBold = glyph.isBoldRequired;
635 // Retrieves and caches the glyph's bitmap.
636 CacheGlyph(glyph, lastFontId, style, slot);
638 // Retrieves and caches the outline glyph's bitmap.
641 style.outline = outlineWidth;
642 CacheGlyph(glyph, lastFontId, style, slotOutline);
645 // Move the origin (0,0) of the mesh to the center of the actor
646 Vector2 position = *(positionsBuffer + i);
650 GlyphInfo tempInfo = *(glyphsBuffer + i);
651 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + i))), characterSpacing, tempInfo.advance);
652 position.x = position.x + calculatedAdvance - tempInfo.xBearing + glyph.xBearing;
653 position.y += tempInfo.yBearing - glyph.yBearing;
656 position = Vector2(roundf(position.x), position.y) - halfTextSize - lineOffsetPosition; // roundf() avoids pixel alignment issues.
658 if(0u != slot.mImageId) // invalid slot id, glyph has failed to be added to atlas
660 Vector2 positionPlusOutlineOffset = position + outlineOffset;
663 // Add an offset to the text.
664 const float outlineWidthOffset = static_cast<float>(outlineWidth);
665 positionPlusOutlineOffset += Vector2(outlineWidthOffset, outlineWidthOffset);
668 // Get the color of the character.
669 const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndicesBuffer + i);
670 const Vector4& color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + colorIndex - 1u);
672 //The new underlined chunk. Add new id if they are not consecutive indices (this is for Markup case)
673 // Examples: "Hello <u>World</u> Hello <u>World</u>", "<u>World</u> Hello <u>World</u>", "<u> World</u> Hello <u>World</u>"
674 if((!isPreUnderlined && isGlyphUnderlined) || (isGlyphUnderlined && (preUnderlineProperties != currentUnderlineProperties)))
677 mapUnderlineChunkIdWithProperties.insert(std::pair<uint32_t, UnderlineStyleProperties>(underlineChunkId, currentUnderlineProperties));
680 //Keep status of underlined for previous glyph to check consecutive indices
681 isPreUnderlined = isGlyphUnderlined;
682 preUnderlineProperties = currentUnderlineProperties;
685 positionPlusOutlineOffset,
690 currentUnderlinePosition,
699 if(isGlyphStrikethrough)
701 //The new strikethrough chunk. Add new id if they are not consecutive indices (this is for Markup case)
702 // Examples: "Hello <s>World</s> Hello <s>World</s>", "<s>World</s> Hello <s>World</s>", "<s> World</s> Hello <s>World</s>"
703 if((!isPreStrikethrough) || (preStrikethroughProperties != currentStrikethroughProperties))
705 strikethroughChunkId++;
706 mapStrikethroughChunkIdWithProperties.insert(std::pair<uint32_t, StrikethroughStyleProperties>(strikethroughChunkId, currentStrikethroughProperties));
710 positionPlusOutlineOffset,
714 isGlyphStrikethrough,
716 maxStrikethroughHeight,
719 strikethroughExtents,
722 strikethroughChunkId);
725 //Keep status of Strikethrough for previous glyph to check consecutive indices
726 isPreStrikethrough = isGlyphStrikethrough;
727 preStrikethroughProperties = currentStrikethroughProperties;
729 lastFontId = glyph.fontId; // Prevents searching for existing blocksizes when string of the same fontId.
732 if(isOutline && (0u != slotOutline.mImageId)) // invalid slot id, glyph has failed to be added to atlas
740 currentUnderlinePosition,
742 meshContainerOutline,
757 // Now remove references for the old text
759 mTextCache.Swap(newTextCache);
761 if(thereAreUnderlinedGlyphs)
763 // Check to see if any of the text needs an underline
764 GenerateUnderlines(meshContainer, extents, viewUnderlineProperties, mapUnderlineChunkIdWithProperties);
767 if(thereAreStrikethroughGlyphs)
769 // Check to see if any of the text needs a strikethrough
770 GenerateStrikethrough(meshContainer, strikethroughExtents, viewStrikethroughProperties, mapStrikethroughChunkIdWithProperties);
773 // For each MeshData object, create a mesh actor and add to the renderable actor
774 bool isShadowDrawn = false;
775 if(!meshContainerOutline.empty())
777 const bool drawShadow = STYLE_DROP_SHADOW == style;
778 CreateActors(meshContainerOutline,
784 animatablePropertyIndex,
787 isShadowDrawn = drawShadow;
790 // For each MeshData object, create a mesh actor and add to the renderable actor
791 if(!meshContainer.empty())
793 const bool drawShadow = !isShadowDrawn && (STYLE_DROP_SHADOW == style);
794 CreateActors(meshContainer,
800 animatablePropertyIndex,
804 #if defined(DEBUG_ENABLED)
805 Toolkit::AtlasGlyphManager::Metrics metrics = mGlyphManager.GetMetrics();
806 DALI_LOG_INFO(gLogFilter, Debug::General, "TextAtlasRenderer::GlyphManager::GlyphCount: %i, AtlasCount: %i, TextureMemoryUse: %iK\n", metrics.mGlyphCount, metrics.mAtlasMetrics.mAtlasCount, metrics.mAtlasMetrics.mTextureMemoryUsed / 1024);
808 if(gLogFilter->IsEnabledFor(Debug::Verbose))
810 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "%s\n", metrics.mVerboseGlyphCounts.c_str());
812 for(uint32_t i = 0; i < metrics.mAtlasMetrics.mAtlasCount; ++i)
814 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);
822 for(Vector<TextCacheEntry>::Iterator oldTextIter = mTextCache.Begin(); oldTextIter != mTextCache.End(); ++oldTextIter)
824 AtlasGlyphManager::GlyphStyle style;
825 style.outline = oldTextIter->mOutlineWidth;
826 style.isItalic = oldTextIter->isItalic;
827 style.isBold = oldTextIter->isBold;
828 mGlyphManager.AdjustReferenceCount(oldTextIter->mFontId, oldTextIter->mIndex, style, -1 /*decrement*/);
830 mTextCache.Resize(0);
833 Actor CreateMeshActor(Actor textControl, Property::Index animatablePropertyIndex, const Vector4& defaultColor, const MeshRecord& meshRecord, const Vector2& actorSize, Style style)
835 VertexBuffer quadVertices = VertexBuffer::New(mQuadVertexFormat);
836 quadVertices.SetData(const_cast<AtlasManager::Vertex2D*>(&meshRecord.mMesh.mVertices[0]), meshRecord.mMesh.mVertices.Size());
838 Geometry quadGeometry = Geometry::New();
839 quadGeometry.AddVertexBuffer(quadVertices);
840 quadGeometry.SetIndexBuffer(&meshRecord.mMesh.mIndices[0], meshRecord.mMesh.mIndices.Size());
842 TextureSet textureSet(mGlyphManager.GetTextures(meshRecord.mAtlasId));
844 // Choose the shader to use.
845 const bool isColorShader = (STYLE_DROP_SHADOW != style) && (Pixel::BGRA8888 == mGlyphManager.GetPixelFormat(meshRecord.mAtlasId));
849 // The glyph is an emoji and is not a shadow.
852 mShaderRgba = Shader::New(SHADER_TEXT_ATLAS_SHADER_VERT, SHADER_TEXT_ATLAS_RGBA_SHADER_FRAG, Shader::Hint::NONE, "TEXT_ATLAS_RGBA");
854 shader = mShaderRgba;
858 // The glyph is text or a shadow.
861 mShaderL8 = Shader::New(SHADER_TEXT_ATLAS_SHADER_VERT, SHADER_TEXT_ATLAS_L8_SHADER_FRAG, Shader::Hint::NONE, "TEXT_ATLAS_L8");
866 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "defaultColor[%f, %f, %f, %f ]\n", defaultColor.r, defaultColor.g, defaultColor.b, defaultColor.a);
868 Dali::Property::Index shaderTextColorIndex = shader.RegisterProperty("textColorAnimatable", defaultColor);
870 if(animatablePropertyIndex != Property::INVALID_INDEX)
872 // create constraint for the animatable text's color Property with textColorAnimatable in the shader.
873 if(shaderTextColorIndex)
875 Constraint constraint = Constraint::New<Vector4>(shader, shaderTextColorIndex, EqualToConstraint());
876 constraint.AddSource(Source(textControl, animatablePropertyIndex));
882 // If not animating the text colour then set to 1's so shader uses the current vertex color
883 shader.RegisterProperty("textColorAnimatable", Vector4(1.0, 1.0, 1.0, 1.0));
886 Dali::Renderer renderer = Dali::Renderer::New(quadGeometry, shader);
887 renderer.SetTextures(textureSet);
888 renderer.SetProperty(Dali::Renderer::Property::BLEND_MODE, BlendMode::ON);
889 renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT + mDepth);
891 Actor actor = Actor::New();
892 #if defined(DEBUG_ENABLED)
893 actor.SetProperty(Dali::Actor::Property::NAME, "Text renderable actor");
895 actor.AddRenderer(renderer);
896 // Keep all of the origins aligned
897 actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
898 actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
899 actor.SetProperty(Actor::Property::SIZE, actorSize);
900 actor.RegisterProperty("uOffset", Vector2::ZERO);
901 actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
906 void StitchTextMesh(std::vector<MeshRecord>& meshContainer,
907 AtlasManager::Mesh2D& newMesh,
908 Vector<Extent>& extents,
910 bool decorationlineGlyph,
911 float underlinePosition,
913 AtlasManager::AtlasSlot& slot,
914 uint32_t underlineChunkId,
915 float strikethroughPosition,
916 uint32_t strikethroughChunkId)
920 float left = newMesh.mVertices[0].mPosition.x;
921 float right = newMesh.mVertices[1].mPosition.x;
923 // Check to see if there's a mesh data object that references the same atlas ?
925 for(std::vector<MeshRecord>::iterator mIt = meshContainer.begin(),
926 mEndIt = meshContainer.end();
930 if(slot.mAtlasId == mIt->mAtlasId)
932 // Append the mesh to the existing mesh and adjust any extents
933 Toolkit::Internal::AtlasMeshFactory::AppendMesh(mIt->mMesh, newMesh);
935 if(decorationlineGlyph)
937 AdjustExtents(extents,
946 strikethroughPosition,
947 strikethroughChunkId);
954 // No mesh data object currently exists that references this atlas, so create a new one
955 MeshRecord meshRecord;
956 meshRecord.mAtlasId = slot.mAtlasId;
957 meshRecord.mMesh = newMesh;
958 meshContainer.push_back(meshRecord);
960 if(decorationlineGlyph)
962 // Adjust extents for this new meshrecord
963 AdjustExtents(extents,
965 meshContainer.size() - 1u,
972 strikethroughPosition,
973 strikethroughChunkId);
978 void AdjustExtents(Vector<Extent>& extents,
979 std::vector<MeshRecord>& meshRecords,
984 float underlinePosition,
986 uint32_t underlineChunkId,
987 float strikethroughPosition,
988 uint32_t strikethroughChunkId)
990 bool foundExtent = false;
991 for(Vector<Extent>::Iterator eIt = extents.Begin(),
992 eEndIt = extents.End();
996 if(Equals(baseLine, eIt->mBaseLine) && underlineChunkId == eIt->mUnderlineChunkId && strikethroughChunkId == eIt->mStrikethroughChunkId)
999 if(left < eIt->mLeft)
1003 if(right > eIt->mRight)
1005 eIt->mRight = right;
1008 if(underlinePosition > eIt->mUnderlinePosition)
1010 eIt->mUnderlinePosition = underlinePosition;
1012 if(lineThickness > eIt->mLineThickness)
1014 eIt->mLineThickness = lineThickness;
1021 extent.mLeft = left;
1022 extent.mRight = right;
1023 extent.mBaseLine = baseLine;
1024 extent.mUnderlinePosition = underlinePosition;
1025 extent.mMeshRecordIndex = index;
1026 extent.mUnderlineChunkId = underlineChunkId;
1027 extent.mLineThickness = lineThickness;
1028 extent.mStrikethroughPosition = strikethroughPosition;
1029 extent.mStrikethroughChunkId = strikethroughChunkId;
1030 extents.PushBack(extent);
1034 void CalculateBlocksSize(const Vector<GlyphInfo>& glyphs)
1036 for(Vector<GlyphInfo>::ConstIterator glyphIt = glyphs.Begin(),
1037 glyphEndIt = glyphs.End();
1038 glyphIt != glyphEndIt;
1041 const FontId fontId = (*glyphIt).fontId;
1042 bool foundFont = false;
1044 for(std::vector<MaxBlockSize>::const_iterator blockIt = mBlockSizes.begin(),
1045 blockEndIt = mBlockSizes.end();
1046 blockIt != blockEndIt;
1049 if((*blockIt).mFontId == fontId) // Different size fonts will have a different fontId
1051 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Text::AtlasRenderer::CalculateBlocksSize match found fontID(%u) glyphIndex(%u)\n", fontId, (*glyphIt).index);
1059 FontMetrics fontMetrics;
1060 mFontClient.GetFontMetrics(fontId, fontMetrics);
1062 MaxBlockSize maxBlockSize;
1063 maxBlockSize.mNeededBlockWidth = static_cast<uint32_t>(fontMetrics.height);
1064 maxBlockSize.mNeededBlockHeight = maxBlockSize.mNeededBlockWidth;
1065 maxBlockSize.mFontId = fontId;
1066 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Text::AtlasRenderer::CalculateBlocksSize New font with no matched blocksize, setting blocksize[%u]\n", maxBlockSize.mNeededBlockWidth);
1067 mBlockSizes.push_back(maxBlockSize);
1072 void GenerateUnderlines(std::vector<MeshRecord>& meshRecords,
1073 Vector<Extent>& extents,
1074 const UnderlineStyleProperties& viewUnderlineProperties,
1075 const std::map<uint32_t, UnderlineStyleProperties>& mapUnderlineChunkIdWithProperties)
1077 AtlasManager::Mesh2D newMesh;
1078 unsigned short faceIndex = 0;
1080 for(Vector<Extent>::ConstIterator eIt = extents.Begin(),
1081 eEndIt = extents.End();
1085 AtlasManager::Vertex2D vert;
1086 uint32_t index = eIt->mMeshRecordIndex;
1087 Vector2 uv = mGlyphManager.GetAtlasSize(meshRecords[index].mAtlasId);
1089 auto pairUnderlineChunkIdWithProperties = mapUnderlineChunkIdWithProperties.find(eIt->mUnderlineChunkId);
1091 const UnderlineStyleProperties underlineProperties = (pairUnderlineChunkIdWithProperties == mapUnderlineChunkIdWithProperties.end())
1092 ? viewUnderlineProperties
1093 : pairUnderlineChunkIdWithProperties->second;
1095 const Vector4& underlineColor = underlineProperties.colorDefined ? underlineProperties.color : viewUnderlineProperties.color;
1096 const Text::Underline::Type& underlineType = underlineProperties.typeDefined ? underlineProperties.type : viewUnderlineProperties.type;
1097 const float& dashedUnderlineGap = underlineProperties.dashGapDefined ? underlineProperties.dashGap : viewUnderlineProperties.dashGap;
1098 const float& dashedUnderlineWidth = underlineProperties.dashWidthDefined ? underlineProperties.dashWidth : viewUnderlineProperties.dashWidth;
1100 // Make sure we don't hit texture edge for single pixel texture ( filled pixel is in top left of every atlas )
1101 float u = HALF / uv.x;
1102 float v = HALF / uv.y;
1103 float thickness = eIt->mLineThickness;
1104 float ShiftLineBy = (underlineType == Text::Underline::Type::DOUBLE) ? (thickness * ONE_AND_A_HALF) : (thickness * HALF);
1105 float baseLine = eIt->mBaseLine + eIt->mUnderlinePosition - ShiftLineBy;
1106 float tlx = eIt->mLeft;
1107 float brx = eIt->mRight;
1109 if(underlineType == Text::Underline::Type::DASHED)
1111 float dashTlx = tlx;
1112 float dashBrx = tlx;
1114 while((dashTlx >= tlx) && (dashTlx < brx) && ((dashTlx + dashedUnderlineWidth) <= brx))
1116 dashBrx = dashTlx + dashedUnderlineWidth;
1118 //The top left edge of the underline
1119 vert.mPosition.x = dashTlx;
1120 vert.mPosition.y = baseLine;
1121 vert.mTexCoords.x = ZERO;
1122 vert.mTexCoords.y = ZERO;
1123 vert.mColor = underlineColor;
1124 newMesh.mVertices.PushBack(vert);
1126 //The top right edge of the underline
1127 vert.mPosition.x = dashBrx;
1128 vert.mPosition.y = baseLine;
1129 vert.mTexCoords.x = u;
1130 vert.mColor = underlineColor;
1131 newMesh.mVertices.PushBack(vert);
1133 //The bottom left edge of the underline
1134 vert.mPosition.x = dashTlx;
1135 vert.mPosition.y = baseLine + thickness;
1136 vert.mTexCoords.x = ZERO;
1137 vert.mTexCoords.y = v;
1138 vert.mColor = underlineColor;
1139 newMesh.mVertices.PushBack(vert);
1141 //The bottom right edge of the underline
1142 vert.mPosition.x = dashBrx;
1143 vert.mPosition.y = baseLine + thickness;
1144 vert.mTexCoords.x = u;
1145 vert.mColor = underlineColor;
1146 newMesh.mVertices.PushBack(vert);
1148 dashTlx = dashBrx + dashedUnderlineGap; // The next dash will start at the right of the current dash plus the gap
1150 // Six indices in counter clockwise winding
1151 newMesh.mIndices.PushBack(faceIndex + 1u);
1152 newMesh.mIndices.PushBack(faceIndex);
1153 newMesh.mIndices.PushBack(faceIndex + 2u);
1154 newMesh.mIndices.PushBack(faceIndex + 2u);
1155 newMesh.mIndices.PushBack(faceIndex + 3u);
1156 newMesh.mIndices.PushBack(faceIndex + 1u);
1160 Toolkit::Internal::AtlasMeshFactory::AppendMesh(meshRecords[index].mMesh, newMesh);
1165 // It's either SOLID or DOUBLE so we need to generate the first solid underline anyway.
1166 vert.mPosition.x = tlx;
1167 vert.mPosition.y = baseLine;
1168 vert.mTexCoords.x = ZERO;
1169 vert.mTexCoords.y = ZERO;
1170 vert.mColor = underlineColor;
1171 newMesh.mVertices.PushBack(vert);
1173 vert.mPosition.x = brx;
1174 vert.mPosition.y = baseLine;
1175 vert.mTexCoords.x = u;
1176 vert.mColor = underlineColor;
1177 newMesh.mVertices.PushBack(vert);
1179 vert.mPosition.x = tlx;
1180 vert.mPosition.y = baseLine + thickness;
1181 vert.mTexCoords.x = ZERO;
1182 vert.mTexCoords.y = v;
1183 vert.mColor = underlineColor;
1184 newMesh.mVertices.PushBack(vert);
1186 vert.mPosition.x = brx;
1187 vert.mPosition.y = baseLine + thickness;
1188 vert.mTexCoords.x = u;
1189 vert.mColor = underlineColor;
1190 newMesh.mVertices.PushBack(vert);
1192 // Six indices in counter clockwise winding
1193 newMesh.mIndices.PushBack(faceIndex + 1u);
1194 newMesh.mIndices.PushBack(faceIndex);
1195 newMesh.mIndices.PushBack(faceIndex + 2u);
1196 newMesh.mIndices.PushBack(faceIndex + 2u);
1197 newMesh.mIndices.PushBack(faceIndex + 3u);
1198 newMesh.mIndices.PushBack(faceIndex + 1u);
1201 Toolkit::Internal::AtlasMeshFactory::AppendMesh(meshRecords[index].mMesh, newMesh);
1203 if(underlineType == Text::Underline::Type::DOUBLE)
1205 baseLine += 2 * thickness;
1207 //The top left edge of the underline
1208 vert.mPosition.x = tlx;
1209 vert.mPosition.y = baseLine; // Vertical start of the second underline
1210 vert.mTexCoords.x = ZERO;
1211 vert.mTexCoords.y = ZERO;
1212 vert.mColor = underlineColor;
1213 newMesh.mVertices.PushBack(vert);
1215 //The top right edge of the underline
1216 vert.mPosition.x = brx;
1217 vert.mPosition.y = baseLine;
1218 vert.mTexCoords.x = u;
1219 vert.mColor = underlineColor;
1220 newMesh.mVertices.PushBack(vert);
1222 //The bottom left edge of the underline
1223 vert.mPosition.x = tlx;
1224 vert.mPosition.y = baseLine + thickness; // Vertical End of the second underline
1225 vert.mTexCoords.x = ZERO;
1226 vert.mTexCoords.y = v;
1227 vert.mColor = underlineColor;
1228 newMesh.mVertices.PushBack(vert);
1230 //The bottom right edge of the underline
1231 vert.mPosition.x = brx;
1232 vert.mPosition.y = baseLine + thickness;
1233 vert.mTexCoords.x = u;
1234 vert.mColor = underlineColor;
1235 newMesh.mVertices.PushBack(vert);
1237 // Six indices in counter clockwise winding
1238 newMesh.mIndices.PushBack(faceIndex + 1u);
1239 newMesh.mIndices.PushBack(faceIndex);
1240 newMesh.mIndices.PushBack(faceIndex + 2u);
1241 newMesh.mIndices.PushBack(faceIndex + 2u);
1242 newMesh.mIndices.PushBack(faceIndex + 3u);
1243 newMesh.mIndices.PushBack(faceIndex + 1u);
1247 Toolkit::Internal::AtlasMeshFactory::AppendMesh(meshRecords[index].mMesh, newMesh);
1253 void GenerateStrikethrough(std::vector<MeshRecord>& meshRecords,
1254 Vector<Extent>& extents,
1255 const StrikethroughStyleProperties& viewStrikethroughProperties,
1256 const std::map<uint32_t, StrikethroughStyleProperties>& mapStrikethroughChunkIdWithProperties)
1258 AtlasManager::Mesh2D newMesh;
1259 unsigned short faceIndex = 0;
1260 for(Vector<Extent>::ConstIterator eIt = extents.Begin(),
1261 eEndIt = extents.End();
1265 AtlasManager::Vertex2D vert;
1266 uint32_t index = eIt->mMeshRecordIndex;
1267 Vector2 uv = mGlyphManager.GetAtlasSize(meshRecords[index].mAtlasId);
1269 auto pairStrikethroughChunkIdWithProperties = mapStrikethroughChunkIdWithProperties.find(eIt->mStrikethroughChunkId);
1271 const StrikethroughStyleProperties strikethroughProperties = (pairStrikethroughChunkIdWithProperties == mapStrikethroughChunkIdWithProperties.end())
1272 ? viewStrikethroughProperties
1273 : pairStrikethroughChunkIdWithProperties->second;
1275 const Vector4& strikethroughColor = strikethroughProperties.colorDefined ? strikethroughProperties.color : viewStrikethroughProperties.color;
1277 // Make sure we don't hit texture edge for single pixel texture ( filled pixel is in top left of every atlas )
1278 float u = HALF / uv.x;
1279 float v = HALF / uv.y;
1280 float thickness = eIt->mLineThickness;
1281 float tlx = eIt->mLeft;
1282 float brx = eIt->mRight;
1283 float strikethroughPosition = eIt->mStrikethroughPosition;
1285 vert.mPosition.x = tlx;
1286 vert.mPosition.y = strikethroughPosition;
1287 vert.mTexCoords.x = ZERO;
1288 vert.mTexCoords.y = ZERO;
1289 vert.mColor = strikethroughColor;
1290 newMesh.mVertices.PushBack(vert);
1292 vert.mPosition.x = brx;
1293 vert.mPosition.y = strikethroughPosition;
1294 vert.mTexCoords.x = u;
1295 vert.mColor = strikethroughColor;
1296 newMesh.mVertices.PushBack(vert);
1298 vert.mPosition.x = tlx;
1299 vert.mPosition.y = strikethroughPosition + thickness;
1300 vert.mTexCoords.x = ZERO;
1301 vert.mTexCoords.y = v;
1302 vert.mColor = strikethroughColor;
1303 newMesh.mVertices.PushBack(vert);
1305 vert.mPosition.x = brx;
1306 vert.mPosition.y = strikethroughPosition + thickness;
1307 vert.mTexCoords.x = u;
1308 vert.mColor = strikethroughColor;
1309 newMesh.mVertices.PushBack(vert);
1311 // Six indices in counter clockwise winding
1312 newMesh.mIndices.PushBack(faceIndex + 1u);
1313 newMesh.mIndices.PushBack(faceIndex);
1314 newMesh.mIndices.PushBack(faceIndex + 2u);
1315 newMesh.mIndices.PushBack(faceIndex + 2u);
1316 newMesh.mIndices.PushBack(faceIndex + 3u);
1317 newMesh.mIndices.PushBack(faceIndex + 1u);
1320 Toolkit::Internal::AtlasMeshFactory::AppendMesh(meshRecords[index].mMesh, newMesh);
1324 Actor mActor; ///< The actor parent which renders the text
1325 AtlasGlyphManager mGlyphManager; ///< Glyph Manager to handle upload and caching
1326 TextAbstraction::FontClient mFontClient; ///< The font client used to supply glyph information
1327 Shader mShaderL8; ///< The shader for glyphs and emoji's shadows.
1328 Shader mShaderRgba; ///< The shader for emojis.
1329 std::vector<MaxBlockSize> mBlockSizes; ///< Maximum size needed to contain a glyph in a block within a new atlas
1330 Vector<TextCacheEntry> mTextCache; ///< Caches data from previous render
1331 Property::Map mQuadVertexFormat; ///< Describes the vertex format for text
1332 int mDepth; ///< DepthIndex passed by control when connect to stage
1335 Text::RendererPtr AtlasRenderer::New()
1337 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Text::AtlasRenderer::New()\n");
1339 return Text::RendererPtr(new AtlasRenderer());
1342 Actor AtlasRenderer::Render(Text::ViewInterface& view,
1344 Property::Index animatablePropertyIndex,
1345 float& alignmentOffset,
1348 DALI_LOG_INFO(gLogFilter, Debug::General, "Text::AtlasRenderer::Render()\n");
1350 UnparentAndReset(mImpl->mActor);
1352 Length numberOfGlyphs = view.GetNumberOfGlyphs();
1354 if(numberOfGlyphs > 0u)
1356 Vector<GlyphInfo> glyphs;
1357 glyphs.Resize(numberOfGlyphs);
1359 Vector<Vector2> positions;
1360 positions.Resize(numberOfGlyphs);
1362 numberOfGlyphs = view.GetGlyphs(glyphs.Begin(),
1368 glyphs.Resize(numberOfGlyphs);
1369 positions.Resize(numberOfGlyphs);
1371 const Vector4* const colorsBuffer = view.GetColors();
1372 const ColorIndex* const colorIndicesBuffer = view.GetColorIndices();
1373 const Vector4& defaultColor = view.GetTextColor();
1375 mImpl->AddGlyphs(view,
1377 animatablePropertyIndex,
1386 /* In the case where AddGlyphs does not create a renderable Actor for example when glyphs are all whitespace create a new Actor. */
1387 /* 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. */
1390 mImpl->mActor = Actor::New();
1394 return mImpl->mActor;
1397 AtlasRenderer::AtlasRenderer()
1402 AtlasRenderer::~AtlasRenderer()
1404 mImpl->RemoveText();