Remove useless iteration when debug mode
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / rendering / atlas / text-atlas-renderer.cpp
index 6b1d82b..847954f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include <dali/public-api/animation/constraints.h>
 #include <dali/public-api/rendering/geometry.h>
 #include <dali/public-api/rendering/renderer.h>
+#include <map>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
+#include <dali-toolkit/internal/text/glyph-metrics-helper.h>
 #include <dali-toolkit/internal/text/glyph-run.h>
 #include <dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager.h>
 #include <dali-toolkit/internal/text/rendering/atlas/atlas-mesh-factory.h>
+#include <dali-toolkit/internal/text/rendering/styles/strikethrough-helper-functions.h>
+#include <dali-toolkit/internal/text/rendering/styles/underline-helper-functions.h>
 #include <dali-toolkit/internal/text/text-view.h>
 
 using namespace Dali;
@@ -46,8 +50,8 @@ Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT
 const float    ZERO(0.0f);
 const float    HALF(0.5f);
 const float    ONE(1.0f);
-const uint32_t DEFAULT_ATLAS_WIDTH  = 512u;
-const uint32_t DEFAULT_ATLAS_HEIGHT = 512u;
+const float    ONE_AND_A_HALF(1.5f);
+const uint32_t DOUBLE_PIXEL_PADDING = 4u; //Padding will be added twice to Atlas
 const uint16_t NO_OUTLINE           = 0u;
 } // namespace
 
@@ -71,7 +75,7 @@ struct AtlasRenderer::Impl
   };
 
   /**
-   * brief Struct used to generate the underline mesh.
+   * brief Struct used to generate the underline/striketthrough mesh.
    * There is one Extent per line of text.
    */
   struct Extent
@@ -81,8 +85,11 @@ struct AtlasRenderer::Impl
       mLeft(0.0f),
       mRight(0.0f),
       mUnderlinePosition(0.0f),
-      mUnderlineThickness(0.0f),
-      mMeshRecordIndex(0u)
+      mLineThickness(0.0f),
+      mMeshRecordIndex(0u),
+      mUnderlineChunkId(0u),
+      mStrikethroughPosition(0.0f),
+      mStrikethroughChunkId(0u)
     {
     }
 
@@ -90,8 +97,11 @@ struct AtlasRenderer::Impl
     float    mLeft;
     float    mRight;
     float    mUnderlinePosition;
-    float    mUnderlineThickness;
+    float    mLineThickness;
     uint32_t mMeshRecordIndex;
+    uint32_t mUnderlineChunkId;
+    float    mStrikethroughPosition;
+    uint32_t mStrikethroughChunkId;
   };
 
   struct MaxBlockSize
@@ -151,27 +161,11 @@ struct AtlasRenderer::Impl
     mQuadVertexFormat["aColor"]    = Property::VECTOR4;
   }
 
-  bool IsGlyphUnderlined(GlyphIndex              index,
-                         const Vector<GlyphRun>& underlineRuns)
-  {
-    for(Vector<GlyphRun>::ConstIterator it    = underlineRuns.Begin(),
-                                        endIt = underlineRuns.End();
-        it != endIt;
-        ++it)
-    {
-      const GlyphRun& run = *it;
-
-      if((run.glyphIndex <= index) && (index < run.glyphIndex + run.numberOfGlyphs))
-      {
-        return true;
-      }
-    }
-
-    return false;
-  }
-
   void CacheGlyph(const GlyphInfo& glyph, FontId lastFontId, const AtlasGlyphManager::GlyphStyle& style, AtlasManager::AtlasSlot& slot)
   {
+    const Size& defaultTextAtlasSize = mFontClient.GetDefaultTextAtlasSize(); //Retrieve default size of text-atlas-block from font-client.
+    const Size& maximumTextAtlasSize = mFontClient.GetMaximumTextAtlasSize(); //Retrieve maximum size of text-atlas-block from font-client.
+
     const bool glyphNotCached = !mGlyphManager.IsCached(glyph.fontId, glyph.index, style, slot); // Check FontGlyphRecord vector for entry with glyph index and fontId
 
     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "AddGlyphs fontID[%u] glyphIndex[%u] [%s]\n", glyph.fontId, glyph.index, (glyphNotCached) ? "not cached" : "cached");
@@ -227,7 +221,7 @@ struct AtlasRenderer::Impl
                                 glyphBufferData.width,
                                 glyphBufferData.height,
                                 glyphBufferData.format,
-                                PixelData::DELETE_ARRAY);
+                                PixelData::FREE);
 
         if(bitmap)
         {
@@ -245,8 +239,20 @@ struct AtlasRenderer::Impl
           // If CheckAtlas in AtlasManager::Add can't fit the bitmap in the current atlas it will create a new atlas
 
           // Setting the block size and size of new atlas does not mean a new one will be created. An existing atlas may still surffice.
-          mGlyphManager.SetNewAtlasSize(DEFAULT_ATLAS_WIDTH,
-                                        DEFAULT_ATLAS_HEIGHT,
+          uint32_t default_width  = defaultTextAtlasSize.width;
+          uint32_t default_height = defaultTextAtlasSize.height;
+
+          while(
+            (blockSize.mNeededBlockWidth >= (default_width - (DOUBLE_PIXEL_PADDING + 1u)) ||
+             blockSize.mNeededBlockHeight >= (default_height - (DOUBLE_PIXEL_PADDING + 1u))) &&
+            (default_width < maximumTextAtlasSize.width &&
+             default_height < maximumTextAtlasSize.height))
+          {
+            default_width <<= 1u;
+            default_height <<= 1u;
+          }
+          mGlyphManager.SetNewAtlasSize(default_width,
+                                        default_height,
                                         blockSize.mNeededBlockWidth,
                                         blockSize.mNeededBlockHeight);
 
@@ -267,26 +273,32 @@ struct AtlasRenderer::Impl
                     const Vector4&           color,
                     uint16_t                 outline,
                     AtlasManager::AtlasSlot& slot,
-                    bool                     underlineGlyph,
+                    bool                     decorationlineGlyph,
                     float                    currentUnderlinePosition,
-                    float                    currentUnderlineThickness,
+                    float                    currentlineThickness,
                     std::vector<MeshRecord>& meshContainer,
                     Vector<TextCacheEntry>&  newTextCache,
-                    Vector<Extent>&          extents)
+                    Vector<Extent>&          extents,
+                    uint32_t                 underlineChunkId,
+                    bool                     isGlyphCached,
+                    uint32_t                 strikethroughChunkId)
   {
     // Generate mesh data for this quad, plugging in our supplied position
     AtlasManager::Mesh2D newMesh;
     mGlyphManager.GenerateMeshData(slot.mImageId, position, newMesh);
 
-    TextCacheEntry textCacheEntry;
-    textCacheEntry.mFontId       = glyph.fontId;
-    textCacheEntry.mImageId      = slot.mImageId;
-    textCacheEntry.mIndex        = glyph.index;
-    textCacheEntry.mOutlineWidth = outline;
-    textCacheEntry.isItalic      = glyph.isItalicRequired;
-    textCacheEntry.isBold        = glyph.isBoldRequired;
-
-    newTextCache.PushBack(textCacheEntry);
+    if(!isGlyphCached)
+    {
+      TextCacheEntry textCacheEntry;
+      textCacheEntry.mFontId       = glyph.fontId;
+      textCacheEntry.mImageId      = slot.mImageId;
+      textCacheEntry.mIndex        = glyph.index;
+      textCacheEntry.mOutlineWidth = outline;
+      textCacheEntry.isItalic      = glyph.isItalicRequired;
+      textCacheEntry.isBold        = glyph.isBoldRequired;
+
+      newTextCache.PushBack(textCacheEntry);
+    }
 
     AtlasManager::Vertex2D* verticesBuffer = newMesh.mVertices.Begin();
 
@@ -300,15 +312,22 @@ struct AtlasRenderer::Impl
       vertex.mColor = color;
     }
 
+    // Since Free Type font doesn't contain the strikethrough-position property,
+    // strikethrough position will be calculated by moving the underline position upwards by half the value of the line height.
+    float strikethroughStartingYPosition = (position.y + glyph.yBearing + currentUnderlinePosition) - ((glyph.height) * HALF);
+
     // Find an existing mesh data object to attach to ( or create a new one, if we can't find one using the same atlas)
     StitchTextMesh(meshContainer,
                    newMesh,
                    extents,
                    position.y + glyph.yBearing,
-                   underlineGlyph,
+                   decorationlineGlyph,
                    currentUnderlinePosition,
-                   currentUnderlineThickness,
-                   slot);
+                   currentlineThickness,
+                   slot,
+                   underlineChunkId,
+                   strikethroughStartingYPosition,
+                   strikethroughChunkId);
   }
 
   void CreateActors(const std::vector<MeshRecord>& meshContainer,
@@ -398,36 +417,73 @@ struct AtlasRenderer::Impl
     std::vector<MeshRecord> meshContainer;
     std::vector<MeshRecord> meshContainerOutline;
     Vector<Extent>          extents;
+    Vector<Extent>          strikethroughExtents;
     mDepth = depth;
 
-    const Vector2& textSize(view.GetLayoutSize());
-    const Vector2  halfTextSize(textSize * 0.5f);
-    const Vector2& shadowOffset(view.GetShadowOffset());
-    const Vector4& shadowColor(view.GetShadowColor());
-    const bool     underlineEnabled = view.IsUnderlineEnabled();
-    const Vector4& underlineColor(view.GetUnderlineColor());
-    const float    underlineHeight = view.GetUnderlineHeight();
-    const uint16_t outlineWidth    = view.GetOutlineWidth();
-    const Vector4& outlineColor(view.GetOutlineColor());
-    const bool     isOutline = 0u != outlineWidth;
+    const Vector2&   textSize(view.GetLayoutSize());
+    const Vector2    halfTextSize(textSize * 0.5f);
+    const Vector2&   shadowOffset(view.GetShadowOffset());
+    const Vector4&   shadowColor(view.GetShadowColor());
+    const bool       underlineEnabled = view.IsUnderlineEnabled();
+    const uint16_t   outlineWidth     = view.GetOutlineWidth();
+    const Vector4&   outlineColor(view.GetOutlineColor());
+    const bool       isOutline            = 0u != outlineWidth;
+    const GlyphInfo* hyphens              = view.GetHyphens();
+    const Length*    hyphenIndices        = view.GetHyphenIndices();
+    const Length     hyphensCount         = view.GetHyphensCount();
+    const bool       strikethroughEnabled = view.IsStrikethroughEnabled();
+    const float      characterSpacing(view.GetCharacterSpacing());
+
+    // Elided text info. Indices according to elided text.
+    const auto startIndexOfGlyphs              = view.GetStartIndexOfElidedGlyphs();
+    const auto firstMiddleIndexOfElidedGlyphs  = view.GetFirstMiddleIndexOfElidedGlyphs();
+    const auto secondMiddleIndexOfElidedGlyphs = view.GetSecondMiddleIndexOfElidedGlyphs();
 
     const bool useDefaultColor = (NULL == colorsBuffer);
 
+    // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
+    TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+
     // Get the underline runs.
-    const Length     numberOfUnderlineRuns = view.GetNumberOfUnderlineRuns();
-    Vector<GlyphRun> underlineRuns;
+    const Length               numberOfUnderlineRuns = view.GetNumberOfUnderlineRuns();
+    Vector<UnderlinedGlyphRun> underlineRuns;
     underlineRuns.Resize(numberOfUnderlineRuns);
     view.GetUnderlineRuns(underlineRuns.Begin(),
                           0u,
                           numberOfUnderlineRuns);
 
-    bool thereAreUnderlinedGlyphs = false;
-
-    float  currentUnderlinePosition  = ZERO;
-    float  currentUnderlineThickness = underlineHeight;
-    FontId lastFontId                = 0;
-    FontId lastUnderlinedFontId      = 0;
-    Style  style                     = STYLE_NORMAL;
+    // Aggregate underline-style-properties from view
+    const UnderlineStyleProperties viewUnderlineProperties{view.GetUnderlineType(),
+                                                           view.GetUnderlineColor(),
+                                                           view.GetUnderlineHeight(),
+                                                           view.GetDashedUnderlineGap(),
+                                                           view.GetDashedUnderlineWidth(),
+                                                           true,
+                                                           true,
+                                                           true,
+                                                           true,
+                                                           true};
+
+    float maxUnderlineHeight = viewUnderlineProperties.height;
+
+    // Get the strikethrough runs.
+    const Length                  numberOfStrikethroughRuns = view.GetNumberOfStrikethroughRuns();
+    Vector<StrikethroughGlyphRun> strikethroughRuns;
+    strikethroughRuns.Resize(numberOfStrikethroughRuns);
+    view.GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
+
+    const StrikethroughStyleProperties viewStrikethroughProperties{view.GetStrikethroughColor(),
+                                                                   view.GetStrikethroughHeight(),
+                                                                   true,
+                                                                   true};
+
+    float maxStrikethroughHeight = viewStrikethroughProperties.height;
+
+    FontId lastFontId                  = 0;
+    Style  style                       = STYLE_NORMAL;
+    float  currentUnderlinePosition    = ZERO;
+    bool   thereAreUnderlinedGlyphs    = false;
+    bool   thereAreStrikethroughGlyphs = false;
 
     if(fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1)
     {
@@ -441,54 +497,111 @@ struct AtlasRenderer::Impl
     const GlyphInfo* const glyphsBuffer    = glyphs.Begin();
     const Vector2* const   positionsBuffer = positions.Begin();
     const Vector2          lineOffsetPosition(minLineOffset, 0.f);
+    uint32_t               hyphenIndex = 0;
+
+    //For septated underlined chunks. (this is for Markup case)
+    uint32_t                                     underlineChunkId = 0u;                            // give id for each chunk.
+    bool                                         isPreUnderlined  = false;                         // status of underlined for previous glyph.
+    std::map<uint32_t, UnderlineStyleProperties> mapUnderlineChunkIdWithProperties;                // mapping underlineChunkId with UnderlineStyleProperties to get properties of underlined chunk
+    UnderlineStyleProperties                     preUnderlineProperties = viewUnderlineProperties; // the previous UnderlineStyleProperties
+
+    //For septated strikethrough chunks. (this is for Markup case)
+    uint32_t                                         strikethroughChunkId = 0u;                                // give id for each chunk.
+    bool                                             isPreStrikethrough   = false;                             // status of strikethrough for previous glyph.
+    std::map<uint32_t, StrikethroughStyleProperties> mapStrikethroughChunkIdWithProperties;                    // mapping strikethroughChunkId with StrikethroughStyleProperties to get properties of strikethrough chunk
+    StrikethroughStyleProperties                     preStrikethroughProperties = viewStrikethroughProperties; // the previous StrikethroughStyleProperties
+
+    const Character*              textBuffer                = view.GetTextBuffer();
+    float                         calculatedAdvance         = 0.f;
+    const Vector<CharacterIndex>& glyphToCharacterMap       = view.GetGlyphsToCharacters();
+    const CharacterIndex*         glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
+
+    //Skip hyphenIndices less than startIndexOfGlyphs or between two middle of elided text
+    if(hyphenIndices)
+    {
+      while((hyphenIndex < hyphensCount) && (hyphenIndices[hyphenIndex] < startIndexOfGlyphs ||
+                                             (hyphenIndices[hyphenIndex] > firstMiddleIndexOfElidedGlyphs && hyphenIndices[hyphenIndex] < secondMiddleIndexOfElidedGlyphs)))
+      {
+        ++hyphenIndex;
+      }
+    }
 
+    //To keep the last fontMetrics of lastDecorativeLinesFontId
+    FontId      lastDecorativeLinesFontId = 0; // DecorativeLines like Undeline and Strikethrough
+    FontMetrics lastDecorativeLinesFontMetrics;
+    fontClient.GetFontMetrics(lastDecorativeLinesFontId, lastDecorativeLinesFontMetrics);
+
+    // Iteration on glyphs
     for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
     {
-      const GlyphInfo& glyph             = *(glyphsBuffer + i);
-      const bool       isGlyphUnderlined = underlineEnabled || IsGlyphUnderlined(i, underlineRuns);
-      thereAreUnderlinedGlyphs           = thereAreUnderlinedGlyphs || isGlyphUnderlined;
+      GlyphInfo glyph;
+      bool      addHyphen = ((hyphenIndex < hyphensCount) && hyphenIndices && ((i + startIndexOfGlyphs) == hyphenIndices[hyphenIndex]));
+      if(addHyphen && hyphens)
+      {
+        glyph = hyphens[hyphenIndex];
+        i--;
+      }
+      else
+      {
+        glyph = *(glyphsBuffer + i);
+      }
+
+      Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
+      const bool                                isGlyphUnderlined           = underlineEnabled || IsGlyphUnderlined(i, underlineRuns, currentUnderlinedGlyphRunIt);
+      const UnderlineStyleProperties            currentUnderlineProperties  = GetCurrentUnderlineProperties(i, isGlyphUnderlined, underlineRuns, currentUnderlinedGlyphRunIt, viewUnderlineProperties);
+      float                                     currentUnderlineHeight      = currentUnderlineProperties.height;
+      thereAreUnderlinedGlyphs                                              = thereAreUnderlinedGlyphs || isGlyphUnderlined;
+
+      Vector<StrikethroughGlyphRun>::ConstIterator currentStrikethroughGlyphRunIt = strikethroughRuns.End();
+      const bool                                   isGlyphStrikethrough           = strikethroughEnabled || IsGlyphStrikethrough(i, strikethroughRuns, currentStrikethroughGlyphRunIt);
+      const StrikethroughStyleProperties           currentStrikethroughProperties = GetCurrentStrikethroughProperties(i, isGlyphStrikethrough, strikethroughRuns, currentStrikethroughGlyphRunIt, viewStrikethroughProperties);
+      float                                        currentStrikethroughHeight     = currentStrikethroughProperties.height;
+      thereAreStrikethroughGlyphs                                                 = thereAreStrikethroughGlyphs || isGlyphStrikethrough;
 
       // No operation for white space
       if(glyph.width && glyph.height)
       {
-        // Are we still using the same fontId as previous
-        if(isGlyphUnderlined && (glyph.fontId != lastUnderlinedFontId))
+        // Check and update decorative-lines informations
+        if(isGlyphUnderlined || isGlyphStrikethrough)
         {
-          // We need to fetch fresh font underline metrics
-          FontMetrics fontMetrics;
-          mFontClient.GetFontMetrics(glyph.fontId, fontMetrics);
-          currentUnderlinePosition = ceil(fabsf(fontMetrics.underlinePosition));
-          const float descender    = ceil(fabsf(fontMetrics.descender));
-
-          if(fabsf(underlineHeight) < Math::MACHINE_EPSILON_1000)
+          bool isDecorativeLinesFontIdUpdated = false;
+          // Are we still using the same fontId as previous
+          if(glyph.fontId != lastDecorativeLinesFontId)
           {
-            currentUnderlineThickness = fontMetrics.underlineThickness;
+            // We need to fetch fresh font metrics
+            lastDecorativeLinesFontId      = glyph.fontId;
+            isDecorativeLinesFontIdUpdated = true;
+            fontClient.GetFontMetrics(lastDecorativeLinesFontId, lastDecorativeLinesFontMetrics);
 
-            // Ensure underline will be at least a pixel high
-            if(currentUnderlineThickness < ONE)
-            {
-              currentUnderlineThickness = ONE;
-            }
-            else
+            if(isGlyphStrikethrough || isGlyphUnderlined)
             {
-              currentUnderlineThickness = ceil(currentUnderlineThickness);
+              //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
+              currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(lastDecorativeLinesFontMetrics);
             }
           }
 
-          // Clamp the underline position at the font descender and check for ( as EFL describes it ) a broken font
-          if(currentUnderlinePosition > descender)
+          if(isGlyphUnderlined && (isDecorativeLinesFontIdUpdated || !(currentUnderlineProperties.IsHeightEqualTo(preUnderlineProperties))))
           {
-            currentUnderlinePosition = descender;
+            //If the Underline Height is changed then we need to recalculate height.
+            if(!(currentUnderlineProperties.IsHeightEqualTo(preUnderlineProperties)))
+            {
+              maxUnderlineHeight = currentUnderlineHeight;
+            }
+
+            CalcualteUnderlineHeight(lastDecorativeLinesFontMetrics, currentUnderlineHeight, maxUnderlineHeight);
           }
 
-          if(fabsf(currentUnderlinePosition) < Math::MACHINE_EPSILON_1000)
+          if(isGlyphStrikethrough && (isDecorativeLinesFontIdUpdated || !(currentStrikethroughProperties.IsHeightEqualTo(preStrikethroughProperties))))
           {
-            // Move offset down by one ( EFL behavior )
-            currentUnderlinePosition = ONE;
-          }
+            //If the Strikethrough Height is changed then we need to recalculate height.
+            if(!(currentStrikethroughProperties.IsHeightEqualTo(preStrikethroughProperties)))
+            {
+              maxStrikethroughHeight = currentStrikethroughHeight;
+            }
 
-          lastUnderlinedFontId = glyph.fontId;
-        } // underline
+            CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
+          }
+        } // decorative-lines
 
         AtlasGlyphManager::GlyphStyle style;
         style.isItalic = glyph.isItalicRequired;
@@ -505,8 +618,17 @@ struct AtlasRenderer::Impl
         }
 
         // Move the origin (0,0) of the mesh to the center of the actor
-        const Vector2& temp     = *(positionsBuffer + i);
-        const Vector2  position = Vector2(roundf(temp.x), temp.y) - halfTextSize - lineOffsetPosition; // roundf() avoids pixel alignment issues.
+        Vector2 position = *(positionsBuffer + i);
+
+        if(addHyphen)
+        {
+          GlyphInfo tempInfo = *(glyphsBuffer + i);
+          calculatedAdvance  = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + i))), characterSpacing, tempInfo.advance);
+          position.x         = position.x + calculatedAdvance - tempInfo.xBearing + glyph.xBearing;
+          position.y += tempInfo.yBearing - glyph.yBearing;
+        }
+
+        position = Vector2(roundf(position.x), position.y) - halfTextSize - lineOffsetPosition; // roundf() avoids pixel alignment issues.
 
         if(0u != slot.mImageId) // invalid slot id, glyph has failed to be added to atlas
         {
@@ -522,6 +644,18 @@ struct AtlasRenderer::Impl
           const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndicesBuffer + i);
           const Vector4&   color      = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + colorIndex - 1u);
 
+          //The new underlined chunk. Add new id if they are not consecutive indices (this is for Markup case)
+          // Examples: "Hello <u>World</u> Hello <u>World</u>", "<u>World</u> Hello <u>World</u>", "<u>   World</u> Hello <u>World</u>"
+          if((!isPreUnderlined && isGlyphUnderlined) || (isGlyphUnderlined && (preUnderlineProperties != currentUnderlineProperties)))
+          {
+            underlineChunkId++;
+            mapUnderlineChunkIdWithProperties.insert(std::pair<uint32_t, UnderlineStyleProperties>(underlineChunkId, currentUnderlineProperties));
+          }
+
+          //Keep status of underlined for previous glyph to check consecutive indices
+          isPreUnderlined        = isGlyphUnderlined;
+          preUnderlineProperties = currentUnderlineProperties;
+
           GenerateMesh(glyph,
                        positionPlusOutlineOffset,
                        color,
@@ -529,10 +663,43 @@ struct AtlasRenderer::Impl
                        slot,
                        isGlyphUnderlined,
                        currentUnderlinePosition,
-                       currentUnderlineThickness,
+                       maxUnderlineHeight,
                        meshContainer,
                        newTextCache,
-                       extents);
+                       extents,
+                       underlineChunkId,
+                       false,
+                       0u);
+
+          if(isGlyphStrikethrough)
+          {
+            //The new strikethrough chunk. Add new id if they are not consecutive indices (this is for Markup case)
+            // Examples: "Hello <s>World</s> Hello <s>World</s>", "<s>World</s> Hello <s>World</s>", "<s>   World</s> Hello <s>World</s>"
+            if((!isPreStrikethrough) || (preStrikethroughProperties != currentStrikethroughProperties))
+            {
+              strikethroughChunkId++;
+              mapStrikethroughChunkIdWithProperties.insert(std::pair<uint32_t, StrikethroughStyleProperties>(strikethroughChunkId, currentStrikethroughProperties));
+            }
+
+            GenerateMesh(glyph,
+                         positionPlusOutlineOffset,
+                         color,
+                         NO_OUTLINE,
+                         slot,
+                         isGlyphStrikethrough,
+                         0.0f,
+                         maxStrikethroughHeight,
+                         meshContainer,
+                         newTextCache,
+                         strikethroughExtents,
+                         0u,
+                         true,
+                         strikethroughChunkId);
+          }
+
+          //Keep status of Strikethrough for previous glyph to check consecutive indices
+          isPreStrikethrough         = isGlyphStrikethrough;
+          preStrikethroughProperties = currentStrikethroughProperties;
 
           lastFontId = glyph.fontId; // Prevents searching for existing blocksizes when string of the same fontId.
         }
@@ -546,12 +713,20 @@ struct AtlasRenderer::Impl
                        slotOutline,
                        false,
                        currentUnderlinePosition,
-                       currentUnderlineThickness,
+                       maxUnderlineHeight,
                        meshContainerOutline,
                        newTextCache,
-                       extents);
+                       extents,
+                       0u,
+                       false,
+                       0u);
         }
       }
+
+      if(addHyphen)
+      {
+        hyphenIndex++;
+      }
     } // glyphs
 
     // Now remove references for the old text
@@ -561,7 +736,13 @@ struct AtlasRenderer::Impl
     if(thereAreUnderlinedGlyphs)
     {
       // Check to see if any of the text needs an underline
-      GenerateUnderlines(meshContainer, extents, underlineColor);
+      GenerateUnderlines(meshContainer, extents, viewUnderlineProperties, mapUnderlineChunkIdWithProperties);
+    }
+
+    if(thereAreStrikethroughGlyphs)
+    {
+      // Check to see if any of the text needs a strikethrough
+      GenerateStrikethrough(meshContainer, strikethroughExtents, viewStrikethroughProperties, mapStrikethroughChunkIdWithProperties);
     }
 
     // For each MeshData object, create a mesh actor and add to the renderable actor
@@ -601,7 +782,7 @@ struct AtlasRenderer::Impl
 
     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "%s\n", metrics.mVerboseGlyphCounts.c_str());
 
-    for(uint32_t i = 0; i < metrics.mAtlasMetrics.mAtlasCount; ++i)
+    for(uint32_t i = 0; gLogFilter->IsEnabledFor(Debug::Verbose) && i < metrics.mAtlasMetrics.mAtlasCount; ++i)
     {
       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);
     }
@@ -698,10 +879,13 @@ struct AtlasRenderer::Impl
                       AtlasManager::Mesh2D&    newMesh,
                       Vector<Extent>&          extents,
                       float                    baseLine,
-                      bool                     underlineGlyph,
+                      bool                     decorationlineGlyph,
                       float                    underlinePosition,
-                      float                    underlineThickness,
-                      AtlasManager::AtlasSlot& slot)
+                      float                    lineThickness,
+                      AtlasManager::AtlasSlot& slot,
+                      uint32_t                 underlineChunkId,
+                      float                    strikethroughPosition,
+                      uint32_t                 strikethroughChunkId)
   {
     if(slot.mImageId)
     {
@@ -720,7 +904,7 @@ struct AtlasRenderer::Impl
           // Append the mesh to the existing mesh and adjust any extents
           Toolkit::Internal::AtlasMeshFactory::AppendMesh(mIt->mMesh, newMesh);
 
-          if(underlineGlyph)
+          if(decorationlineGlyph)
           {
             AdjustExtents(extents,
                           meshContainer,
@@ -729,7 +913,10 @@ struct AtlasRenderer::Impl
                           right,
                           baseLine,
                           underlinePosition,
-                          underlineThickness);
+                          lineThickness,
+                          underlineChunkId,
+                          strikethroughPosition,
+                          strikethroughChunkId);
           }
 
           return;
@@ -742,7 +929,7 @@ struct AtlasRenderer::Impl
       meshRecord.mMesh    = newMesh;
       meshContainer.push_back(meshRecord);
 
-      if(underlineGlyph)
+      if(decorationlineGlyph)
       {
         // Adjust extents for this new meshrecord
         AdjustExtents(extents,
@@ -752,7 +939,10 @@ struct AtlasRenderer::Impl
                       right,
                       baseLine,
                       underlinePosition,
-                      underlineThickness);
+                      lineThickness,
+                      underlineChunkId,
+                      strikethroughPosition,
+                      strikethroughChunkId);
       }
     }
   }
@@ -764,7 +954,10 @@ struct AtlasRenderer::Impl
                      float                    right,
                      float                    baseLine,
                      float                    underlinePosition,
-                     float                    underlineThickness)
+                     float                    lineThickness,
+                     uint32_t                 underlineChunkId,
+                     float                    strikethroughPosition,
+                     uint32_t                 strikethroughChunkId)
   {
     bool foundExtent = false;
     for(Vector<Extent>::Iterator eIt    = extents.Begin(),
@@ -772,7 +965,7 @@ struct AtlasRenderer::Impl
         eIt != eEndIt;
         ++eIt)
     {
-      if(Equals(baseLine, eIt->mBaseLine))
+      if(Equals(baseLine, eIt->mBaseLine) && underlineChunkId == eIt->mUnderlineChunkId && strikethroughChunkId == eIt->mStrikethroughChunkId)
       {
         foundExtent = true;
         if(left < eIt->mLeft)
@@ -788,21 +981,24 @@ struct AtlasRenderer::Impl
         {
           eIt->mUnderlinePosition = underlinePosition;
         }
-        if(underlineThickness > eIt->mUnderlineThickness)
+        if(lineThickness > eIt->mLineThickness)
         {
-          eIt->mUnderlineThickness = underlineThickness;
+          eIt->mLineThickness = lineThickness;
         }
       }
     }
     if(!foundExtent)
     {
       Extent extent;
-      extent.mLeft               = left;
-      extent.mRight              = right;
-      extent.mBaseLine           = baseLine;
-      extent.mUnderlinePosition  = underlinePosition;
-      extent.mUnderlineThickness = underlineThickness;
-      extent.mMeshRecordIndex    = index;
+      extent.mLeft                  = left;
+      extent.mRight                 = right;
+      extent.mBaseLine              = baseLine;
+      extent.mUnderlinePosition     = underlinePosition;
+      extent.mMeshRecordIndex       = index;
+      extent.mUnderlineChunkId      = underlineChunkId;
+      extent.mLineThickness         = lineThickness;
+      extent.mStrikethroughPosition = strikethroughPosition;
+      extent.mStrikethroughChunkId  = strikethroughChunkId;
       extents.PushBack(extent);
     }
   }
@@ -845,9 +1041,191 @@ struct AtlasRenderer::Impl
     }
   }
 
-  void GenerateUnderlines(std::vector<MeshRecord>& meshRecords,
-                          Vector<Extent>&          extents,
-                          const Vector4&           underlineColor)
+  void GenerateUnderlines(std::vector<MeshRecord>&                            meshRecords,
+                          Vector<Extent>&                                     extents,
+                          const UnderlineStyleProperties&                     viewUnderlineProperties,
+                          const std::map<uint32_t, UnderlineStyleProperties>& mapUnderlineChunkIdWithProperties)
+  {
+    AtlasManager::Mesh2D newMesh;
+    unsigned short       faceIndex = 0;
+
+    for(Vector<Extent>::ConstIterator eIt    = extents.Begin(),
+                                      eEndIt = extents.End();
+        eIt != eEndIt;
+        ++eIt)
+    {
+      AtlasManager::Vertex2D vert;
+      uint32_t               index = eIt->mMeshRecordIndex;
+      Vector2                uv    = mGlyphManager.GetAtlasSize(meshRecords[index].mAtlasId);
+
+      auto pairUnderlineChunkIdWithProperties = mapUnderlineChunkIdWithProperties.find(eIt->mUnderlineChunkId);
+
+      const UnderlineStyleProperties underlineProperties = (pairUnderlineChunkIdWithProperties == mapUnderlineChunkIdWithProperties.end())
+                                                             ? viewUnderlineProperties
+                                                             : pairUnderlineChunkIdWithProperties->second;
+
+      const Vector4&               underlineColor       = underlineProperties.colorDefined ? underlineProperties.color : viewUnderlineProperties.color;
+      const Text::Underline::Type& underlineType        = underlineProperties.typeDefined ? underlineProperties.type : viewUnderlineProperties.type;
+      const float&                 dashedUnderlineGap   = underlineProperties.dashGapDefined ? underlineProperties.dashGap : viewUnderlineProperties.dashGap;
+      const float&                 dashedUnderlineWidth = underlineProperties.dashWidthDefined ? underlineProperties.dashWidth : viewUnderlineProperties.dashWidth;
+
+      // Make sure we don't hit texture edge for single pixel texture ( filled pixel is in top left of every atlas )
+      float u           = HALF / uv.x;
+      float v           = HALF / uv.y;
+      float thickness   = eIt->mLineThickness;
+      float ShiftLineBy = (underlineType == Text::Underline::Type::DOUBLE) ? (thickness * ONE_AND_A_HALF) : (thickness * HALF);
+      float baseLine    = eIt->mBaseLine + eIt->mUnderlinePosition - ShiftLineBy;
+      float tlx         = eIt->mLeft;
+      float brx         = eIt->mRight;
+
+      if(underlineType == Text::Underline::Type::DASHED)
+      {
+        float dashTlx = tlx;
+        float dashBrx = tlx;
+
+        while((dashTlx >= tlx) && (dashTlx < brx) && ((dashTlx + dashedUnderlineWidth) <= brx))
+        {
+          dashBrx = dashTlx + dashedUnderlineWidth;
+
+          //The top left edge of the underline
+          vert.mPosition.x  = dashTlx;
+          vert.mPosition.y  = baseLine;
+          vert.mTexCoords.x = ZERO;
+          vert.mTexCoords.y = ZERO;
+          vert.mColor       = underlineColor;
+          newMesh.mVertices.PushBack(vert);
+
+          //The top right edge of the underline
+          vert.mPosition.x  = dashBrx;
+          vert.mPosition.y  = baseLine;
+          vert.mTexCoords.x = u;
+          vert.mColor       = underlineColor;
+          newMesh.mVertices.PushBack(vert);
+
+          //The bottom left edge of the underline
+          vert.mPosition.x  = dashTlx;
+          vert.mPosition.y  = baseLine + thickness;
+          vert.mTexCoords.x = ZERO;
+          vert.mTexCoords.y = v;
+          vert.mColor       = underlineColor;
+          newMesh.mVertices.PushBack(vert);
+
+          //The bottom right edge of the underline
+          vert.mPosition.x  = dashBrx;
+          vert.mPosition.y  = baseLine + thickness;
+          vert.mTexCoords.x = u;
+          vert.mColor       = underlineColor;
+          newMesh.mVertices.PushBack(vert);
+
+          dashTlx = dashBrx + dashedUnderlineGap; // The next dash will start at the right of the current dash plus the gap
+
+          // Six indices in counter clockwise winding
+          newMesh.mIndices.PushBack(faceIndex + 1u);
+          newMesh.mIndices.PushBack(faceIndex);
+          newMesh.mIndices.PushBack(faceIndex + 2u);
+          newMesh.mIndices.PushBack(faceIndex + 2u);
+          newMesh.mIndices.PushBack(faceIndex + 3u);
+          newMesh.mIndices.PushBack(faceIndex + 1u);
+
+          faceIndex += 4;
+
+          Toolkit::Internal::AtlasMeshFactory::AppendMesh(meshRecords[index].mMesh, newMesh);
+        }
+      }
+      else
+      {
+        // It's either SOLID or DOUBLE so we need to generate the first solid underline anyway.
+        vert.mPosition.x  = tlx;
+        vert.mPosition.y  = baseLine;
+        vert.mTexCoords.x = ZERO;
+        vert.mTexCoords.y = ZERO;
+        vert.mColor       = underlineColor;
+        newMesh.mVertices.PushBack(vert);
+
+        vert.mPosition.x  = brx;
+        vert.mPosition.y  = baseLine;
+        vert.mTexCoords.x = u;
+        vert.mColor       = underlineColor;
+        newMesh.mVertices.PushBack(vert);
+
+        vert.mPosition.x  = tlx;
+        vert.mPosition.y  = baseLine + thickness;
+        vert.mTexCoords.x = ZERO;
+        vert.mTexCoords.y = v;
+        vert.mColor       = underlineColor;
+        newMesh.mVertices.PushBack(vert);
+
+        vert.mPosition.x  = brx;
+        vert.mPosition.y  = baseLine + thickness;
+        vert.mTexCoords.x = u;
+        vert.mColor       = underlineColor;
+        newMesh.mVertices.PushBack(vert);
+
+        // Six indices in counter clockwise winding
+        newMesh.mIndices.PushBack(faceIndex + 1u);
+        newMesh.mIndices.PushBack(faceIndex);
+        newMesh.mIndices.PushBack(faceIndex + 2u);
+        newMesh.mIndices.PushBack(faceIndex + 2u);
+        newMesh.mIndices.PushBack(faceIndex + 3u);
+        newMesh.mIndices.PushBack(faceIndex + 1u);
+        faceIndex += 4;
+
+        Toolkit::Internal::AtlasMeshFactory::AppendMesh(meshRecords[index].mMesh, newMesh);
+
+        if(underlineType == Text::Underline::Type::DOUBLE)
+        {
+          baseLine += 2 * thickness;
+
+          //The top left edge of the underline
+          vert.mPosition.x  = tlx;
+          vert.mPosition.y  = baseLine; // Vertical start of the second underline
+          vert.mTexCoords.x = ZERO;
+          vert.mTexCoords.y = ZERO;
+          vert.mColor       = underlineColor;
+          newMesh.mVertices.PushBack(vert);
+
+          //The top right edge of the underline
+          vert.mPosition.x  = brx;
+          vert.mPosition.y  = baseLine;
+          vert.mTexCoords.x = u;
+          vert.mColor       = underlineColor;
+          newMesh.mVertices.PushBack(vert);
+
+          //The bottom left edge of the underline
+          vert.mPosition.x  = tlx;
+          vert.mPosition.y  = baseLine + thickness; // Vertical End of the second underline
+          vert.mTexCoords.x = ZERO;
+          vert.mTexCoords.y = v;
+          vert.mColor       = underlineColor;
+          newMesh.mVertices.PushBack(vert);
+
+          //The bottom right edge of the underline
+          vert.mPosition.x  = brx;
+          vert.mPosition.y  = baseLine + thickness;
+          vert.mTexCoords.x = u;
+          vert.mColor       = underlineColor;
+          newMesh.mVertices.PushBack(vert);
+
+          // Six indices in counter clockwise winding
+          newMesh.mIndices.PushBack(faceIndex + 1u);
+          newMesh.mIndices.PushBack(faceIndex);
+          newMesh.mIndices.PushBack(faceIndex + 2u);
+          newMesh.mIndices.PushBack(faceIndex + 2u);
+          newMesh.mIndices.PushBack(faceIndex + 3u);
+          newMesh.mIndices.PushBack(faceIndex + 1u);
+
+          faceIndex += 4;
+
+          Toolkit::Internal::AtlasMeshFactory::AppendMesh(meshRecords[index].mMesh, newMesh);
+        }
+      }
+    }
+  }
+
+  void GenerateStrikethrough(std::vector<MeshRecord>&                                meshRecords,
+                             Vector<Extent>&                                         extents,
+                             const StrikethroughStyleProperties&                     viewStrikethroughProperties,
+                             const std::map<uint32_t, StrikethroughStyleProperties>& mapStrikethroughChunkIdWithProperties)
   {
     AtlasManager::Mesh2D newMesh;
     unsigned short       faceIndex = 0;
@@ -860,38 +1238,46 @@ struct AtlasRenderer::Impl
       uint32_t               index = eIt->mMeshRecordIndex;
       Vector2                uv    = mGlyphManager.GetAtlasSize(meshRecords[index].mAtlasId);
 
+      auto pairStrikethroughChunkIdWithProperties = mapStrikethroughChunkIdWithProperties.find(eIt->mStrikethroughChunkId);
+
+      const StrikethroughStyleProperties strikethroughProperties = (pairStrikethroughChunkIdWithProperties == mapStrikethroughChunkIdWithProperties.end())
+                                                                     ? viewStrikethroughProperties
+                                                                     : pairStrikethroughChunkIdWithProperties->second;
+
+      const Vector4& strikethroughColor = strikethroughProperties.colorDefined ? strikethroughProperties.color : viewStrikethroughProperties.color;
+
       // Make sure we don't hit texture edge for single pixel texture ( filled pixel is in top left of every atlas )
-      float u         = HALF / uv.x;
-      float v         = HALF / uv.y;
-      float thickness = eIt->mUnderlineThickness;
-      float baseLine  = eIt->mBaseLine + eIt->mUnderlinePosition - (thickness * HALF);
-      float tlx       = eIt->mLeft;
-      float brx       = eIt->mRight;
+      float u                     = HALF / uv.x;
+      float v                     = HALF / uv.y;
+      float thickness             = eIt->mLineThickness;
+      float tlx                   = eIt->mLeft;
+      float brx                   = eIt->mRight;
+      float strikethroughPosition = eIt->mStrikethroughPosition;
 
       vert.mPosition.x  = tlx;
-      vert.mPosition.y  = baseLine;
+      vert.mPosition.y  = strikethroughPosition;
       vert.mTexCoords.x = ZERO;
       vert.mTexCoords.y = ZERO;
-      vert.mColor       = underlineColor;
+      vert.mColor       = strikethroughColor;
       newMesh.mVertices.PushBack(vert);
 
       vert.mPosition.x  = brx;
-      vert.mPosition.y  = baseLine;
+      vert.mPosition.y  = strikethroughPosition;
       vert.mTexCoords.x = u;
-      vert.mColor       = underlineColor;
+      vert.mColor       = strikethroughColor;
       newMesh.mVertices.PushBack(vert);
 
       vert.mPosition.x  = tlx;
-      vert.mPosition.y  = baseLine + thickness;
+      vert.mPosition.y  = strikethroughPosition + thickness;
       vert.mTexCoords.x = ZERO;
       vert.mTexCoords.y = v;
-      vert.mColor       = underlineColor;
+      vert.mColor       = strikethroughColor;
       newMesh.mVertices.PushBack(vert);
 
       vert.mPosition.x  = brx;
-      vert.mPosition.y  = baseLine + thickness;
+      vert.mPosition.y  = strikethroughPosition + thickness;
       vert.mTexCoords.x = u;
-      vert.mColor       = underlineColor;
+      vert.mColor       = strikethroughColor;
       newMesh.mVertices.PushBack(vert);
 
       // Six indices in counter clockwise winding