[dali_2.3.42] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / controller / text-controller-impl-model-updater.cpp
index ff4697b..3d579e9 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.
 #include <dali-toolkit/internal/text/controller/text-controller-impl-model-updater.h>
 
 // EXTERNAL INCLUDES
 #include <dali-toolkit/internal/text/controller/text-controller-impl-model-updater.h>
 
 // EXTERNAL INCLUDES
+#include <chrono>
 #include <dali/integration-api/debug.h>
 #include <dali/integration-api/debug.h>
+#include <dali/integration-api/trace.h>
+#include <dali/public-api/math/math-utils.h>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/text/bidirectional-support.h>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/text/bidirectional-support.h>
@@ -39,6 +42,20 @@ namespace
 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
 #endif
 
 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
 #endif
 
+DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_TEXT_PERFORMANCE_MARKER, false);
+
+#if defined(TRACE_ENABLED)
+uint32_t GetMilliSeconds()
+{
+  // Get the time of a monotonic clock since its epoch.
+  auto epoch = std::chrono::steady_clock::now().time_since_epoch();
+
+  auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(epoch);
+
+  return static_cast<uint32_t>(duration.count());
+}
+#endif
+
 // The relative luminance of a color is defined as (L = 0.2126 * R + 0.7152 * G + 0.0722 * B)
 // based on W3C Recommendations (https://www.w3.org/TR/WCAG20/)
 constexpr float         BRIGHTNESS_THRESHOLD = 0.179f;
 // The relative luminance of a color is defined as (L = 0.2126 * R + 0.7152 * G + 0.0722 * B)
 // based on W3C Recommendations (https://www.w3.org/TR/WCAG20/)
 constexpr float         BRIGHTNESS_THRESHOLD = 0.179f;
@@ -66,6 +83,7 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
     // Nothing to do if no operations are pending and required.
     return false;
   }
     // Nothing to do if no operations are pending and required.
     return false;
   }
+  DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_MODEL_UPDATE");
 
   Vector<Character>& srcCharacters = impl.mModel->mLogicalModel->mText;
   Vector<Character>  displayCharacters;
 
   Vector<Character>& srcCharacters = impl.mModel->mLogicalModel->mText;
   Vector<Character>  displayCharacters;
@@ -94,32 +112,39 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
   impl.CalculateTextUpdateIndices(paragraphCharacters);
 
   // Check whether the indices for updating the text is valid
   impl.CalculateTextUpdateIndices(paragraphCharacters);
 
   // Check whether the indices for updating the text is valid
-  if(numberOfCharacters > 0u &&
-     (impl.mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters ||
-      impl.mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters))
+  if(impl.mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters ||
+     impl.mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters)
   {
   {
-    std::string currentText;
-    Utf32ToUtf8(impl.mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText);
-
-    DALI_LOG_ERROR("Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n");
-    DALI_LOG_ERROR("Number of characters: %d, current text is: %s\n", numberOfCharacters, currentText.c_str());
-
-    // Dump mTextUpdateInfo
-    DALI_LOG_ERROR("Dump mTextUpdateInfo:\n");
-    DALI_LOG_ERROR("     mTextUpdateInfo.mCharacterIndex = %u\n", impl.mTextUpdateInfo.mCharacterIndex);
-    DALI_LOG_ERROR("     mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", impl.mTextUpdateInfo.mNumberOfCharactersToRemove);
-    DALI_LOG_ERROR("     mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", impl.mTextUpdateInfo.mNumberOfCharactersToAdd);
-    DALI_LOG_ERROR("     mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", impl.mTextUpdateInfo.mPreviousNumberOfCharacters);
-    DALI_LOG_ERROR("     mTextUpdateInfo.mParagraphCharacterIndex = %u\n", impl.mTextUpdateInfo.mParagraphCharacterIndex);
-    DALI_LOG_ERROR("     mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", impl.mTextUpdateInfo.mRequestedNumberOfCharacters);
-    DALI_LOG_ERROR("     mTextUpdateInfo.mStartGlyphIndex = %u\n", impl.mTextUpdateInfo.mStartGlyphIndex);
-    DALI_LOG_ERROR("     mTextUpdateInfo.mStartLineIndex = %u\n", impl.mTextUpdateInfo.mStartLineIndex);
-    DALI_LOG_ERROR("     mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", impl.mTextUpdateInfo.mEstimatedNumberOfLines);
-    DALI_LOG_ERROR("     mTextUpdateInfo.mClearAll = %d\n", impl.mTextUpdateInfo.mClearAll);
-    DALI_LOG_ERROR("     mTextUpdateInfo.mFullRelayoutNeeded = %d\n", impl.mTextUpdateInfo.mFullRelayoutNeeded);
-    DALI_LOG_ERROR("     mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", impl.mTextUpdateInfo.mIsLastCharacterNewParagraph);
-
-    return false;
+    if(numberOfCharacters == 0u)
+    {
+      impl.mTextUpdateInfo.Clear();
+      impl.mTextUpdateInfo.mClearAll = true;
+    }
+    else // numberOfCharacters > 0u
+    {
+      std::string currentText;
+      Utf32ToUtf8(impl.mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText);
+
+      DALI_LOG_ERROR("Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n");
+      DALI_LOG_ERROR("Number of characters: %d, current text is: %s paragraphCharacters: %d\n", numberOfCharacters, currentText.c_str(), paragraphCharacters);
+
+      // Dump mTextUpdateInfo
+      DALI_LOG_ERROR("Dump mTextUpdateInfo:\n");
+      DALI_LOG_ERROR("     mTextUpdateInfo.mCharacterIndex = %u\n", impl.mTextUpdateInfo.mCharacterIndex);
+      DALI_LOG_ERROR("     mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", impl.mTextUpdateInfo.mNumberOfCharactersToRemove);
+      DALI_LOG_ERROR("     mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", impl.mTextUpdateInfo.mNumberOfCharactersToAdd);
+      DALI_LOG_ERROR("     mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", impl.mTextUpdateInfo.mPreviousNumberOfCharacters);
+      DALI_LOG_ERROR("     mTextUpdateInfo.mParagraphCharacterIndex = %u\n", impl.mTextUpdateInfo.mParagraphCharacterIndex);
+      DALI_LOG_ERROR("     mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", impl.mTextUpdateInfo.mRequestedNumberOfCharacters);
+      DALI_LOG_ERROR("     mTextUpdateInfo.mStartGlyphIndex = %u\n", impl.mTextUpdateInfo.mStartGlyphIndex);
+      DALI_LOG_ERROR("     mTextUpdateInfo.mStartLineIndex = %u\n", impl.mTextUpdateInfo.mStartLineIndex);
+      DALI_LOG_ERROR("     mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", impl.mTextUpdateInfo.mEstimatedNumberOfLines);
+      DALI_LOG_ERROR("     mTextUpdateInfo.mClearAll = %d\n", impl.mTextUpdateInfo.mClearAll);
+      DALI_LOG_ERROR("     mTextUpdateInfo.mFullRelayoutNeeded = %d\n", impl.mTextUpdateInfo.mFullRelayoutNeeded);
+      DALI_LOG_ERROR("     mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", impl.mTextUpdateInfo.mIsLastCharacterNewParagraph);
+
+      return false;
+    }
   }
 
   startIndex = impl.mTextUpdateInfo.mParagraphCharacterIndex;
   }
 
   startIndex = impl.mTextUpdateInfo.mParagraphCharacterIndex;
@@ -146,10 +171,8 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
     // is not shaped together).
     lineBreakInfo.Resize(numberOfCharacters, TextAbstraction::LINE_NO_BREAK);
 
     // is not shaped together).
     lineBreakInfo.Resize(numberOfCharacters, TextAbstraction::LINE_NO_BREAK);
 
-    SetLineBreakInfo(utf32Characters,
-                     startIndex,
-                     requestedNumberOfCharacters,
-                     lineBreakInfo);
+    TextAbstraction::Segmentation segmentation = TextAbstraction::Segmentation::Get();
+    SetLineBreakInfo(segmentation, utf32Characters, startIndex, requestedNumberOfCharacters, lineBreakInfo);
 
     if(impl.mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
        impl.mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::MIXED))
 
     if(impl.mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
        impl.mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::MIXED))
@@ -157,6 +180,8 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
       CharacterIndex end                 = startIndex + requestedNumberOfCharacters;
       LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
 
       CharacterIndex end                 = startIndex + requestedNumberOfCharacters;
       LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
 
+      TextAbstraction::Hyphenation hyphenation = TextAbstraction::Hyphenation::Get();
+
       for(CharacterIndex index = startIndex; index < end; index++)
       {
         CharacterIndex wordEnd = index;
       for(CharacterIndex index = startIndex; index < end; index++)
       {
         CharacterIndex wordEnd = index;
@@ -170,7 +195,7 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
           wordEnd++;
         }
 
           wordEnd++;
         }
 
-        Vector<bool> hyphens = GetWordHyphens(utf32Characters.Begin() + index, wordEnd - index, nullptr);
+        Vector<bool> hyphens = GetWordHyphens(hyphenation, utf32Characters.Begin() + index, wordEnd - index, nullptr);
 
         for(CharacterIndex i = 0; i < (wordEnd - index); i++)
         {
 
         for(CharacterIndex i = 0; i < (wordEnd - index); i++)
         {
@@ -237,7 +262,7 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
         // Set the normal font and the placeholder font.
         defaultFontDescription = impl.mFontDefaults->mFontDescription;
 
         // Set the normal font and the placeholder font.
         defaultFontDescription = impl.mFontDefaults->mFontDescription;
 
-        if(impl.mTextFitEnabled)
+        if(impl.mTextFitEnabled || impl.mTextFitArrayEnabled)
         {
           defaultPointSize = impl.mFontDefaults->mFitPointSize * numberOfPointsPerOneUnitOfPointSize;
         }
         {
           defaultPointSize = impl.mFontDefaults->mFitPointSize * numberOfPointsPerOneUnitOfPointSize;
         }
@@ -249,11 +274,13 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
 
       // Validates the fonts. If there is a character with no assigned font it sets a default one.
       // After this call, fonts are validated.
 
       // Validates the fonts. If there is a character with no assigned font it sets a default one.
       // After this call, fonts are validated.
-      multilanguageSupport.ValidateFonts(utf32Characters,
+      multilanguageSupport.ValidateFonts(impl.mFontClient,
+                                         utf32Characters,
                                          scripts,
                                          fontDescriptionRuns,
                                          defaultFontDescription,
                                          defaultPointSize,
                                          scripts,
                                          fontDescriptionRuns,
                                          defaultFontDescription,
                                          defaultPointSize,
+                                         impl.GetFontSizeScale(),
                                          startIndex,
                                          requestedNumberOfCharacters,
                                          validFonts);
                                          startIndex,
                                          requestedNumberOfCharacters,
                                          validFonts);
@@ -269,8 +296,11 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
     Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = impl.mModel->mLogicalModel->mBidirectionalParagraphInfo;
     bidirectionalInfo.Reserve(numberOfParagraphs);
 
     Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = impl.mModel->mLogicalModel->mBidirectionalParagraphInfo;
     bidirectionalInfo.Reserve(numberOfParagraphs);
 
+    TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
+
     // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
     // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
-    SetBidirectionalInfo(utf32Characters,
+    SetBidirectionalInfo(bidirectionalSupport,
+                         utf32Characters,
                          scripts,
                          lineBreakInfo,
                          startIndex,
                          scripts,
                          lineBreakInfo,
                          startIndex,
@@ -283,7 +313,8 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
     {
       // Only set the character directions if there is right to left characters.
       Vector<CharacterDirection>& directions = impl.mModel->mLogicalModel->mCharacterDirections;
     {
       // Only set the character directions if there is right to left characters.
       Vector<CharacterDirection>& directions = impl.mModel->mLogicalModel->mCharacterDirections;
-      GetCharactersDirection(bidirectionalInfo,
+      GetCharactersDirection(bidirectionalSupport,
+                             bidirectionalInfo,
                              numberOfCharacters,
                              startIndex,
                              requestedNumberOfCharacters,
                              numberOfCharacters,
                              startIndex,
                              requestedNumberOfCharacters,
@@ -292,7 +323,8 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
       // This paragraph has right to left text. Some characters may need to be mirrored.
       // TODO: consider if the mirrored string can be stored as well.
 
       // This paragraph has right to left text. Some characters may need to be mirrored.
       // TODO: consider if the mirrored string can be stored as well.
 
-      textMirrored = GetMirroredText(utf32Characters,
+      textMirrored = GetMirroredText(bidirectionalSupport,
+                                     utf32Characters,
                                      directions,
                                      bidirectionalInfo,
                                      startIndex,
                                      directions,
                                      bidirectionalInfo,
                                      startIndex,
@@ -314,11 +346,30 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
   newParagraphGlyphs.Reserve(numberOfParagraphs);
 
   const Length currentNumberOfGlyphs = glyphs.Count();
   newParagraphGlyphs.Reserve(numberOfParagraphs);
 
   const Length currentNumberOfGlyphs = glyphs.Count();
+
+#if defined(TRACE_ENABLED)
+  uint32_t logThreshold = TextAbstraction::FontClient::GetPerformanceLogThresholdTime();
+  bool     logEnabled   = TextAbstraction::FontClient::IsPerformanceLogEnabled();
+
+  uint32_t timeStamps[6];
+  uint32_t timeStampIndex = 0;
+
+  if(logEnabled)
+  {
+    timeStamps[timeStampIndex++] = GetMilliSeconds();
+  }
+#endif
+
   if(Controller::NO_OPERATION != (Controller::SHAPE_TEXT & operations))
   {
     const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
   if(Controller::NO_OPERATION != (Controller::SHAPE_TEXT & operations))
   {
     const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
+
+    TextAbstraction::Shaping shaping = TextAbstraction::Shaping::Get();
+
     // Shapes the text.
     // Shapes the text.
-    ShapeText(textToShape,
+    ShapeText(shaping,
+              impl.mFontClient,
+              textToShape,
               lineBreakInfo,
               scripts,
               validFonts,
               lineBreakInfo,
               scripts,
               validFonts,
@@ -337,6 +388,13 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
     updated = true;
   }
 
     updated = true;
   }
 
+#if defined(TRACE_ENABLED)
+  if(logEnabled)
+  {
+    timeStamps[timeStampIndex++] = GetMilliSeconds();
+  }
+#endif
+
   const Length numberOfGlyphs = static_cast<Length>(glyphs.Count()) - currentNumberOfGlyphs;
 
   if(Controller::NO_OPERATION != (Controller::GET_GLYPH_METRICS & operations))
   const Length numberOfGlyphs = static_cast<Length>(glyphs.Count()) - currentNumberOfGlyphs;
 
   if(Controller::NO_OPERATION != (Controller::GET_GLYPH_METRICS & operations))
@@ -357,6 +415,13 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
     updated = true;
   }
 
     updated = true;
   }
 
+#if defined(TRACE_ENABLED)
+  if(logEnabled)
+  {
+    timeStamps[timeStampIndex++] = GetMilliSeconds();
+  }
+#endif
+
   if((nullptr != impl.mEventData) &&
      impl.mEventData->mPreEditFlag &&
      (0u != impl.mModel->mVisualModel->mCharactersToGlyph.Count()))
   if((nullptr != impl.mEventData) &&
      impl.mEventData->mPreEditFlag &&
      (0u != impl.mModel->mVisualModel->mCharactersToGlyph.Count()))
@@ -387,7 +452,8 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
           impl.mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
 
           //Mark-up processor case
           impl.mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
 
           //Mark-up processor case
-          if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled())
+          if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
+             impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
           {
             impl.CopyUnderlinedFromLogicalToVisualModels(false);
           }
           {
             impl.CopyUnderlinedFromLogicalToVisualModels(false);
           }
@@ -403,13 +469,13 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
           impl.mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
 
           Vector4 backgroundColor = impl.mModel->mVisualModel->GetBackgroundColor();
           impl.mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
 
           Vector4 backgroundColor = impl.mModel->mVisualModel->GetBackgroundColor();
-          if(backgroundColor.a == 0) // There is no text background color.
+          if(Dali::EqualsZero(backgroundColor.a)) // There is no text background color.
           {
             // Try use the control's background color.
             if(nullptr != impl.mEditableControlInterface)
             {
               impl.mEditableControlInterface->GetControlBackgroundColor(backgroundColor);
           {
             // Try use the control's background color.
             if(nullptr != impl.mEditableControlInterface)
             {
               impl.mEditableControlInterface->GetControlBackgroundColor(backgroundColor);
-              if(backgroundColor.a == 0) // There is no control background color.
+              if(Dali::EqualsZero(backgroundColor.a)) // There is no control background color.
               {
                 // Determines black or white color according to text color.
                 // Based on W3C Recommendations (https://www.w3.org/TR/WCAG20/)
               {
                 // Determines black or white color according to text color.
                 // Based on W3C Recommendations (https://www.w3.org/TR/WCAG20/)
@@ -428,7 +494,8 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
           impl.mModel->mLogicalModel->mColorRuns.PushBack(colorRun);
 
           //Mark-up processor case
           impl.mModel->mLogicalModel->mColorRuns.PushBack(colorRun);
 
           //Mark-up processor case
-          if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled())
+          if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
+             impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
           {
             impl.CopyUnderlinedFromLogicalToVisualModels(false);
           }
           {
             impl.CopyUnderlinedFromLogicalToVisualModels(false);
           }
@@ -443,7 +510,8 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
           impl.mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
 
           //Mark-up processor case
           impl.mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
 
           //Mark-up processor case
-          if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled())
+          if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
+             impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
           {
             impl.CopyUnderlinedFromLogicalToVisualModels(false);
           }
           {
             impl.CopyUnderlinedFromLogicalToVisualModels(false);
           }
@@ -464,7 +532,8 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
           impl.mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
 
           //Mark-up processor case
           impl.mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
 
           //Mark-up processor case
-          if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled())
+          if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
+             impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
           {
             impl.CopyUnderlinedFromLogicalToVisualModels(false);
           }
           {
             impl.CopyUnderlinedFromLogicalToVisualModels(false);
           }
@@ -485,7 +554,8 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
           impl.mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
 
           //Mark-up processor case
           impl.mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
 
           //Mark-up processor case
-          if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled())
+          if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
+             impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
           {
             impl.CopyUnderlinedFromLogicalToVisualModels(false);
           }
           {
             impl.CopyUnderlinedFromLogicalToVisualModels(false);
           }
@@ -506,7 +576,8 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
           impl.mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
 
           //Mark-up processor case
           impl.mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
 
           //Mark-up processor case
-          if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled())
+          if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
+             impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
           {
             impl.CopyUnderlinedFromLogicalToVisualModels(false);
           }
           {
             impl.CopyUnderlinedFromLogicalToVisualModels(false);
           }
@@ -527,7 +598,8 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
           impl.mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
 
           //Mark-up processor case
           impl.mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
 
           //Mark-up processor case
-          if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled())
+          if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
+             impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
           {
             impl.CopyUnderlinedFromLogicalToVisualModels(false);
           }
           {
             impl.CopyUnderlinedFromLogicalToVisualModels(false);
           }
@@ -544,6 +616,13 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
     updated = true;
   }
 
     updated = true;
   }
 
+#if defined(TRACE_ENABLED)
+  if(logEnabled)
+  {
+    timeStamps[timeStampIndex++] = GetMilliSeconds();
+  }
+#endif
+
   if(Controller::NO_OPERATION != (Controller::COLOR & operations))
   {
     // Set the color runs in glyphs.
   if(Controller::NO_OPERATION != (Controller::COLOR & operations))
   {
     // Set the color runs in glyphs.
@@ -569,13 +648,23 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
     updated = true;
   }
 
     updated = true;
   }
 
+#if defined(TRACE_ENABLED)
+  if(logEnabled)
+  {
+    timeStamps[timeStampIndex++] = GetMilliSeconds();
+  }
+#endif
+
   if((Controller::NO_OPERATION != (Controller::SHAPE_TEXT & operations)) &&
      !((nullptr != impl.mEventData) &&
        impl.mEventData->mPreEditFlag &&
        (0u != impl.mModel->mVisualModel->mCharactersToGlyph.Count())))
   {
     //Mark-up processor case
   if((Controller::NO_OPERATION != (Controller::SHAPE_TEXT & operations)) &&
      !((nullptr != impl.mEventData) &&
        impl.mEventData->mPreEditFlag &&
        (0u != impl.mModel->mVisualModel->mCharactersToGlyph.Count())))
   {
     //Mark-up processor case
-    if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled())
+    if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
+       impl.mModel->mLogicalModel->mUnderlineRunsUpdated ||
+       impl.mModel->mLogicalModel->mCharacterSpacingRunsUpdated ||
+       impl.mModel->mLogicalModel->mStrikethroughRunsUpdated)
     {
       impl.CopyUnderlinedFromLogicalToVisualModels(true);
       impl.CopyStrikethroughFromLogicalToVisualModels();
     {
       impl.CopyUnderlinedFromLogicalToVisualModels(true);
       impl.CopyStrikethroughFromLogicalToVisualModels();
@@ -585,6 +674,26 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
     updated = true;
   }
 
     updated = true;
   }
 
+#if defined(TRACE_ENABLED)
+  if(logEnabled)
+  {
+    timeStamps[timeStampIndex++] = GetMilliSeconds();
+    uint32_t timeShape   = timeStamps[1] - timeStamps[0];
+    uint32_t timeGlyph   = timeStamps[2] - timeStamps[1];
+    uint32_t timePreedit = timeStamps[3] - timeStamps[2];
+    uint32_t timeColor   = timeStamps[4] - timeStamps[3];
+    uint32_t timeCopy    = timeStamps[5] - timeStamps[4];
+
+    if(timeStamps[5] - timeStamps[0] > logThreshold)
+    {
+      std::string currentText;
+      Utf32ToUtf8(impl.mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText);
+      DALI_LOG_DEBUG_INFO("DALI_TEXT_MODEL_UPDATE shape:%u ms, glyph:%u ms, preedit:%u ms, color:%u ms, copy:%u ms\n", timeShape, timeGlyph, timePreedit, timeColor, timeCopy);
+      DALI_LOG_DEBUG_INFO("DALI_TEXT_MODEL_UPDATE chars:%d, text:%s\n", numberOfCharacters, currentText.c_str());
+    }
+  }
+#endif
+
   // The estimated number of lines. Used to avoid reallocations when layouting.
   impl.mTextUpdateInfo.mEstimatedNumberOfLines = std::max(impl.mModel->mVisualModel->mLines.Count(), impl.mModel->mLogicalModel->mParagraphInfo.Count());
 
   // The estimated number of lines. Used to avoid reallocations when layouting.
   impl.mTextUpdateInfo.mEstimatedNumberOfLines = std::max(impl.mModel->mVisualModel->mLines.Count(), impl.mModel->mLogicalModel->mParagraphInfo.Count());