[Tizen] Introduce CUTOUT Property 63/310963/1
authorANZ1217 <chihun.jeong@samsung.com>
Tue, 2 Apr 2024 10:49:46 +0000 (19:49 +0900)
committerANZ1217 <chihun.jeong@samsung.com>
Fri, 10 May 2024 08:36:21 +0000 (17:36 +0900)
Change-Id: I09ebfd4577be1d35c4701dd366cf476248024a83

20 files changed:
automated-tests/src/dali-toolkit/utc-Dali-TextLabel.cpp
dali-toolkit/devel-api/controls/text-controls/text-label-devel.h
dali-toolkit/internal/controls/text-controls/text-label-impl.cpp
dali-toolkit/internal/text/controller/text-controller-impl.h
dali-toolkit/internal/text/controller/text-controller.cpp
dali-toolkit/internal/text/controller/text-controller.h
dali-toolkit/internal/text/rendering/text-typesetter.cpp
dali-toolkit/internal/text/rendering/text-typesetter.h
dali-toolkit/internal/text/rendering/view-model.cpp
dali-toolkit/internal/text/rendering/view-model.h
dali-toolkit/internal/text/text-model-interface.h
dali-toolkit/internal/text/text-model.cpp
dali-toolkit/internal/text/text-model.h
dali-toolkit/internal/text/text-view-interface.h
dali-toolkit/internal/text/text-view.cpp
dali-toolkit/internal/text/text-view.h
dali-toolkit/internal/text/visual-model-impl.cpp
dali-toolkit/internal/text/visual-model-impl.h
dali-toolkit/internal/visuals/text/text-visual.cpp
dali-toolkit/internal/visuals/text/text-visual.h

index 576bd0f..aed1457 100644 (file)
@@ -82,6 +82,7 @@ const char* const PROPERTY_NAME_ANCHOR_CLICKED_COLOR = "anchorClickedColor";
 
 const char* const PROPERTY_NAME_REMOVE_FRONT_INSET    = "removeFrontInset";
 const char* const PROPERTY_NAME_REMOVE_BACK_INSET     = "removeBackInset";
+const char* const PROPERTY_NAME_REMOVE_CUTOUT         = "cutout";
 
 const std::string  DEFAULT_FONT_DIR("/resources/fonts");
 const unsigned int EMOJI_FONT_SIZE = 3840u; // 60 * 64
@@ -365,6 +366,7 @@ int UtcDaliToolkitTextLabelGetPropertyP(void)
   DALI_TEST_CHECK(label.GetPropertyIndex(PROPERTY_NAME_ANCHOR_CLICKED_COLOR) == DevelTextLabel::Property::ANCHOR_CLICKED_COLOR);
   DALI_TEST_CHECK(label.GetPropertyIndex(PROPERTY_NAME_REMOVE_FRONT_INSET) == DevelTextLabel::Property::REMOVE_FRONT_INSET);
   DALI_TEST_CHECK(label.GetPropertyIndex(PROPERTY_NAME_REMOVE_BACK_INSET) == DevelTextLabel::Property::REMOVE_BACK_INSET);
+  DALI_TEST_CHECK(label.GetPropertyIndex(PROPERTY_NAME_REMOVE_CUTOUT) == DevelTextLabel::Property::CUTOUT);
 
   END_TEST;
 }
@@ -1041,6 +1043,14 @@ int UtcDaliToolkitTextLabelSetPropertyP(void)
   application.SendNotification();
   application.Render();
 
+  // Check cutout Property
+  DALI_TEST_CHECK(!label.GetProperty<bool>(DevelTextLabel::Property::CUTOUT));
+  label.SetProperty(DevelTextLabel::Property::CUTOUT, true);
+  DALI_TEST_CHECK(label.GetProperty<bool>(DevelTextLabel::Property::CUTOUT));
+
+  application.SendNotification();
+  application.Render();
+
   END_TEST;
 }
 
@@ -1794,6 +1804,16 @@ int UtcDaliToolkitTextLabelColorComponents(void)
 
   DALI_TEST_EQUALS(drawTrace.FindMethod("DrawArrays"), false, TEST_LOCATION); // Rendering should be skipped
 
+  label.SetProperty(DevelTextLabel::Property::CUTOUT, true);
+
+  drawTrace.Reset();
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(drawTrace.FindMethod("DrawArrays"), true, TEST_LOCATION); // When cutout is enabled, should not be skipped
+
+  label.SetProperty(DevelTextLabel::Property::CUTOUT, false);
   label.SetProperty(TextLabel::Property::TEXT_COLOR, Color::RED);
 
   drawTrace.Reset();
index ed2e5d9..4c00662 100644 (file)
@@ -230,6 +230,12 @@ enum Type
    * @details Name "removeBackInset", type Property::BOOLEAN.
    */
   REMOVE_BACK_INSET,
+
+  /**
+   * @brief Whether to make the elements transparent, such as background or outline behind the text.
+   * @details Name "cutout", type Property::BOOLEAN.
+   */
+  CUTOUT,
 };
 
 } // namespace Property
index 27d2998..50ae898 100644 (file)
@@ -48,6 +48,7 @@
 #include <dali-toolkit/public-api/align-enumerations.h>
 #include <dali-toolkit/public-api/visuals/text-visual-properties.h>
 #include <dali-toolkit/public-api/visuals/visual-properties.h>
+#include <dali-toolkit/public-api/visuals/color-visual-properties.h>
 
 // DEVEL INCLUDES
 #include <dali-toolkit/devel-api/controls/text-controls/text-label-devel.h>
@@ -146,6 +147,7 @@ DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextLabel, "anchorColor",
 DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextLabel, "anchorClickedColor",           VECTOR4, ANCHOR_CLICKED_COLOR           )
 DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextLabel, "removeFrontInset",             BOOLEAN, REMOVE_FRONT_INSET             )
 DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextLabel, "removeBackInset",              BOOLEAN, REMOVE_BACK_INSET              )
+DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextLabel, "cutout",                       BOOLEAN, CUTOUT                         )
 
 DALI_ANIMATABLE_PROPERTY_REGISTRATION_WITH_DEFAULT(Toolkit, TextLabel, "textColor",      Color::BLACK,     TEXT_COLOR   )
 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit,    TextLabel, "textColorRed",   TEXT_COLOR_RED,   TEXT_COLOR, 0)
@@ -604,6 +606,19 @@ void TextLabel::SetProperty(BaseObject* object, Property::Index index, const Pro
         impl.mController->SetRemoveBackInset(remove);
         break;
       }
+      case Toolkit::DevelTextLabel::Property::CUTOUT:
+      {
+        const bool cutout = value.Get<bool>();
+
+        impl.mController->SetTextCutout(cutout);
+
+        // Property doesn't affect the layout, only Visual must be updated
+        TextVisual::EnableRendererUpdate(impl.mVisual);
+
+        // No need to trigger full re-layout. Instead call UpdateRenderer() directly
+        TextVisual::UpdateRenderer(impl.mVisual);
+        break;
+      }
     }
 
     // Request relayout when text update is needed. It's necessary to call it
@@ -888,6 +903,11 @@ Property::Value TextLabel::GetProperty(BaseObject* object, Property::Index index
         value = impl.mController->IsRemoveBackInset();
         break;
       }
+      case Toolkit::DevelTextLabel::Property::CUTOUT:
+      {
+        value = impl.mController->IsTextCutout();
+        break;
+      }
     }
   }
 
@@ -1080,6 +1100,48 @@ void TextLabel::OnPropertySet(Property::Index index, const Property::Value& prop
       CommonTextUtils::SynchronizeTextAnchorsInParent(Self(), mController, mAnchorActors);
       break;
     }
+    case Toolkit::Control::Property::BACKGROUND:
+    {
+      const Vector4 backgroundColor = propertyValue.Get<Vector4>();
+
+      if(mController->IsTextCutout())
+      {
+        DevelControl::EnableVisual(*this, Toolkit::Control::Property::BACKGROUND, false);
+        mController->SetBackgroundWithCutoutEnabled(true);
+        mController->SetBackgroundColorWithCutout(backgroundColor);
+      }
+
+      break;
+    }
+    case Toolkit::DevelTextLabel::Property::CUTOUT:
+    {
+      const bool cutoutEnabled = propertyValue.Get<bool>();
+
+      if(cutoutEnabled)
+      {
+        Vector4 backgroundColor = Vector4::ZERO;
+
+        const Property::Map backgroundMap = Self().GetProperty(Toolkit::Control::Property::BACKGROUND).Get<Property::Map>();
+        Property::Value* backgroundValue = backgroundMap.Find(ColorVisual::Property::MIX_COLOR);
+        if(backgroundValue)
+        {
+          backgroundColor = backgroundValue->Get<Vector4>();
+        }
+
+        DevelControl::EnableVisual(*this, Toolkit::Control::Property::BACKGROUND, false);
+        mController->SetBackgroundWithCutoutEnabled(true);
+        mController->SetBackgroundColorWithCutout(backgroundColor);
+      }
+      else
+      {
+        DevelControl::EnableVisual(*this, Toolkit::Control::Property::BACKGROUND, true);
+
+        Property::Map backgroundMapSet;
+        mController->SetBackgroundWithCutoutEnabled(false);
+      }
+
+      TextVisual::SetRequireRender(mVisual, cutoutEnabled);
+    }
     default:
     {
       Control::OnPropertySet(index, propertyValue); // up call to control for non-handled properties
index 3c813d4..cd1b85e 100644 (file)
@@ -372,7 +372,8 @@ struct Controller::Impl
     mTextFitChanged(false),
     mTextFitArrayEnabled(false),
     mIsLayoutDirectionChanged(false),
-    mIsUserInteractionEnabled(true)
+    mIsUserInteractionEnabled(true),
+    mTextCutout(false)
   {
     mModel = Model::New();
 
@@ -1104,6 +1105,7 @@ public:
   bool  mTextFitArrayEnabled : 1;      ///< Whether the text's fit array is enabled.
   bool  mIsLayoutDirectionChanged : 1; ///< Whether the layout has changed.
   bool  mIsUserInteractionEnabled : 1; ///< Whether the user interaction is enabled.
+  bool  mTextCutout : 1;               ///< Whether the text cutout enabled.
 
 private:
   friend ControllerImplEventHandler;
index 60f7765..4e0a51e 100644 (file)
@@ -324,6 +324,21 @@ void Controller::SetRemoveBackInset(bool remove)
   mImpl->mModel->mRemoveBackInset = remove;
 }
 
+bool Controller::IsTextCutout() const
+{
+  return mImpl->mTextCutout;
+}
+
+void Controller::SetTextCutout(bool cutout)
+{
+  if(cutout != mImpl->mTextCutout)
+  {
+    mImpl->mModel->mVisualModel->SetCutoutEnabled(cutout);
+    mImpl->mTextCutout = cutout;
+    mImpl->RequestRelayout();
+  }
+}
+
 void Controller::ChangedLayoutDirection()
 {
   mImpl->mIsLayoutDirectionChanged = true;
@@ -1530,6 +1545,28 @@ void Controller::SetVisualTransformOffset(Vector2 offset)
   mImpl->mModel->mVisualTransformOffset = offset;
 }
 
+void Controller::SetBackgroundWithCutoutEnabled(bool cutout)
+{
+  mImpl->mModel->mVisualModel->SetBackgroundWithCutoutEnabled(cutout);
+  RequestRelayout();
+}
+
+bool Controller::IsBackgroundWithCutoutEnabled() const
+{
+  return mImpl->mModel->mVisualModel->IsBackgroundWithCutoutEnabled();
+}
+
+void Controller::SetBackgroundColorWithCutout(const Vector4& color)
+{
+  mImpl->mModel->mVisualModel->SetBackgroundColorWithCutout(color);
+  mImpl->RequestRelayout();
+}
+
+const Vector4 Controller::GetBackgroundColorWithCutout() const
+{
+  return mImpl->mModel->mVisualModel->GetBackgroundColorWithCutout();
+}
+
 Controller::UpdateTextType Controller::Relayout(const Size& size, Dali::LayoutDirection::Type layoutDirection)
 {
   return Relayouter::Relayout(*this, size, layoutDirection);
index 07d5754..1fd57c0 100644 (file)
@@ -1670,6 +1670,34 @@ public: // Default style & Input style
    */
   void SetVisualTransformOffset(Vector2 offset);
 
+  /**
+   * @brief Sets whether background color with cutout is enabled.
+   *
+   * @param[in] enable True if enabled.
+   */
+  void SetBackgroundWithCutoutEnabled(bool enable);
+
+  /**
+   * @brief Whether background color with cutout is enabled.
+   *
+   * @return True if enabled.
+   */
+  bool IsBackgroundWithCutoutEnabled() const;
+
+  /**
+   * @brief Sets whether background color with cutout.
+   *
+   * @param[in] color The color to set.
+   */
+  void SetBackgroundColorWithCutout(const Vector4& color);
+
+  /**
+   * @brief Retrieves background color with cutout.
+   *
+   * @return The color.
+   */
+  const Vector4 GetBackgroundColorWithCutout() const;
+
 public: // Queries & retrieves.
   /**
    * @brief Return the layout engine.
@@ -1846,6 +1874,18 @@ public: // Queries & retrieves.
   void SetRemoveBackInset(bool remove);
 
   /**
+   * @brief Retrieves cutout value to model
+   * @return The value of cutout for the text
+   */
+  bool IsTextCutout() const;
+
+  /**
+   * @brief Sets cutout value to model
+   * @param[in] cutout The value of cutout for the text
+   */
+  void SetTextCutout(bool cutout);
+
+  /**
    * @brief Sets SetMatchLayoutDirection value to model
    * @param[in] match The value of matchLayoutDirection for the text
    */
index 7d095b4..b8eddcd 100644 (file)
@@ -20,6 +20,7 @@
 
 // EXTERNAL INCLUDES
 #include <dali/devel-api/text-abstraction/font-client.h>
+#include <dali/integration-api/debug.h>
 #include <dali/integration-api/trace.h>
 #include <dali/public-api/common/constants.h>
 #include <dali/public-api/math/math-utils.h>
@@ -584,6 +585,8 @@ Devel::PixelBuffer DrawGlyphsBackground(const ViewModel* model, Devel::PixelBuff
   const Vector2* const    positionBuffer               = model->GetLayout();
   const Vector4* const    backgroundColorsBuffer       = model->GetBackgroundColors();
   const ColorIndex* const backgroundColorIndicesBuffer = model->GetBackgroundColorIndices();
+  const bool              removeFrontInset             = model->IsRemoveFrontInset();
+  const bool              removeBackInset              = model->IsRemoveBackInset();
 
   const DevelText::VerticalLineAlignment::Type verLineAlign = model->GetVerticalLineAlignment();
 
@@ -653,14 +656,36 @@ Devel::PixelBuffer DrawGlyphsBackground(const ViewModel* model, Devel::PixelBuff
       }
 
       // Calculate the positions of leftmost and rightmost glyphs in the current line
-      if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
+      if(removeFrontInset)
       {
-        left = position->x - glyphInfo->xBearing;
+        if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
+        {
+          left = position->x;
+        }
+      }
+      else
+      {
+        const float originPositionLeft = position->x - glyphInfo->xBearing;
+        if((originPositionLeft < left) || (backgroundColorIndex != prevBackgroundColorIndex))
+        {
+          left = originPositionLeft;
+        }
       }
 
-      if(position->x + glyphInfo->width > right)
+      if(removeBackInset)
       {
-        right = position->x - glyphInfo->xBearing + glyphInfo->advance;
+        if(position->x + glyphInfo->width > right)
+        {
+          right = position->x - position->x + glyphInfo->width;
+        }
+      }
+      else
+      {
+        const float originPositionRight = position->x - glyphInfo->xBearing + glyphInfo->advance;
+        if(originPositionRight > right)
+        {
+          right = originPositionRight;
+        }
       }
 
       prevBackgroundColorIndex = backgroundColorIndex;
@@ -896,6 +921,24 @@ ViewModel* Typesetter::GetViewModel()
 
 PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
 {
+  Devel::PixelBuffer result = RenderWithPixelBuffer(size, textDirection, behaviour, ignoreHorizontalAlignment, pixelFormat);
+  PixelData pixelData = Devel::PixelBuffer::Convert(result);
+
+  return pixelData;
+}
+
+PixelData Typesetter::RenderWithCutout(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, Devel::PixelBuffer mask, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, float originAlpha)
+{
+  Devel::PixelBuffer result = RenderWithPixelBuffer(size, textDirection, behaviour, ignoreHorizontalAlignment, pixelFormat);
+  SetMaskForImageBuffer(mask, result, size.width, size.height, originAlpha);
+
+  PixelData pixelData = Devel::PixelBuffer::Convert(result);
+
+  return pixelData;
+}
+
+Devel::PixelBuffer Typesetter::RenderWithPixelBuffer(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
+{
   DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_RENDERING_TYPESETTER");
   // @todo. This initial implementation for a TextLabel has only one visible page.
 
@@ -904,12 +947,10 @@ PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirect
 
   // Retrieves the layout size.
   const Size& layoutSize = mModel->GetLayoutSize();
-
   const int32_t outlineWidth = static_cast<int32_t>(mModel->GetOutlineWidth());
 
   // Set the offset for the horizontal alignment according to the text direction and outline width.
   int32_t penX = 0;
-
   switch(mModel->GetHorizontalAlignment())
   {
     case HorizontalAlignment::BEGIN:
@@ -931,7 +972,6 @@ PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirect
 
   // Set the offset for the vertical alignment.
   int32_t penY = 0u;
-
   switch(mModel->GetVerticalAlignment())
   {
     case VerticalAlignment::TOP:
@@ -1054,6 +1094,18 @@ PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirect
       CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight, true);
     }
 
+    // Generate the background_with_mask if enabled
+    const bool backgroundWithCutoutEnabled   = mModel->IsBackgroundWithCutoutEnabled();
+    if((backgroundWithCutoutEnabled) && RENDER_OVERLAY_STYLE != behaviour)
+    {
+      Devel::PixelBuffer backgroundImageBuffer;
+
+      backgroundImageBuffer = CreateFullBackgroundBuffer(bufferWidth, bufferHeight, mModel->GetBackgroundColorWithCutout());
+
+      // Combine the two buffers
+      CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight, true);
+    }
+
     if(RENDER_OVERLAY_STYLE == behaviour)
     {
       if(mModel->IsUnderlineEnabled())
@@ -1090,10 +1142,30 @@ PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirect
     }
   }
 
-  // Create the final PixelData for the combined image buffer
-  PixelData pixelData = Devel::PixelBuffer::Convert(imageBuffer);
+  return imageBuffer;
+}
 
-  return pixelData;
+Devel::PixelBuffer Typesetter::CreateFullBackgroundBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Vector4& backgroundColor)
+{
+  const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
+  uint8_t backgroundColorAlpha = static_cast<uint8_t>(backgroundColor.a * 255.f);
+
+  Devel::PixelBuffer buffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
+
+  uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(buffer.GetBuffer());
+
+  uint32_t packedBackgroundColor       = 0u;
+  uint8_t* packedBackgroundColorBuffer = reinterpret_cast<uint8_t*>(&packedBackgroundColor);
+
+  // Write the color to the pixel buffer
+  *(packedBackgroundColorBuffer + 3u) = backgroundColorAlpha;
+  *(packedBackgroundColorBuffer + 2u) = static_cast<uint8_t>(backgroundColor.b * backgroundColorAlpha);
+  *(packedBackgroundColorBuffer + 1u) = static_cast<uint8_t>(backgroundColor.g * backgroundColorAlpha);
+  *(packedBackgroundColorBuffer)      = static_cast<uint8_t>(backgroundColor.r * backgroundColorAlpha);
+
+  std::fill(bitmapBuffer, bitmapBuffer + bufferSizeInt, packedBackgroundColor);
+
+  return buffer;
 }
 
 Devel::PixelBuffer Typesetter::CreateImageBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Typesetter::Style style, const bool ignoreHorizontalAlignment, const Pixel::Format pixelFormat, const int32_t horizontalOffset, const int32_t verticalOffset, const GlyphIndex fromGlyphIndex, const GlyphIndex toGlyphIndex)
@@ -1108,6 +1180,9 @@ Devel::PixelBuffer Typesetter::CreateImageBuffer(const uint32_t bufferWidth, con
   const GlyphInfo* __restrict__ hyphens                 = mModel->GetHyphens();
   const Length* __restrict__ hyphenIndices              = mModel->GetHyphenIndices();
   const Length hyphensCount                             = mModel->GetHyphensCount();
+  const bool removeFrontInset                           = mModel->IsRemoveFrontInset();
+  const bool removeBackInset                            = mModel->IsRemoveBackInset();
+  const bool cutoutEnabled                              = mModel->IsCutoutEnabled();
 
   // Elided text info. Indices according to elided text and Ellipsis position.
   const auto startIndexOfGlyphs              = mModel->GetStartIndexOfElidedGlyphs();
@@ -1336,14 +1411,36 @@ Devel::PixelBuffer Typesetter::CreateImageBuffer(const uint32_t bufferWidth, con
       }
 
       // Calculate the positions of leftmost and rightmost glyphs in the current line
-      if(position.x < lineExtentLeft)
+      if(removeFrontInset)
+      {
+        if(position.x < lineExtentLeft)
+        {
+          lineExtentLeft = position.x;
+        }
+      }
+      else
       {
-        lineExtentLeft = position.x;
+        const float originPositionLeft = position.x - glyphInfo->xBearing;
+        if(originPositionLeft < lineExtentLeft)
+        {
+          lineExtentLeft = originPositionLeft;
+        }
       }
 
-      if(position.x + glyphInfo->width > lineExtentRight)
+      if(removeBackInset)
       {
-        lineExtentRight = position.x + glyphInfo->width;
+        if(position.x + glyphInfo->width > lineExtentRight)
+        {
+          lineExtentRight = position.x + glyphInfo->width;
+        }
+      }
+      else
+      {
+        const float originPositionRight = position.x - glyphInfo->xBearing + glyphInfo->advance;
+        if(originPositionRight > lineExtentRight)
+        {
+          lineExtentRight = originPositionRight;
+        }
       }
 
       // Retrieves the glyph's color.
@@ -1363,6 +1460,12 @@ Devel::PixelBuffer Typesetter::CreateImageBuffer(const uint32_t bufferWidth, con
         color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
       }
 
+      if(style == Typesetter::STYLE_NONE && cutoutEnabled)
+      {
+        // Temporarily adjust the transparency to 1.f
+        color.a = 1.f;
+      }
+
       // Premultiply alpha
       color.r *= color.a;
       color.g *= color.a;
@@ -1528,6 +1631,58 @@ Devel::PixelBuffer Typesetter::ApplyStrikethroughMarkupImageBuffer(Devel::PixelB
   return topPixelBuffer;
 }
 
+void Typesetter::SetMaskForImageBuffer(Devel::PixelBuffer& __restrict__ topPixelBuffer, Devel::PixelBuffer& __restrict__ bottomPixelBuffer, const uint32_t bufferWidth, const uint32_t bufferHeight, float originAlpha)
+{
+  // Assume that we always combine two RGBA images
+  // Jump with 4bytes for optimize runtime.
+  uint32_t* topBuffer    = reinterpret_cast<uint32_t*>(topPixelBuffer.GetBuffer());
+  uint32_t* bottomBuffer = reinterpret_cast<uint32_t*>(bottomPixelBuffer.GetBuffer());
+
+  if(topBuffer == NULL || bottomBuffer == NULL)
+  {
+    // Nothing to do if one of both buffers are empty.
+    return;
+  }
+
+  const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
+
+  for(uint32_t pixelIndex = 0; pixelIndex < bufferSizeInt; ++pixelIndex)
+  {
+    uint32_t topBufferColor = *(topBuffer);
+    uint32_t bottomBufferColor = *(bottomBuffer);
+    uint8_t* __restrict__ topBufferColorBuffer = reinterpret_cast<uint8_t*>(&topBufferColor);
+    uint8_t* __restrict__ bottomBufferColorBuffer = reinterpret_cast<uint8_t*>(&bottomBufferColor);
+
+    uint8_t topAlpha = topBufferColorBuffer[3];
+    uint8_t bottomAlpha = 255 - topAlpha;
+
+    float tempTop[4], tempBottom[4];
+
+    // Return the transparency of the text to original.
+    tempTop[0] = static_cast<float>(topBufferColorBuffer[0]) * originAlpha;
+    tempTop[1] = static_cast<float>(topBufferColorBuffer[1]) * originAlpha;
+    tempTop[2] = static_cast<float>(topBufferColorBuffer[2]) * originAlpha;
+    tempTop[3] = static_cast<float>(topBufferColorBuffer[3]) * originAlpha;
+
+    // Manual blending.
+    tempBottom[0] = static_cast<float>(bottomBufferColorBuffer[0]) * static_cast<float>(bottomAlpha) / 255.f;
+    tempBottom[1] = static_cast<float>(bottomBufferColorBuffer[1]) * static_cast<float>(bottomAlpha) / 255.f;
+    tempBottom[2] = static_cast<float>(bottomBufferColorBuffer[2]) * static_cast<float>(bottomAlpha) / 255.f;
+    tempBottom[3] = static_cast<float>(bottomBufferColorBuffer[3]) * static_cast<float>(bottomAlpha) / 255.f;
+
+    bottomBufferColorBuffer[0] = static_cast<uint8_t>(std::min(255u, static_cast<uint32_t>(tempBottom[0] + tempTop[0])));
+    bottomBufferColorBuffer[1] = static_cast<uint8_t>(std::min(255u, static_cast<uint32_t>(tempBottom[1] + tempTop[1])));
+    bottomBufferColorBuffer[2] = static_cast<uint8_t>(std::min(255u, static_cast<uint32_t>(tempBottom[2] + tempTop[2])));
+    bottomBufferColorBuffer[3] = static_cast<uint8_t>(std::min(255u, static_cast<uint32_t>(tempBottom[3] + tempTop[3])));
+
+    *(bottomBuffer) = bottomBufferColor;
+
+    // Increase each buffer's pointer.
+    ++topBuffer;
+    ++bottomBuffer;
+  }
+}
+
 Typesetter::Typesetter(const ModelInterface* const model)
 : mModel(new ViewModel(model))
 {
index 296126e..7d268c9 100644 (file)
@@ -110,6 +110,61 @@ public:
    */
   PixelData Render(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour = RENDER_TEXT_AND_STYLES, bool ignoreHorizontalAlignment = false, Pixel::Format pixelFormat = Pixel::RGBA8888);
 
+  /**
+   * @brief After the Render, use the pixel information of the given cutoutBuffer to make the part where the pixel is drawn transparent.
+   *
+   * @param[in] size The renderer size.
+   * @param[in] textDirection The direction of the text.
+   * @param[in] cutoutBuffer The buffer to use pixel information to cutout.
+   * @param[in] behaviour The behaviour of how to render the text (i.e. whether to render the text only or the styles only or both).
+   * @param[in] ignoreHorizontalAlignment Whether to ignore the horizontal alignment (i.e. always render as if HORIZONTAL_ALIGN_BEGIN).
+   * @param[in] pixelFormat The format of the pixel in the image that the text is rendered as (i.e. either Pixel::BGRA8888 or Pixel::L8).
+   * @param[in] originAlpha The original alpha of text.
+   *
+   * @return A pixel data with the text rendered.
+   */
+  PixelData RenderWithCutout(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, Devel::PixelBuffer cutoutBuffer, RenderBehaviour behaviour = RENDER_TEXT_AND_STYLES, bool ignoreHorizontalAlignment = false, Pixel::Format pixelFormat = Pixel::RGBA8888, float originAlpha = 1.f);
+
+  /**
+   * @brief Renders the text, return as Devel::PixelBuffer.
+   *
+   * This function is used to obtain the PixelBuffer required for cutout.
+   *
+   * @param[in] size The renderer size.
+   * @param[in] textDirection The direction of the text.
+   * @param[in] cutoutBuffer The buffer to use pixel information to cutout.
+   * @param[in] behaviour The behaviour of how to render the text (i.e. whether to render the text only or the styles only or both).
+   * @param[in] ignoreHorizontalAlignment Whether to ignore the horizontal alignment (i.e. always render as if HORIZONTAL_ALIGN_BEGIN).
+   * @param[in] pixelFormat The format of the pixel in the image that the text is rendered as (i.e. either Pixel::BGRA8888 or Pixel::L8).
+   *
+   * @return A pixel data with the text rendered.
+   */
+  Devel::PixelBuffer RenderWithPixelBuffer(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour = RENDER_TEXT_AND_STYLES, bool ignoreHorizontalAlignment = false, Pixel::Format pixelFormat = Pixel::RGBA8888);
+
+  /**
+   * @brief Create & draw the image buffer of single background color.
+   *
+   * @param[in] bufferWidth The width of the image buffer.
+   * @param[in] bufferHeight The height of the image buffer.
+   * @param[in] backgroundColor The backgroundColor of image buffer.
+   *
+   * @return An image buffer with the text.
+   */
+  Devel::PixelBuffer CreateFullBackgroundBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Vector4& backgroundColor);
+
+  /**
+   * @brief Set Mask for two pixel buffer.
+   *
+   * The alpha value of bottomPixelBuffer is decreased as the alpha value of topPixelBuffer is higher.
+   *
+   * @param[in, out] topPixelBuffer The top layer buffer.
+   * @param[in, out] bottomPixelBuffer The bottom layer buffer.
+   * @param[in] bufferWidth The width of the image buffer.
+   * @param[in] bufferHeight The height of the image buffer.
+   * @param[in] originAlpha The original alpha value of the text.
+   */
+  void SetMaskForImageBuffer(Devel::PixelBuffer& __restrict__ topPixelBuffer, Devel::PixelBuffer& __restrict__ bottomPixelBuffer, const uint32_t bufferWidth, const uint32_t bufferHeight, float originAlpha);
+
 private:
   /**
    * @brief Private constructor.
index 3404be2..7e3a4e3 100644 (file)
@@ -769,6 +769,31 @@ const Vector<FontDescriptionRun>& ViewModel::GetFontDescriptionRuns() const
   return mModel->GetFontDescriptionRuns();
 }
 
+bool ViewModel::IsRemoveFrontInset() const
+{
+  return mModel->IsRemoveFrontInset();
+}
+
+bool ViewModel::IsRemoveBackInset() const
+{
+  return mModel->IsRemoveBackInset();
+}
+
+bool ViewModel::IsCutoutEnabled() const
+{
+  return mModel->IsCutoutEnabled();
+}
+
+const bool ViewModel::IsBackgroundWithCutoutEnabled() const
+{
+  return mModel->IsBackgroundWithCutoutEnabled();
+}
+
+const Vector4& ViewModel::GetBackgroundColorWithCutout() const
+{
+  return mModel->GetBackgroundColorWithCutout();
+}
+
 } // namespace Text
 
 } // namespace Toolkit
index 9adcb65..dc7363d 100644 (file)
@@ -388,6 +388,31 @@ public:
    */
   const Vector<FontDescriptionRun>& GetFontDescriptionRuns() const override;
 
+  /**
+   * @copydoc ModelInterface::IsRemoveFrontInset()
+   */
+  bool IsRemoveFrontInset() const override;
+
+  /**
+   * @copydoc ModelInterface::IsRemoveBackInset()
+   */
+  bool IsRemoveBackInset() const override;
+
+  /**
+   * @copydoc ModelInterface::IsCutoutEnabled()
+   */
+  bool IsCutoutEnabled() const override;
+
+  /**
+   * @copydoc ModelInterface::IsBackgroundWithCutoutEnabled()
+   */
+  const bool IsBackgroundWithCutoutEnabled() const override;
+
+  /**
+   * @copydoc ModelInterface::GetBackgroundColorWithCutout()
+   */
+  const Vector4& GetBackgroundColorWithCutout() const override;
+
 private:
   const ModelInterface* const mModel;                           ///< Pointer to the text's model.
   Vector<GlyphInfo>           mElidedGlyphs;                    ///< Stores the glyphs of the elided text.
index 212ec0d..ba9f6ca 100644 (file)
@@ -508,6 +508,41 @@ public:
    * @return The reference for font description runs.
    */
   virtual const Vector<FontDescriptionRun>& GetFontDescriptionRuns() const = 0;
+
+  /**
+   * @brief Retrieves the remove front inset is enabled or not.
+   *
+   * @return boolean if it is enabled.
+   */
+  virtual bool IsRemoveFrontInset() const = 0;
+
+  /**
+   * @brief Retrieves the remove back inset is enabled or not.
+   *
+   * @return boolean if it is enabled.
+   */
+  virtual bool IsRemoveBackInset() const = 0;
+
+  /**
+   * @brief Retrieves the cutout is enabled or not.
+   *
+   * @return boolean if it is enabled.
+   */
+  virtual bool IsCutoutEnabled() const = 0;
+
+  /**
+   * @brief Retrieves the background with cutout is enabled or not.
+   *
+   * @return boolean if it is enabled.
+   */
+  virtual const bool IsBackgroundWithCutoutEnabled() const = 0;
+
+  /**
+   * @brief Retrieves the color of the background with cutout.
+   *
+   * @return The color of the background with cutout.
+   */
+  virtual const Vector4& GetBackgroundColorWithCutout() const = 0;
 };
 
 } // namespace Text
index 01c6f60..bbe126b 100644 (file)
@@ -356,6 +356,31 @@ const Vector<FontDescriptionRun>& Model::GetFontDescriptionRuns() const
   return mLogicalModel->mFontDescriptionRuns;
 }
 
+bool Model::IsRemoveFrontInset() const
+{
+  return mRemoveFrontInset;
+}
+
+bool Model::IsRemoveBackInset() const
+{
+  return mRemoveBackInset;
+}
+
+bool Model::IsCutoutEnabled() const
+{
+  return mVisualModel->IsCutoutEnabled();
+}
+
+const bool Model::IsBackgroundWithCutoutEnabled() const
+{
+  return mVisualModel->IsBackgroundWithCutoutEnabled();
+}
+
+const Vector4& Model::GetBackgroundColorWithCutout() const
+{
+  return mVisualModel->GetBackgroundColorWithCutout();
+}
+
 Model::Model()
 : mLogicalModel(),
   mVisualModel(),
index eb0d8a6..5661035 100644 (file)
@@ -374,6 +374,31 @@ public:
    */
   const Vector<FontDescriptionRun>& GetFontDescriptionRuns() const override;
 
+  /**
+   * @copydoc ModelInterface::IsRemoveFrontInset()
+   */
+  bool IsRemoveFrontInset() const override;
+
+  /**
+   * @copydoc ModelInterface::IsRemoveBackInset()
+   */
+  bool IsRemoveBackInset() const override;
+
+  /**
+   * @copydoc ModelInterface::IsCutoutEnabled()
+   */
+  bool IsCutoutEnabled() const override;
+
+  /**
+   * @copydoc ModelInterface::IsBackgroundWithCutoutEnabled()
+   */
+  const bool IsBackgroundWithCutoutEnabled() const override;
+
+  /**
+   * @copydoc ModelInterface::GetBackgroundColorWithCutout()
+   */
+  const Vector4& GetBackgroundColorWithCutout() const override;
+
 private: // Private contructors & copy operator.
   /**
    * @brief Private constructor.
index 10ff4ff..5fbbfea 100644 (file)
@@ -392,6 +392,13 @@ public:
    * @return GetGlyphsToCharacters.
    */
   virtual const Vector<CharacterIndex>& GetGlyphsToCharacters() const = 0;
+
+  /**
+   * @brief Returns whether cutout is enabled or not.
+   *
+   * @return The cutout state.
+   */
+  virtual bool IsCutoutEnabled() const = 0;
 };
 
 } // namespace Text
index 66d0af9..3b678dd 100644 (file)
@@ -1024,4 +1024,13 @@ const Vector<CharacterIndex>& View::GetGlyphsToCharacters() const
   return mImpl->mVisualModel->GetGlyphsToCharacters();
 }
 
+bool View::IsCutoutEnabled() const
+{
+  if(mImpl->mVisualModel)
+  {
+    return mImpl->mVisualModel->IsCutoutEnabled();
+  }
+  return false;
+}
+
 } // namespace Dali::Toolkit::Text
index 9b12d03..d141f4b 100644 (file)
@@ -288,6 +288,11 @@ public:
    */
   const Vector<CharacterIndex>& GetGlyphsToCharacters() const override;
 
+  /**
+   * @copydoc Dali::Toolkit::Text::ViewInterface::IsCutoutEnabled()
+   */
+  bool IsCutoutEnabled() const override;
+
 private:
   // Undefined
   View(const View& handle);
index fd6b683..3fdd6b8 100644 (file)
@@ -665,6 +665,36 @@ const Vector<CharacterIndex>& VisualModel::GetGlyphsToCharacters() const
   return mGlyphsToCharacters;
 }
 
+void VisualModel::SetCutoutEnabled(bool enable)
+{
+  mCutoutEnabled = enable;
+}
+
+bool VisualModel::IsCutoutEnabled() const
+{
+  return mCutoutEnabled;
+}
+
+void VisualModel::SetBackgroundWithCutoutEnabled(bool enable)
+{
+  mBackgroundWithCutoutEnabled = enable;
+}
+
+bool VisualModel::IsBackgroundWithCutoutEnabled() const
+{
+  return mBackgroundWithCutoutEnabled;
+}
+
+void VisualModel::SetBackgroundColorWithCutout(const Vector4& color)
+{
+  mBackgroundColorWithCutout = color;
+}
+
+const Vector4& VisualModel::GetBackgroundColorWithCutout() const
+{
+  return mBackgroundColorWithCutout;
+}
+
 VisualModel::~VisualModel()
 {
 }
@@ -709,8 +739,9 @@ VisualModel::VisualModel()
   mBackgroundEnabled(false),
   mMarkupProcessorEnabled(false),
   mStrikethroughEnabled(false),
-  mCharacterSpacing(0.0f)
-
+  mCharacterSpacing(0.0f),
+  mCutoutEnabled(false),
+  mBackgroundWithCutoutEnabled(false)
 {
 }
 
index edebaf6..7ef55b5 100644 (file)
@@ -662,6 +662,48 @@ public:
    */
   const Vector<CharacterSpacingGlyphRun>& GetCharacterSpacingGlyphRuns() const;
 
+  /**
+   * @brief Sets the cutout flag.
+   *
+   * @param[in] enable true if cutouted.
+   */
+  void SetCutoutEnabled(bool enable);
+
+  /**
+   * @brief Returns whether the text is cutouted or not.
+   *
+   * @return cutout state.
+   */
+  bool IsCutoutEnabled() const;
+
+  /**
+   * @brief Sets the background with cutout flag.
+   *
+   * @param[in] enable true if background enabled.
+   */
+  void SetBackgroundWithCutoutEnabled(bool enable);
+
+  /**
+   * @brief Returns whether the text is cutouted or not.
+   *
+   * @return True if enabled.
+   */
+  bool IsBackgroundWithCutoutEnabled() const;
+
+  /**
+   * @brief Sets the Color of background with cutout.
+   *
+   * @param[in] color The color to set.
+   */
+  void SetBackgroundColorWithCutout(const Vector4& color);
+
+  /**
+   * @brief Retrieves the Color of background with cutout.
+   *
+   * @return The color.
+   */
+  const Vector4& GetBackgroundColorWithCutout() const;
+
 protected:
   /**
    * @brief A reference counted object may only be deleted by calling Unreference().
@@ -681,37 +723,38 @@ private:
   VisualModel& operator=(const VisualModel& handle);
 
 public:
-  Vector<GlyphInfo>                mGlyphs;                 ///< For each glyph, the font's id, glyph's index within the font and glyph's metrics.
-  Vector<CharacterIndex>           mGlyphsToCharacters;     ///< For each glyph, the index of the first character.
-  Vector<GlyphIndex>               mCharactersToGlyph;      ///< For each character, the index of the first glyph.
-  Vector<Length>                   mCharactersPerGlyph;     ///< For each glyph, the number of characters that form the glyph.
-  Vector<Length>                   mGlyphsPerCharacter;     ///< For each character, the number of glyphs that are shaped.
-  Vector<Vector2>                  mGlyphPositions;         ///< For each glyph, the position.
-  Vector<LineRun>                  mLines;                  ///< The laid out lines.
-  Vector<UnderlinedGlyphRun>       mUnderlineRuns;          ///< Runs of glyphs that are underlined.
-  Vector<Vector4>                  mColors;                 ///< Colors of the glyphs.
-  Vector<ColorIndex>               mColorIndices;           ///< Indices to the vector of colors for each glyphs.
-  Vector<Vector4>                  mBackgroundColors;       ///< Background colors of the glyphs.
-  Vector<ColorIndex>               mBackgroundColorIndices; ///< Indices to the vector of background colors for each glyphs.
-  Vector4                          mTextColor;              ///< The text color
-  Vector4                          mShadowColor;            ///< Color of drop shadow
-  Vector4                          mUnderlineColor;         ///< Color of underline
-  Vector4                          mOutlineColor;           ///< Color of outline
-  Vector4                          mBackgroundColor;        ///< Color of text background
-  Vector4                          mStrikethroughColor;     ///< Color of text background
-  Size                             mControlSize;            ///< The size of the UI control.
-  Vector2                          mShadowOffset;           ///< Offset for drop shadow, 0 indicates no shadow
-  Vector2                          mOutlineOffset;          ///< Offset for outline
-  float                            mUnderlineHeight;        ///< Fixed height for underline to override font metrics.
-  float                            mStrikethroughHeight;    ///< Fixed height for strikethrough to override font metrics.
-  Text::Underline::Type            mUnderlineType;          ///< The type of the underline.
-  float                            mDashedUnderlineWidth;   ///< The width of the dashes of the dashed underline.
-  float                            mDashedUnderlineGap;     ///< The gap between the dashes of the dashed underline.
-  float                            mShadowBlurRadius;       ///< Blur radius of shadow, 0 indicates no blur.
+  Vector<GlyphInfo>                mGlyphs;                     ///< For each glyph, the font's id, glyph's index within the font and glyph's metrics.
+  Vector<CharacterIndex>           mGlyphsToCharacters;         ///< For each glyph, the index of the first character.
+  Vector<GlyphIndex>               mCharactersToGlyph;          ///< For each character, the index of the first glyph.
+  Vector<Length>                   mCharactersPerGlyph;         ///< For each glyph, the number of characters that form the glyph.
+  Vector<Length>                   mGlyphsPerCharacter;         ///< For each character, the number of glyphs that are shaped.
+  Vector<Vector2>                  mGlyphPositions;             ///< For each glyph, the position.
+  Vector<LineRun>                  mLines;                      ///< The laid out lines.
+  Vector<UnderlinedGlyphRun>       mUnderlineRuns;              ///< Runs of glyphs that are underlined.
+  Vector<Vector4>                  mColors;                     ///< Colors of the glyphs.
+  Vector<ColorIndex>               mColorIndices;               ///< Indices to the vector of colors for each glyphs.
+  Vector<Vector4>                  mBackgroundColors;           ///< Background colors of the glyphs.
+  Vector<ColorIndex>               mBackgroundColorIndices;     ///< Indices to the vector of background colors for each glyphs.
+  Vector4                          mTextColor;                  ///< The text color
+  Vector4                          mShadowColor;                ///< Color of drop shadow
+  Vector4                          mUnderlineColor;             ///< Color of underline
+  Vector4                          mOutlineColor;               ///< Color of outline
+  Vector4                          mBackgroundColor;            ///< Color of text background
+  Vector4                          mStrikethroughColor;         ///< Color of text background
+  Size                             mControlSize;                ///< The size of the UI control.
+  Vector2                          mShadowOffset;               ///< Offset for drop shadow, 0 indicates no shadow
+  Vector2                          mOutlineOffset;              ///< Offset for outline
+  float                            mUnderlineHeight;            ///< Fixed height for underline to override font metrics.
+  float                            mStrikethroughHeight;        ///< Fixed height for strikethrough to override font metrics.
+  Text::Underline::Type            mUnderlineType;              ///< The type of the underline.
+  float                            mDashedUnderlineWidth;       ///< The width of the dashes of the dashed underline.
+  float                            mDashedUnderlineGap;         ///< The gap between the dashes of the dashed underline.
+  float                            mShadowBlurRadius;           ///< Blur radius of shadow, 0 indicates no blur.
   float                            mOutlineBlurRadius;      ///< Blur radius of outline, 0 indicates no blur.
-  uint16_t                         mOutlineWidth;           ///< Width of outline.
-  Vector<StrikethroughGlyphRun>    mStrikethroughRuns;      ///< Runs of glyphs that have strikethrough.
-  Vector<CharacterSpacingGlyphRun> mCharacterSpacingRuns;   ///< Runs of glyphs that have character-spacing.
+  uint16_t                         mOutlineWidth;               ///< Width of outline.
+  Vector<StrikethroughGlyphRun>    mStrikethroughRuns;          ///< Runs of glyphs that have strikethrough.
+  Vector<CharacterSpacingGlyphRun> mCharacterSpacingRuns;       ///< Runs of glyphs that have character-spacing.
+  Vector4                          mBackgroundColorWithCutout;  ///< Background color with cutout.
 
 private:
   Size mNaturalSize;    ///< Size of the text with no line wrapping.
@@ -729,13 +772,15 @@ private:
   bool                              mTextElideEnabled : 1;            ///< Whether the text's elide is enabled.
 
 public:
-  bool       mUnderlineEnabled : 1;       ///< Underline enabled flag
-  bool       mUnderlineColorSet : 1;      ///< Has the underline color been explicitly set?
-  bool       mBackgroundEnabled : 1;      ///< Background enabled flag
-  bool       mMarkupProcessorEnabled : 1; ///< Markup-processor enabled flag
-  HyphenInfo mHyphen;                     ///< Contains hyphen glyph info & the character index to draw hyphen after.
-  bool       mStrikethroughEnabled : 1;   ///< Strikethrough enabled flag
-  float      mCharacterSpacing;           ///< Contains the value of the character spacing.
+  bool       mUnderlineEnabled : 1;             ///< Underline enabled flag
+  bool       mUnderlineColorSet : 1;            ///< Has the underline color been explicitly set?
+  bool       mBackgroundEnabled : 1;            ///< Background enabled flag
+  bool       mMarkupProcessorEnabled : 1;       ///< Markup-processor enabled flag
+  HyphenInfo mHyphen;                           ///< Contains hyphen glyph info & the character index to draw hyphen after.
+  bool       mStrikethroughEnabled : 1;         ///< Strikethrough enabled flag
+  float      mCharacterSpacing;                 ///< Contains the value of the character spacing.
+  bool       mCutoutEnabled : 1;                ///< Cutout enabled flag
+  bool       mBackgroundWithCutoutEnabled : 1;  ///< Background with cutout enabled flag.
 };
 
 } // namespace Text
index 33e2c41..3ad2c9a 100644 (file)
@@ -53,7 +53,7 @@ namespace
 {
 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_TEXT_PERFORMANCE_MARKER, false);
 
-const int CUSTOM_PROPERTY_COUNT(2); // uTextColorAnimatable, uHasMultipleTextColors
+const int CUSTOM_PROPERTY_COUNT(3); // uTextColorAnimatable, uHasMultipleTextColors, requireRender
 
 /**
  * Return Property index for the given string key
@@ -137,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,7 +259,9 @@ TextVisual::TextVisual(VisualFactoryCache& factoryCache, TextVisualShaderFactory
   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;
@@ -276,6 +278,7 @@ 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));
 }
 
@@ -340,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();
   }
@@ -502,9 +506,12 @@ 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));
 
   auto textLengthUtf32 = mController->GetNumberOfCharacters();
 
@@ -564,16 +571,28 @@ 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(backgroundWithCutoutEnabled)
+      {
+        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);
 
@@ -787,6 +806,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();
         }
       }
@@ -796,6 +816,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);
@@ -803,22 +825,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;
   }
@@ -851,6 +899,12 @@ Shader TextVisual::GetTextShader(VisualFactoryCache& factoryCache, const TextVis
   return shader;
 }
 
+void TextVisual::SetRequireRender(bool requireRender)
+{
+  mTextRequireRender = requireRender;
+  mImpl->mRenderer.SetProperty(mTextRequireRenderPropertyIndex, mTextRequireRender);
+}
+
 } // namespace Internal
 
 } // namespace Toolkit
index 7be059c..7208491 100644 (file)
@@ -116,6 +116,16 @@ public:
   };
 
   /**
+   * @brief Set the text to be always rendered
+   * @param[in] visual The text visual.
+   * @param[in] requireRender Whether to text always rendered.
+   */
+  static void SetRequireRender(Toolkit::Visual::Base visual, bool requireRender)
+  {
+    GetVisualObject(visual).SetRequireRender(requireRender);
+  };
+
+  /**
    * @brief Instantly updates the renderer
    * @param[in] visual The text visual.
    */
@@ -293,6 +303,12 @@ private:
   Shader GetTextShader(VisualFactoryCache& factoryCache, const TextVisualShaderFeature::FeatureBuilder& featureBuilder);
 
   /**
+   * @brief Set the text to be always rendered
+   * @param[in] requireRender Whether to text always rendered.
+   */
+  void SetRequireRender(bool requireRender);
+
+  /**
    * @brief Retrieve the TextVisual object.
    * @param[in] visual A handle to the TextVisual
    * @return The TextVisual object
@@ -318,7 +334,9 @@ private:
   Property::Index   mHasMultipleTextColorsIndex;       ///< The index of uHasMultipleTextColors proeprty.
   Property::Index   mAnimatableTextColorPropertyIndex; ///< The index of animatable text color property registered by the control.
   Property::Index   mTextColorAnimatableIndex;         ///< The index of uTextColorAnimatable property.
+  Property::Index   mTextRequireRenderPropertyIndex;   ///< The index of requireRender property.
   bool              mRendererUpdateNeeded : 1;         ///< The flag to indicate whether the renderer needs to be updated.
+  bool              mTextRequireRender : 1;            ///< The flag to indicate whether the text needs to be rendered.
   RendererContainer mRendererList;
 };