[Tizen] Support asan build option
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / rendering / atlas / text-atlas-renderer.cpp
1 /*
2  * Copyright (c) 2024 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.h>
20
21 // EXTERNAL INCLUDES
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>
28 #include <map>
29
30 // INTERNAL INCLUDES
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>
40
41 using namespace Dali;
42 using namespace Dali::Toolkit;
43 using namespace Dali::Toolkit::Text;
44
45 namespace
46 {
47 #if defined(DEBUG_ENABLED)
48 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_RENDERING");
49 #endif
50
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;
57 } // namespace
58
59 struct AtlasRenderer::Impl
60 {
61   enum Style
62   {
63     STYLE_NORMAL,
64     STYLE_DROP_SHADOW
65   };
66
67   struct MeshRecord
68   {
69     MeshRecord()
70     : mAtlasId(0u)
71     {
72     }
73
74     uint32_t             mAtlasId;
75     AtlasManager::Mesh2D mMesh;
76   };
77
78   /**
79    * brief Struct used to generate the underline/striketthrough mesh.
80    * There is one Extent per line of text.
81    */
82   struct Extent
83   {
84     Extent()
85     : mBaseLine(0.0f),
86       mLeft(0.0f),
87       mRight(0.0f),
88       mUnderlinePosition(0.0f),
89       mLineThickness(0.0f),
90       mMeshRecordIndex(0u),
91       mUnderlineChunkId(0u),
92       mStrikethroughPosition(0.0f),
93       mStrikethroughChunkId(0u)
94     {
95     }
96
97     float    mBaseLine;
98     float    mLeft;
99     float    mRight;
100     float    mUnderlinePosition;
101     float    mLineThickness;
102     uint32_t mMeshRecordIndex;
103     uint32_t mUnderlineChunkId;
104     float    mStrikethroughPosition;
105     uint32_t mStrikethroughChunkId;
106   };
107
108   struct MaxBlockSize
109   {
110     MaxBlockSize()
111     : mFontId(0),
112       mNeededBlockWidth(0),
113       mNeededBlockHeight(0)
114     {
115     }
116
117     FontId   mFontId;
118     uint32_t mNeededBlockWidth;
119     uint32_t mNeededBlockHeight;
120   };
121
122   struct CheckEntry
123   {
124     CheckEntry()
125     : mFontId(0),
126       mIndex(0)
127     {
128     }
129
130     FontId           mFontId;
131     Text::GlyphIndex mIndex;
132   };
133
134   struct TextCacheEntry
135   {
136     TextCacheEntry()
137     : mFontId{0u},
138       mIndex{0u},
139       mImageId{0u},
140       mOutlineWidth{0u},
141       isItalic{false},
142       isBold{false}
143     {
144     }
145
146     FontId           mFontId;
147     Text::GlyphIndex mIndex;
148     uint32_t         mImageId;
149     uint16_t         mOutlineWidth;
150     bool             isItalic : 1;
151     bool             isBold : 1;
152   };
153
154   Impl()
155   : mDepth(0)
156   {
157     mGlyphManager = AtlasGlyphManager::Get();
158     mFontClient   = TextAbstraction::FontClient::Get();
159
160     mQuadVertexFormat["aPosition"] = Property::VECTOR2;
161     mQuadVertexFormat["aTexCoord"] = Property::VECTOR2;
162     mQuadVertexFormat["aColor"]    = Property::VECTOR4;
163   }
164
165   void CacheGlyph(const GlyphInfo& glyph, FontId lastFontId, const AtlasGlyphManager::GlyphStyle& style, AtlasManager::AtlasSlot& slot)
166   {
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.
169
170     const bool glyphNotCached = !mGlyphManager.IsCached(glyph.fontId, glyph.index, style, slot); // Check FontGlyphRecord vector for entry with glyph index and fontId
171
172     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "AddGlyphs fontID[%u] glyphIndex[%u] [%s]\n", glyph.fontId, glyph.index, (glyphNotCached) ? "not cached" : "cached");
173
174     if(glyphNotCached)
175     {
176       MaxBlockSize& blockSize = mBlockSizes[0u];
177
178       if(lastFontId != glyph.fontId)
179       {
180         uint32_t index = 0u;
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();
185             it != endIt;
186             ++it, ++index)
187         {
188           const MaxBlockSize& blockSizeEntry = *it;
189           if(blockSizeEntry.mFontId == glyph.fontId)
190           {
191             blockSize = mBlockSizes[index];
192           }
193         }
194       }
195
196       // Create a new image for the glyph
197       PixelData bitmap;
198
199       // Whether the glyph is an outline.
200       const bool isOutline = 0u != style.outline;
201
202       // Whether the current glyph is a color one.
203       const bool isColorGlyph = mFontClient.IsColorGlyph(glyph.fontId, glyph.index);
204
205       if(!isOutline || (isOutline && !isColorGlyph))
206       {
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;
211
212         mFontClient.CreateBitmap(glyph.fontId,
213                                  glyph.index,
214                                  glyph.isItalicRequired,
215                                  glyph.isBoldRequired,
216                                  glyphBufferData,
217                                  style.outline);
218
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)
222         {
223           uint8_t* newBuffer = (uint8_t*)malloc(glyphBufferSize);
224           if(DALI_LIKELY(newBuffer != nullptr))
225           {
226             TextAbstraction::GlyphBufferData::Decompress(glyphBufferData, newBuffer);
227             if(glyphBufferData.isBufferOwned)
228             {
229               // Release previous buffer
230               free(glyphBufferData.buffer);
231             }
232             glyphBufferData.isBufferOwned   = true;
233             glyphBufferData.buffer          = newBuffer;
234             glyphBufferData.compressionType = TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION;
235           }
236         }
237
238         // Create the pixel data.
239         bitmap = PixelData::New(glyphBufferData.buffer,
240                                 glyphBufferSize,
241                                 glyphBufferData.width,
242                                 glyphBufferData.height,
243                                 glyphBufferData.format,
244                                 PixelData::FREE);
245
246         // Change buffer ownership.
247         glyphBufferData.isBufferOwned = false;
248
249         if(bitmap)
250         {
251           // Ensure that the next image will fit into the current block size
252           if(bitmap.GetWidth() > blockSize.mNeededBlockWidth)
253           {
254             blockSize.mNeededBlockWidth = bitmap.GetWidth();
255           }
256
257           if(bitmap.GetHeight() > blockSize.mNeededBlockHeight)
258           {
259             blockSize.mNeededBlockHeight = bitmap.GetHeight();
260           }
261
262           // If CheckAtlas in AtlasManager::Add can't fit the bitmap in the current atlas it will create a new atlas
263
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;
267
268           while(
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))
273           {
274             default_width <<= 1u;
275             default_height <<= 1u;
276           }
277           mGlyphManager.SetNewAtlasSize(default_width,
278                                         default_height,
279                                         blockSize.mNeededBlockWidth,
280                                         blockSize.mNeededBlockHeight);
281
282           // Locate a new slot for our glyph
283           mGlyphManager.Add(glyph, style, bitmap, slot); // slot will be 0 is glyph not added
284         }
285       }
286     }
287     else
288     {
289       // We have 2+ copies of the same glyph
290       mGlyphManager.AdjustReferenceCount(glyph.fontId, glyph.index, style, 1); //increment
291     }
292   }
293
294   void GenerateMesh(const GlyphInfo&         glyph,
295                     const Vector2&           position,
296                     const Vector4&           color,
297                     uint16_t                 outline,
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,
306                     bool                     isGlyphCached,
307                     uint32_t                 strikethroughChunkId)
308   {
309     // Generate mesh data for this quad, plugging in our supplied position
310     AtlasManager::Mesh2D newMesh;
311     mGlyphManager.GenerateMeshData(slot.mImageId, position, newMesh);
312
313     if(!isGlyphCached)
314     {
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;
322
323       newTextCache.PushBack(textCacheEntry);
324     }
325
326     AtlasManager::Vertex2D* verticesBuffer = newMesh.mVertices.Begin();
327
328     for(unsigned int index = 0u, size = newMesh.mVertices.Count();
329         index < size;
330         ++index)
331     {
332       AtlasManager::Vertex2D& vertex = *(verticesBuffer + index);
333
334       // Set the color of the vertex.
335       vertex.mColor = color;
336     }
337
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);
341
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,
344                    newMesh,
345                    extents,
346                    position.y + glyph.yBearing,
347                    decorationlineGlyph,
348                    currentUnderlinePosition,
349                    currentlineThickness,
350                    slot,
351                    underlineChunkId,
352                    strikethroughStartingYPosition,
353                    strikethroughChunkId);
354   }
355
356   void CreateActors(const std::vector<MeshRecord>& meshContainer,
357                     const Size&                    textSize,
358                     const Vector4&                 color,
359                     const Vector4&                 shadowColor,
360                     const Vector2&                 shadowOffset,
361                     Actor                          textControl,
362                     Property::Index                animatablePropertyIndex,
363                     bool                           drawShadow)
364   {
365     if(!mActor)
366     {
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);
373     }
374
375     for(std::vector<MeshRecord>::const_iterator it    = meshContainer.begin(),
376                                                 endIt = meshContainer.end();
377         it != endIt;
378         ++it)
379     {
380       const MeshRecord& meshRecord = *it;
381
382       Actor actor = CreateMeshActor(textControl, animatablePropertyIndex, color, meshRecord, textSize, STYLE_NORMAL);
383
384       // Whether the actor has renderers.
385       const bool hasRenderer = actor.GetRendererCount() > 0u;
386
387       // Create an effect if necessary
388       if(hasRenderer &&
389          drawShadow)
390       {
391         // Change the color of the vertices.
392         for(Vector<AtlasManager::Vertex2D>::Iterator vIt    = meshRecord.mMesh.mVertices.Begin(),
393                                                      vEndIt = meshRecord.mMesh.mVertices.End();
394             vIt != vEndIt;
395             ++vIt)
396         {
397           AtlasManager::Vertex2D& vertex = *vIt;
398
399           vertex.mColor = shadowColor;
400         }
401
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");
405 #endif
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);
412       }
413
414       if(hasRenderer)
415       {
416         mActor.Add(actor);
417       }
418     }
419   }
420
421   void AddGlyphs(Text::ViewInterface&     view,
422                  Actor                    textControl,
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,
429                  int                      depth,
430                  float                    minLineOffset)
431   {
432     AtlasManager::AtlasSlot slot;
433     slot.mImageId = 0u;
434     slot.mAtlasId = 0u;
435
436     AtlasManager::AtlasSlot slotOutline;
437     slotOutline.mImageId = 0u;
438     slotOutline.mAtlasId = 0u;
439
440     std::vector<MeshRecord> meshContainer;
441     std::vector<MeshRecord> meshContainerOutline;
442     Vector<Extent>          extents;
443     Vector<Extent>          strikethroughExtents;
444     mDepth = depth;
445
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());
460
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();
465
466     const bool useDefaultColor = (NULL == colorsBuffer);
467
468     // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
469     TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
470
471     // Get the underline runs.
472     const Length               numberOfUnderlineRuns = view.GetNumberOfUnderlineRuns();
473     Vector<UnderlinedGlyphRun> underlineRuns;
474     underlineRuns.Resize(numberOfUnderlineRuns);
475     view.GetUnderlineRuns(underlineRuns.Begin(),
476                           0u,
477                           numberOfUnderlineRuns);
478
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(),
485                                                            true,
486                                                            true,
487                                                            true,
488                                                            true,
489                                                            true};
490
491     float maxUnderlineHeight = viewUnderlineProperties.height;
492
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);
498
499     const StrikethroughStyleProperties viewStrikethroughProperties{view.GetStrikethroughColor(),
500                                                                    view.GetStrikethroughHeight(),
501                                                                    true,
502                                                                    true};
503
504     float maxStrikethroughHeight = viewStrikethroughProperties.height;
505
506     FontId lastFontId                  = 0;
507     Style  style                       = STYLE_NORMAL;
508     float  currentUnderlinePosition    = ZERO;
509     bool   thereAreUnderlinedGlyphs    = false;
510     bool   thereAreStrikethroughGlyphs = false;
511
512     if(fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1)
513     {
514       style = STYLE_DROP_SHADOW;
515     }
516
517     CalculateBlocksSize(glyphs);
518
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;
525
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
531
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
537
538     const Character*              textBuffer                = view.GetTextBuffer();
539     float                         calculatedAdvance         = 0.f;
540     const Vector<CharacterIndex>& glyphToCharacterMap       = view.GetGlyphsToCharacters();
541     const CharacterIndex*         glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
542
543     //Skip hyphenIndices less than startIndexOfGlyphs or between two middle of elided text
544     if(hyphenIndices)
545     {
546       while((hyphenIndex < hyphensCount) && (hyphenIndices[hyphenIndex] < startIndexOfGlyphs ||
547                                              (hyphenIndices[hyphenIndex] > firstMiddleIndexOfElidedGlyphs && hyphenIndices[hyphenIndex] < secondMiddleIndexOfElidedGlyphs)))
548       {
549         ++hyphenIndex;
550       }
551     }
552
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);
557
558     // Iteration on glyphs
559     for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
560     {
561       GlyphInfo glyph;
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)
565       {
566         glyph = hyphens[hyphenIndex];
567         i--;
568       }
569       else
570       {
571         glyph = *(glyphsBuffer + i);
572       }
573
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;
579
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;
585
586       // No operation for white space
587       if(!Dali::EqualsZero(glyph.width) && !Dali::EqualsZero(glyph.height))
588       {
589         // Check and update decorative-lines informations
590         if(isGlyphUnderlined || isGlyphStrikethrough)
591         {
592           bool isDecorativeLinesFontIdUpdated = false;
593           // Are we still using the same fontId as previous
594           if(glyph.fontId != lastDecorativeLinesFontId)
595           {
596             // We need to fetch fresh font metrics
597             lastDecorativeLinesFontId      = glyph.fontId;
598             isDecorativeLinesFontIdUpdated = true;
599             fontClient.GetFontMetrics(lastDecorativeLinesFontId, lastDecorativeLinesFontMetrics);
600
601             if(isGlyphStrikethrough || isGlyphUnderlined)
602             {
603               //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
604               currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(lastDecorativeLinesFontMetrics);
605             }
606           }
607
608           if(isGlyphUnderlined && (isDecorativeLinesFontIdUpdated || !(currentUnderlineProperties.IsHeightEqualTo(preUnderlineProperties))))
609           {
610             //If the Underline Height is changed then we need to recalculate height.
611             if(!(currentUnderlineProperties.IsHeightEqualTo(preUnderlineProperties)))
612             {
613               maxUnderlineHeight = currentUnderlineHeight;
614             }
615
616             CalcualteUnderlineHeight(lastDecorativeLinesFontMetrics, currentUnderlineHeight, maxUnderlineHeight);
617           }
618
619           if(isGlyphStrikethrough && (isDecorativeLinesFontIdUpdated || !(currentStrikethroughProperties.IsHeightEqualTo(preStrikethroughProperties))))
620           {
621             //If the Strikethrough Height is changed then we need to recalculate height.
622             if(!(currentStrikethroughProperties.IsHeightEqualTo(preStrikethroughProperties)))
623             {
624               maxStrikethroughHeight = currentStrikethroughHeight;
625             }
626
627             CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
628           }
629         } // decorative-lines
630
631         AtlasGlyphManager::GlyphStyle style;
632         style.isItalic = glyph.isItalicRequired;
633         style.isBold   = glyph.isBoldRequired;
634
635         // Retrieves and caches the glyph's bitmap.
636         CacheGlyph(glyph, lastFontId, style, slot);
637
638         // Retrieves and caches the outline glyph's bitmap.
639         if(isOutline)
640         {
641           style.outline = outlineWidth;
642           CacheGlyph(glyph, lastFontId, style, slotOutline);
643         }
644
645         // Move the origin (0,0) of the mesh to the center of the actor
646         Vector2 position = *(positionsBuffer + i);
647
648         if(addHyphen)
649         {
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;
654         }
655
656         position = Vector2(roundf(position.x), position.y) - halfTextSize - lineOffsetPosition; // roundf() avoids pixel alignment issues.
657
658         if(0u != slot.mImageId) // invalid slot id, glyph has failed to be added to atlas
659         {
660           Vector2 positionPlusOutlineOffset = position + outlineOffset;
661           if(isOutline)
662           {
663             // Add an offset to the text.
664             const float outlineWidthOffset = static_cast<float>(outlineWidth);
665             positionPlusOutlineOffset += Vector2(outlineWidthOffset, outlineWidthOffset);
666           }
667
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);
671
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)))
675           {
676             underlineChunkId++;
677             mapUnderlineChunkIdWithProperties.insert(std::pair<uint32_t, UnderlineStyleProperties>(underlineChunkId, currentUnderlineProperties));
678           }
679
680           //Keep status of underlined for previous glyph to check consecutive indices
681           isPreUnderlined        = isGlyphUnderlined;
682           preUnderlineProperties = currentUnderlineProperties;
683
684           GenerateMesh(glyph,
685                        positionPlusOutlineOffset,
686                        color,
687                        NO_OUTLINE,
688                        slot,
689                        isGlyphUnderlined,
690                        currentUnderlinePosition,
691                        maxUnderlineHeight,
692                        meshContainer,
693                        newTextCache,
694                        extents,
695                        underlineChunkId,
696                        false,
697                        0u);
698
699           if(isGlyphStrikethrough)
700           {
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))
704             {
705               strikethroughChunkId++;
706               mapStrikethroughChunkIdWithProperties.insert(std::pair<uint32_t, StrikethroughStyleProperties>(strikethroughChunkId, currentStrikethroughProperties));
707             }
708
709             GenerateMesh(glyph,
710                          positionPlusOutlineOffset,
711                          color,
712                          NO_OUTLINE,
713                          slot,
714                          isGlyphStrikethrough,
715                          0.0f,
716                          maxStrikethroughHeight,
717                          meshContainer,
718                          newTextCache,
719                          strikethroughExtents,
720                          0u,
721                          true,
722                          strikethroughChunkId);
723           }
724
725           //Keep status of Strikethrough for previous glyph to check consecutive indices
726           isPreStrikethrough         = isGlyphStrikethrough;
727           preStrikethroughProperties = currentStrikethroughProperties;
728
729           lastFontId = glyph.fontId; // Prevents searching for existing blocksizes when string of the same fontId.
730         }
731
732         if(isOutline && (0u != slotOutline.mImageId)) // invalid slot id, glyph has failed to be added to atlas
733         {
734           GenerateMesh(glyph,
735                        position,
736                        outlineColor,
737                        outlineWidth,
738                        slotOutline,
739                        false,
740                        currentUnderlinePosition,
741                        maxUnderlineHeight,
742                        meshContainerOutline,
743                        newTextCache,
744                        extents,
745                        0u,
746                        false,
747                        0u);
748         }
749       }
750
751       if(addHyphen)
752       {
753         hyphenIndex++;
754       }
755     } // glyphs
756
757     // Now remove references for the old text
758     RemoveText();
759     mTextCache.Swap(newTextCache);
760
761     if(thereAreUnderlinedGlyphs)
762     {
763       // Check to see if any of the text needs an underline
764       GenerateUnderlines(meshContainer, extents, viewUnderlineProperties, mapUnderlineChunkIdWithProperties);
765     }
766
767     if(thereAreStrikethroughGlyphs)
768     {
769       // Check to see if any of the text needs a strikethrough
770       GenerateStrikethrough(meshContainer, strikethroughExtents, viewStrikethroughProperties, mapStrikethroughChunkIdWithProperties);
771     }
772
773     // For each MeshData object, create a mesh actor and add to the renderable actor
774     bool isShadowDrawn = false;
775     if(!meshContainerOutline.empty())
776     {
777       const bool drawShadow = STYLE_DROP_SHADOW == style;
778       CreateActors(meshContainerOutline,
779                    textSize,
780                    outlineColor,
781                    shadowColor,
782                    shadowOffset,
783                    textControl,
784                    animatablePropertyIndex,
785                    drawShadow);
786
787       isShadowDrawn = drawShadow;
788     }
789
790     // For each MeshData object, create a mesh actor and add to the renderable actor
791     if(!meshContainer.empty())
792     {
793       const bool drawShadow = !isShadowDrawn && (STYLE_DROP_SHADOW == style);
794       CreateActors(meshContainer,
795                    textSize,
796                    defaultColor,
797                    shadowColor,
798                    shadowOffset,
799                    textControl,
800                    animatablePropertyIndex,
801                    drawShadow);
802     }
803
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);
807
808     if(gLogFilter->IsEnabledFor(Debug::Verbose))
809     {
810       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "%s\n", metrics.mVerboseGlyphCounts.c_str());
811
812       for(uint32_t i = 0; i < metrics.mAtlasMetrics.mAtlasCount; ++i)
813       {
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);
815       }
816     }
817 #endif
818   }
819
820   void RemoveText()
821   {
822     for(Vector<TextCacheEntry>::Iterator oldTextIter = mTextCache.Begin(); oldTextIter != mTextCache.End(); ++oldTextIter)
823     {
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*/);
829     }
830     mTextCache.Resize(0);
831   }
832
833   Actor CreateMeshActor(Actor textControl, Property::Index animatablePropertyIndex, const Vector4& defaultColor, const MeshRecord& meshRecord, const Vector2& actorSize, Style style)
834   {
835     VertexBuffer quadVertices = VertexBuffer::New(mQuadVertexFormat);
836     quadVertices.SetData(const_cast<AtlasManager::Vertex2D*>(&meshRecord.mMesh.mVertices[0]), meshRecord.mMesh.mVertices.Size());
837
838     Geometry quadGeometry = Geometry::New();
839     quadGeometry.AddVertexBuffer(quadVertices);
840     quadGeometry.SetIndexBuffer(&meshRecord.mMesh.mIndices[0], meshRecord.mMesh.mIndices.Size());
841
842     TextureSet textureSet(mGlyphManager.GetTextures(meshRecord.mAtlasId));
843
844     // Choose the shader to use.
845     const bool isColorShader = (STYLE_DROP_SHADOW != style) && (Pixel::BGRA8888 == mGlyphManager.GetPixelFormat(meshRecord.mAtlasId));
846     Shader     shader;
847     if(isColorShader)
848     {
849       // The glyph is an emoji and is not a shadow.
850       if(!mShaderRgba)
851       {
852         mShaderRgba = Shader::New(SHADER_TEXT_ATLAS_SHADER_VERT, SHADER_TEXT_ATLAS_RGBA_SHADER_FRAG, Shader::Hint::NONE, "TEXT_ATLAS_RGBA");
853       }
854       shader = mShaderRgba;
855     }
856     else
857     {
858       // The glyph is text or a shadow.
859       if(!mShaderL8)
860       {
861         mShaderL8 = Shader::New(SHADER_TEXT_ATLAS_SHADER_VERT, SHADER_TEXT_ATLAS_L8_SHADER_FRAG, Shader::Hint::NONE, "TEXT_ATLAS_L8");
862       }
863       shader = mShaderL8;
864     }
865
866     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "defaultColor[%f, %f, %f, %f ]\n", defaultColor.r, defaultColor.g, defaultColor.b, defaultColor.a);
867
868     Dali::Property::Index shaderTextColorIndex = shader.RegisterProperty("textColorAnimatable", defaultColor);
869
870     if(animatablePropertyIndex != Property::INVALID_INDEX)
871     {
872       // create constraint for the animatable text's color Property with textColorAnimatable in the shader.
873       if(shaderTextColorIndex)
874       {
875         Constraint constraint = Constraint::New<Vector4>(shader, shaderTextColorIndex, EqualToConstraint());
876         constraint.AddSource(Source(textControl, animatablePropertyIndex));
877         constraint.Apply();
878       }
879     }
880     else
881     {
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));
884     }
885
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);
890
891     Actor actor = Actor::New();
892 #if defined(DEBUG_ENABLED)
893     actor.SetProperty(Dali::Actor::Property::NAME, "Text renderable actor");
894 #endif
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);
902
903     return actor;
904   }
905
906   void StitchTextMesh(std::vector<MeshRecord>& meshContainer,
907                       AtlasManager::Mesh2D&    newMesh,
908                       Vector<Extent>&          extents,
909                       float                    baseLine,
910                       bool                     decorationlineGlyph,
911                       float                    underlinePosition,
912                       float                    lineThickness,
913                       AtlasManager::AtlasSlot& slot,
914                       uint32_t                 underlineChunkId,
915                       float                    strikethroughPosition,
916                       uint32_t                 strikethroughChunkId)
917   {
918     if(slot.mImageId)
919     {
920       float left  = newMesh.mVertices[0].mPosition.x;
921       float right = newMesh.mVertices[1].mPosition.x;
922
923       // Check to see if there's a mesh data object that references the same atlas ?
924       uint32_t index = 0;
925       for(std::vector<MeshRecord>::iterator mIt    = meshContainer.begin(),
926                                             mEndIt = meshContainer.end();
927           mIt != mEndIt;
928           ++mIt, ++index)
929       {
930         if(slot.mAtlasId == mIt->mAtlasId)
931         {
932           // Append the mesh to the existing mesh and adjust any extents
933           Toolkit::Internal::AtlasMeshFactory::AppendMesh(mIt->mMesh, newMesh);
934
935           if(decorationlineGlyph)
936           {
937             AdjustExtents(extents,
938                           meshContainer,
939                           index,
940                           left,
941                           right,
942                           baseLine,
943                           underlinePosition,
944                           lineThickness,
945                           underlineChunkId,
946                           strikethroughPosition,
947                           strikethroughChunkId);
948           }
949
950           return;
951         }
952       }
953
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);
959
960       if(decorationlineGlyph)
961       {
962         // Adjust extents for this new meshrecord
963         AdjustExtents(extents,
964                       meshContainer,
965                       meshContainer.size() - 1u,
966                       left,
967                       right,
968                       baseLine,
969                       underlinePosition,
970                       lineThickness,
971                       underlineChunkId,
972                       strikethroughPosition,
973                       strikethroughChunkId);
974       }
975     }
976   }
977
978   void AdjustExtents(Vector<Extent>&          extents,
979                      std::vector<MeshRecord>& meshRecords,
980                      uint32_t                 index,
981                      float                    left,
982                      float                    right,
983                      float                    baseLine,
984                      float                    underlinePosition,
985                      float                    lineThickness,
986                      uint32_t                 underlineChunkId,
987                      float                    strikethroughPosition,
988                      uint32_t                 strikethroughChunkId)
989   {
990     bool foundExtent = false;
991     for(Vector<Extent>::Iterator eIt    = extents.Begin(),
992                                  eEndIt = extents.End();
993         eIt != eEndIt;
994         ++eIt)
995     {
996       if(Equals(baseLine, eIt->mBaseLine) && underlineChunkId == eIt->mUnderlineChunkId && strikethroughChunkId == eIt->mStrikethroughChunkId)
997       {
998         foundExtent = true;
999         if(left < eIt->mLeft)
1000         {
1001           eIt->mLeft = left;
1002         }
1003         if(right > eIt->mRight)
1004         {
1005           eIt->mRight = right;
1006         }
1007
1008         if(underlinePosition > eIt->mUnderlinePosition)
1009         {
1010           eIt->mUnderlinePosition = underlinePosition;
1011         }
1012         if(lineThickness > eIt->mLineThickness)
1013         {
1014           eIt->mLineThickness = lineThickness;
1015         }
1016       }
1017     }
1018     if(!foundExtent)
1019     {
1020       Extent extent;
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);
1031     }
1032   }
1033
1034   void CalculateBlocksSize(const Vector<GlyphInfo>& glyphs)
1035   {
1036     for(Vector<GlyphInfo>::ConstIterator glyphIt    = glyphs.Begin(),
1037                                          glyphEndIt = glyphs.End();
1038         glyphIt != glyphEndIt;
1039         ++glyphIt)
1040     {
1041       const FontId fontId    = (*glyphIt).fontId;
1042       bool         foundFont = false;
1043
1044       for(std::vector<MaxBlockSize>::const_iterator blockIt    = mBlockSizes.begin(),
1045                                                     blockEndIt = mBlockSizes.end();
1046           blockIt != blockEndIt;
1047           ++blockIt)
1048       {
1049         if((*blockIt).mFontId == fontId) // Different size fonts will have a different fontId
1050         {
1051           DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Text::AtlasRenderer::CalculateBlocksSize match found fontID(%u) glyphIndex(%u)\n", fontId, (*glyphIt).index);
1052           foundFont = true;
1053           break;
1054         }
1055       }
1056
1057       if(!foundFont)
1058       {
1059         FontMetrics fontMetrics;
1060         mFontClient.GetFontMetrics(fontId, fontMetrics);
1061
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);
1068       }
1069     }
1070   }
1071
1072   void GenerateUnderlines(std::vector<MeshRecord>&                            meshRecords,
1073                           Vector<Extent>&                                     extents,
1074                           const UnderlineStyleProperties&                     viewUnderlineProperties,
1075                           const std::map<uint32_t, UnderlineStyleProperties>& mapUnderlineChunkIdWithProperties)
1076   {
1077     AtlasManager::Mesh2D newMesh;
1078     unsigned short       faceIndex = 0;
1079
1080     for(Vector<Extent>::ConstIterator eIt    = extents.Begin(),
1081                                       eEndIt = extents.End();
1082         eIt != eEndIt;
1083         ++eIt)
1084     {
1085       AtlasManager::Vertex2D vert;
1086       uint32_t               index = eIt->mMeshRecordIndex;
1087       Vector2                uv    = mGlyphManager.GetAtlasSize(meshRecords[index].mAtlasId);
1088
1089       auto pairUnderlineChunkIdWithProperties = mapUnderlineChunkIdWithProperties.find(eIt->mUnderlineChunkId);
1090
1091       const UnderlineStyleProperties underlineProperties = (pairUnderlineChunkIdWithProperties == mapUnderlineChunkIdWithProperties.end())
1092                                                              ? viewUnderlineProperties
1093                                                              : pairUnderlineChunkIdWithProperties->second;
1094
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;
1099
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;
1108
1109       if(underlineType == Text::Underline::Type::DASHED)
1110       {
1111         float dashTlx = tlx;
1112         float dashBrx = tlx;
1113
1114         while((dashTlx >= tlx) && (dashTlx < brx) && ((dashTlx + dashedUnderlineWidth) <= brx))
1115         {
1116           dashBrx = dashTlx + dashedUnderlineWidth;
1117
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);
1125
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);
1132
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);
1140
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);
1147
1148           dashTlx = dashBrx + dashedUnderlineGap; // The next dash will start at the right of the current dash plus the gap
1149
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);
1157
1158           faceIndex += 4;
1159
1160           Toolkit::Internal::AtlasMeshFactory::AppendMesh(meshRecords[index].mMesh, newMesh);
1161         }
1162       }
1163       else
1164       {
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);
1172
1173         vert.mPosition.x  = brx;
1174         vert.mPosition.y  = baseLine;
1175         vert.mTexCoords.x = u;
1176         vert.mColor       = underlineColor;
1177         newMesh.mVertices.PushBack(vert);
1178
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);
1185
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);
1191
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);
1199         faceIndex += 4;
1200
1201         Toolkit::Internal::AtlasMeshFactory::AppendMesh(meshRecords[index].mMesh, newMesh);
1202
1203         if(underlineType == Text::Underline::Type::DOUBLE)
1204         {
1205           baseLine += 2 * thickness;
1206
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);
1214
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);
1221
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);
1229
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);
1236
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);
1244
1245           faceIndex += 4;
1246
1247           Toolkit::Internal::AtlasMeshFactory::AppendMesh(meshRecords[index].mMesh, newMesh);
1248         }
1249       }
1250     }
1251   }
1252
1253   void GenerateStrikethrough(std::vector<MeshRecord>&                                meshRecords,
1254                              Vector<Extent>&                                         extents,
1255                              const StrikethroughStyleProperties&                     viewStrikethroughProperties,
1256                              const std::map<uint32_t, StrikethroughStyleProperties>& mapStrikethroughChunkIdWithProperties)
1257   {
1258     AtlasManager::Mesh2D newMesh;
1259     unsigned short       faceIndex = 0;
1260     for(Vector<Extent>::ConstIterator eIt    = extents.Begin(),
1261                                       eEndIt = extents.End();
1262         eIt != eEndIt;
1263         ++eIt)
1264     {
1265       AtlasManager::Vertex2D vert;
1266       uint32_t               index = eIt->mMeshRecordIndex;
1267       Vector2                uv    = mGlyphManager.GetAtlasSize(meshRecords[index].mAtlasId);
1268
1269       auto pairStrikethroughChunkIdWithProperties = mapStrikethroughChunkIdWithProperties.find(eIt->mStrikethroughChunkId);
1270
1271       const StrikethroughStyleProperties strikethroughProperties = (pairStrikethroughChunkIdWithProperties == mapStrikethroughChunkIdWithProperties.end())
1272                                                                      ? viewStrikethroughProperties
1273                                                                      : pairStrikethroughChunkIdWithProperties->second;
1274
1275       const Vector4& strikethroughColor = strikethroughProperties.colorDefined ? strikethroughProperties.color : viewStrikethroughProperties.color;
1276
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;
1284
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);
1291
1292       vert.mPosition.x  = brx;
1293       vert.mPosition.y  = strikethroughPosition;
1294       vert.mTexCoords.x = u;
1295       vert.mColor       = strikethroughColor;
1296       newMesh.mVertices.PushBack(vert);
1297
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);
1304
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);
1310
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);
1318       faceIndex += 4;
1319
1320       Toolkit::Internal::AtlasMeshFactory::AppendMesh(meshRecords[index].mMesh, newMesh);
1321     }
1322   }
1323
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
1333 };
1334
1335 Text::RendererPtr AtlasRenderer::New()
1336 {
1337   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Text::AtlasRenderer::New()\n");
1338
1339   return Text::RendererPtr(new AtlasRenderer());
1340 }
1341
1342 Actor AtlasRenderer::Render(Text::ViewInterface& view,
1343                             Actor                textControl,
1344                             Property::Index      animatablePropertyIndex,
1345                             float&               alignmentOffset,
1346                             int                  depth)
1347 {
1348   DALI_LOG_INFO(gLogFilter, Debug::General, "Text::AtlasRenderer::Render()\n");
1349
1350   UnparentAndReset(mImpl->mActor);
1351
1352   Length numberOfGlyphs = view.GetNumberOfGlyphs();
1353
1354   if(numberOfGlyphs > 0u)
1355   {
1356     Vector<GlyphInfo> glyphs;
1357     glyphs.Resize(numberOfGlyphs);
1358
1359     Vector<Vector2> positions;
1360     positions.Resize(numberOfGlyphs);
1361
1362     numberOfGlyphs = view.GetGlyphs(glyphs.Begin(),
1363                                     positions.Begin(),
1364                                     alignmentOffset,
1365                                     0u,
1366                                     numberOfGlyphs);
1367
1368     glyphs.Resize(numberOfGlyphs);
1369     positions.Resize(numberOfGlyphs);
1370
1371     const Vector4* const    colorsBuffer       = view.GetColors();
1372     const ColorIndex* const colorIndicesBuffer = view.GetColorIndices();
1373     const Vector4&          defaultColor       = view.GetTextColor();
1374
1375     mImpl->AddGlyphs(view,
1376                      textControl,
1377                      animatablePropertyIndex,
1378                      positions,
1379                      glyphs,
1380                      defaultColor,
1381                      colorsBuffer,
1382                      colorIndicesBuffer,
1383                      depth,
1384                      alignmentOffset);
1385
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. */
1388     if(!mImpl->mActor)
1389     {
1390       mImpl->mActor = Actor::New();
1391     }
1392   }
1393
1394   return mImpl->mActor;
1395 }
1396
1397 AtlasRenderer::AtlasRenderer()
1398 {
1399   mImpl = new Impl();
1400 }
1401
1402 AtlasRenderer::~AtlasRenderer()
1403 {
1404   mImpl->RemoveText();
1405   delete mImpl;
1406 }