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