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