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