d408450a66198f3c1f20ee79c0481fc31723c600
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / rendering / atlas / text-atlas-renderer.cpp
1 /*
2  * Copyright (c) 2022 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/rendering/geometry.h>
26 #include <dali/public-api/rendering/renderer.h>
27 #include <map>
28
29 // INTERNAL INCLUDES
30 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
31 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
32 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
33 #include <dali-toolkit/internal/text/glyph-run.h>
34 #include <dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager.h>
35 #include <dali-toolkit/internal/text/rendering/atlas/atlas-mesh-factory.h>
36 #include <dali-toolkit/internal/text/rendering/styles/strikethrough-helper-functions.h>
37 #include <dali-toolkit/internal/text/rendering/styles/underline-helper-functions.h>
38 #include <dali-toolkit/internal/text/text-view.h>
39
40 using namespace Dali;
41 using namespace Dali::Toolkit;
42 using namespace Dali::Toolkit::Text;
43
44 namespace
45 {
46 #if defined(DEBUG_ENABLED)
47 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_RENDERING");
48 #endif
49
50 const float    ZERO(0.0f);
51 const float    HALF(0.5f);
52 const float    ONE(1.0f);
53 const float    ONE_AND_A_HALF(1.5f);
54 const uint32_t DOUBLE_PIXEL_PADDING = 4u; //Padding will be added twice to Atlas
55 const uint16_t NO_OUTLINE           = 0u;
56 } // namespace
57
58 struct AtlasRenderer::Impl
59 {
60   enum Style
61   {
62     STYLE_NORMAL,
63     STYLE_DROP_SHADOW
64   };
65
66   struct MeshRecord
67   {
68     MeshRecord()
69     : mAtlasId(0u)
70     {
71     }
72
73     uint32_t             mAtlasId;
74     AtlasManager::Mesh2D mMesh;
75   };
76
77   /**
78    * brief Struct used to generate the underline/striketthrough mesh.
79    * There is one Extent per line of text.
80    */
81   struct Extent
82   {
83     Extent()
84     : mBaseLine(0.0f),
85       mLeft(0.0f),
86       mRight(0.0f),
87       mUnderlinePosition(0.0f),
88       mLineThickness(0.0f),
89       mMeshRecordIndex(0u),
90       mUnderlineChunkId(0u),
91       mStrikethroughPosition(0.0f),
92       mStrikethroughChunkId(0u)
93     {
94     }
95
96     float    mBaseLine;
97     float    mLeft;
98     float    mRight;
99     float    mUnderlinePosition;
100     float    mLineThickness;
101     uint32_t mMeshRecordIndex;
102     uint32_t mUnderlineChunkId;
103     float    mStrikethroughPosition;
104     uint32_t mStrikethroughChunkId;
105   };
106
107   struct MaxBlockSize
108   {
109     MaxBlockSize()
110     : mFontId(0),
111       mNeededBlockWidth(0),
112       mNeededBlockHeight(0)
113     {
114     }
115
116     FontId   mFontId;
117     uint32_t mNeededBlockWidth;
118     uint32_t mNeededBlockHeight;
119   };
120
121   struct CheckEntry
122   {
123     CheckEntry()
124     : mFontId(0),
125       mIndex(0)
126     {
127     }
128
129     FontId           mFontId;
130     Text::GlyphIndex mIndex;
131   };
132
133   struct TextCacheEntry
134   {
135     TextCacheEntry()
136     : mFontId{0u},
137       mIndex{0u},
138       mImageId{0u},
139       mOutlineWidth{0u},
140       isItalic{false},
141       isBold{false}
142     {
143     }
144
145     FontId           mFontId;
146     Text::GlyphIndex mIndex;
147     uint32_t         mImageId;
148     uint16_t         mOutlineWidth;
149     bool             isItalic : 1;
150     bool             isBold : 1;
151   };
152
153   Impl()
154   : mDepth(0)
155   {
156     mGlyphManager = AtlasGlyphManager::Get();
157     mFontClient   = TextAbstraction::FontClient::Get();
158
159     mQuadVertexFormat["aPosition"] = Property::VECTOR2;
160     mQuadVertexFormat["aTexCoord"] = Property::VECTOR2;
161     mQuadVertexFormat["aColor"]    = Property::VECTOR4;
162   }
163
164   void CacheGlyph(const GlyphInfo& glyph, FontId lastFontId, const AtlasGlyphManager::GlyphStyle& style, AtlasManager::AtlasSlot& slot)
165   {
166     const Size& defaultTextAtlasSize = mFontClient.GetDefaultTextAtlasSize(); //Retrieve default size of text-atlas-block from font-client.
167     const Size& maximumTextAtlasSize = mFontClient.GetMaximumTextAtlasSize(); //Retrieve maximum size of text-atlas-block from font-client.
168
169     const bool glyphNotCached = !mGlyphManager.IsCached(glyph.fontId, glyph.index, style, slot); // Check FontGlyphRecord vector for entry with glyph index and fontId
170
171     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "AddGlyphs fontID[%u] glyphIndex[%u] [%s]\n", glyph.fontId, glyph.index, (glyphNotCached) ? "not cached" : "cached");
172
173     if(glyphNotCached)
174     {
175       MaxBlockSize& blockSize = mBlockSizes[0u];
176
177       if(lastFontId != glyph.fontId)
178       {
179         uint32_t index = 0u;
180         // 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.
181         // CalculateBlocksSize() above ensures a block size entry exists.
182         for(std::vector<MaxBlockSize>::const_iterator it    = mBlockSizes.begin(),
183                                                       endIt = mBlockSizes.end();
184             it != endIt;
185             ++it, ++index)
186         {
187           const MaxBlockSize& blockSizeEntry = *it;
188           if(blockSizeEntry.mFontId == glyph.fontId)
189           {
190             blockSize = mBlockSizes[index];
191           }
192         }
193       }
194
195       // Create a new image for the glyph
196       PixelData bitmap;
197
198       // Whether the glyph is an outline.
199       const bool isOutline = 0u != style.outline;
200
201       // Whether the current glyph is a color one.
202       const bool isColorGlyph = mFontClient.IsColorGlyph(glyph.fontId, glyph.index);
203
204       if(!isOutline || (isOutline && !isColorGlyph))
205       {
206         // Retrieve the emoji's bitmap.
207         TextAbstraction::FontClient::GlyphBufferData glyphBufferData;
208         glyphBufferData.width  = isColorGlyph ? glyph.width : 0; // Desired width and height.
209         glyphBufferData.height = isColorGlyph ? glyph.height : 0;
210
211         mFontClient.CreateBitmap(glyph.fontId,
212                                  glyph.index,
213                                  glyph.isItalicRequired,
214                                  glyph.isBoldRequired,
215                                  glyphBufferData,
216                                  style.outline);
217
218         // Create the pixel data.
219         bitmap = PixelData::New(glyphBufferData.buffer,
220                                 glyphBufferData.width * glyphBufferData.height * GetBytesPerPixel(glyphBufferData.format),
221                                 glyphBufferData.width,
222                                 glyphBufferData.height,
223                                 glyphBufferData.format,
224                                 PixelData::FREE);
225
226         if(bitmap)
227         {
228           // Ensure that the next image will fit into the current block size
229           if(bitmap.GetWidth() > blockSize.mNeededBlockWidth)
230           {
231             blockSize.mNeededBlockWidth = bitmap.GetWidth();
232           }
233
234           if(bitmap.GetHeight() > blockSize.mNeededBlockHeight)
235           {
236             blockSize.mNeededBlockHeight = bitmap.GetHeight();
237           }
238
239           // If CheckAtlas in AtlasManager::Add can't fit the bitmap in the current atlas it will create a new atlas
240
241           // Setting the block size and size of new atlas does not mean a new one will be created. An existing atlas may still surffice.
242           uint32_t default_width  = defaultTextAtlasSize.width;
243           uint32_t default_height = defaultTextAtlasSize.height;
244
245           while(
246             (blockSize.mNeededBlockWidth >= (default_width - (DOUBLE_PIXEL_PADDING + 1u)) ||
247              blockSize.mNeededBlockHeight >= (default_height - (DOUBLE_PIXEL_PADDING + 1u))) &&
248             (default_width < maximumTextAtlasSize.width &&
249              default_height < maximumTextAtlasSize.height))
250           {
251             default_width <<= 1u;
252             default_height <<= 1u;
253           }
254           mGlyphManager.SetNewAtlasSize(default_width,
255                                         default_height,
256                                         blockSize.mNeededBlockWidth,
257                                         blockSize.mNeededBlockHeight);
258
259           // Locate a new slot for our glyph
260           mGlyphManager.Add(glyph, style, bitmap, slot); // slot will be 0 is glyph not added
261         }
262       }
263     }
264     else
265     {
266       // We have 2+ copies of the same glyph
267       mGlyphManager.AdjustReferenceCount(glyph.fontId, glyph.index, style, 1); //increment
268     }
269   }
270
271   void GenerateMesh(const GlyphInfo&         glyph,
272                     const Vector2&           position,
273                     const Vector4&           color,
274                     uint16_t                 outline,
275                     AtlasManager::AtlasSlot& slot,
276                     bool                     decorationlineGlyph,
277                     float                    currentUnderlinePosition,
278                     float                    currentlineThickness,
279                     std::vector<MeshRecord>& meshContainer,
280                     Vector<TextCacheEntry>&  newTextCache,
281                     Vector<Extent>&          extents,
282                     uint32_t                 underlineChunkId,
283                     bool                     isGlyphCached,
284                     uint32_t                 strikethroughChunkId)
285   {
286     // Generate mesh data for this quad, plugging in our supplied position
287     AtlasManager::Mesh2D newMesh;
288     mGlyphManager.GenerateMeshData(slot.mImageId, position, newMesh);
289
290     if(!isGlyphCached)
291     {
292       TextCacheEntry textCacheEntry;
293       textCacheEntry.mFontId       = glyph.fontId;
294       textCacheEntry.mImageId      = slot.mImageId;
295       textCacheEntry.mIndex        = glyph.index;
296       textCacheEntry.mOutlineWidth = outline;
297       textCacheEntry.isItalic      = glyph.isItalicRequired;
298       textCacheEntry.isBold        = glyph.isBoldRequired;
299
300       newTextCache.PushBack(textCacheEntry);
301     }
302
303     AtlasManager::Vertex2D* verticesBuffer = newMesh.mVertices.Begin();
304
305     for(unsigned int index = 0u, size = newMesh.mVertices.Count();
306         index < size;
307         ++index)
308     {
309       AtlasManager::Vertex2D& vertex = *(verticesBuffer + index);
310
311       // Set the color of the vertex.
312       vertex.mColor = color;
313     }
314
315     // Since Free Type font doesn't contain the strikethrough-position property,
316     // strikethrough position will be calculated by moving the underline position upwards by half the value of the line height.
317     float strikethroughStartingYPosition = (position.y + glyph.yBearing + currentUnderlinePosition) - ((glyph.height) * HALF);
318
319     // Find an existing mesh data object to attach to ( or create a new one, if we can't find one using the same atlas)
320     StitchTextMesh(meshContainer,
321                    newMesh,
322                    extents,
323                    position.y + glyph.yBearing,
324                    decorationlineGlyph,
325                    currentUnderlinePosition,
326                    currentlineThickness,
327                    slot,
328                    underlineChunkId,
329                    strikethroughStartingYPosition,
330                    strikethroughChunkId);
331   }
332
333   void CreateActors(const std::vector<MeshRecord>& meshContainer,
334                     const Size&                    textSize,
335                     const Vector4&                 color,
336                     const Vector4&                 shadowColor,
337                     const Vector2&                 shadowOffset,
338                     Actor                          textControl,
339                     Property::Index                animatablePropertyIndex,
340                     bool                           drawShadow)
341   {
342     if(!mActor)
343     {
344       // Create a container actor to act as a common parent for text and shadow, to avoid color inheritence issues.
345       mActor = Actor::New();
346       mActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
347       mActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
348       mActor.SetProperty(Actor::Property::SIZE, textSize);
349       mActor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
350     }
351
352     for(std::vector<MeshRecord>::const_iterator it    = meshContainer.begin(),
353                                                 endIt = meshContainer.end();
354         it != endIt;
355         ++it)
356     {
357       const MeshRecord& meshRecord = *it;
358
359       Actor actor = CreateMeshActor(textControl, animatablePropertyIndex, color, meshRecord, textSize, STYLE_NORMAL);
360
361       // Whether the actor has renderers.
362       const bool hasRenderer = actor.GetRendererCount() > 0u;
363
364       // Create an effect if necessary
365       if(hasRenderer &&
366          drawShadow)
367       {
368         // Change the color of the vertices.
369         for(Vector<AtlasManager::Vertex2D>::Iterator vIt    = meshRecord.mMesh.mVertices.Begin(),
370                                                      vEndIt = meshRecord.mMesh.mVertices.End();
371             vIt != vEndIt;
372             ++vIt)
373         {
374           AtlasManager::Vertex2D& vertex = *vIt;
375
376           vertex.mColor = shadowColor;
377         }
378
379         Actor shadowActor = CreateMeshActor(textControl, animatablePropertyIndex, color, meshRecord, textSize, STYLE_DROP_SHADOW);
380 #if defined(DEBUG_ENABLED)
381         shadowActor.SetProperty(Dali::Actor::Property::NAME, "Text Shadow renderable actor");
382 #endif
383         // Offset shadow in x and y
384         shadowActor.RegisterProperty("uOffset", shadowOffset);
385         Dali::Renderer renderer(shadowActor.GetRendererAt(0));
386         int            depthIndex = renderer.GetProperty<int>(Dali::Renderer::Property::DEPTH_INDEX);
387         renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, depthIndex - 1);
388         mActor.Add(shadowActor);
389       }
390
391       if(hasRenderer)
392       {
393         mActor.Add(actor);
394       }
395     }
396   }
397
398   void AddGlyphs(Text::ViewInterface&     view,
399                  Actor                    textControl,
400                  Property::Index          animatablePropertyIndex,
401                  const Vector<Vector2>&   positions,
402                  const Vector<GlyphInfo>& glyphs,
403                  const Vector4&           defaultColor,
404                  const Vector4* const     colorsBuffer,
405                  const ColorIndex* const  colorIndicesBuffer,
406                  int                      depth,
407                  float                    minLineOffset)
408   {
409     AtlasManager::AtlasSlot slot;
410     slot.mImageId = 0u;
411     slot.mAtlasId = 0u;
412
413     AtlasManager::AtlasSlot slotOutline;
414     slotOutline.mImageId = 0u;
415     slotOutline.mAtlasId = 0u;
416
417     std::vector<MeshRecord> meshContainer;
418     std::vector<MeshRecord> meshContainerOutline;
419     Vector<Extent>          extents;
420     Vector<Extent>          strikethroughExtents;
421     mDepth = depth;
422
423     const Vector2&   textSize(view.GetLayoutSize());
424     const Vector2    halfTextSize(textSize * 0.5f);
425     const Vector2&   shadowOffset(view.GetShadowOffset());
426     const Vector4&   shadowColor(view.GetShadowColor());
427     const bool       underlineEnabled = view.IsUnderlineEnabled();
428     const uint16_t   outlineWidth     = view.GetOutlineWidth();
429     const Vector4&   outlineColor(view.GetOutlineColor());
430     const bool       isOutline            = 0u != outlineWidth;
431     const GlyphInfo* hyphens              = view.GetHyphens();
432     const Length*    hyphenIndices        = view.GetHyphenIndices();
433     const Length     hyphensCount         = view.GetHyphensCount();
434     const bool       strikethroughEnabled = view.IsStrikethroughEnabled();
435     const float      characterSpacing(view.GetCharacterSpacing());
436
437     // Elided text info. Indices according to elided text.
438     const auto startIndexOfGlyphs              = view.GetStartIndexOfElidedGlyphs();
439     const auto firstMiddleIndexOfElidedGlyphs  = view.GetFirstMiddleIndexOfElidedGlyphs();
440     const auto secondMiddleIndexOfElidedGlyphs = view.GetSecondMiddleIndexOfElidedGlyphs();
441
442     const bool useDefaultColor = (NULL == colorsBuffer);
443
444     // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
445     TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
446
447     // Get the underline runs.
448     const Length               numberOfUnderlineRuns = view.GetNumberOfUnderlineRuns();
449     Vector<UnderlinedGlyphRun> underlineRuns;
450     underlineRuns.Resize(numberOfUnderlineRuns);
451     view.GetUnderlineRuns(underlineRuns.Begin(),
452                           0u,
453                           numberOfUnderlineRuns);
454
455     // Aggregate underline-style-properties from view
456     const UnderlineStyleProperties viewUnderlineProperties{view.GetUnderlineType(),
457                                                            view.GetUnderlineColor(),
458                                                            view.GetUnderlineHeight(),
459                                                            view.GetDashedUnderlineGap(),
460                                                            view.GetDashedUnderlineWidth(),
461                                                            true,
462                                                            true,
463                                                            true,
464                                                            true,
465                                                            true};
466
467     float maxUnderlineHeight = viewUnderlineProperties.height;
468
469     // Get the strikethrough runs.
470     const Length                  numberOfStrikethroughRuns = view.GetNumberOfStrikethroughRuns();
471     Vector<StrikethroughGlyphRun> strikethroughRuns;
472     strikethroughRuns.Resize(numberOfStrikethroughRuns);
473     view.GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
474
475     const StrikethroughStyleProperties viewStrikethroughProperties{view.GetStrikethroughColor(),
476                                                                    view.GetStrikethroughHeight(),
477                                                                    true,
478                                                                    true};
479
480     float maxStrikethroughHeight = viewStrikethroughProperties.height;
481
482     FontId lastFontId                  = 0;
483     Style  style                       = STYLE_NORMAL;
484     float  currentUnderlinePosition    = ZERO;
485     bool   thereAreUnderlinedGlyphs    = false;
486     bool   thereAreStrikethroughGlyphs = false;
487
488     if(fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1)
489     {
490       style = STYLE_DROP_SHADOW;
491     }
492
493     CalculateBlocksSize(glyphs);
494
495     // Avoid emptying mTextCache (& removing references) until after incremented references for the new text
496     Vector<TextCacheEntry> newTextCache;
497     const GlyphInfo* const glyphsBuffer    = glyphs.Begin();
498     const Vector2* const   positionsBuffer = positions.Begin();
499     const Vector2          lineOffsetPosition(minLineOffset, 0.f);
500     uint32_t               hyphenIndex = 0;
501
502     //For septated underlined chunks. (this is for Markup case)
503     uint32_t                                     underlineChunkId = 0u;                            // give id for each chunk.
504     bool                                         isPreUnderlined  = false;                         // status of underlined for previous glyph.
505     std::map<uint32_t, UnderlineStyleProperties> mapUnderlineChunkIdWithProperties;                // mapping underlineChunkId with UnderlineStyleProperties to get properties of underlined chunk
506     UnderlineStyleProperties                     preUnderlineProperties = viewUnderlineProperties; // the previous UnderlineStyleProperties
507
508     //For septated strikethrough chunks. (this is for Markup case)
509     uint32_t                                         strikethroughChunkId = 0u;                                // give id for each chunk.
510     bool                                             isPreStrikethrough   = false;                             // status of strikethrough for previous glyph.
511     std::map<uint32_t, StrikethroughStyleProperties> mapStrikethroughChunkIdWithProperties;                    // mapping strikethroughChunkId with StrikethroughStyleProperties to get properties of strikethrough chunk
512     StrikethroughStyleProperties                     preStrikethroughProperties = viewStrikethroughProperties; // the previous StrikethroughStyleProperties
513
514     const Character*              textBuffer                = view.GetTextBuffer();
515     float                         calculatedAdvance         = 0.f;
516     const Vector<CharacterIndex>& glyphToCharacterMap       = view.GetGlyphsToCharacters();
517     const CharacterIndex*         glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
518
519     //Skip hyphenIndices less than startIndexOfGlyphs or between two middle of elided text
520     if(hyphenIndices)
521     {
522       while((hyphenIndex < hyphensCount) && (hyphenIndices[hyphenIndex] < startIndexOfGlyphs ||
523                                              (hyphenIndices[hyphenIndex] > firstMiddleIndexOfElidedGlyphs && hyphenIndices[hyphenIndex] < secondMiddleIndexOfElidedGlyphs)))
524       {
525         ++hyphenIndex;
526       }
527     }
528
529     //To keep the last fontMetrics of lastDecorativeLinesFontId
530     FontId      lastDecorativeLinesFontId = 0; // DecorativeLines like Undeline and Strikethrough
531     FontMetrics lastDecorativeLinesFontMetrics;
532     fontClient.GetFontMetrics(lastDecorativeLinesFontId, lastDecorativeLinesFontMetrics);
533
534     // Iteration on glyphs
535     for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
536     {
537       GlyphInfo glyph;
538       bool      addHyphen = ((hyphenIndex < hyphensCount) && hyphenIndices && ((i + startIndexOfGlyphs) == hyphenIndices[hyphenIndex]));
539       if(addHyphen && hyphens)
540       {
541         glyph = hyphens[hyphenIndex];
542         i--;
543       }
544       else
545       {
546         glyph = *(glyphsBuffer + i);
547       }
548
549       Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
550       const bool                                isGlyphUnderlined           = underlineEnabled || IsGlyphUnderlined(i, underlineRuns, currentUnderlinedGlyphRunIt);
551       const UnderlineStyleProperties            currentUnderlineProperties  = GetCurrentUnderlineProperties(i, isGlyphUnderlined, underlineRuns, currentUnderlinedGlyphRunIt, viewUnderlineProperties);
552       float                                     currentUnderlineHeight      = currentUnderlineProperties.height;
553       thereAreUnderlinedGlyphs                                              = thereAreUnderlinedGlyphs || isGlyphUnderlined;
554
555       Vector<StrikethroughGlyphRun>::ConstIterator currentStrikethroughGlyphRunIt = strikethroughRuns.End();
556       const bool                                   isGlyphStrikethrough           = strikethroughEnabled || IsGlyphStrikethrough(i, strikethroughRuns, currentStrikethroughGlyphRunIt);
557       const StrikethroughStyleProperties           currentStrikethroughProperties = GetCurrentStrikethroughProperties(i, isGlyphStrikethrough, strikethroughRuns, currentStrikethroughGlyphRunIt, viewStrikethroughProperties);
558       float                                        currentStrikethroughHeight     = currentStrikethroughProperties.height;
559       thereAreStrikethroughGlyphs                                                 = thereAreStrikethroughGlyphs || isGlyphStrikethrough;
560
561       // No operation for white space
562       if(glyph.width && glyph.height)
563       {
564         // Check and update decorative-lines informations
565         if(isGlyphUnderlined || isGlyphStrikethrough)
566         {
567           bool isDecorativeLinesFontIdUpdated = false;
568           // Are we still using the same fontId as previous
569           if(glyph.fontId != lastDecorativeLinesFontId)
570           {
571             // We need to fetch fresh font metrics
572             lastDecorativeLinesFontId      = glyph.fontId;
573             isDecorativeLinesFontIdUpdated = true;
574             fontClient.GetFontMetrics(lastDecorativeLinesFontId, lastDecorativeLinesFontMetrics);
575
576             if(isGlyphStrikethrough || isGlyphUnderlined)
577             {
578               //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
579               currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(lastDecorativeLinesFontMetrics);
580             }
581           }
582
583           if(isGlyphUnderlined && (isDecorativeLinesFontIdUpdated || !(currentUnderlineProperties.IsHeightEqualTo(preUnderlineProperties))))
584           {
585             //If the Underline Height is changed then we need to recalculate height.
586             if(!(currentUnderlineProperties.IsHeightEqualTo(preUnderlineProperties)))
587             {
588               maxUnderlineHeight = currentUnderlineHeight;
589             }
590
591             CalcualteUnderlineHeight(lastDecorativeLinesFontMetrics, currentUnderlineHeight, maxUnderlineHeight);
592           }
593
594           if(isGlyphStrikethrough && (isDecorativeLinesFontIdUpdated || !(currentStrikethroughProperties.IsHeightEqualTo(preStrikethroughProperties))))
595           {
596             //If the Strikethrough Height is changed then we need to recalculate height.
597             if(!(currentStrikethroughProperties.IsHeightEqualTo(preStrikethroughProperties)))
598             {
599               maxStrikethroughHeight = currentStrikethroughHeight;
600             }
601
602             CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
603           }
604         } // decorative-lines
605
606         AtlasGlyphManager::GlyphStyle style;
607         style.isItalic = glyph.isItalicRequired;
608         style.isBold   = glyph.isBoldRequired;
609
610         // Retrieves and caches the glyph's bitmap.
611         CacheGlyph(glyph, lastFontId, style, slot);
612
613         // Retrieves and caches the outline glyph's bitmap.
614         if(isOutline)
615         {
616           style.outline = outlineWidth;
617           CacheGlyph(glyph, lastFontId, style, slotOutline);
618         }
619
620         // Move the origin (0,0) of the mesh to the center of the actor
621         Vector2 position = *(positionsBuffer + i);
622
623         if(addHyphen)
624         {
625           GlyphInfo tempInfo = *(glyphsBuffer + i);
626           calculatedAdvance  = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + i))), characterSpacing, tempInfo.advance);
627           position.x         = position.x + calculatedAdvance - tempInfo.xBearing + glyph.xBearing;
628           position.y += tempInfo.yBearing - glyph.yBearing;
629         }
630
631         position = Vector2(roundf(position.x), position.y) - halfTextSize - lineOffsetPosition; // roundf() avoids pixel alignment issues.
632
633         if(0u != slot.mImageId) // invalid slot id, glyph has failed to be added to atlas
634         {
635           Vector2 positionPlusOutlineOffset = position;
636           if(isOutline)
637           {
638             // Add an offset to the text.
639             const float outlineWidthOffset = static_cast<float>(outlineWidth);
640             positionPlusOutlineOffset += Vector2(outlineWidthOffset, outlineWidthOffset);
641           }
642
643           // Get the color of the character.
644           const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndicesBuffer + i);
645           const Vector4&   color      = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + colorIndex - 1u);
646
647           //The new underlined chunk. Add new id if they are not consecutive indices (this is for Markup case)
648           // Examples: "Hello <u>World</u> Hello <u>World</u>", "<u>World</u> Hello <u>World</u>", "<u>   World</u> Hello <u>World</u>"
649           if((!isPreUnderlined && isGlyphUnderlined) || (isGlyphUnderlined && (preUnderlineProperties != currentUnderlineProperties)))
650           {
651             underlineChunkId++;
652             mapUnderlineChunkIdWithProperties.insert(std::pair<uint32_t, UnderlineStyleProperties>(underlineChunkId, currentUnderlineProperties));
653           }
654
655           //Keep status of underlined for previous glyph to check consecutive indices
656           isPreUnderlined        = isGlyphUnderlined;
657           preUnderlineProperties = currentUnderlineProperties;
658
659           GenerateMesh(glyph,
660                        positionPlusOutlineOffset,
661                        color,
662                        NO_OUTLINE,
663                        slot,
664                        isGlyphUnderlined,
665                        currentUnderlinePosition,
666                        maxUnderlineHeight,
667                        meshContainer,
668                        newTextCache,
669                        extents,
670                        underlineChunkId,
671                        false,
672                        0u);
673
674           if(isGlyphStrikethrough)
675           {
676             //The new strikethrough chunk. Add new id if they are not consecutive indices (this is for Markup case)
677             // Examples: "Hello <s>World</s> Hello <s>World</s>", "<s>World</s> Hello <s>World</s>", "<s>   World</s> Hello <s>World</s>"
678             if((!isPreStrikethrough) || (preStrikethroughProperties != currentStrikethroughProperties))
679             {
680               strikethroughChunkId++;
681               mapStrikethroughChunkIdWithProperties.insert(std::pair<uint32_t, StrikethroughStyleProperties>(strikethroughChunkId, currentStrikethroughProperties));
682             }
683
684             GenerateMesh(glyph,
685                          positionPlusOutlineOffset,
686                          color,
687                          NO_OUTLINE,
688                          slot,
689                          isGlyphStrikethrough,
690                          0.0f,
691                          maxStrikethroughHeight,
692                          meshContainer,
693                          newTextCache,
694                          strikethroughExtents,
695                          0u,
696                          true,
697                          strikethroughChunkId);
698           }
699
700           //Keep status of Strikethrough for previous glyph to check consecutive indices
701           isPreStrikethrough         = isGlyphStrikethrough;
702           preStrikethroughProperties = currentStrikethroughProperties;
703
704           lastFontId = glyph.fontId; // Prevents searching for existing blocksizes when string of the same fontId.
705         }
706
707         if(isOutline && (0u != slotOutline.mImageId)) // invalid slot id, glyph has failed to be added to atlas
708         {
709           GenerateMesh(glyph,
710                        position,
711                        outlineColor,
712                        outlineWidth,
713                        slotOutline,
714                        false,
715                        currentUnderlinePosition,
716                        maxUnderlineHeight,
717                        meshContainerOutline,
718                        newTextCache,
719                        extents,
720                        0u,
721                        false,
722                        0u);
723         }
724       }
725
726       if(addHyphen)
727       {
728         hyphenIndex++;
729       }
730     } // glyphs
731
732     // Now remove references for the old text
733     RemoveText();
734     mTextCache.Swap(newTextCache);
735
736     if(thereAreUnderlinedGlyphs)
737     {
738       // Check to see if any of the text needs an underline
739       GenerateUnderlines(meshContainer, extents, viewUnderlineProperties, mapUnderlineChunkIdWithProperties);
740     }
741
742     if(thereAreStrikethroughGlyphs)
743     {
744       // Check to see if any of the text needs a strikethrough
745       GenerateStrikethrough(meshContainer, strikethroughExtents, viewStrikethroughProperties, mapStrikethroughChunkIdWithProperties);
746     }
747
748     // For each MeshData object, create a mesh actor and add to the renderable actor
749     bool isShadowDrawn = false;
750     if(!meshContainerOutline.empty())
751     {
752       const bool drawShadow = STYLE_DROP_SHADOW == style;
753       CreateActors(meshContainerOutline,
754                    textSize,
755                    outlineColor,
756                    shadowColor,
757                    shadowOffset,
758                    textControl,
759                    animatablePropertyIndex,
760                    drawShadow);
761
762       isShadowDrawn = drawShadow;
763     }
764
765     // For each MeshData object, create a mesh actor and add to the renderable actor
766     if(!meshContainer.empty())
767     {
768       const bool drawShadow = !isShadowDrawn && (STYLE_DROP_SHADOW == style);
769       CreateActors(meshContainer,
770                    textSize,
771                    defaultColor,
772                    shadowColor,
773                    shadowOffset,
774                    textControl,
775                    animatablePropertyIndex,
776                    drawShadow);
777     }
778
779 #if defined(DEBUG_ENABLED)
780     Toolkit::AtlasGlyphManager::Metrics metrics = mGlyphManager.GetMetrics();
781     DALI_LOG_INFO(gLogFilter, Debug::General, "TextAtlasRenderer::GlyphManager::GlyphCount: %i, AtlasCount: %i, TextureMemoryUse: %iK\n", metrics.mGlyphCount, metrics.mAtlasMetrics.mAtlasCount, metrics.mAtlasMetrics.mTextureMemoryUsed / 1024);
782
783     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "%s\n", metrics.mVerboseGlyphCounts.c_str());
784
785     for(uint32_t i = 0; i < metrics.mAtlasMetrics.mAtlasCount; ++i)
786     {
787       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);
788     }
789 #endif
790   }
791
792   void RemoveText()
793   {
794     for(Vector<TextCacheEntry>::Iterator oldTextIter = mTextCache.Begin(); oldTextIter != mTextCache.End(); ++oldTextIter)
795     {
796       AtlasGlyphManager::GlyphStyle style;
797       style.outline  = oldTextIter->mOutlineWidth;
798       style.isItalic = oldTextIter->isItalic;
799       style.isBold   = oldTextIter->isBold;
800       mGlyphManager.AdjustReferenceCount(oldTextIter->mFontId, oldTextIter->mIndex, style, -1 /*decrement*/);
801     }
802     mTextCache.Resize(0);
803   }
804
805   Actor CreateMeshActor(Actor textControl, Property::Index animatablePropertyIndex, const Vector4& defaultColor, const MeshRecord& meshRecord, const Vector2& actorSize, Style style)
806   {
807     VertexBuffer quadVertices = VertexBuffer::New(mQuadVertexFormat);
808     quadVertices.SetData(const_cast<AtlasManager::Vertex2D*>(&meshRecord.mMesh.mVertices[0]), meshRecord.mMesh.mVertices.Size());
809
810     Geometry quadGeometry = Geometry::New();
811     quadGeometry.AddVertexBuffer(quadVertices);
812     quadGeometry.SetIndexBuffer(&meshRecord.mMesh.mIndices[0], meshRecord.mMesh.mIndices.Size());
813
814     TextureSet textureSet(mGlyphManager.GetTextures(meshRecord.mAtlasId));
815
816     // Choose the shader to use.
817     const bool isColorShader = (STYLE_DROP_SHADOW != style) && (Pixel::BGRA8888 == mGlyphManager.GetPixelFormat(meshRecord.mAtlasId));
818     Shader     shader;
819     if(isColorShader)
820     {
821       // The glyph is an emoji and is not a shadow.
822       if(!mShaderRgba)
823       {
824         mShaderRgba = Shader::New(SHADER_TEXT_ATLAS_SHADER_VERT, SHADER_TEXT_ATLAS_RGBA_SHADER_FRAG);
825       }
826       shader = mShaderRgba;
827     }
828     else
829     {
830       // The glyph is text or a shadow.
831       if(!mShaderL8)
832       {
833         mShaderL8 = Shader::New(SHADER_TEXT_ATLAS_SHADER_VERT, SHADER_TEXT_ATLAS_L8_SHADER_FRAG);
834       }
835       shader = mShaderL8;
836     }
837
838     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "defaultColor[%f, %f, %f, %f ]\n", defaultColor.r, defaultColor.g, defaultColor.b, defaultColor.a);
839
840     Dali::Property::Index shaderTextColorIndex = shader.RegisterProperty("textColorAnimatable", defaultColor);
841
842     if(animatablePropertyIndex != Property::INVALID_INDEX)
843     {
844       // create constraint for the animatable text's color Property with textColorAnimatable in the shader.
845       if(shaderTextColorIndex)
846       {
847         Constraint constraint = Constraint::New<Vector4>(shader, shaderTextColorIndex, EqualToConstraint());
848         constraint.AddSource(Source(textControl, animatablePropertyIndex));
849         constraint.Apply();
850       }
851     }
852     else
853     {
854       // If not animating the text colour then set to 1's so shader uses the current vertex color
855       shader.RegisterProperty("textColorAnimatable", Vector4(1.0, 1.0, 1.0, 1.0));
856     }
857
858     Dali::Renderer renderer = Dali::Renderer::New(quadGeometry, shader);
859     renderer.SetTextures(textureSet);
860     renderer.SetProperty(Dali::Renderer::Property::BLEND_MODE, BlendMode::ON);
861     renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT + mDepth);
862
863     Actor actor = Actor::New();
864 #if defined(DEBUG_ENABLED)
865     actor.SetProperty(Dali::Actor::Property::NAME, "Text renderable actor");
866 #endif
867     actor.AddRenderer(renderer);
868     // Keep all of the origins aligned
869     actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
870     actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
871     actor.SetProperty(Actor::Property::SIZE, actorSize);
872     actor.RegisterProperty("uOffset", Vector2::ZERO);
873     actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
874
875     return actor;
876   }
877
878   void StitchTextMesh(std::vector<MeshRecord>& meshContainer,
879                       AtlasManager::Mesh2D&    newMesh,
880                       Vector<Extent>&          extents,
881                       float                    baseLine,
882                       bool                     decorationlineGlyph,
883                       float                    underlinePosition,
884                       float                    lineThickness,
885                       AtlasManager::AtlasSlot& slot,
886                       uint32_t                 underlineChunkId,
887                       float                    strikethroughPosition,
888                       uint32_t                 strikethroughChunkId)
889   {
890     if(slot.mImageId)
891     {
892       float left  = newMesh.mVertices[0].mPosition.x;
893       float right = newMesh.mVertices[1].mPosition.x;
894
895       // Check to see if there's a mesh data object that references the same atlas ?
896       uint32_t index = 0;
897       for(std::vector<MeshRecord>::iterator mIt    = meshContainer.begin(),
898                                             mEndIt = meshContainer.end();
899           mIt != mEndIt;
900           ++mIt, ++index)
901       {
902         if(slot.mAtlasId == mIt->mAtlasId)
903         {
904           // Append the mesh to the existing mesh and adjust any extents
905           Toolkit::Internal::AtlasMeshFactory::AppendMesh(mIt->mMesh, newMesh);
906
907           if(decorationlineGlyph)
908           {
909             AdjustExtents(extents,
910                           meshContainer,
911                           index,
912                           left,
913                           right,
914                           baseLine,
915                           underlinePosition,
916                           lineThickness,
917                           underlineChunkId,
918                           strikethroughPosition,
919                           strikethroughChunkId);
920           }
921
922           return;
923         }
924       }
925
926       // No mesh data object currently exists that references this atlas, so create a new one
927       MeshRecord meshRecord;
928       meshRecord.mAtlasId = slot.mAtlasId;
929       meshRecord.mMesh    = newMesh;
930       meshContainer.push_back(meshRecord);
931
932       if(decorationlineGlyph)
933       {
934         // Adjust extents for this new meshrecord
935         AdjustExtents(extents,
936                       meshContainer,
937                       meshContainer.size() - 1u,
938                       left,
939                       right,
940                       baseLine,
941                       underlinePosition,
942                       lineThickness,
943                       underlineChunkId,
944                       strikethroughPosition,
945                       strikethroughChunkId);
946       }
947     }
948   }
949
950   void AdjustExtents(Vector<Extent>&          extents,
951                      std::vector<MeshRecord>& meshRecords,
952                      uint32_t                 index,
953                      float                    left,
954                      float                    right,
955                      float                    baseLine,
956                      float                    underlinePosition,
957                      float                    lineThickness,
958                      uint32_t                 underlineChunkId,
959                      float                    strikethroughPosition,
960                      uint32_t                 strikethroughChunkId)
961   {
962     bool foundExtent = false;
963     for(Vector<Extent>::Iterator eIt    = extents.Begin(),
964                                  eEndIt = extents.End();
965         eIt != eEndIt;
966         ++eIt)
967     {
968       if(Equals(baseLine, eIt->mBaseLine) && underlineChunkId == eIt->mUnderlineChunkId && strikethroughChunkId == eIt->mStrikethroughChunkId)
969       {
970         foundExtent = true;
971         if(left < eIt->mLeft)
972         {
973           eIt->mLeft = left;
974         }
975         if(right > eIt->mRight)
976         {
977           eIt->mRight = right;
978         }
979
980         if(underlinePosition > eIt->mUnderlinePosition)
981         {
982           eIt->mUnderlinePosition = underlinePosition;
983         }
984         if(lineThickness > eIt->mLineThickness)
985         {
986           eIt->mLineThickness = lineThickness;
987         }
988       }
989     }
990     if(!foundExtent)
991     {
992       Extent extent;
993       extent.mLeft                  = left;
994       extent.mRight                 = right;
995       extent.mBaseLine              = baseLine;
996       extent.mUnderlinePosition     = underlinePosition;
997       extent.mMeshRecordIndex       = index;
998       extent.mUnderlineChunkId      = underlineChunkId;
999       extent.mLineThickness         = lineThickness;
1000       extent.mStrikethroughPosition = strikethroughPosition;
1001       extent.mStrikethroughChunkId  = strikethroughChunkId;
1002       extents.PushBack(extent);
1003     }
1004   }
1005
1006   void CalculateBlocksSize(const Vector<GlyphInfo>& glyphs)
1007   {
1008     for(Vector<GlyphInfo>::ConstIterator glyphIt    = glyphs.Begin(),
1009                                          glyphEndIt = glyphs.End();
1010         glyphIt != glyphEndIt;
1011         ++glyphIt)
1012     {
1013       const FontId fontId    = (*glyphIt).fontId;
1014       bool         foundFont = false;
1015
1016       for(std::vector<MaxBlockSize>::const_iterator blockIt    = mBlockSizes.begin(),
1017                                                     blockEndIt = mBlockSizes.end();
1018           blockIt != blockEndIt;
1019           ++blockIt)
1020       {
1021         if((*blockIt).mFontId == fontId) // Different size fonts will have a different fontId
1022         {
1023           DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Text::AtlasRenderer::CalculateBlocksSize match found fontID(%u) glyphIndex(%u)\n", fontId, (*glyphIt).index);
1024           foundFont = true;
1025           break;
1026         }
1027       }
1028
1029       if(!foundFont)
1030       {
1031         FontMetrics fontMetrics;
1032         mFontClient.GetFontMetrics(fontId, fontMetrics);
1033
1034         MaxBlockSize maxBlockSize;
1035         maxBlockSize.mNeededBlockWidth  = static_cast<uint32_t>(fontMetrics.height);
1036         maxBlockSize.mNeededBlockHeight = maxBlockSize.mNeededBlockWidth;
1037         maxBlockSize.mFontId            = fontId;
1038         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Text::AtlasRenderer::CalculateBlocksSize New font with no matched blocksize, setting blocksize[%u]\n", maxBlockSize.mNeededBlockWidth);
1039         mBlockSizes.push_back(maxBlockSize);
1040       }
1041     }
1042   }
1043
1044   void GenerateUnderlines(std::vector<MeshRecord>&                            meshRecords,
1045                           Vector<Extent>&                                     extents,
1046                           const UnderlineStyleProperties&                     viewUnderlineProperties,
1047                           const std::map<uint32_t, UnderlineStyleProperties>& mapUnderlineChunkIdWithProperties)
1048   {
1049     AtlasManager::Mesh2D newMesh;
1050     unsigned short       faceIndex = 0;
1051
1052     for(Vector<Extent>::ConstIterator eIt    = extents.Begin(),
1053                                       eEndIt = extents.End();
1054         eIt != eEndIt;
1055         ++eIt)
1056     {
1057       AtlasManager::Vertex2D vert;
1058       uint32_t               index = eIt->mMeshRecordIndex;
1059       Vector2                uv    = mGlyphManager.GetAtlasSize(meshRecords[index].mAtlasId);
1060
1061       auto pairUnderlineChunkIdWithProperties = mapUnderlineChunkIdWithProperties.find(eIt->mUnderlineChunkId);
1062
1063       const UnderlineStyleProperties underlineProperties = (pairUnderlineChunkIdWithProperties == mapUnderlineChunkIdWithProperties.end())
1064                                                              ? viewUnderlineProperties
1065                                                              : pairUnderlineChunkIdWithProperties->second;
1066
1067       const Vector4&               underlineColor       = underlineProperties.colorDefined ? underlineProperties.color : viewUnderlineProperties.color;
1068       const Text::Underline::Type& underlineType        = underlineProperties.typeDefined ? underlineProperties.type : viewUnderlineProperties.type;
1069       const float&                 dashedUnderlineGap   = underlineProperties.dashGapDefined ? underlineProperties.dashGap : viewUnderlineProperties.dashGap;
1070       const float&                 dashedUnderlineWidth = underlineProperties.dashWidthDefined ? underlineProperties.dashWidth : viewUnderlineProperties.dashWidth;
1071
1072       // Make sure we don't hit texture edge for single pixel texture ( filled pixel is in top left of every atlas )
1073       float u           = HALF / uv.x;
1074       float v           = HALF / uv.y;
1075       float thickness   = eIt->mLineThickness;
1076       float ShiftLineBy = (underlineType == Text::Underline::Type::DOUBLE) ? (thickness * ONE_AND_A_HALF) : (thickness * HALF);
1077       float baseLine    = eIt->mBaseLine + eIt->mUnderlinePosition - ShiftLineBy;
1078       float tlx         = eIt->mLeft;
1079       float brx         = eIt->mRight;
1080
1081       if(underlineType == Text::Underline::Type::DASHED)
1082       {
1083         float dashTlx = tlx;
1084         float dashBrx = tlx;
1085
1086         while((dashTlx >= tlx) && (dashTlx < brx) && ((dashTlx + dashedUnderlineWidth) <= brx))
1087         {
1088           dashBrx = dashTlx + dashedUnderlineWidth;
1089
1090           //The top left edge of the underline
1091           vert.mPosition.x  = dashTlx;
1092           vert.mPosition.y  = baseLine;
1093           vert.mTexCoords.x = ZERO;
1094           vert.mTexCoords.y = ZERO;
1095           vert.mColor       = underlineColor;
1096           newMesh.mVertices.PushBack(vert);
1097
1098           //The top right edge of the underline
1099           vert.mPosition.x  = dashBrx;
1100           vert.mPosition.y  = baseLine;
1101           vert.mTexCoords.x = u;
1102           vert.mColor       = underlineColor;
1103           newMesh.mVertices.PushBack(vert);
1104
1105           //The bottom left edge of the underline
1106           vert.mPosition.x  = dashTlx;
1107           vert.mPosition.y  = baseLine + thickness;
1108           vert.mTexCoords.x = ZERO;
1109           vert.mTexCoords.y = v;
1110           vert.mColor       = underlineColor;
1111           newMesh.mVertices.PushBack(vert);
1112
1113           //The bottom right edge of the underline
1114           vert.mPosition.x  = dashBrx;
1115           vert.mPosition.y  = baseLine + thickness;
1116           vert.mTexCoords.x = u;
1117           vert.mColor       = underlineColor;
1118           newMesh.mVertices.PushBack(vert);
1119
1120           dashTlx = dashBrx + dashedUnderlineGap; // The next dash will start at the right of the current dash plus the gap
1121
1122           // Six indices in counter clockwise winding
1123           newMesh.mIndices.PushBack(faceIndex + 1u);
1124           newMesh.mIndices.PushBack(faceIndex);
1125           newMesh.mIndices.PushBack(faceIndex + 2u);
1126           newMesh.mIndices.PushBack(faceIndex + 2u);
1127           newMesh.mIndices.PushBack(faceIndex + 3u);
1128           newMesh.mIndices.PushBack(faceIndex + 1u);
1129
1130           faceIndex += 4;
1131
1132           Toolkit::Internal::AtlasMeshFactory::AppendMesh(meshRecords[index].mMesh, newMesh);
1133         }
1134       }
1135       else
1136       {
1137         // It's either SOLID or DOUBLE so we need to generate the first solid underline anyway.
1138         vert.mPosition.x  = tlx;
1139         vert.mPosition.y  = baseLine;
1140         vert.mTexCoords.x = ZERO;
1141         vert.mTexCoords.y = ZERO;
1142         vert.mColor       = underlineColor;
1143         newMesh.mVertices.PushBack(vert);
1144
1145         vert.mPosition.x  = brx;
1146         vert.mPosition.y  = baseLine;
1147         vert.mTexCoords.x = u;
1148         vert.mColor       = underlineColor;
1149         newMesh.mVertices.PushBack(vert);
1150
1151         vert.mPosition.x  = tlx;
1152         vert.mPosition.y  = baseLine + thickness;
1153         vert.mTexCoords.x = ZERO;
1154         vert.mTexCoords.y = v;
1155         vert.mColor       = underlineColor;
1156         newMesh.mVertices.PushBack(vert);
1157
1158         vert.mPosition.x  = brx;
1159         vert.mPosition.y  = baseLine + thickness;
1160         vert.mTexCoords.x = u;
1161         vert.mColor       = underlineColor;
1162         newMesh.mVertices.PushBack(vert);
1163
1164         // Six indices in counter clockwise winding
1165         newMesh.mIndices.PushBack(faceIndex + 1u);
1166         newMesh.mIndices.PushBack(faceIndex);
1167         newMesh.mIndices.PushBack(faceIndex + 2u);
1168         newMesh.mIndices.PushBack(faceIndex + 2u);
1169         newMesh.mIndices.PushBack(faceIndex + 3u);
1170         newMesh.mIndices.PushBack(faceIndex + 1u);
1171         faceIndex += 4;
1172
1173         Toolkit::Internal::AtlasMeshFactory::AppendMesh(meshRecords[index].mMesh, newMesh);
1174
1175         if(underlineType == Text::Underline::Type::DOUBLE)
1176         {
1177           baseLine += 2 * thickness;
1178
1179           //The top left edge of the underline
1180           vert.mPosition.x  = tlx;
1181           vert.mPosition.y  = baseLine; // Vertical start of the second underline
1182           vert.mTexCoords.x = ZERO;
1183           vert.mTexCoords.y = ZERO;
1184           vert.mColor       = underlineColor;
1185           newMesh.mVertices.PushBack(vert);
1186
1187           //The top right edge of the underline
1188           vert.mPosition.x  = brx;
1189           vert.mPosition.y  = baseLine;
1190           vert.mTexCoords.x = u;
1191           vert.mColor       = underlineColor;
1192           newMesh.mVertices.PushBack(vert);
1193
1194           //The bottom left edge of the underline
1195           vert.mPosition.x  = tlx;
1196           vert.mPosition.y  = baseLine + thickness; // Vertical End of the second underline
1197           vert.mTexCoords.x = ZERO;
1198           vert.mTexCoords.y = v;
1199           vert.mColor       = underlineColor;
1200           newMesh.mVertices.PushBack(vert);
1201
1202           //The bottom right edge of the underline
1203           vert.mPosition.x  = brx;
1204           vert.mPosition.y  = baseLine + thickness;
1205           vert.mTexCoords.x = u;
1206           vert.mColor       = underlineColor;
1207           newMesh.mVertices.PushBack(vert);
1208
1209           // Six indices in counter clockwise winding
1210           newMesh.mIndices.PushBack(faceIndex + 1u);
1211           newMesh.mIndices.PushBack(faceIndex);
1212           newMesh.mIndices.PushBack(faceIndex + 2u);
1213           newMesh.mIndices.PushBack(faceIndex + 2u);
1214           newMesh.mIndices.PushBack(faceIndex + 3u);
1215           newMesh.mIndices.PushBack(faceIndex + 1u);
1216
1217           faceIndex += 4;
1218
1219           Toolkit::Internal::AtlasMeshFactory::AppendMesh(meshRecords[index].mMesh, newMesh);
1220         }
1221       }
1222     }
1223   }
1224
1225   void GenerateStrikethrough(std::vector<MeshRecord>&                                meshRecords,
1226                              Vector<Extent>&                                         extents,
1227                              const StrikethroughStyleProperties&                     viewStrikethroughProperties,
1228                              const std::map<uint32_t, StrikethroughStyleProperties>& mapStrikethroughChunkIdWithProperties)
1229   {
1230     AtlasManager::Mesh2D newMesh;
1231     unsigned short       faceIndex = 0;
1232     for(Vector<Extent>::ConstIterator eIt    = extents.Begin(),
1233                                       eEndIt = extents.End();
1234         eIt != eEndIt;
1235         ++eIt)
1236     {
1237       AtlasManager::Vertex2D vert;
1238       uint32_t               index = eIt->mMeshRecordIndex;
1239       Vector2                uv    = mGlyphManager.GetAtlasSize(meshRecords[index].mAtlasId);
1240
1241       auto pairStrikethroughChunkIdWithProperties = mapStrikethroughChunkIdWithProperties.find(eIt->mStrikethroughChunkId);
1242
1243       const StrikethroughStyleProperties strikethroughProperties = (pairStrikethroughChunkIdWithProperties == mapStrikethroughChunkIdWithProperties.end())
1244                                                                      ? viewStrikethroughProperties
1245                                                                      : pairStrikethroughChunkIdWithProperties->second;
1246
1247       const Vector4& strikethroughColor = strikethroughProperties.colorDefined ? strikethroughProperties.color : viewStrikethroughProperties.color;
1248
1249       // Make sure we don't hit texture edge for single pixel texture ( filled pixel is in top left of every atlas )
1250       float u                     = HALF / uv.x;
1251       float v                     = HALF / uv.y;
1252       float thickness             = eIt->mLineThickness;
1253       float tlx                   = eIt->mLeft;
1254       float brx                   = eIt->mRight;
1255       float strikethroughPosition = eIt->mStrikethroughPosition;
1256
1257       vert.mPosition.x  = tlx;
1258       vert.mPosition.y  = strikethroughPosition;
1259       vert.mTexCoords.x = ZERO;
1260       vert.mTexCoords.y = ZERO;
1261       vert.mColor       = strikethroughColor;
1262       newMesh.mVertices.PushBack(vert);
1263
1264       vert.mPosition.x  = brx;
1265       vert.mPosition.y  = strikethroughPosition;
1266       vert.mTexCoords.x = u;
1267       vert.mColor       = strikethroughColor;
1268       newMesh.mVertices.PushBack(vert);
1269
1270       vert.mPosition.x  = tlx;
1271       vert.mPosition.y  = strikethroughPosition + thickness;
1272       vert.mTexCoords.x = ZERO;
1273       vert.mTexCoords.y = v;
1274       vert.mColor       = strikethroughColor;
1275       newMesh.mVertices.PushBack(vert);
1276
1277       vert.mPosition.x  = brx;
1278       vert.mPosition.y  = strikethroughPosition + thickness;
1279       vert.mTexCoords.x = u;
1280       vert.mColor       = strikethroughColor;
1281       newMesh.mVertices.PushBack(vert);
1282
1283       // Six indices in counter clockwise winding
1284       newMesh.mIndices.PushBack(faceIndex + 1u);
1285       newMesh.mIndices.PushBack(faceIndex);
1286       newMesh.mIndices.PushBack(faceIndex + 2u);
1287       newMesh.mIndices.PushBack(faceIndex + 2u);
1288       newMesh.mIndices.PushBack(faceIndex + 3u);
1289       newMesh.mIndices.PushBack(faceIndex + 1u);
1290       faceIndex += 4;
1291
1292       Toolkit::Internal::AtlasMeshFactory::AppendMesh(meshRecords[index].mMesh, newMesh);
1293     }
1294   }
1295
1296   Actor                       mActor;            ///< The actor parent which renders the text
1297   AtlasGlyphManager           mGlyphManager;     ///< Glyph Manager to handle upload and caching
1298   TextAbstraction::FontClient mFontClient;       ///< The font client used to supply glyph information
1299   Shader                      mShaderL8;         ///< The shader for glyphs and emoji's shadows.
1300   Shader                      mShaderRgba;       ///< The shader for emojis.
1301   std::vector<MaxBlockSize>   mBlockSizes;       ///< Maximum size needed to contain a glyph in a block within a new atlas
1302   Vector<TextCacheEntry>      mTextCache;        ///< Caches data from previous render
1303   Property::Map               mQuadVertexFormat; ///< Describes the vertex format for text
1304   int                         mDepth;            ///< DepthIndex passed by control when connect to stage
1305 };
1306
1307 Text::RendererPtr AtlasRenderer::New()
1308 {
1309   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Text::AtlasRenderer::New()\n");
1310
1311   return Text::RendererPtr(new AtlasRenderer());
1312 }
1313
1314 Actor AtlasRenderer::Render(Text::ViewInterface& view,
1315                             Actor                textControl,
1316                             Property::Index      animatablePropertyIndex,
1317                             float&               alignmentOffset,
1318                             int                  depth)
1319 {
1320   DALI_LOG_INFO(gLogFilter, Debug::General, "Text::AtlasRenderer::Render()\n");
1321
1322   UnparentAndReset(mImpl->mActor);
1323
1324   Length numberOfGlyphs = view.GetNumberOfGlyphs();
1325
1326   if(numberOfGlyphs > 0u)
1327   {
1328     Vector<GlyphInfo> glyphs;
1329     glyphs.Resize(numberOfGlyphs);
1330
1331     Vector<Vector2> positions;
1332     positions.Resize(numberOfGlyphs);
1333
1334     numberOfGlyphs = view.GetGlyphs(glyphs.Begin(),
1335                                     positions.Begin(),
1336                                     alignmentOffset,
1337                                     0u,
1338                                     numberOfGlyphs);
1339
1340     glyphs.Resize(numberOfGlyphs);
1341     positions.Resize(numberOfGlyphs);
1342
1343     const Vector4* const    colorsBuffer       = view.GetColors();
1344     const ColorIndex* const colorIndicesBuffer = view.GetColorIndices();
1345     const Vector4&          defaultColor       = view.GetTextColor();
1346
1347     mImpl->AddGlyphs(view,
1348                      textControl,
1349                      animatablePropertyIndex,
1350                      positions,
1351                      glyphs,
1352                      defaultColor,
1353                      colorsBuffer,
1354                      colorIndicesBuffer,
1355                      depth,
1356                      alignmentOffset);
1357
1358     /* In the case where AddGlyphs does not create a renderable Actor for example when glyphs are all whitespace create a new Actor. */
1359     /* 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. */
1360     if(!mImpl->mActor)
1361     {
1362       mImpl->mActor = Actor::New();
1363     }
1364   }
1365
1366   return mImpl->mActor;
1367 }
1368
1369 AtlasRenderer::AtlasRenderer()
1370 {
1371   mImpl = new Impl();
1372 }
1373
1374 AtlasRenderer::~AtlasRenderer()
1375 {
1376   mImpl->RemoveText();
1377   delete mImpl;
1378 }