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