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