[dali_2.3.42] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / text / text-visual.cpp
index 6ca476c..aab56ea 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 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.
  *
  * 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>
 
 // EXTERNAL INCLUDES
 #include <dali/devel-api/adaptor-framework/image-loading.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/integration-api/debug.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/integration-api/debug.h>
+#include <dali/integration-api/pixel-data-integ.h>
 #include <dali/integration-api/trace.h>
 #include <string.h>
 
 #include <dali/integration-api/trace.h>
 #include <string.h>
 
@@ -37,7 +37,7 @@
 #include <dali-toolkit/internal/text/text-effects-style.h>
 #include <dali-toolkit/internal/text/text-enumerations-impl.h>
 #include <dali-toolkit/internal/text/text-font-style.h>
 #include <dali-toolkit/internal/text/text-effects-style.h>
 #include <dali-toolkit/internal/text/text-enumerations-impl.h>
 #include <dali-toolkit/internal/text/text-font-style.h>
-#include <dali-toolkit/internal/visuals/image-atlas-manager.h>
+#include <dali-toolkit/internal/visuals/image/image-atlas-manager.h>
 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
@@ -52,11 +52,28 @@ namespace Internal
 {
 namespace
 {
 {
 namespace
 {
-DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_PERFORMANCE_MARKER, false);
+DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_TEXT_PERFORMANCE_MARKER, false);
+DALI_INIT_TRACE_FILTER(gTraceFilter2, DALI_TRACE_TEXT_ASYNC, false);
 
 
-const int CUSTOM_PROPERTY_COUNT(5); // anim,premul,size,offset,multicol
+const int CUSTOM_PROPERTY_COUNT(3); // uTextColorAnimatable, uHasMultipleTextColors, requireRender
 
 
-const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
+const float VERTICAL_ALIGNMENT_TABLE[Text::VerticalAlignment::BOTTOM + 1] =
+  {
+    0.0f, // VerticalAlignment::TOP
+    0.5f, // VerticalAlignment::CENTER
+    1.0f  // VerticalAlignment::BOTTOM
+};
+
+#ifdef TRACE_ENABLED
+const char* GetRequestTypeName(Text::Async::RequestType type)
+{
+  if(type < Text::Async::RENDER_FIXED_SIZE || type > Text::Async::COMPUTE_HEIGHT_FOR_WIDTH)
+  {
+    return "INVALID_REQUEST_TYPE";
+  }
+  return Text::Async::RequestTypeName[type];
+}
+#endif
 
 /**
  * Return Property index for the given string key
 
 /**
  * Return Property index for the given string key
@@ -140,7 +157,7 @@ void TextColorConstraint(Vector4& current, const PropertyInputContainer& inputs)
 void OpacityConstraint(float& current, const PropertyInputContainer& inputs)
 {
   // Make zero if the alpha value of text color is zero to skip rendering text
 void OpacityConstraint(float& current, const PropertyInputContainer& inputs)
 {
   // Make zero if the alpha value of text color is zero to skip rendering text
-  if(EqualsZero(inputs[0]->GetVector4().a))
+  if(EqualsZero(inputs[0]->GetVector4().a) && !inputs[1]->GetBoolean())
   {
     current = 0.0f;
   }
   {
     current = 0.0f;
   }
@@ -254,14 +271,24 @@ void TextVisual::EnablePreMultipliedAlpha(bool preMultiplied)
 }
 
 TextVisual::TextVisual(VisualFactoryCache& factoryCache, TextVisualShaderFactory& shaderFactory)
 }
 
 TextVisual::TextVisual(VisualFactoryCache& factoryCache, TextVisualShaderFactory& shaderFactory)
-: Visual::Base(factoryCache, Visual::FittingMode::FIT_KEEP_ASPECT_RATIO, Toolkit::Visual::TEXT),
+: Visual::Base(factoryCache, Visual::FittingMode::DONT_CARE, Toolkit::Visual::TEXT),
   mController(Text::Controller::New()),
   mTypesetter(Text::Typesetter::New(mController->GetTextModel())),
   mController(Text::Controller::New()),
   mTypesetter(Text::Typesetter::New(mController->GetTextModel())),
+  mAsyncTextInterface(nullptr),
   mTextVisualShaderFactory(shaderFactory),
   mTextShaderFeatureCache(),
   mTextVisualShaderFactory(shaderFactory),
   mTextShaderFeatureCache(),
+  mHasMultipleTextColorsIndex(Property::INVALID_INDEX),
   mAnimatableTextColorPropertyIndex(Property::INVALID_INDEX),
   mTextColorAnimatableIndex(Property::INVALID_INDEX),
   mAnimatableTextColorPropertyIndex(Property::INVALID_INDEX),
   mTextColorAnimatableIndex(Property::INVALID_INDEX),
-  mRendererUpdateNeeded(false)
+  mTextRequireRenderPropertyIndex(Property::INVALID_INDEX),
+  mRendererUpdateNeeded(false),
+  mTextRequireRender(false),
+  mTextLoadingTaskId(0u),
+  mNaturalSizeTaskId(0u),
+  mHeightForWidthTaskId(0u),
+  mIsTextLoadingTaskRunning(false),
+  mIsNaturalSizeTaskRunning(false),
+  mIsHeightForWidthTaskRunning(false)
 {
   // Enable the pre-multiplied alpha to improve the text quality
   mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
 {
   // Enable the pre-multiplied alpha to improve the text quality
   mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
@@ -273,11 +300,14 @@ TextVisual::~TextVisual()
 
 void TextVisual::OnInitialize()
 {
 
 void TextVisual::OnInitialize()
 {
-  Geometry geometry = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY);
-  Shader   shader   = GetTextShader(mFactoryCache, TextVisualShaderFeature::FeatureBuilder());
+  Geometry geometry       = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY);
+  auto     featureBuilder = TextVisualShaderFeature::FeatureBuilder();
+  Shader   shader         = GetTextShader(mFactoryCache, featureBuilder);
 
   mImpl->mRenderer = VisualRenderer::New(geometry, shader);
   mImpl->mRenderer.ReserveCustomProperties(CUSTOM_PROPERTY_COUNT);
 
   mImpl->mRenderer = VisualRenderer::New(geometry, shader);
   mImpl->mRenderer.ReserveCustomProperties(CUSTOM_PROPERTY_COUNT);
+  mTextRequireRenderPropertyIndex = mImpl->mRenderer.RegisterUniqueProperty("requireRender", mTextRequireRender);
+  mHasMultipleTextColorsIndex     = mImpl->mRenderer.RegisterUniqueProperty("uHasMultipleTextColors", static_cast<float>(false));
 }
 
 void TextVisual::DoSetProperties(const Property::Map& propertyMap)
 }
 
 void TextVisual::DoSetProperties(const Property::Map& propertyMap)
@@ -341,6 +371,7 @@ void TextVisual::DoSetOnScene(Actor& actor)
       // VisualRenderer::Property::OPACITY uses same animatable property internally.
       mOpacityConstraint = Constraint::New<float>(mImpl->mRenderer, Dali::DevelRenderer::Property::OPACITY, OpacityConstraint);
       mOpacityConstraint.AddSource(Source(actor, mAnimatableTextColorPropertyIndex));
       // 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.AddSource(Source(mImpl->mRenderer, mTextRequireRenderPropertyIndex));
     }
     mOpacityConstraint.Apply();
   }
     }
     mOpacityConstraint.Apply();
   }
@@ -351,12 +382,13 @@ void TextVisual::DoSetOnScene(Actor& actor)
   UpdateRenderer();
 }
 
   UpdateRenderer();
 }
 
-void TextVisual::RemoveRenderer(Actor& actor)
+void TextVisual::RemoveRenderer(Actor& actor, bool removeDefaultRenderer)
 {
   for(RendererContainer::iterator iter = mRendererList.begin(); iter != mRendererList.end(); ++iter)
   {
     Renderer renderer = (*iter);
 {
   for(RendererContainer::iterator iter = mRendererList.begin(); iter != mRendererList.end(); ++iter)
   {
     Renderer renderer = (*iter);
-    if(renderer)
+    if(renderer &&
+       (removeDefaultRenderer || (renderer != mImpl->mRenderer)))
     {
       // Removes the renderer from the actor.
       actor.RemoveRenderer(renderer);
     {
       // Removes the renderer from the actor.
       actor.RemoveRenderer(renderer);
@@ -368,6 +400,11 @@ void TextVisual::RemoveRenderer(Actor& actor)
 
 void TextVisual::DoSetOffScene(Actor& actor)
 {
 
 void TextVisual::DoSetOffScene(Actor& actor)
 {
+  if(mController->GetRenderMode() != DevelTextLabel::Render::SYNC && mIsTextLoadingTaskRunning)
+  {
+    Text::AsyncTextManager::Get().RequestCancel(mTextLoadingTaskId);
+    mIsTextLoadingTaskRunning = false;
+  }
   if(mColorConstraint)
   {
     mColorConstraint.Remove();
   if(mColorConstraint)
   {
     mColorConstraint.Remove();
@@ -377,7 +414,7 @@ void TextVisual::DoSetOffScene(Actor& actor)
     mOpacityConstraint.Remove();
   }
 
     mOpacityConstraint.Remove();
   }
 
-  RemoveRenderer(actor);
+  RemoveRenderer(actor, true);
 
   // Resets the control handle.
   mControl.Reset();
 
   // Resets the control handle.
   mControl.Reset();
@@ -490,6 +527,11 @@ void TextVisual::DoSetProperty(Dali::Property::Index index, const Dali::Property
 
 void TextVisual::UpdateRenderer()
 {
 
 void TextVisual::UpdateRenderer()
 {
+  if(mController->GetRenderMode() != DevelTextLabel::Render::SYNC)
+  {
+    return;
+  }
+
   Actor control = mControl.GetHandle();
   if(!control)
   {
   Actor control = mControl.GetHandle();
   if(!control)
   {
@@ -503,17 +545,19 @@ void TextVisual::UpdateRenderer()
   const bool isWidthRelative  = fabsf(mImpl->mTransform.mOffsetSizeMode.z) < Math::MACHINE_EPSILON_1000;
   const bool isHeightRelative = fabsf(mImpl->mTransform.mOffsetSizeMode.w) < Math::MACHINE_EPSILON_1000;
 
   const bool isWidthRelative  = fabsf(mImpl->mTransform.mOffsetSizeMode.z) < Math::MACHINE_EPSILON_1000;
   const bool isHeightRelative = fabsf(mImpl->mTransform.mOffsetSizeMode.w) < Math::MACHINE_EPSILON_1000;
 
+  const float controlWidth  = mImpl->mControlSize.width;
+  const float controlHeight = mImpl->mControlSize.height;
+
   // Round the size and offset to avoid pixel alignement issues.
   // Round the size and offset to avoid pixel alignement issues.
-  relayoutSize.width  = floorf(0.5f + (isWidthRelative ? mImpl->mControlSize.width * mImpl->mTransform.mSize.x : mImpl->mTransform.mSize.width));
-  relayoutSize.height = floorf(0.5f + (isHeightRelative ? mImpl->mControlSize.height * mImpl->mTransform.mSize.y : mImpl->mTransform.mSize.height));
+  relayoutSize.width  = floorf(0.5f + (isWidthRelative ? controlWidth * mImpl->mTransform.mSize.x : mImpl->mTransform.mSize.width));
+  relayoutSize.height = floorf(0.5f + (isHeightRelative ? controlHeight * mImpl->mTransform.mSize.y : mImpl->mTransform.mSize.height));
 
 
-  std::string text;
-  mController->GetText(text);
+  auto textLengthUtf32 = mController->GetNumberOfCharacters();
 
 
-  if((fabsf(relayoutSize.width) < Math::MACHINE_EPSILON_1000) || (fabsf(relayoutSize.height) < Math::MACHINE_EPSILON_1000) || text.empty())
+  if((fabsf(relayoutSize.width) < Math::MACHINE_EPSILON_1000) || (fabsf(relayoutSize.height) < Math::MACHINE_EPSILON_1000) || textLengthUtf32 == 0u)
   {
     // Remove the texture set and any renderer previously set.
   {
     // Remove the texture set and any renderer previously set.
-    RemoveRenderer(control);
+    RemoveRenderer(control, true);
 
     // Nothing else to do if the relayout size is zero.
     ResourceReady(Toolkit::Visual::ResourceStatus::READY);
 
     // Nothing else to do if the relayout size is zero.
     ResourceReady(Toolkit::Visual::ResourceStatus::READY);
@@ -529,7 +573,8 @@ void TextVisual::UpdateRenderer()
     mRendererUpdateNeeded = false;
 
     // Remove the texture set and any renderer previously set.
     mRendererUpdateNeeded = false;
 
     // Remove the texture set and any renderer previously set.
-    RemoveRenderer(control);
+    // Note, we don't need to remove the mImpl->Renderer, since it will be added again after AddRenderer call.
+    RemoveRenderer(control, false);
 
     if((relayoutSize.width > Math::MACHINE_EPSILON_1000) &&
        (relayoutSize.height > Math::MACHINE_EPSILON_1000))
 
     if((relayoutSize.width > Math::MACHINE_EPSILON_1000) &&
        (relayoutSize.height > Math::MACHINE_EPSILON_1000))
@@ -566,16 +611,39 @@ void TextVisual::UpdateRenderer()
         shadowEnabled = true;
       }
 
         shadowEnabled = true;
       }
 
-      const bool outlineEnabled             = (mController->GetTextModel()->GetOutlineWidth() > Math::MACHINE_EPSILON_1);
-      const bool backgroundEnabled          = mController->GetTextModel()->IsBackgroundEnabled();
-      const bool markupOrSpannedText        = mController->IsMarkupProcessorEnabled() || mController->GetTextModel()->IsSpannedTextPlaced();
-      const bool markupUnderlineEnabled     = markupOrSpannedText && mController->GetTextModel()->IsMarkupUnderlineSet();
-      const bool markupStrikethroughEnabled = markupOrSpannedText && mController->GetTextModel()->IsMarkupStrikethroughSet();
-      const bool underlineEnabled           = mController->GetTextModel()->IsUnderlineEnabled() || markupUnderlineEnabled;
-      const bool strikethroughEnabled       = mController->GetTextModel()->IsStrikethroughEnabled() || markupStrikethroughEnabled;
-      const bool backgroundMarkupSet        = mController->GetTextModel()->IsMarkupBackgroundColorSet();
-      const bool styleEnabled               = (shadowEnabled || outlineEnabled || backgroundEnabled || markupOrSpannedText || backgroundMarkupSet);
-      const bool isOverlayStyle             = underlineEnabled || strikethroughEnabled;
+      const bool outlineEnabled              = (mController->GetTextModel()->GetOutlineWidth() > Math::MACHINE_EPSILON_1);
+      const bool backgroundEnabled           = mController->GetTextModel()->IsBackgroundEnabled();
+      const bool markupOrSpannedText         = mController->IsMarkupProcessorEnabled() || mController->GetTextModel()->IsSpannedTextPlaced();
+      const bool markupUnderlineEnabled      = markupOrSpannedText && mController->GetTextModel()->IsMarkupUnderlineSet();
+      const bool markupStrikethroughEnabled  = markupOrSpannedText && mController->GetTextModel()->IsMarkupStrikethroughSet();
+      const bool underlineEnabled            = mController->GetTextModel()->IsUnderlineEnabled() || markupUnderlineEnabled;
+      const bool strikethroughEnabled        = mController->GetTextModel()->IsStrikethroughEnabled() || markupStrikethroughEnabled;
+      const bool backgroundMarkupSet         = mController->GetTextModel()->IsMarkupBackgroundColorSet();
+      const bool cutoutEnabled               = mController->IsTextCutout();
+      const bool backgroundWithCutoutEnabled = mController->GetTextModel()->IsBackgroundWithCutoutEnabled();
+      const bool styleEnabled                = (shadowEnabled || outlineEnabled || backgroundEnabled || markupOrSpannedText || backgroundMarkupSet || cutoutEnabled || backgroundWithCutoutEnabled);
+      const bool isOverlayStyle              = underlineEnabled || strikethroughEnabled;
+
+      // if background with cutout is enabled, This text visual must render the entire control size.
+
+      if(cutoutEnabled)
+      {
+        // mTransform stores the size and offset of the current visual.
+        // padding and alignment information is stored in mOffset.
+        // When Cutout Enabled, the current visual must draw the entire control.
+        // so set the size to controlSize and offset to 0.
+
+        relayoutSize                   = Vector2(controlWidth, controlHeight);
+        mImpl->mTransform.mSize.width  = controlWidth;
+        mImpl->mTransform.mSize.height = controlHeight;
+
+        // Relayout to the original size has been completed, so save only the offset information and use it in typesetter.
+
+        Vector2 originOffset = Vector2(mImpl->mTransform.mOffset.x, mImpl->mTransform.mOffset.y);
+        mController->SetOffsetWithCutout(originOffset);
+        mImpl->mTransform.mOffset.x = 0;
+        mImpl->mTransform.mOffset.y = 0;
+      }
 
       AddRenderer(control, relayoutSize, hasMultipleTextColors, containsColorGlyph, styleEnabled, isOverlayStyle);
 
 
       AddRenderer(control, relayoutSize, hasMultipleTextColors, containsColorGlyph, styleEnabled, isOverlayStyle);
 
@@ -600,7 +668,7 @@ void TextVisual::AddTexture(TextureSet& textureSet, PixelData& data, Sampler& sa
 void TextVisual::AddTilingTexture(TextureSet& textureSet, TilingInfo& tilingInfo, PixelData& data, Sampler& sampler, unsigned int textureSetIndex)
 {
   Texture texture = Texture::New(Dali::TextureType::TEXTURE_2D,
 void TextVisual::AddTilingTexture(TextureSet& textureSet, TilingInfo& tilingInfo, PixelData& data, Sampler& sampler, unsigned int textureSetIndex)
 {
   Texture texture = Texture::New(Dali::TextureType::TEXTURE_2D,
-                                 tilingInfo.textPixelFormat,
+                                 data.GetPixelFormat(),
                                  tilingInfo.width,
                                  tilingInfo.height);
   DevelTexture::UploadSubPixelData(texture, data, 0u, tilingInfo.offsetHeight, tilingInfo.width, tilingInfo.height);
                                  tilingInfo.width,
                                  tilingInfo.height);
   DevelTexture::UploadSubPixelData(texture, data, 0u, tilingInfo.offsetHeight, tilingInfo.width, tilingInfo.height);
@@ -642,15 +710,14 @@ void TextVisual::CreateTextureSet(TilingInfo& info, VisualRenderer& renderer, Sa
 
   renderer.SetTextures(textureSet);
 
 
   renderer.SetTextures(textureSet);
 
-  //Register transform properties
+  // Register transform properties
   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);
   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.
 
   // Set size and offset for the tiling.
-  renderer.SetProperty(VisualRenderer::Property::TRANSFORM_SIZE, Vector2(info.width, info.height));
+  renderer.SetProperty(VisualRenderer::Property::TRANSFORM_SIZE, Vector2(static_cast<float>(info.width), static_cast<float>(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()));
   renderer.SetProperty(VisualRenderer::Property::TRANSFORM_OFFSET, info.transformOffset);
   renderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
   renderer.RegisterProperty("uHasMultipleTextColors", static_cast<float>(mTextShaderFeatureCache.IsEnabledMultiColor()));
@@ -658,6 +725,430 @@ void TextVisual::CreateTextureSet(TilingInfo& info, VisualRenderer& renderer, Sa
   mRendererList.push_back(renderer);
 }
 
   mRendererList.push_back(renderer);
 }
 
+// From async text manager
+void TextVisual::LoadComplete(bool loadingSuccess, const TextInformation& textInformation)
+{
+  Text::AsyncTextParameters parameters = textInformation.parameters;
+
+#ifdef TRACE_ENABLED
+  if(gTraceFilter2 && gTraceFilter2->IsTraceEnabled())
+  {
+    DALI_LOG_RELEASE_INFO("LoadComplete, success:%d, type:%s\n", loadingSuccess, GetRequestTypeName(parameters.requestType));
+  }
+#endif
+
+  switch(parameters.requestType)
+  {
+    case Text::Async::RENDER_FIXED_SIZE:
+    case Text::Async::RENDER_FIXED_WIDTH:
+    case Text::Async::RENDER_CONSTRAINT:
+    {
+      mIsTextLoadingTaskRunning = false;
+      break;
+    }
+    case Text::Async::COMPUTE_NATURAL_SIZE:
+    {
+      mIsNaturalSizeTaskRunning = false;
+      break;
+    }
+    case Text::Async::COMPUTE_HEIGHT_FOR_WIDTH:
+    {
+      mIsHeightForWidthTaskRunning = false;
+      break;
+    }
+    default:
+    {
+      DALI_LOG_ERROR("Unexpected request type : %d\n", parameters.requestType);
+      break;
+    }
+  }
+
+  Toolkit::Visual::ResourceStatus resourceStatus;
+
+  if(loadingSuccess)
+  {
+    resourceStatus = Toolkit::Visual::ResourceStatus::READY;
+
+    Text::AsyncTextRenderInfo renderInfo = textInformation.renderInfo;
+
+    if(parameters.requestType == Text::Async::COMPUTE_NATURAL_SIZE || parameters.requestType == Text::Async::COMPUTE_HEIGHT_FOR_WIDTH)
+    {
+      if(mAsyncTextInterface)
+      {
+        mAsyncTextInterface->AsyncSizeComputed(renderInfo);
+        return;
+      }
+    }
+
+    Actor control = mControl.GetHandle();
+    if(!control)
+    {
+      // Nothing to do.
+      ResourceReady(Toolkit::Visual::ResourceStatus::READY);
+      return;
+    }
+
+    // Calculate the size of the visual that can fit the text.
+    // The size of the text after it has been laid-out, size of pixel data buffer.
+    Size layoutSize(static_cast<float>(renderInfo.width), static_cast<float>(renderInfo.height));
+
+    // Calculate the offset for vertical alignment only, as the layout engine will do the horizontal alignment.
+    Vector2 alignmentOffset;
+    alignmentOffset.x = 0.0f;
+    alignmentOffset.y = (parameters.textHeight - layoutSize.y) * VERTICAL_ALIGNMENT_TABLE[parameters.verticalAlignment];
+
+    // Size of the text control including padding.
+    Vector2 textControlSize(parameters.textWidth + (parameters.padding.start + parameters.padding.end), parameters.textHeight + (parameters.padding.top + parameters.padding.bottom));
+
+    if(parameters.isAutoScrollEnabled)
+    {
+      // In case of auto scroll, the layout width (renderInfo's width) is the natural size of the text.
+      // Since the layout size is the size of the visual transform, it should be reset to the text area excluding padding.
+      layoutSize.width = parameters.textWidth;
+    }
+
+    Vector2 visualTransformOffset;
+    if(renderInfo.isCutout)
+    {
+      // When Cutout Enabled, the current visual must draw the entire control.
+      // so set the size to controlSize and offset to 0.
+      visualTransformOffset.x = 0.0f;
+      visualTransformOffset.y = 0.0f;
+
+      // The layout size is set to the text control size including padding.
+      layoutSize = textControlSize;
+    }
+    else
+    {
+      // This affects font rendering quality.
+      // It need to be integerized.
+      visualTransformOffset.x = roundf(parameters.padding.start + alignmentOffset.x);
+      visualTransformOffset.y = roundf(parameters.padding.top + alignmentOffset.y);
+    }
+
+    SetRequireRender(renderInfo.isCutout);
+
+    // Transform offset is used for subpixel data upload in text tiling.
+    // We should set the transform before creating a tiling texture.
+    Property::Map visualTransform;
+    visualTransform.Add(Toolkit::Visual::Transform::Property::SIZE, layoutSize)
+      .Add(Toolkit::Visual::Transform::Property::SIZE_POLICY, Vector2(Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE))
+      .Add(Toolkit::Visual::Transform::Property::OFFSET, visualTransformOffset)
+      .Add(Toolkit::Visual::Transform::Property::OFFSET_POLICY, Vector2(Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE))
+      .Add(Toolkit::Visual::Transform::Property::ORIGIN, Toolkit::Align::TOP_BEGIN)
+      .Add(Toolkit::Visual::Transform::Property::ANCHOR_POINT, Toolkit::Align::TOP_BEGIN);
+    SetTransformAndSize(visualTransform, textControlSize);
+
+    Shader shader = GetTextShader(mFactoryCache, TextVisualShaderFeature::FeatureBuilder().EnableMultiColor(renderInfo.hasMultipleTextColors).EnableEmoji(renderInfo.containsColorGlyph).EnableStyle(renderInfo.styleEnabled).EnableOverlay(renderInfo.isOverlayStyle));
+    mImpl->mRenderer.SetShader(shader);
+
+    // Remove the texture set and any renderer previously set.
+    RemoveRenderer(control, false);
+
+    // Get the maximum texture size.
+    const int maxTextureSize = Dali::GetMaxTextureSize();
+
+    // No tiling required. Use the default renderer.
+    if(renderInfo.height < static_cast<uint32_t>(maxTextureSize))
+    {
+      // Filter mode needs to be set to linear to produce better quality while scaling.
+      Sampler sampler = Sampler::New();
+      sampler.SetFilterMode(FilterMode::LINEAR, FilterMode::LINEAR);
+
+      TextureSet textureSet = TextureSet::New();
+
+      uint32_t textureSetIndex = 0u;
+      AddTexture(textureSet, renderInfo.textPixelData, sampler, textureSetIndex);
+      ++textureSetIndex;
+
+      if(mTextShaderFeatureCache.IsEnabledStyle())
+      {
+        // Create RGBA texture for all the text styles that render in the background (without the text itself)
+        AddTexture(textureSet, renderInfo.stylePixelData, sampler, textureSetIndex);
+        ++textureSetIndex;
+      }
+      if(mTextShaderFeatureCache.IsEnabledOverlay())
+      {
+        // Create RGBA texture for overlay styles such as underline and strikethrough (without the text itself)
+        AddTexture(textureSet, renderInfo.overlayStylePixelData, 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
+        AddTexture(textureSet, renderInfo.maskPixelData, sampler, textureSetIndex);
+      }
+
+      mImpl->mRenderer.SetTextures(textureSet);
+      // Register transform properties
+      mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
+      mImpl->mRenderer.SetProperty(mHasMultipleTextColorsIndex, static_cast<float>(mTextShaderFeatureCache.IsEnabledMultiColor()));
+      mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
+
+      mRendererList.push_back(mImpl->mRenderer);
+    }
+    else
+    {
+      // Filter mode needs to be set to linear to produce better quality while scaling.
+      Sampler sampler = Sampler::New();
+      sampler.SetFilterMode(FilterMode::LINEAR, FilterMode::LINEAR);
+
+      int verifiedWidth  = static_cast<int>(renderInfo.width);
+      int verifiedHeight = static_cast<int>(renderInfo.height);
+
+      // Set information for creating textures.
+      TilingInfo info(verifiedWidth, maxTextureSize);
+
+      // Get the pixel data of text.
+      info.textPixelData = renderInfo.textPixelData;
+
+      if(mTextShaderFeatureCache.IsEnabledStyle())
+      {
+        info.stylePixelData = renderInfo.stylePixelData;
+      }
+
+      if(mTextShaderFeatureCache.IsEnabledOverlay())
+      {
+        info.overlayStylePixelData = renderInfo.overlayStylePixelData;
+      }
+
+      if(mTextShaderFeatureCache.IsEnabledEmoji() && !mTextShaderFeatureCache.IsEnabledMultiColor())
+      {
+        info.maskPixelData = renderInfo.maskPixelData;
+      }
+
+      // Get the current offset for recalculate the offset when tiling.
+      Property::Map retMap;
+      mImpl->mTransform.GetPropertyMap(retMap);
+      Property::Value* offsetValue = retMap.Find(Dali::Toolkit::Visual::Transform::Property::OFFSET);
+      if(offsetValue)
+      {
+        offsetValue->Get(info.transformOffset);
+      }
+
+      // Create a textureset in the default renderer.
+      CreateTextureSet(info, mImpl->mRenderer, sampler);
+
+      verifiedHeight -= maxTextureSize;
+
+      Geometry geometry = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY);
+
+      // Create a renderer by cutting maxTextureSize.
+      while(verifiedHeight > 0)
+      {
+        VisualRenderer tilingRenderer = VisualRenderer::New(geometry, shader);
+        tilingRenderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, Toolkit::DepthIndex::CONTENT);
+        // New offset position of buffer for tiling.
+        info.offsetHeight += static_cast<uint32_t>(maxTextureSize);
+        // New height for tiling.
+        info.height = (verifiedHeight - maxTextureSize) > 0 ? maxTextureSize : verifiedHeight;
+        // New offset for tiling.
+        info.transformOffset.y += static_cast<float>(maxTextureSize);
+
+        // Create a textureset int the new tiling renderer.
+        CreateTextureSet(info, tilingRenderer, sampler);
+
+        verifiedHeight -= maxTextureSize;
+      }
+    }
+
+    mImpl->mFlags &= ~Visual::Base::Impl::IS_ATLASING_APPLIED;
+
+    const Vector4& defaultColor = parameters.textColor;
+
+    for(RendererContainer::iterator iter = mRendererList.begin(); iter != mRendererList.end(); ++iter)
+    {
+      Renderer renderer = (*iter);
+      if(renderer)
+      {
+        control.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(control, 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(control, mAnimatableTextColorPropertyIndex));
+            opacityConstraint.AddSource(Source(mImpl->mRenderer, mTextRequireRenderPropertyIndex));
+            opacityConstraint.Apply();
+          }
+        }
+      }
+    }
+
+    if(mAsyncTextInterface && parameters.isAutoScrollEnabled)
+    {
+      mAsyncTextInterface->AsyncSetupAutoScroll(renderInfo);
+    }
+
+    if(mAsyncTextInterface && parameters.isTextFitEnabled)
+    {
+      mAsyncTextInterface->AsyncTextFitChanged(parameters.fontSize);
+    }
+
+    if(mAsyncTextInterface)
+    {
+      mAsyncTextInterface->AsyncLoadComplete(renderInfo);
+    }
+
+    // Ignore current result when user re-request async load during load complete callback.
+    if(mIsTextLoadingTaskRunning)
+    {
+      // Remove the texture set and any renderer previously set.
+      RemoveRenderer(control, true);
+      return;
+    }
+  }
+  else
+  {
+    resourceStatus = Toolkit::Visual::ResourceStatus::FAILED;
+  }
+
+  // Signal to observers ( control ) that resources are ready. Must be all resources.
+  ResourceReady(resourceStatus);
+}
+
+void TextVisual::SetAsyncTextInterface(Text::AsyncTextInterface* asyncTextInterface)
+{
+  mAsyncTextInterface = asyncTextInterface;
+}
+
+void TextVisual::RequestAsyncSizeComputation(Text::AsyncTextParameters& parameters)
+{
+#ifdef TRACE_ENABLED
+  if(gTraceFilter2 && gTraceFilter2->IsTraceEnabled())
+  {
+    DALI_LOG_RELEASE_INFO("Request size computation, type:%s\n", GetRequestTypeName(parameters.requestType));
+  }
+#endif
+
+  switch(parameters.requestType)
+  {
+    case Text::Async::COMPUTE_NATURAL_SIZE:
+    {
+      if(mIsNaturalSizeTaskRunning)
+      {
+        Text::AsyncTextManager::Get().RequestCancel(mNaturalSizeTaskId);
+      }
+      mIsNaturalSizeTaskRunning = true;
+
+      TextLoadObserver* textLoadObserver = this;
+      mNaturalSizeTaskId                 = Text::AsyncTextManager::Get().RequestLoad(parameters, textLoadObserver);
+      break;
+    }
+    case Text::Async::COMPUTE_HEIGHT_FOR_WIDTH:
+    {
+      if(mIsHeightForWidthTaskRunning)
+      {
+        Text::AsyncTextManager::Get().RequestCancel(mHeightForWidthTaskId);
+      }
+      mIsHeightForWidthTaskRunning = true;
+
+      TextLoadObserver* textLoadObserver = this;
+      mHeightForWidthTaskId              = Text::AsyncTextManager::Get().RequestLoad(parameters, textLoadObserver);
+      break;
+    }
+    default:
+    {
+      DALI_LOG_ERROR("Unexpected request type : %d\n", parameters.requestType);
+      break;
+    }
+  }
+}
+
+bool TextVisual::UpdateAsyncRenderer(Text::AsyncTextParameters& parameters)
+{
+  Actor control = mControl.GetHandle();
+  if(!control)
+  {
+    // Nothing to do.
+    ResourceReady(Toolkit::Visual::ResourceStatus::READY);
+    return false;
+  }
+
+  if((fabsf(parameters.textWidth) < Math::MACHINE_EPSILON_1000) || (fabsf(parameters.textHeight) < Math::MACHINE_EPSILON_1000) ||
+     parameters.text.empty())
+  {
+    if(mIsTextLoadingTaskRunning)
+    {
+      Text::AsyncTextManager::Get().RequestCancel(mTextLoadingTaskId);
+      mIsTextLoadingTaskRunning = false;
+    }
+
+    // Remove the texture set and any renderer previously set.
+    RemoveRenderer(control, true);
+
+    // Nothing else to do if the relayout size is zero.
+    ResourceReady(Toolkit::Visual::ResourceStatus::READY);
+
+    if(mAsyncTextInterface)
+    {
+      Text::AsyncTextRenderInfo renderInfo;
+      if(parameters.requestType == Text::Async::RENDER_FIXED_SIZE)
+      {
+        renderInfo.renderedSize = Size(parameters.textWidth, parameters.textHeight);
+      }
+      else if(parameters.requestType == Text::Async::RENDER_FIXED_WIDTH)
+      {
+        renderInfo.renderedSize = Size(parameters.textWidth, 0.0f);
+      }
+      else
+      {
+        renderInfo.renderedSize = Size::ZERO;
+      }
+
+      renderInfo.manualRendered = parameters.manualRender;
+      mAsyncTextInterface->AsyncLoadComplete(renderInfo);
+    }
+
+    return true;
+  }
+
+  // Get the maximum texture size.
+  const int maxTextureSize = Dali::GetMaxTextureSize();
+
+  if(parameters.textWidth > maxTextureSize)
+  {
+    DALI_LOG_WARNING("layoutSize(%f) > maxTextureSize(%d): To guarantee the behavior of Texture::New, layoutSize must not be bigger than maxTextureSize\n", parameters.textWidth, maxTextureSize);
+    parameters.textWidth = maxTextureSize;
+  }
+
+  // This does not mean whether task is actually running or waiting.
+  // It is whether text visual received a completion callback after requesting a task.
+  if(mIsTextLoadingTaskRunning)
+  {
+    Text::AsyncTextManager::Get().RequestCancel(mTextLoadingTaskId);
+  }
+
+#ifdef TRACE_ENABLED
+  if(gTraceFilter2 && gTraceFilter2->IsTraceEnabled())
+  {
+    DALI_LOG_RELEASE_INFO("Request render, type:%s\n", GetRequestTypeName(parameters.requestType));
+  }
+#endif
+
+  mIsTextLoadingTaskRunning          = true;
+  TextLoadObserver* textLoadObserver = this;
+  mTextLoadingTaskId                 = Text::AsyncTextManager::Get().RequestLoad(parameters, textLoadObserver);
+
+  return true;
+}
+
 void TextVisual::AddRenderer(Actor& actor, const Vector2& size, bool hasMultipleTextColors, bool containsColorGlyph, bool styleEnabled, bool isOverlayStyle)
 {
   Shader shader = GetTextShader(mFactoryCache, TextVisualShaderFeature::FeatureBuilder().EnableMultiColor(hasMultipleTextColors).EnableEmoji(containsColorGlyph).EnableStyle(styleEnabled).EnableOverlay(isOverlayStyle));
 void TextVisual::AddRenderer(Actor& actor, const Vector2& size, bool hasMultipleTextColors, bool containsColorGlyph, bool styleEnabled, bool isOverlayStyle)
 {
   Shader shader = GetTextShader(mFactoryCache, TextVisualShaderFeature::FeatureBuilder().EnableMultiColor(hasMultipleTextColors).EnableEmoji(containsColorGlyph).EnableStyle(styleEnabled).EnableOverlay(isOverlayStyle));
@@ -674,9 +1165,9 @@ void TextVisual::AddRenderer(Actor& actor, const Vector2& size, bool hasMultiple
     TextureSet textureSet = GetTextTexture(size);
 
     mImpl->mRenderer.SetTextures(textureSet);
     TextureSet textureSet = GetTextTexture(size);
 
     mImpl->mRenderer.SetTextures(textureSet);
-    //Register transform properties
+    // Register transform properties
     mImpl->mTransform.SetUniforms(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(mHasMultipleTextColorsIndex, static_cast<float>(hasMultipleTextColors));
     mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
 
     mRendererList.push_back(mImpl->mRenderer);
     mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
 
     mRendererList.push_back(mImpl->mRenderer);
@@ -701,7 +1192,7 @@ void TextVisual::AddRenderer(Actor& actor, const Vector2& size, bool hasMultiple
     int verifiedHeight = data.GetHeight();
 
     // Set information for creating textures.
     int verifiedHeight = data.GetHeight();
 
     // Set information for creating textures.
-    TilingInfo info(verifiedWidth, maxTextureSize, textPixelFormat);
+    TilingInfo info(verifiedWidth, maxTextureSize);
 
     // Get the pixel data of text.
     info.textPixelData = data;
 
     // Get the pixel data of text.
     info.textPixelData = data;
@@ -767,6 +1258,7 @@ void TextVisual::AddRenderer(Actor& actor, const Vector2& size, bool hasMultiple
     Renderer renderer = (*iter);
     if(renderer)
     {
     Renderer renderer = (*iter);
     if(renderer)
     {
+      // Note, AddRenderer will ignore renderer if it is already added. @SINCE 2_3.22
       actor.AddRenderer(renderer);
 
       if(renderer != mImpl->mRenderer)
       actor.AddRenderer(renderer);
 
       if(renderer != mImpl->mRenderer)
@@ -789,6 +1281,7 @@ void TextVisual::AddRenderer(Actor& actor, const Vector2& size, bool hasMultiple
           // VisualRenderer::Property::OPACITY uses same animatable property internally.
           Constraint opacityConstraint = Constraint::New<float>(renderer, Dali::DevelRenderer::Property::OPACITY, OpacityConstraint);
           opacityConstraint.AddSource(Source(actor, mAnimatableTextColorPropertyIndex));
           // 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.AddSource(Source(mImpl->mRenderer, mTextRequireRenderPropertyIndex));
           opacityConstraint.Apply();
         }
       }
           opacityConstraint.Apply();
         }
       }
@@ -798,6 +1291,8 @@ void TextVisual::AddRenderer(Actor& actor, const Vector2& size, bool hasMultiple
 
 TextureSet TextVisual::GetTextTexture(const Vector2& size)
 {
 
 TextureSet TextVisual::GetTextTexture(const Vector2& size)
 {
+  const bool cutoutEnabled = mController->IsTextCutout();
+
   // Filter mode needs to be set to linear to produce better quality while scaling.
   Sampler sampler = Sampler::New();
   sampler.SetFilterMode(FilterMode::LINEAR, FilterMode::LINEAR);
   // Filter mode needs to be set to linear to produce better quality while scaling.
   Sampler sampler = Sampler::New();
   sampler.SetFilterMode(FilterMode::LINEAR, FilterMode::LINEAR);
@@ -805,22 +1300,47 @@ TextureSet TextVisual::GetTextTexture(const Vector2& size)
   TextureSet textureSet = TextureSet::New();
 
   // Create RGBA texture if the text contains emojis or multiple text colors, otherwise L8 texture
   TextureSet textureSet = TextureSet::New();
 
   // Create RGBA texture if the text contains emojis or multiple text colors, otherwise L8 texture
-  Pixel::Format textPixelFormat = (mTextShaderFeatureCache.IsEnabledEmoji() || mTextShaderFeatureCache.IsEnabledMultiColor()) ? Pixel::RGBA8888 : Pixel::L8;
+  Pixel::Format textPixelFormat = (mTextShaderFeatureCache.IsEnabledEmoji() || mTextShaderFeatureCache.IsEnabledMultiColor() || cutoutEnabled) ? Pixel::RGBA8888 : Pixel::L8;
 
   // Check the text direction
 
   // Check the text direction
-  Toolkit::DevelText::TextDirection::Type textDirection = mController->GetTextDirection();
-
+  Toolkit::DevelText::TextDirection::Type textDirection   = mController->GetTextDirection();
+  uint32_t                                textureSetIndex = 0u;
   // Create a texture for the text without any styles
   // Create a texture for the text without any styles
-  PixelData data = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_NO_STYLES, false, textPixelFormat);
 
 
-  uint32_t textureSetIndex = 0u;
-  AddTexture(textureSet, data, sampler, textureSetIndex);
-  ++textureSetIndex;
+  Devel::PixelBuffer cutoutData;
+  float              cutoutAlpha = mController->GetTextModel()->GetDefaultColor().a;
+  if(cutoutEnabled)
+  {
+    cutoutData = mTypesetter->RenderWithPixelBuffer(size, textDirection, Text::Typesetter::RENDER_NO_STYLES, false, textPixelFormat);
+
+    // Make transparent buffer.
+    // If the cutout is enabled, a separate texture is not used for the text.
+    Devel::PixelBuffer buffer = mTypesetter->CreateFullBackgroundBuffer(1, 1, Vector4(0.f, 0.f, 0.f, 0.f));
+    PixelData          data   = Devel::PixelBuffer::Convert(buffer);
+    AddTexture(textureSet, data, sampler, textureSetIndex);
+    ++textureSetIndex;
+  }
+  else
+  {
+    PixelData data = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_NO_STYLES, false, textPixelFormat);
+    AddTexture(textureSet, data, sampler, textureSetIndex);
+    ++textureSetIndex;
+  }
 
   if(mTextShaderFeatureCache.IsEnabledStyle())
   {
     // Create RGBA texture for all the text styles that render in the background (without the text itself)
 
   if(mTextShaderFeatureCache.IsEnabledStyle())
   {
     // 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);
+    PixelData styleData;
+
+    if(cutoutEnabled && cutoutData)
+    {
+      styleData = mTypesetter->RenderWithCutout(size, textDirection, cutoutData, Text::Typesetter::RENDER_NO_TEXT, false, Pixel::RGBA8888, cutoutAlpha);
+    }
+    else
+    {
+      styleData = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_NO_TEXT, false, Pixel::RGBA8888);
+    }
+
     AddTexture(textureSet, styleData, sampler, textureSetIndex);
     ++textureSetIndex;
   }
     AddTexture(textureSet, styleData, sampler, textureSetIndex);
     ++textureSetIndex;
   }
@@ -844,16 +1364,28 @@ TextureSet TextVisual::GetTextTexture(const Vector2& size)
   return textureSet;
 }
 
   return textureSet;
 }
 
-Shader TextVisual::GetTextShader(VisualFactoryCache& factoryCache, const TextVisualShaderFeature::FeatureBuilder& featureBuilder)
+Shader TextVisual::GetTextShader(VisualFactoryCache& factoryCache, TextVisualShaderFeature::FeatureBuilder& featureBuilder)
 {
   // Cache feature builder informations.
   mTextShaderFeatureCache = featureBuilder;
 
   Shader shader = mTextVisualShaderFactory.GetShader(factoryCache, mTextShaderFeatureCache);
 {
   // Cache feature builder informations.
   mTextShaderFeatureCache = featureBuilder;
 
   Shader shader = mTextVisualShaderFactory.GetShader(factoryCache, mTextShaderFeatureCache);
-  shader.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT);
   return shader;
 }
 
   return shader;
 }
 
+void TextVisual::SetRequireRender(bool requireRender)
+{
+  // Avoid function calls if there is no change.
+  if(mTextRequireRender != requireRender)
+  {
+    mTextRequireRender = requireRender;
+    if(mImpl->mRenderer)
+    {
+      mImpl->mRenderer.SetProperty(mTextRequireRenderPropertyIndex, mTextRequireRender);
+    }
+  }
+}
+
 } // namespace Internal
 
 } // namespace Toolkit
 } // namespace Internal
 
 } // namespace Toolkit