[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / controller / text-controller-impl-model-updater.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/controller/text-controller-impl-model-updater.h>
20
21 // EXTERNAL INCLUDES
22 #include <chrono>
23 #include <dali/integration-api/debug.h>
24 #include <dali/integration-api/trace.h>
25 #include <dali/public-api/math/math-utils.h>
26
27 // INTERNAL INCLUDES
28 #include <dali-toolkit/internal/text/bidirectional-support.h>
29 #include <dali-toolkit/internal/text/character-set-conversion.h>
30 #include <dali-toolkit/internal/text/color-segmentation.h>
31 #include <dali-toolkit/internal/text/hyphenator.h>
32 #include <dali-toolkit/internal/text/multi-language-support.h>
33 #include <dali-toolkit/internal/text/segmentation.h>
34 #include <dali-toolkit/internal/text/shaper.h>
35 #include <dali-toolkit/internal/text/text-editable-control-interface.h>
36
37 namespace Dali::Toolkit::Text
38 {
39 namespace
40 {
41 #if defined(DEBUG_ENABLED)
42 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
43 #endif
44
45 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_TEXT_PERFORMANCE_MARKER, false);
46
47 #if defined(TRACE_ENABLED)
48 uint32_t GetMilliSeconds()
49 {
50   // Get the time of a monotonic clock since its epoch.
51   auto epoch = std::chrono::steady_clock::now().time_since_epoch();
52
53   auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(epoch);
54
55   return static_cast<uint32_t>(duration.count());
56 }
57 #endif
58
59 // The relative luminance of a color is defined as (L = 0.2126 * R + 0.7152 * G + 0.0722 * B)
60 // based on W3C Recommendations (https://www.w3.org/TR/WCAG20/)
61 constexpr float         BRIGHTNESS_THRESHOLD = 0.179f;
62 constexpr float         CONSTANT_R           = 0.2126f;
63 constexpr float         CONSTANT_G           = 0.7152f;
64 constexpr float         CONSTANT_B           = 0.0722f;
65 constexpr Dali::Vector4 BLACK(0.f, 0.f, 0.f, 1.f);
66 constexpr Dali::Vector4 WHITE(1.f, 1.f, 1.f, 1.f);
67 constexpr Dali::Vector4 LIGHT_BLUE(0.75f, 0.96f, 1.f, 1.f);
68 constexpr Dali::Vector4 BACKGROUND_SUB4(0.58f, 0.87f, 0.96f, 1.f);
69 constexpr Dali::Vector4 BACKGROUND_SUB5(0.83f, 0.94f, 0.98f, 1.f);
70 constexpr Dali::Vector4 BACKGROUND_SUB6(1.f, 0.5f, 0.5f, 1.f);
71 constexpr Dali::Vector4 BACKGROUND_SUB7(1.f, 0.8f, 0.8f, 1.f);
72 } // namespace
73
74 bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask operationsRequired)
75 {
76   DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::UpdateModel\n");
77
78   // Calculate the operations to be done.
79   const OperationsMask operations = static_cast<OperationsMask>(impl.mOperationsPending & operationsRequired);
80
81   if(Controller::NO_OPERATION == operations)
82   {
83     // Nothing to do if no operations are pending and required.
84     return false;
85   }
86   DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_MODEL_UPDATE");
87
88   Vector<Character>& srcCharacters = impl.mModel->mLogicalModel->mText;
89   Vector<Character>  displayCharacters;
90   bool               useHiddenText = false;
91   if(impl.mHiddenInput && impl.mEventData != nullptr)
92   {
93     if(impl.mEventData->mIsShowingPlaceholderText)
94     {
95       impl.mHiddenInput->InitPreviousTextCount();
96     }
97     else
98     {
99       impl.mHiddenInput->Substitute(srcCharacters, displayCharacters, impl.mEventData->mPrimaryCursorPosition);
100       useHiddenText = true;
101     }
102   }
103
104   Vector<Character>& utf32Characters    = useHiddenText ? displayCharacters : srcCharacters;
105   const Length       numberOfCharacters = utf32Characters.Count();
106
107   // Index to the first character of the first paragraph to be updated.
108   CharacterIndex startIndex = 0u;
109   // Number of characters of the paragraphs to be removed.
110   Length paragraphCharacters = 0u;
111
112   impl.CalculateTextUpdateIndices(paragraphCharacters);
113
114   // Check whether the indices for updating the text is valid
115   if(impl.mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters ||
116      impl.mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters)
117   {
118     if(numberOfCharacters == 0u)
119     {
120       impl.mTextUpdateInfo.Clear();
121       impl.mTextUpdateInfo.mClearAll = true;
122     }
123     else // numberOfCharacters > 0u
124     {
125       std::string currentText;
126       Utf32ToUtf8(impl.mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText);
127
128       DALI_LOG_ERROR("Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n");
129       DALI_LOG_ERROR("Number of characters: %d, current text is: %s paragraphCharacters: %d\n", numberOfCharacters, currentText.c_str(), paragraphCharacters);
130
131       // Dump mTextUpdateInfo
132       DALI_LOG_ERROR("Dump mTextUpdateInfo:\n");
133       DALI_LOG_ERROR("     mTextUpdateInfo.mCharacterIndex = %u\n", impl.mTextUpdateInfo.mCharacterIndex);
134       DALI_LOG_ERROR("     mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", impl.mTextUpdateInfo.mNumberOfCharactersToRemove);
135       DALI_LOG_ERROR("     mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", impl.mTextUpdateInfo.mNumberOfCharactersToAdd);
136       DALI_LOG_ERROR("     mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", impl.mTextUpdateInfo.mPreviousNumberOfCharacters);
137       DALI_LOG_ERROR("     mTextUpdateInfo.mParagraphCharacterIndex = %u\n", impl.mTextUpdateInfo.mParagraphCharacterIndex);
138       DALI_LOG_ERROR("     mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", impl.mTextUpdateInfo.mRequestedNumberOfCharacters);
139       DALI_LOG_ERROR("     mTextUpdateInfo.mStartGlyphIndex = %u\n", impl.mTextUpdateInfo.mStartGlyphIndex);
140       DALI_LOG_ERROR("     mTextUpdateInfo.mStartLineIndex = %u\n", impl.mTextUpdateInfo.mStartLineIndex);
141       DALI_LOG_ERROR("     mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", impl.mTextUpdateInfo.mEstimatedNumberOfLines);
142       DALI_LOG_ERROR("     mTextUpdateInfo.mClearAll = %d\n", impl.mTextUpdateInfo.mClearAll);
143       DALI_LOG_ERROR("     mTextUpdateInfo.mFullRelayoutNeeded = %d\n", impl.mTextUpdateInfo.mFullRelayoutNeeded);
144       DALI_LOG_ERROR("     mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", impl.mTextUpdateInfo.mIsLastCharacterNewParagraph);
145
146       return false;
147     }
148   }
149
150   startIndex = impl.mTextUpdateInfo.mParagraphCharacterIndex;
151
152   if(impl.mTextUpdateInfo.mClearAll ||
153      (0u != paragraphCharacters))
154   {
155     impl.ClearModelData(startIndex, startIndex + ((paragraphCharacters > 0u) ? paragraphCharacters - 1u : 0u), operations);
156   }
157
158   impl.mTextUpdateInfo.mClearAll = false;
159
160   // Whether the model is updated.
161   bool updated = false;
162
163   Vector<LineBreakInfo>& lineBreakInfo               = impl.mModel->mLogicalModel->mLineBreakInfo;
164   const Length           requestedNumberOfCharacters = impl.mTextUpdateInfo.mRequestedNumberOfCharacters;
165
166   if(Controller::NO_OPERATION != (Controller::GET_LINE_BREAKS & operations))
167   {
168     // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
169     // calculate the bidirectional info for each 'paragraph'.
170     // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
171     // is not shaped together).
172     lineBreakInfo.Resize(numberOfCharacters, TextAbstraction::LINE_NO_BREAK);
173
174     SetLineBreakInfo(utf32Characters,
175                      startIndex,
176                      requestedNumberOfCharacters,
177                      lineBreakInfo);
178
179     if(impl.mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
180        impl.mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::MIXED))
181     {
182       CharacterIndex end                 = startIndex + requestedNumberOfCharacters;
183       LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
184
185       for(CharacterIndex index = startIndex; index < end; index++)
186       {
187         CharacterIndex wordEnd = index;
188         while((*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_ALLOW_BREAK) && (*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_MUST_BREAK))
189         {
190           wordEnd++;
191         }
192
193         if((wordEnd + 1) == end) // add last char
194         {
195           wordEnd++;
196         }
197
198         Vector<bool> hyphens = GetWordHyphens(utf32Characters.Begin() + index, wordEnd - index, nullptr);
199
200         for(CharacterIndex i = 0; i < (wordEnd - index); i++)
201         {
202           if(hyphens[i])
203           {
204             *(lineBreakInfoBuffer + index + i) = TextAbstraction::LINE_HYPHENATION_BREAK;
205           }
206         }
207
208         index = wordEnd;
209       }
210     }
211
212     // Create the paragraph info.
213     impl.mModel->mLogicalModel->CreateParagraphInfo(startIndex,
214                                                     requestedNumberOfCharacters);
215     updated = true;
216   }
217
218   const bool getScripts    = Controller::NO_OPERATION != (Controller::GET_SCRIPTS & operations);
219   const bool validateFonts = Controller::NO_OPERATION != (Controller::VALIDATE_FONTS & operations);
220
221   Vector<ScriptRun>& scripts    = impl.mModel->mLogicalModel->mScriptRuns;
222   Vector<FontRun>&   validFonts = impl.mModel->mLogicalModel->mFontRuns;
223
224   if(getScripts || validateFonts)
225   {
226     // Validates the fonts assigned by the application or assigns default ones.
227     // It makes sure all the characters are going to be rendered by the correct font.
228     MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
229
230     if(getScripts)
231     {
232       // Retrieves the scripts used in the text.
233       multilanguageSupport.SetScripts(utf32Characters,
234                                       startIndex,
235                                       requestedNumberOfCharacters,
236                                       scripts);
237     }
238
239     if(validateFonts)
240     {
241       // Validate the fonts set through the mark-up string.
242       Vector<FontDescriptionRun>& fontDescriptionRuns = impl.mModel->mLogicalModel->mFontDescriptionRuns;
243
244       // Get the default font's description.
245       TextAbstraction::FontDescription defaultFontDescription;
246       TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE * impl.GetFontSizeScale();
247
248       //Get the number of points per one unit of point-size
249       uint32_t numberOfPointsPerOneUnitOfPointSize = impl.mFontClient.GetNumberOfPointsPerOneUnitOfPointSize();
250
251       if(impl.IsShowingPlaceholderText() && impl.mEventData && (nullptr != impl.mEventData->mPlaceholderFont))
252       {
253         // If the placeholder font is set specifically, only placeholder font is changed.
254         defaultFontDescription = impl.mEventData->mPlaceholderFont->mFontDescription;
255         if(impl.mEventData->mPlaceholderFont->sizeDefined)
256         {
257           defaultPointSize = impl.mEventData->mPlaceholderFont->mDefaultPointSize * impl.GetFontSizeScale() * numberOfPointsPerOneUnitOfPointSize;
258         }
259       }
260       else if(nullptr != impl.mFontDefaults)
261       {
262         // Set the normal font and the placeholder font.
263         defaultFontDescription = impl.mFontDefaults->mFontDescription;
264
265         if(impl.mTextFitEnabled || impl.mTextFitArrayEnabled)
266         {
267           defaultPointSize = impl.mFontDefaults->mFitPointSize * numberOfPointsPerOneUnitOfPointSize;
268         }
269         else
270         {
271           defaultPointSize = impl.mFontDefaults->mDefaultPointSize * impl.GetFontSizeScale() * numberOfPointsPerOneUnitOfPointSize;
272         }
273       }
274
275       // Validates the fonts. If there is a character with no assigned font it sets a default one.
276       // After this call, fonts are validated.
277       multilanguageSupport.ValidateFonts(utf32Characters,
278                                          scripts,
279                                          fontDescriptionRuns,
280                                          defaultFontDescription,
281                                          defaultPointSize,
282                                          impl.GetFontSizeScale(),
283                                          startIndex,
284                                          requestedNumberOfCharacters,
285                                          validFonts);
286     }
287     updated = true;
288   }
289
290   Vector<Character> mirroredUtf32Characters;
291   bool              textMirrored       = false;
292   const Length      numberOfParagraphs = impl.mModel->mLogicalModel->mParagraphInfo.Count();
293   if(Controller::NO_OPERATION != (Controller::BIDI_INFO & operations))
294   {
295     Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = impl.mModel->mLogicalModel->mBidirectionalParagraphInfo;
296     bidirectionalInfo.Reserve(numberOfParagraphs);
297
298     // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
299     SetBidirectionalInfo(utf32Characters,
300                          scripts,
301                          lineBreakInfo,
302                          startIndex,
303                          requestedNumberOfCharacters,
304                          bidirectionalInfo,
305                          (impl.mModel->mMatchLayoutDirection != DevelText::MatchLayoutDirection::CONTENTS),
306                          impl.mLayoutDirection);
307
308     if(0u != bidirectionalInfo.Count())
309     {
310       // Only set the character directions if there is right to left characters.
311       Vector<CharacterDirection>& directions = impl.mModel->mLogicalModel->mCharacterDirections;
312       GetCharactersDirection(bidirectionalInfo,
313                              numberOfCharacters,
314                              startIndex,
315                              requestedNumberOfCharacters,
316                              directions);
317
318       // This paragraph has right to left text. Some characters may need to be mirrored.
319       // TODO: consider if the mirrored string can be stored as well.
320
321       textMirrored = GetMirroredText(utf32Characters,
322                                      directions,
323                                      bidirectionalInfo,
324                                      startIndex,
325                                      requestedNumberOfCharacters,
326                                      mirroredUtf32Characters);
327     }
328     else
329     {
330       // There is no right to left characters. Clear the directions vector.
331       impl.mModel->mLogicalModel->mCharacterDirections.Clear();
332     }
333     updated = true;
334   }
335
336   Vector<GlyphInfo>&      glyphs                = impl.mModel->mVisualModel->mGlyphs;
337   Vector<CharacterIndex>& glyphsToCharactersMap = impl.mModel->mVisualModel->mGlyphsToCharacters;
338   Vector<Length>&         charactersPerGlyph    = impl.mModel->mVisualModel->mCharactersPerGlyph;
339   Vector<GlyphIndex>      newParagraphGlyphs;
340   newParagraphGlyphs.Reserve(numberOfParagraphs);
341
342   const Length currentNumberOfGlyphs = glyphs.Count();
343
344 #if defined(TRACE_ENABLED)
345   uint32_t logThreshold = TextAbstraction::FontClient::GetPerformanceLogThresholdTime();
346   bool     logEnabled   = TextAbstraction::FontClient::IsPerformanceLogEnabled();
347
348   uint32_t timeStamps[6];
349   uint32_t timeStampIndex = 0;
350
351   if(logEnabled)
352   {
353     timeStamps[timeStampIndex++] = GetMilliSeconds();
354   }
355 #endif
356
357   if(Controller::NO_OPERATION != (Controller::SHAPE_TEXT & operations))
358   {
359     const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
360     // Shapes the text.
361     ShapeText(textToShape,
362               lineBreakInfo,
363               scripts,
364               validFonts,
365               startIndex,
366               impl.mTextUpdateInfo.mStartGlyphIndex,
367               requestedNumberOfCharacters,
368               glyphs,
369               glyphsToCharactersMap,
370               charactersPerGlyph,
371               newParagraphGlyphs);
372
373     // Create the 'number of glyphs' per character and the glyph to character conversion tables.
374     impl.mModel->mVisualModel->CreateGlyphsPerCharacterTable(startIndex, impl.mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters);
375     impl.mModel->mVisualModel->CreateCharacterToGlyphTable(startIndex, impl.mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters);
376
377     updated = true;
378   }
379
380 #if defined(TRACE_ENABLED)
381   if(logEnabled)
382   {
383     timeStamps[timeStampIndex++] = GetMilliSeconds();
384   }
385 #endif
386
387   const Length numberOfGlyphs = static_cast<Length>(glyphs.Count()) - currentNumberOfGlyphs;
388
389   if(Controller::NO_OPERATION != (Controller::GET_GLYPH_METRICS & operations))
390   {
391     GlyphInfo* glyphsBuffer = glyphs.Begin();
392     impl.mMetrics->GetGlyphMetrics(glyphsBuffer + impl.mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs);
393
394     // Update the width and advance of all new paragraph characters.
395     for(Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it)
396     {
397       const GlyphIndex index = *it;
398       GlyphInfo&       glyph = *(glyphsBuffer + index);
399
400       glyph.xBearing = 0.f;
401       glyph.width    = 0.f;
402       glyph.advance  = 0.f;
403     }
404     updated = true;
405   }
406
407 #if defined(TRACE_ENABLED)
408   if(logEnabled)
409   {
410     timeStamps[timeStampIndex++] = GetMilliSeconds();
411   }
412 #endif
413
414   if((nullptr != impl.mEventData) &&
415      impl.mEventData->mPreEditFlag &&
416      (0u != impl.mModel->mVisualModel->mCharactersToGlyph.Count()))
417   {
418     Dali::InputMethodContext::PreEditAttributeDataContainer attrs;
419     impl.mEventData->mInputMethodContext.GetPreeditStyle(attrs);
420     Dali::InputMethodContext::PreeditStyle type = Dali::InputMethodContext::PreeditStyle::NONE;
421
422     // Check the type of preedit and run it.
423     for(Dali::InputMethodContext::PreEditAttributeDataContainer::Iterator it = attrs.Begin(), endIt = attrs.End(); it != endIt; it++)
424     {
425       Dali::InputMethodContext::PreeditAttributeData attrData = *it;
426       DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::UpdateModel PreeditStyle type : %d  start %d end %d \n", attrData.preeditType, attrData.startIndex, attrData.endIndex);
427       type = attrData.preeditType;
428
429       // Check the number of commit characters for the start position.
430       unsigned int numberOfCommit  = impl.mEventData->mPrimaryCursorPosition - impl.mEventData->mPreEditLength;
431       Length       numberOfIndices = attrData.endIndex - attrData.startIndex;
432
433       switch(type)
434       {
435         case Dali::InputMethodContext::PreeditStyle::UNDERLINE:
436         {
437           // Add the underline for the pre-edit text.
438           UnderlinedGlyphRun underlineRun;
439           underlineRun.glyphRun.glyphIndex     = attrData.startIndex + numberOfCommit;
440           underlineRun.glyphRun.numberOfGlyphs = numberOfIndices;
441           impl.mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
442
443           //Mark-up processor case
444           if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
445              impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
446           {
447             impl.CopyUnderlinedFromLogicalToVisualModels(false);
448           }
449           break;
450         }
451         case Dali::InputMethodContext::PreeditStyle::REVERSE:
452         {
453           Vector4  textColor = impl.mModel->mVisualModel->GetTextColor();
454           ColorRun backgroundColorRun;
455           backgroundColorRun.characterRun.characterIndex     = attrData.startIndex + numberOfCommit;
456           backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
457           backgroundColorRun.color                           = textColor;
458           impl.mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
459
460           Vector4 backgroundColor = impl.mModel->mVisualModel->GetBackgroundColor();
461           if(Dali::EqualsZero(backgroundColor.a)) // There is no text background color.
462           {
463             // Try use the control's background color.
464             if(nullptr != impl.mEditableControlInterface)
465             {
466               impl.mEditableControlInterface->GetControlBackgroundColor(backgroundColor);
467               if(Dali::EqualsZero(backgroundColor.a)) // There is no control background color.
468               {
469                 // Determines black or white color according to text color.
470                 // Based on W3C Recommendations (https://www.w3.org/TR/WCAG20/)
471                 float L         = CONSTANT_R * textColor.r + CONSTANT_G * textColor.g + CONSTANT_B * textColor.b;
472                 backgroundColor = L > BRIGHTNESS_THRESHOLD ? BLACK : WHITE;
473               }
474             }
475           }
476
477           Vector<ColorRun> colorRuns;
478           colorRuns.Resize(1u);
479           ColorRun& colorRun                       = *(colorRuns.Begin());
480           colorRun.color                           = backgroundColor;
481           colorRun.characterRun.characterIndex     = attrData.startIndex + numberOfCommit;
482           colorRun.characterRun.numberOfCharacters = numberOfIndices;
483           impl.mModel->mLogicalModel->mColorRuns.PushBack(colorRun);
484
485           //Mark-up processor case
486           if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
487              impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
488           {
489             impl.CopyUnderlinedFromLogicalToVisualModels(false);
490           }
491           break;
492         }
493         case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT:
494         {
495           ColorRun backgroundColorRun;
496           backgroundColorRun.characterRun.characterIndex     = attrData.startIndex + numberOfCommit;
497           backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
498           backgroundColorRun.color                           = LIGHT_BLUE;
499           impl.mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
500
501           //Mark-up processor case
502           if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
503              impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
504           {
505             impl.CopyUnderlinedFromLogicalToVisualModels(false);
506           }
507           break;
508         }
509         case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_1:
510         {
511           // CUSTOM_PLATFORM_STYLE_1 should be drawn with background and underline together.
512           ColorRun backgroundColorRun;
513           backgroundColorRun.characterRun.characterIndex     = attrData.startIndex + numberOfCommit;
514           backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
515           backgroundColorRun.color                           = BACKGROUND_SUB4;
516           impl.mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
517
518           UnderlinedGlyphRun underlineRun;
519           underlineRun.glyphRun.glyphIndex     = attrData.startIndex + numberOfCommit;
520           underlineRun.glyphRun.numberOfGlyphs = numberOfIndices;
521           impl.mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
522
523           //Mark-up processor case
524           if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
525              impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
526           {
527             impl.CopyUnderlinedFromLogicalToVisualModels(false);
528           }
529           break;
530         }
531         case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2:
532         {
533           // CUSTOM_PLATFORM_STYLE_2 should be drawn with background and underline together.
534           ColorRun backgroundColorRun;
535           backgroundColorRun.characterRun.characterIndex     = attrData.startIndex + numberOfCommit;
536           backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
537           backgroundColorRun.color                           = BACKGROUND_SUB5;
538           impl.mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
539
540           UnderlinedGlyphRun underlineRun;
541           underlineRun.glyphRun.glyphIndex     = attrData.startIndex + numberOfCommit;
542           underlineRun.glyphRun.numberOfGlyphs = numberOfIndices;
543           impl.mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
544
545           //Mark-up processor case
546           if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
547              impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
548           {
549             impl.CopyUnderlinedFromLogicalToVisualModels(false);
550           }
551           break;
552         }
553         case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3:
554         {
555           // CUSTOM_PLATFORM_STYLE_3 should be drawn with background and underline together.
556           ColorRun backgroundColorRun;
557           backgroundColorRun.characterRun.characterIndex     = attrData.startIndex + numberOfCommit;
558           backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
559           backgroundColorRun.color                           = BACKGROUND_SUB6;
560           impl.mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
561
562           UnderlinedGlyphRun underlineRun;
563           underlineRun.glyphRun.glyphIndex     = attrData.startIndex + numberOfCommit;
564           underlineRun.glyphRun.numberOfGlyphs = numberOfIndices;
565           impl.mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
566
567           //Mark-up processor case
568           if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
569              impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
570           {
571             impl.CopyUnderlinedFromLogicalToVisualModels(false);
572           }
573           break;
574         }
575         case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4:
576         {
577           // CUSTOM_PLATFORM_STYLE_4 should be drawn with background and underline together.
578           ColorRun backgroundColorRun;
579           backgroundColorRun.characterRun.characterIndex     = attrData.startIndex + numberOfCommit;
580           backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
581           backgroundColorRun.color                           = BACKGROUND_SUB7;
582           impl.mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
583
584           UnderlinedGlyphRun underlineRun;
585           underlineRun.glyphRun.glyphIndex     = attrData.startIndex + numberOfCommit;
586           underlineRun.glyphRun.numberOfGlyphs = numberOfIndices;
587           impl.mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
588
589           //Mark-up processor case
590           if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
591              impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
592           {
593             impl.CopyUnderlinedFromLogicalToVisualModels(false);
594           }
595           break;
596         }
597         case Dali::InputMethodContext::PreeditStyle::NONE:
598         default:
599         {
600           break;
601         }
602       }
603     }
604     attrs.Clear();
605     updated = true;
606   }
607
608 #if defined(TRACE_ENABLED)
609   if(logEnabled)
610   {
611     timeStamps[timeStampIndex++] = GetMilliSeconds();
612   }
613 #endif
614
615   if(Controller::NO_OPERATION != (Controller::COLOR & operations))
616   {
617     // Set the color runs in glyphs.
618     SetColorSegmentationInfo(impl.mModel->mLogicalModel->mColorRuns,
619                              impl.mModel->mVisualModel->mCharactersToGlyph,
620                              impl.mModel->mVisualModel->mGlyphsPerCharacter,
621                              startIndex,
622                              impl.mTextUpdateInfo.mStartGlyphIndex,
623                              requestedNumberOfCharacters,
624                              impl.mModel->mVisualModel->mColors,
625                              impl.mModel->mVisualModel->mColorIndices);
626
627     // Set the background color runs in glyphs.
628     SetColorSegmentationInfo(impl.mModel->mLogicalModel->mBackgroundColorRuns,
629                              impl.mModel->mVisualModel->mCharactersToGlyph,
630                              impl.mModel->mVisualModel->mGlyphsPerCharacter,
631                              startIndex,
632                              impl.mTextUpdateInfo.mStartGlyphIndex,
633                              requestedNumberOfCharacters,
634                              impl.mModel->mVisualModel->mBackgroundColors,
635                              impl.mModel->mVisualModel->mBackgroundColorIndices);
636
637     updated = true;
638   }
639
640 #if defined(TRACE_ENABLED)
641   if(logEnabled)
642   {
643     timeStamps[timeStampIndex++] = GetMilliSeconds();
644   }
645 #endif
646
647   if((Controller::NO_OPERATION != (Controller::SHAPE_TEXT & operations)) &&
648      !((nullptr != impl.mEventData) &&
649        impl.mEventData->mPreEditFlag &&
650        (0u != impl.mModel->mVisualModel->mCharactersToGlyph.Count())))
651   {
652     //Mark-up processor case
653     if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
654        impl.mModel->mLogicalModel->mUnderlineRunsUpdated ||
655        impl.mModel->mLogicalModel->mCharacterSpacingRunsUpdated ||
656        impl.mModel->mLogicalModel->mStrikethroughRunsUpdated)
657     {
658       impl.CopyUnderlinedFromLogicalToVisualModels(true);
659       impl.CopyStrikethroughFromLogicalToVisualModels();
660       impl.CopyCharacterSpacingFromLogicalToVisualModels();
661     }
662
663     updated = true;
664   }
665
666 #if defined(TRACE_ENABLED)
667   if(logEnabled)
668   {
669     timeStamps[timeStampIndex++] = GetMilliSeconds();
670     uint32_t timeShape   = timeStamps[1] - timeStamps[0];
671     uint32_t timeGlyph   = timeStamps[2] - timeStamps[1];
672     uint32_t timePreedit = timeStamps[3] - timeStamps[2];
673     uint32_t timeColor   = timeStamps[4] - timeStamps[3];
674     uint32_t timeCopy    = timeStamps[5] - timeStamps[4];
675
676     if(timeStamps[5] - timeStamps[0] > logThreshold)
677     {
678       std::string currentText;
679       Utf32ToUtf8(impl.mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText);
680       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);
681       DALI_LOG_DEBUG_INFO("DALI_TEXT_MODEL_UPDATE chars:%d, text:%s\n", numberOfCharacters, currentText.c_str());
682     }
683   }
684 #endif
685
686   // The estimated number of lines. Used to avoid reallocations when layouting.
687   impl.mTextUpdateInfo.mEstimatedNumberOfLines = std::max(impl.mModel->mVisualModel->mLines.Count(), impl.mModel->mLogicalModel->mParagraphInfo.Count());
688
689   // Set the previous number of characters for the next time the text is updated.
690   impl.mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
691
692   return updated;
693 }
694
695 } // namespace Dali::Toolkit::Text