Upload tiling text texture without copy
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / text / text-visual.cpp
index 870ac09..14e1ce3 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.
 
 // EXTERNAL INCLUDES
 #include <dali/devel-api/adaptor-framework/image-loading.h>
-#include <dali/devel-api/adaptor-framework/window-devel.h>
 #include <dali/devel-api/images/pixel-data-devel.h>
 #include <dali/devel-api/rendering/renderer-devel.h>
+#include <dali/devel-api/rendering/texture-devel.h>
 #include <dali/devel-api/text-abstraction/text-abstraction-definitions.h>
-#include <dali/public-api/animation/constraints.h>
+#include <dali/integration-api/debug.h>
 #include <string.h>
 
 // INTERNAL HEADER
@@ -51,6 +51,8 @@ namespace Internal
 {
 namespace
 {
+const int CUSTOM_PROPERTY_COUNT(5); // anim,premul,size,offset,multicol
+
 const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
 
 /**
@@ -147,9 +149,9 @@ void OpacityConstraint(float& current, const PropertyInputContainer& inputs)
 
 } // unnamed namespace
 
-TextVisualPtr TextVisual::New(VisualFactoryCache& factoryCache, const Property::Map& properties)
+TextVisualPtr TextVisual::New(VisualFactoryCache& factoryCache, TextVisualShaderFactory& shaderFactory, const Property::Map& properties)
 {
-  TextVisualPtr textVisualPtr(new TextVisual(factoryCache));
+  TextVisualPtr textVisualPtr(new TextVisual(factoryCache, shaderFactory));
   textVisualPtr->SetProperties(properties);
   textVisualPtr->Initialize();
   return textVisualPtr;
@@ -225,6 +227,9 @@ void TextVisual::DoCreatePropertyMap(Property::Map& map) const
 
   GetBackgroundProperties(mController, value, Text::EffectStyle::DEFAULT);
   map.Insert(Toolkit::DevelTextVisual::Property::BACKGROUND, value);
+
+  GetStrikethroughProperties(mController, value, Text::EffectStyle::DEFAULT);
+  map.Insert(Toolkit::DevelTextVisual::Property::STRIKETHROUGH, value);
 }
 
 void TextVisual::DoCreateInstancePropertyMap(Property::Map& map) const
@@ -236,13 +241,27 @@ void TextVisual::DoCreateInstancePropertyMap(Property::Map& map) const
   map.Insert(Toolkit::TextVisual::Property::TEXT, text);
 }
 
-TextVisual::TextVisual(VisualFactoryCache& factoryCache)
+void TextVisual::EnablePreMultipliedAlpha(bool preMultiplied)
+{
+  // Make always enable pre multiplied alpha whether preMultiplied value is false.
+  if(!preMultiplied)
+  {
+    DALI_LOG_WARNING("Note : TextVisual cannot disable PreMultipliedAlpha\n");
+  }
+}
+
+TextVisual::TextVisual(VisualFactoryCache& factoryCache, TextVisualShaderFactory& shaderFactory)
 : Visual::Base(factoryCache, Visual::FittingMode::FIT_KEEP_ASPECT_RATIO, Toolkit::Visual::TEXT),
   mController(Text::Controller::New()),
   mTypesetter(Text::Typesetter::New(mController->GetTextModel())),
+  mTextVisualShaderFactory(shaderFactory),
+  mTextShaderFeatureCache(),
   mAnimatableTextColorPropertyIndex(Property::INVALID_INDEX),
+  mTextColorAnimatableIndex(Property::INVALID_INDEX),
   mRendererUpdateNeeded(false)
 {
+  // Enable the pre-multiplied alpha to improve the text quality
+  mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
 }
 
 TextVisual::~TextVisual()
@@ -252,9 +271,10 @@ TextVisual::~TextVisual()
 void TextVisual::OnInitialize()
 {
   Geometry geometry = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY);
-  Shader   shader   = GetTextShader(mFactoryCache, TextType::SINGLE_COLOR_TEXT, TextType::NO_EMOJI, TextType::NO_STYLES);
+  Shader   shader   = GetTextShader(mFactoryCache, TextVisualShaderFeature::FeatureBuilder());
 
-  mImpl->mRenderer = Renderer::New(geometry, shader);
+  mImpl->mRenderer = VisualRenderer::New(geometry, shader);
+  mImpl->mRenderer.ReserveCustomProperties(CUSTOM_PROPERTY_COUNT);
 }
 
 void TextVisual::DoSetProperties(const Property::Map& propertyMap)
@@ -289,33 +309,42 @@ void TextVisual::DoSetOnScene(Actor& actor)
 
   mImpl->mRenderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, Toolkit::DepthIndex::CONTENT);
 
-  // Enable the pre-multiplied alpha to improve the text quality
-  EnablePreMultipliedAlpha(true);
-
-  const Vector4&        defaultColor         = mController->GetTextModel()->GetDefaultColor();
-  Dali::Property::Index shaderTextColorIndex = mImpl->mRenderer.RegisterProperty("uTextColorAnimatable", defaultColor);
+  const Vector4& defaultColor = mController->GetTextModel()->GetDefaultColor();
+  if(mTextColorAnimatableIndex == Property::INVALID_INDEX)
+  {
+    mTextColorAnimatableIndex = mImpl->mRenderer.RegisterUniqueProperty("uTextColorAnimatable", defaultColor);
+  }
+  else
+  {
+    mImpl->mRenderer.SetProperty(mTextColorAnimatableIndex, defaultColor);
+  }
 
   if(mAnimatableTextColorPropertyIndex != Property::INVALID_INDEX)
   {
     // Create constraint for the animatable text's color Property with uTextColorAnimatable in the renderer.
-    if(shaderTextColorIndex != Property::INVALID_INDEX)
+    if(mTextColorAnimatableIndex != Property::INVALID_INDEX)
     {
-      Constraint colorConstraint = Constraint::New<Vector4>(mImpl->mRenderer, shaderTextColorIndex, TextColorConstraint);
-      colorConstraint.AddSource(Source(actor, mAnimatableTextColorPropertyIndex));
-      colorConstraint.Apply();
-
-      // Make zero if the alpha value of text color is zero to skip rendering text
-      Constraint opacityConstraint = Constraint::New<float>(mImpl->mRenderer, Dali::DevelRenderer::Property::OPACITY, OpacityConstraint);
-      opacityConstraint.AddSource(Source(actor, mAnimatableTextColorPropertyIndex));
-      opacityConstraint.Apply();
+      if(!mColorConstraint)
+      {
+        mColorConstraint = Constraint::New<Vector4>(mImpl->mRenderer, mTextColorAnimatableIndex, TextColorConstraint);
+        mColorConstraint.AddSource(Source(actor, mAnimatableTextColorPropertyIndex));
+      }
+      mColorConstraint.Apply();
     }
+
+    // Make zero if the alpha value of text color is zero to skip rendering text
+    if(!mOpacityConstraint)
+    {
+      // VisualRenderer::Property::OPACITY uses same animatable property internally.
+      mOpacityConstraint = Constraint::New<float>(mImpl->mRenderer, Dali::DevelRenderer::Property::OPACITY, OpacityConstraint);
+      mOpacityConstraint.AddSource(Source(actor, mAnimatableTextColorPropertyIndex));
+    }
+    mOpacityConstraint.Apply();
   }
 
   // Renderer needs textures and to be added to control
   mRendererUpdateNeeded = true;
 
-  mRendererList.push_back(mImpl->mRenderer);
-
   UpdateRenderer();
 }
 
@@ -336,6 +365,15 @@ void TextVisual::RemoveRenderer(Actor& actor)
 
 void TextVisual::DoSetOffScene(Actor& actor)
 {
+  if(mColorConstraint)
+  {
+    mColorConstraint.Remove();
+  }
+  if(mOpacityConstraint)
+  {
+    mOpacityConstraint.Remove();
+  }
+
   RemoveRenderer(actor);
 
   // Resets the control handle.
@@ -439,6 +477,11 @@ void TextVisual::DoSetProperty(Dali::Property::Index index, const Dali::Property
       SetBackgroundProperties(mController, propertyValue, Text::EffectStyle::DEFAULT);
       break;
     }
+    case Toolkit::DevelTextVisual::Property::STRIKETHROUGH:
+    {
+      SetStrikethroughProperties(mController, propertyValue, Text::EffectStyle::DEFAULT);
+      break;
+    }
   }
 }
 
@@ -474,15 +517,7 @@ void TextVisual::UpdateRenderer()
     return;
   }
 
-  Dali::LayoutDirection::Type layoutDirection;
-  if(mController->IsMatchSystemLanguageDirection())
-  {
-    layoutDirection = static_cast<Dali::LayoutDirection::Type>(DevelWindow::Get(control).GetRootLayer().GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get<int>());
-  }
-  else
-  {
-    layoutDirection = static_cast<Dali::LayoutDirection::Type>(control.GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get<int>());
-  }
+  Dali::LayoutDirection::Type layoutDirection = mController->GetLayoutDirection(control);
 
   const Text::Controller::UpdateTextType updateTextType = mController->Relayout(relayoutSize, layoutDirection);
 
@@ -528,14 +563,17 @@ void TextVisual::UpdateRenderer()
         shadowEnabled = true;
       }
 
-      const bool underlineEnabled       = mController->GetTextModel()->IsUnderlineEnabled();
-      const bool outlineEnabled         = (mController->GetTextModel()->GetOutlineWidth() > Math::MACHINE_EPSILON_1);
-      const bool backgroundEnabled      = mController->GetTextModel()->IsBackgroundEnabled();
-      const bool markupProcessorEnabled = mController->IsMarkupProcessorEnabled();
-
-      const bool styleEnabled = (shadowEnabled || underlineEnabled || outlineEnabled || backgroundEnabled || markupProcessorEnabled);
+      const bool outlineEnabled             = (mController->GetTextModel()->GetOutlineWidth() > Math::MACHINE_EPSILON_1);
+      const bool backgroundEnabled          = mController->GetTextModel()->IsBackgroundEnabled();
+      const bool markupProcessorEnabled     = mController->IsMarkupProcessorEnabled();
+      const bool markupUnderlineEnabled     = markupProcessorEnabled && mController->GetTextModel()->IsMarkupUnderlineSet();
+      const bool markupStrikethroughEnabled = markupProcessorEnabled && mController->GetTextModel()->IsMarkupStrikethroughSet();
+      const bool underlineEnabled           = mController->GetTextModel()->IsUnderlineEnabled() || markupUnderlineEnabled;
+      const bool strikethroughEnabled       = mController->GetTextModel()->IsStrikethroughEnabled() || markupStrikethroughEnabled;
+      const bool styleEnabled               = (shadowEnabled || outlineEnabled || backgroundEnabled || markupProcessorEnabled);
+      const bool isOverlayStyle             = underlineEnabled || strikethroughEnabled;
 
-      AddRenderer(control, relayoutSize, hasMultipleTextColors, containsColorGlyph, styleEnabled);
+      AddRenderer(control, relayoutSize, hasMultipleTextColors, containsColorGlyph, styleEnabled, isOverlayStyle);
 
       // Text rendered and ready to display
       ResourceReady(Toolkit::Visual::ResourceStatus::READY);
@@ -555,68 +593,70 @@ void TextVisual::AddTexture(TextureSet& textureSet, PixelData& data, Sampler& sa
   textureSet.SetSampler(textureSetIndex, sampler);
 }
 
-PixelData TextVisual::ConvertToPixelData(unsigned char* buffer, int width, int height, int offsetPosition, const Pixel::Format textPixelFormat)
+void TextVisual::AddTilingTexture(TextureSet& textureSet, TilingInfo& tilingInfo, PixelData& data, Sampler& sampler, unsigned int textureSetIndex)
 {
-  int            bpp        = Pixel::GetBytesPerPixel(textPixelFormat);
-  unsigned int   bufferSize = width * height * bpp;
-  unsigned char* dstBuffer  = static_cast<unsigned char*>(malloc(bufferSize));
-  memcpy(dstBuffer, buffer + offsetPosition * bpp, bufferSize);
-  PixelData pixelData = Dali::PixelData::New(dstBuffer,
-                                             bufferSize,
-                                             width,
-                                             height,
-                                             textPixelFormat,
-                                             Dali::PixelData::FREE);
-  return pixelData;
+  Texture texture = Texture::New(Dali::TextureType::TEXTURE_2D,
+                                 tilingInfo.textPixelFormat,
+                                 tilingInfo.width,
+                                 tilingInfo.height);
+  DevelTexture::UploadSubPixelData(texture, data, 0u, tilingInfo.offsetHeight, tilingInfo.width, tilingInfo.height);
+
+  textureSet.SetTexture(textureSetIndex, texture);
+  textureSet.SetSampler(textureSetIndex, sampler);
 }
 
-void TextVisual::CreateTextureSet(TilingInfo& info, Renderer& renderer, Sampler& sampler, bool hasMultipleTextColors, bool containsColorGlyph, bool styleEnabled)
+void TextVisual::CreateTextureSet(TilingInfo& info, VisualRenderer& renderer, Sampler& sampler)
 {
-  TextureSet   textureSet      = TextureSet::New();
-  unsigned int textureSetIndex = 0u;
+  TextureSet textureSet      = TextureSet::New();
+  uint32_t   textureSetIndex = 0u;
 
   // Convert the buffer to pixel data to make it a texture.
-  if(info.textBuffer)
+
+  if(info.textPixelData)
   {
-    PixelData data = ConvertToPixelData(info.textBuffer, info.width, info.height, info.offsetPosition, info.textPixelFormat);
-    AddTexture(textureSet, data, sampler, textureSetIndex);
+    AddTilingTexture(textureSet, info, info.textPixelData, sampler, textureSetIndex);
     ++textureSetIndex;
   }
 
-  if(styleEnabled && info.styleBuffer)
+  if(mTextShaderFeatureCache.IsEnabledStyle() && info.stylePixelData)
   {
-    PixelData styleData = ConvertToPixelData(info.styleBuffer, info.width, info.height, info.offsetPosition, Pixel::RGBA8888);
-    AddTexture(textureSet, styleData, sampler, textureSetIndex);
+    AddTilingTexture(textureSet, info, info.stylePixelData, sampler, textureSetIndex);
     ++textureSetIndex;
   }
 
-  if(containsColorGlyph && !hasMultipleTextColors && info.maskBuffer)
+  if(mTextShaderFeatureCache.IsEnabledOverlay() && info.overlayStylePixelData)
   {
-    PixelData maskData = ConvertToPixelData(info.maskBuffer, info.width, info.height, info.offsetPosition, Pixel::L8);
-    AddTexture(textureSet, maskData, sampler, textureSetIndex);
+    AddTilingTexture(textureSet, info, info.overlayStylePixelData, sampler, textureSetIndex);
+    ++textureSetIndex;
+  }
+
+  if(mTextShaderFeatureCache.IsEnabledEmoji() && !mTextShaderFeatureCache.IsEnabledMultiColor() && info.maskPixelData)
+  {
+    AddTilingTexture(textureSet, info, info.maskPixelData, sampler, textureSetIndex);
+    ++textureSetIndex;
   }
 
   renderer.SetTextures(textureSet);
 
   //Register transform properties
-  mImpl->mTransform.RegisterUniforms(renderer, Direction::LEFT_TO_RIGHT);
+  mImpl->mTransform.SetUniforms(renderer, Direction::LEFT_TO_RIGHT);
 
   // Enable the pre-multiplied alpha to improve the text quality
   renderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, true);
   renderer.RegisterProperty(PREMULTIPLIED_ALPHA, 1.0f);
 
   // Set size and offset for the tiling.
-  renderer.RegisterProperty(SIZE, Vector2(info.width, info.height));
-  renderer.RegisterProperty(OFFSET, Vector2(info.offSet.x, info.offSet.y));
-  renderer.RegisterProperty("uHasMultipleTextColors", static_cast<float>(hasMultipleTextColors));
+  renderer.SetProperty(VisualRenderer::Property::TRANSFORM_SIZE, Vector2(info.width, info.height));
+  renderer.SetProperty(VisualRenderer::Property::TRANSFORM_OFFSET, info.transformOffset);
   renderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
+  renderer.RegisterProperty("uHasMultipleTextColors", static_cast<float>(mTextShaderFeatureCache.IsEnabledMultiColor()));
 
   mRendererList.push_back(renderer);
 }
 
-void TextVisual::AddRenderer(Actor& actor, const Vector2& size, bool hasMultipleTextColors, bool containsColorGlyph, bool styleEnabled)
+void TextVisual::AddRenderer(Actor& actor, const Vector2& size, bool hasMultipleTextColors, bool containsColorGlyph, bool styleEnabled, bool isOverlayStyle)
 {
-  Shader shader = GetTextShader(mFactoryCache, hasMultipleTextColors, containsColorGlyph, styleEnabled);
+  Shader shader = GetTextShader(mFactoryCache, TextVisualShaderFeature::FeatureBuilder().EnableMultiColor(hasMultipleTextColors).EnableEmoji(containsColorGlyph).EnableStyle(styleEnabled).EnableOverlay(isOverlayStyle));
   mImpl->mRenderer.SetShader(shader);
 
   // Get the maximum size.
@@ -625,11 +665,11 @@ void TextVisual::AddRenderer(Actor& actor, const Vector2& size, bool hasMultiple
   // No tiling required. Use the default renderer.
   if(size.height < maxTextureSize)
   {
-    TextureSet textureSet = GetTextTexture(size, hasMultipleTextColors, containsColorGlyph, styleEnabled);
+    TextureSet textureSet = GetTextTexture(size);
 
     mImpl->mRenderer.SetTextures(textureSet);
     //Register transform properties
-    mImpl->mTransform.RegisterUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
+    mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
     mImpl->mRenderer.RegisterProperty("uHasMultipleTextColors", static_cast<float>(hasMultipleTextColors));
     mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
 
@@ -657,24 +697,25 @@ void TextVisual::AddRenderer(Actor& actor, const Vector2& size, bool hasMultiple
     // Set information for creating textures.
     TilingInfo info(verifiedWidth, maxTextureSize, textPixelFormat);
 
-    // Get the buffer of text.
-    Dali::DevelPixelData::PixelDataBuffer textPixelData = Dali::DevelPixelData::ReleasePixelDataBuffer(data);
-    info.textBuffer                                     = textPixelData.buffer;
+    // Get the pixel data of text.
+    info.textPixelData = data;
 
-    if(styleEnabled)
+    if(mTextShaderFeatureCache.IsEnabledStyle())
     {
       // Create RGBA texture for all the text styles (without the text itself)
-      PixelData                             styleData      = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_NO_TEXT, false, Pixel::RGBA8888);
-      Dali::DevelPixelData::PixelDataBuffer stylePixelData = Dali::DevelPixelData::ReleasePixelDataBuffer(styleData);
-      info.styleBuffer                                     = stylePixelData.buffer;
+      info.stylePixelData = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_NO_TEXT, false, Pixel::RGBA8888);
+    }
+
+    if(mTextShaderFeatureCache.IsEnabledOverlay())
+    {
+      // Create RGBA texture for all the overlay styles
+      info.overlayStylePixelData = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_OVERLAY_STYLE, false, Pixel::RGBA8888);
     }
 
-    if(containsColorGlyph && !hasMultipleTextColors)
+    if(mTextShaderFeatureCache.IsEnabledEmoji() && !mTextShaderFeatureCache.IsEnabledMultiColor())
     {
       // Create a L8 texture as a mask to avoid color glyphs (e.g. emojis) to be affected by text color animation
-      PixelData                             maskData      = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_MASK, false, Pixel::L8);
-      Dali::DevelPixelData::PixelDataBuffer maskPixelData = Dali::DevelPixelData::ReleasePixelDataBuffer(maskData);
-      info.maskBuffer                                     = maskPixelData.buffer;
+      info.maskPixelData = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_MASK, false, Pixel::L8);
     }
 
     // Get the current offset for recalculate the offset when tiling.
@@ -683,30 +724,29 @@ void TextVisual::AddRenderer(Actor& actor, const Vector2& size, bool hasMultiple
     Property::Value* offsetValue = retMap.Find(Dali::Toolkit::Visual::Transform::Property::OFFSET);
     if(offsetValue)
     {
-      offsetValue->Get(info.offSet);
+      offsetValue->Get(info.transformOffset);
     }
 
     // Create a textureset in the default renderer.
-    CreateTextureSet(info, mImpl->mRenderer, sampler, hasMultipleTextColors, containsColorGlyph, styleEnabled);
+    CreateTextureSet(info, mImpl->mRenderer, sampler);
 
     verifiedHeight -= maxTextureSize;
 
     Geometry geometry = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY);
 
-    int offsetPosition = verifiedWidth * maxTextureSize;
     // Create a renderer by cutting maxTextureSize.
     while(verifiedHeight > 0)
     {
-      Renderer tilingRenderer = Renderer::New(geometry, shader);
+      VisualRenderer tilingRenderer = VisualRenderer::New(geometry, shader);
       tilingRenderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, Toolkit::DepthIndex::CONTENT);
       // New offset position of buffer for tiling.
-      info.offsetPosition += offsetPosition;
+      info.offsetHeight += maxTextureSize;
       // New height for tiling.
       info.height = (verifiedHeight - maxTextureSize) > 0 ? maxTextureSize : verifiedHeight;
       // New offset for tiling.
-      info.offSet.y += maxTextureSize;
+      info.transformOffset.y += maxTextureSize;
       // Create a textureset int the new tiling renderer.
-      CreateTextureSet(info, tilingRenderer, sampler, hasMultipleTextColors, containsColorGlyph, styleEnabled);
+      CreateTextureSet(info, tilingRenderer, sampler);
 
       verifiedHeight -= maxTextureSize;
     }
@@ -714,17 +754,43 @@ void TextVisual::AddRenderer(Actor& actor, const Vector2& size, bool hasMultiple
 
   mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
 
+  const Vector4& defaultColor = mController->GetTextModel()->GetDefaultColor();
+
   for(RendererContainer::iterator iter = mRendererList.begin(); iter != mRendererList.end(); ++iter)
   {
     Renderer renderer = (*iter);
     if(renderer)
     {
       actor.AddRenderer(renderer);
+
+      if(renderer != mImpl->mRenderer)
+      {
+        // Set constraint for text label's color for non-default renderers.
+        if(mAnimatableTextColorPropertyIndex != Property::INVALID_INDEX)
+        {
+          // Register unique property, or get property for default renderer.
+          Property::Index index = renderer.RegisterUniqueProperty("uTextColorAnimatable", defaultColor);
+
+          // Create constraint for the animatable text's color Property with uTextColorAnimatable in the renderer.
+          if(index != Property::INVALID_INDEX)
+          {
+            Constraint colorConstraint = Constraint::New<Vector4>(renderer, index, TextColorConstraint);
+            colorConstraint.AddSource(Source(actor, mAnimatableTextColorPropertyIndex));
+            colorConstraint.Apply();
+          }
+
+          // Make zero if the alpha value of text color is zero to skip rendering text
+          // VisualRenderer::Property::OPACITY uses same animatable property internally.
+          Constraint opacityConstraint = Constraint::New<float>(renderer, Dali::DevelRenderer::Property::OPACITY, OpacityConstraint);
+          opacityConstraint.AddSource(Source(actor, mAnimatableTextColorPropertyIndex));
+          opacityConstraint.Apply();
+        }
+      }
     }
   }
 }
 
-TextureSet TextVisual::GetTextTexture(const Vector2& size, bool hasMultipleTextColors, bool containsColorGlyph, bool styleEnabled)
+TextureSet TextVisual::GetTextTexture(const Vector2& size)
 {
   // Filter mode needs to be set to linear to produce better quality while scaling.
   Sampler sampler = Sampler::New();
@@ -733,7 +799,7 @@ TextureSet TextVisual::GetTextTexture(const Vector2& size, bool hasMultipleTextC
   TextureSet textureSet = TextureSet::New();
 
   // Create RGBA texture if the text contains emojis or multiple text colors, otherwise L8 texture
-  Pixel::Format textPixelFormat = (containsColorGlyph || hasMultipleTextColors) ? Pixel::RGBA8888 : Pixel::L8;
+  Pixel::Format textPixelFormat = (mTextShaderFeatureCache.IsEnabledEmoji() || mTextShaderFeatureCache.IsEnabledMultiColor()) ? Pixel::RGBA8888 : Pixel::L8;
 
   // Check the text direction
   Toolkit::DevelText::TextDirection::Type textDirection = mController->GetTextDirection();
@@ -741,23 +807,27 @@ TextureSet TextVisual::GetTextTexture(const Vector2& size, bool hasMultipleTextC
   // Create a texture for the text without any styles
   PixelData data = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_NO_STYLES, false, textPixelFormat);
 
-  // It may happen the image atlas can't handle a pixel data it exceeds the maximum size.
-  // In that case, create a texture. TODO: should tile the text.
-  unsigned int textureSetIndex = 0u;
-
+  uint32_t textureSetIndex = 0u;
   AddTexture(textureSet, data, sampler, textureSetIndex);
   ++textureSetIndex;
 
-  if(styleEnabled)
+  if(mTextShaderFeatureCache.IsEnabledStyle())
   {
-    // Create RGBA texture for all the text styles (without the text itself)
+    // Create RGBA texture for all the text styles that render in the background (without the text itself)
     PixelData styleData = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_NO_TEXT, false, Pixel::RGBA8888);
-
     AddTexture(textureSet, styleData, sampler, textureSetIndex);
     ++textureSetIndex;
   }
 
-  if(containsColorGlyph && !hasMultipleTextColors)
+  if(mTextShaderFeatureCache.IsEnabledOverlay())
+  {
+    // Create RGBA texture for overlay styles such as underline and strikethrough (without the text itself)
+    PixelData overlayStyleData = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_OVERLAY_STYLE, false, Pixel::RGBA8888);
+    AddTexture(textureSet, overlayStyleData, sampler, textureSetIndex);
+    ++textureSetIndex;
+  }
+
+  if(mTextShaderFeatureCache.IsEnabledEmoji() && !mTextShaderFeatureCache.IsEnabledMultiColor())
   {
     // Create a L8 texture as a mask to avoid color glyphs (e.g. emojis) to be affected by text color animation
     PixelData maskData = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_MASK, false, Pixel::L8);
@@ -768,73 +838,13 @@ TextureSet TextVisual::GetTextTexture(const Vector2& size, bool hasMultipleTextC
   return textureSet;
 }
 
-Shader TextVisual::GetTextShader(VisualFactoryCache& factoryCache, bool hasMultipleTextColors, bool containsColorGlyph, bool styleEnabled)
+Shader TextVisual::GetTextShader(VisualFactoryCache& factoryCache, const TextVisualShaderFeature::FeatureBuilder& featureBuilder)
 {
-  Shader shader;
-
-  if(hasMultipleTextColors && !styleEnabled)
-  {
-    // We don't animate text color if the text contains multiple colors
-    shader = factoryCache.GetShader(VisualFactoryCache::TEXT_SHADER_MULTI_COLOR_TEXT);
-    if(!shader)
-    {
-      shader = Shader::New(SHADER_TEXT_VISUAL_SHADER_VERT, SHADER_TEXT_VISUAL_MULTI_COLOR_TEXT_SHADER_FRAG);
-      shader.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT);
-      factoryCache.SaveShader(VisualFactoryCache::TEXT_SHADER_MULTI_COLOR_TEXT, shader);
-    }
-  }
-  else if(hasMultipleTextColors && styleEnabled)
-  {
-    // We don't animate text color if the text contains multiple colors
-    shader = factoryCache.GetShader(VisualFactoryCache::TEXT_SHADER_MULTI_COLOR_TEXT_WITH_STYLE);
-    if(!shader)
-    {
-      shader = Shader::New(SHADER_TEXT_VISUAL_SHADER_VERT, SHADER_TEXT_VISUAL_MULTI_COLOR_TEXT_WITH_STYLE_SHADER_FRAG);
-      shader.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT);
-      factoryCache.SaveShader(VisualFactoryCache::TEXT_SHADER_MULTI_COLOR_TEXT_WITH_STYLE, shader);
-    }
-  }
-  else if(!hasMultipleTextColors && !containsColorGlyph && !styleEnabled)
-  {
-    shader = factoryCache.GetShader(VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT);
-    if(!shader)
-    {
-      shader = Shader::New(SHADER_TEXT_VISUAL_SHADER_VERT, SHADER_TEXT_VISUAL_SINGLE_COLOR_TEXT_SHADER_FRAG);
-      shader.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT);
-      factoryCache.SaveShader(VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT, shader);
-    }
-  }
-  else if(!hasMultipleTextColors && !containsColorGlyph && styleEnabled)
-  {
-    shader = factoryCache.GetShader(VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_STYLE);
-    if(!shader)
-    {
-      shader = Shader::New(SHADER_TEXT_VISUAL_SHADER_VERT, SHADER_TEXT_VISUAL_SINGLE_COLOR_TEXT_WITH_STYLE_SHADER_FRAG);
-      shader.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT);
-      factoryCache.SaveShader(VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_STYLE, shader);
-    }
-  }
-  else if(!hasMultipleTextColors && containsColorGlyph && !styleEnabled)
-  {
-    shader = factoryCache.GetShader(VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_EMOJI);
-    if(!shader)
-    {
-      shader = Shader::New(SHADER_TEXT_VISUAL_SHADER_VERT, SHADER_TEXT_VISUAL_SINGLE_COLOR_TEXT_WITH_EMOJI_SHADER_FRAG);
-      shader.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT);
-      factoryCache.SaveShader(VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_EMOJI, shader);
-    }
-  }
-  else // if( !hasMultipleTextColors && containsColorGlyph && styleEnabled )
-  {
-    shader = factoryCache.GetShader(VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_STYLE_AND_EMOJI);
-    if(!shader)
-    {
-      shader = Shader::New(SHADER_TEXT_VISUAL_SHADER_VERT, SHADER_TEXT_VISUAL_SINGLE_COLOR_TEXT_WITH_STYLE_AND_EMOJI_SHADER_FRAG);
-      shader.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT);
-      factoryCache.SaveShader(VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_STYLE_AND_EMOJI, shader);
-    }
-  }
+  // Cache feature builder informations.
+  mTextShaderFeatureCache = featureBuilder;
 
+  Shader shader = mTextVisualShaderFactory.GetShader(factoryCache, mTextShaderFeatureCache);
+  shader.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT);
   return shader;
 }