[dali_2.3.24] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / text / text-visual.cpp
index a504f2f..5d2de15 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.
@@ -20,7 +20,6 @@
 
 // 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>
@@ -54,9 +53,7 @@ namespace
 {
 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_TEXT_PERFORMANCE_MARKER, false);
 
-const int CUSTOM_PROPERTY_COUNT(5); // anim,premul,size,offset,multicol
-
-const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
+const int CUSTOM_PROPERTY_COUNT(3); // uTextColorAnimatable, uHasMultipleTextColors, requireRender
 
 /**
  * Return Property index for the given string key
@@ -140,7 +137,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
-  if(EqualsZero(inputs[0]->GetVector4().a))
+  if(EqualsZero(inputs[0]->GetVector4().a) && !inputs[1]->GetBoolean())
   {
     current = 0.0f;
   }
@@ -259,9 +256,12 @@ TextVisual::TextVisual(VisualFactoryCache& factoryCache, TextVisualShaderFactory
   mTypesetter(Text::Typesetter::New(mController->GetTextModel())),
   mTextVisualShaderFactory(shaderFactory),
   mTextShaderFeatureCache(),
+  mHasMultipleTextColorsIndex(Property::INVALID_INDEX),
   mAnimatableTextColorPropertyIndex(Property::INVALID_INDEX),
   mTextColorAnimatableIndex(Property::INVALID_INDEX),
-  mRendererUpdateNeeded(false)
+  mTextRequireRenderPropertyIndex(Property::INVALID_INDEX),
+  mRendererUpdateNeeded(false),
+  mTextRequireRender(false)
 {
   // Enable the pre-multiplied alpha to improve the text quality
   mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
@@ -278,6 +278,8 @@ void TextVisual::OnInitialize()
 
   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)
@@ -341,6 +343,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));
+      mOpacityConstraint.AddSource(Source(mImpl->mRenderer, mTextRequireRenderPropertyIndex));
     }
     mOpacityConstraint.Apply();
   }
@@ -351,12 +354,13 @@ void TextVisual::DoSetOnScene(Actor& actor)
   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);
-    if(renderer)
+    if(renderer &&
+       (removeDefaultRenderer || (renderer != mImpl->mRenderer)))
     {
       // Removes the renderer from the actor.
       actor.RemoveRenderer(renderer);
@@ -377,7 +381,7 @@ void TextVisual::DoSetOffScene(Actor& actor)
     mOpacityConstraint.Remove();
   }
 
-  RemoveRenderer(actor);
+  RemoveRenderer(actor, true);
 
   // Resets the control handle.
   mControl.Reset();
@@ -503,17 +507,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 float controlWidth = mImpl->mControlSize.width;
+  const float controlHeight = mImpl->mControlSize.height;
+
   // 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.
-    RemoveRenderer(control);
+    RemoveRenderer(control, true);
 
     // Nothing else to do if the relayout size is zero.
     ResourceReady(Toolkit::Visual::ResourceStatus::READY);
@@ -529,7 +535,8 @@ void TextVisual::UpdateRenderer()
     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))
@@ -566,16 +573,29 @@ void TextVisual::UpdateRenderer()
         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)
+      {
+        relayoutSize = Vector2(controlWidth, controlHeight);
+        mImpl->mTransform.mSize.width = controlWidth;
+        mImpl->mTransform.mSize.height = controlHeight;
+        mImpl->mTransform.mOffset.x = 0;
+        mImpl->mTransform.mOffset.y = 0;
+      }
 
       AddRenderer(control, relayoutSize, hasMultipleTextColors, containsColorGlyph, styleEnabled, isOverlayStyle);
 
@@ -647,7 +667,7 @@ void TextVisual::CreateTextureSet(TilingInfo& info, VisualRenderer& renderer, Sa
 
   // 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);
+  renderer.SetProperty(VisualRenderer::Property::VISUAL_PRE_MULTIPLIED_ALPHA, true);
 
   // Set size and offset for the tiling.
   renderer.SetProperty(VisualRenderer::Property::TRANSFORM_SIZE, Vector2(info.width, info.height));
@@ -676,7 +696,7 @@ void TextVisual::AddRenderer(Actor& actor, const Vector2& size, bool hasMultiple
     mImpl->mRenderer.SetTextures(textureSet);
     //Register transform properties
     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);
@@ -767,6 +787,7 @@ void TextVisual::AddRenderer(Actor& actor, const Vector2& size, bool hasMultiple
     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)
@@ -789,6 +810,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));
+          opacityConstraint.AddSource(Source(mImpl->mRenderer, mTextRequireRenderPropertyIndex));
           opacityConstraint.Apply();
         }
       }
@@ -798,6 +820,8 @@ void TextVisual::AddRenderer(Actor& actor, const Vector2& size, bool hasMultiple
 
 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);
@@ -805,22 +829,48 @@ 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
-  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
   Toolkit::DevelText::TextDirection::Type textDirection = mController->GetTextDirection();
-
+  uint32_t textureSetIndex = 0u;
   // 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)
-    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;
   }
@@ -850,10 +900,18 @@ Shader TextVisual::GetTextShader(VisualFactoryCache& factoryCache, const TextVis
   mTextShaderFeatureCache = featureBuilder;
 
   Shader shader = mTextVisualShaderFactory.GetShader(factoryCache, mTextShaderFeatureCache);
-  shader.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT);
   return shader;
 }
 
+void TextVisual::SetRequireRender(bool requireRender)
+{
+  mTextRequireRender = requireRender;
+  if(mImpl->mRenderer)
+  {
+    mImpl->mRenderer.SetProperty(mTextRequireRenderPropertyIndex, mTextRequireRender);
+  }
+}
+
 } // namespace Internal
 
 } // namespace Toolkit