[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / devel-api / text / text-utils-devel.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 // FILE HEADER
19 #include <dali-toolkit/devel-api/text/text-utils-devel.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/scripting/enum-helper.h>
23 #include <dali/devel-api/text-abstraction/font-client.h>
24 #include <dali/devel-api/text-abstraction/text-renderer-layout-helper.h>
25 #include <dali/devel-api/text-abstraction/text-renderer.h>
26 #include <dali/integration-api/debug.h>
27 #include <cstring>
28 #include <limits>
29
30 // INTERNAL INCLUDES
31 #include <dali-toolkit/internal/text/bidirectional-support.h>
32 #include <dali-toolkit/internal/text/character-set-conversion.h>
33 #include <dali-toolkit/internal/text/color-segmentation.h>
34 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
35 #include <dali-toolkit/internal/text/layouts/layout-engine.h>
36 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
37 #include <dali-toolkit/internal/text/markup-processor/markup-processor.h>
38 #include <dali-toolkit/internal/text/multi-language-support.h>
39 #include <dali-toolkit/internal/text/rendering/styles/character-spacing-helper-functions.h>
40 #include <dali-toolkit/internal/text/segmentation.h>
41 #include <dali-toolkit/internal/text/shaper.h>
42 #include <dali-toolkit/internal/text/text-enumerations-impl.h>
43 #include <dali-toolkit/internal/text/text-font-style.h>
44 #include <dali-toolkit/internal/text/text-model.h>
45
46 namespace Dali
47 {
48 using namespace TextAbstraction;
49
50 namespace Toolkit
51 {
52 using namespace Text;
53
54 namespace DevelText
55 {
56 namespace Layout
57 {
58 /**
59  * @brief The text's layout.
60  */
61 enum Type
62 {
63   SINGLELINE, ///< The text is laid out on a single line.
64   MULTILINE,  ///< The text is laid out in multiple lines.
65   CIRCULAR,   ///< The text is laid out on a single line following a circular path.
66 };
67
68 } // namespace Layout
69
70 namespace CircularAlignment
71 {
72 /**
73  * @brief The enumerations for the circular alignment.
74  */
75 enum Type
76 {
77   BEGIN,  ///< The text is aligned to the @p begin angle of the arc (or to the @p begin+increment if it's a RTL text).
78   CENTER, ///< The text is centered within the arc.
79   END,    ///< The text is aligned to the @p begin+increment angle of the arc (or to the @p begin if it's a RTL text).
80 };
81
82 } // namespace CircularAlignment
83
84 const float TO_POINT_26_DOT_6 = 64.f;
85 const float TO_FLOAT          = 1.f / 255.f;
86 const float TO_UCHAR          = 255.f;
87 const bool  RTL               = true;
88 const float TWO_PI            = 2.f * Dali::Math::PI;    ///< 360 degrees in radians
89 const float RAD_135           = Math::PI_2 + Math::PI_4; ///< 135 degrees in radians;
90 const float RAD_225           = RAD_135 + Math::PI_2;    ///< 225 degrees in radians;
91 const float RAD_270           = 3.f * Math::PI_2;        ///< 270 degrees in radians;
92 const float RAD_315           = RAD_225 + Math::PI_2;    ///< 315 degrees in radians;
93 const float MAX_INT           = std::numeric_limits<int>::max();
94
95 DALI_ENUM_TO_STRING_TABLE_BEGIN(LAYOUT_TYPE)
96   DALI_ENUM_TO_STRING_WITH_SCOPE(DevelText::Layout, SINGLELINE)
97   DALI_ENUM_TO_STRING_WITH_SCOPE(DevelText::Layout, MULTILINE)
98   DALI_ENUM_TO_STRING_WITH_SCOPE(DevelText::Layout, CIRCULAR)
99 DALI_ENUM_TO_STRING_TABLE_END(LAYOUT_TYPE)
100
101 DALI_ENUM_TO_STRING_TABLE_BEGIN(CIRCULAR_ALIGNMENT_TYPE)
102   DALI_ENUM_TO_STRING_WITH_SCOPE(DevelText::CircularAlignment, BEGIN)
103   DALI_ENUM_TO_STRING_WITH_SCOPE(DevelText::CircularAlignment, CENTER)
104   DALI_ENUM_TO_STRING_WITH_SCOPE(DevelText::CircularAlignment, END)
105 DALI_ENUM_TO_STRING_TABLE_END(CIRCULAR_ALIGNMENT_TYPE)
106
107 struct InternalDataModel
108 {
109   InternalDataModel(FontClient&    fontClient,
110                     MetricsPtr     metrics,
111                     Text::ModelPtr textModel)
112   : fontClient(fontClient),
113     metrics(metrics),
114     textModel(textModel),
115     numberOfCharacters{0u},
116     isTextMirrored{false},
117     numberOfGlyphs{0u}
118   {
119     layoutEngine.SetMetrics(metrics);
120   }
121
122   FontClient&               fontClient;
123   MetricsPtr                metrics;
124   Text::Layout::Engine      layoutEngine; ///< The layout engine.
125   Text::ModelPtr            textModel;    ///< Pointer to the text's model.
126   Vector<ColorBlendingMode> blendingMode; ///< How embedded items and bitmap font glyphs are blended with color text.
127   Vector<bool>              isEmoji;      ///< Whether the glyph is an emoji.
128
129   Vector<Character> mirroredUtf32Characters; // The utf32Characters Characters but mirrored if there are RTL text.
130
131   Length numberOfCharacters; // The number of characters (not glyphs!).
132   bool   isTextMirrored;     // Whether the text has been mirrored.
133
134   Length numberOfGlyphs;
135   Size   textLayoutArea;
136 };
137
138 bool GetLayoutEnumeration(const Property::Value& propertyValue, DevelText::Layout::Type& layout)
139 {
140   return Scripting::GetEnumerationProperty(propertyValue, LAYOUT_TYPE_TABLE, LAYOUT_TYPE_TABLE_COUNT, layout);
141 }
142
143 bool GetCircularAlignmentEnumeration(const Property::Value& propertyValue, DevelText::CircularAlignment::Type& circularAlignment)
144 {
145   return Scripting::GetEnumerationProperty(propertyValue, CIRCULAR_ALIGNMENT_TYPE_TABLE, CIRCULAR_ALIGNMENT_TYPE_TABLE_COUNT, circularAlignment);
146 }
147
148 void ShapeTextPreprocess(const RendererParameters& textParameters, TextAbstraction::TextRenderer::Parameters& rendererParameters, InternalDataModel& internalDataModel)
149 {
150   MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
151   const uint8_t*       utf8                 = NULL; // pointer to the first character of the text (encoded in utf8)
152   Length               textSize             = 0u;   // The length of the utf8 string.
153
154   Length&            numberOfCharacters      = internalDataModel.numberOfCharacters;
155   Vector<Character>& mirroredUtf32Characters = internalDataModel.mirroredUtf32Characters;
156   Text::ModelPtr&    textModel               = internalDataModel.textModel;
157
158   Vector<Character>&                     utf32Characters     = textModel->mLogicalModel->mText;                       // Characters encoded in utf32.
159   Vector<LineBreakInfo>&                 lineBreakInfo       = textModel->mLogicalModel->mLineBreakInfo;              // The line break info.
160   Vector<ScriptRun>&                     scripts             = textModel->mLogicalModel->mScriptRuns;                 // Charactes's script.
161   Vector<FontDescriptionRun>&            fontDescriptionRuns = textModel->mLogicalModel->mFontDescriptionRuns;        // Desired font descriptions.
162   Vector<FontRun>&                       validFonts          = textModel->mLogicalModel->mFontRuns;                   // Validated fonts.
163   Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo   = textModel->mLogicalModel->mBidirectionalParagraphInfo; // The bidirectional info per paragraph.
164   Vector<CharacterDirection>&            directions          = textModel->mLogicalModel->mCharacterDirections;        // Character's directions.
165   Vector<ColorRun>&                      colorRuns           = textModel->mLogicalModel->mColorRuns;                  // colors of the text.
166
167   // the default font's description.
168   FontDescription defaultFontDescription;
169   PointSize26Dot6 defaultPointSize = FontClient::DEFAULT_POINT_SIZE;
170
171   ////////////////////////////////////////////////////////////////////////////////
172   // Process the markup string if the mark-up processor is enabled.
173   ////////////////////////////////////////////////////////////////////////////////
174
175   MarkupProcessData markupProcessData(colorRuns,
176                                       fontDescriptionRuns,
177                                       textModel->mLogicalModel->mEmbeddedItems,
178                                       textModel->mLogicalModel->mAnchors,
179                                       textModel->mLogicalModel->mUnderlinedCharacterRuns,
180                                       textModel->mLogicalModel->mBackgroundColorRuns,
181                                       textModel->mLogicalModel->mStrikethroughCharacterRuns,
182                                       textModel->mLogicalModel->mBoundedParagraphRuns,
183                                       textModel->mLogicalModel->mCharacterSpacingCharacterRuns);
184
185   if(textParameters.markupEnabled)
186   {
187     MarkupPropertyData markupPropertyData(Color::MEDIUM_BLUE, Color::DARK_MAGENTA);
188
189     ProcessMarkupString(textParameters.text, markupPropertyData, markupProcessData);
190     textSize = markupProcessData.markupProcessedText.size();
191
192     // This is a bit horrible but std::string returns a (signed) char*
193     utf8 = reinterpret_cast<const uint8_t*>(markupProcessData.markupProcessedText.c_str());
194   }
195   else
196   {
197     textSize = textParameters.text.size();
198
199     // This is a bit horrible but std::string returns a (signed) char*
200     utf8 = reinterpret_cast<const uint8_t*>(textParameters.text.c_str());
201   }
202
203   ////////////////////////////////////////////////////////////////////////////////
204   // Convert from utf8 to utf32
205   ////////////////////////////////////////////////////////////////////////////////
206
207   utf32Characters.Resize(textSize);
208
209   // Transform a text array encoded in utf8 into an array encoded in utf32.
210   // It returns the actual number of characters.
211   numberOfCharacters = Utf8ToUtf32(utf8, textSize, utf32Characters.Begin());
212   utf32Characters.Resize(numberOfCharacters);
213
214   ////////////////////////////////////////////////////////////////////////////////
215   // Retrieve the Line and Word Break Info.
216   ////////////////////////////////////////////////////////////////////////////////
217
218   lineBreakInfo.Resize(numberOfCharacters, LINE_NO_BREAK);
219
220   SetLineBreakInfo(utf32Characters,
221                    0u,
222                    numberOfCharacters,
223                    lineBreakInfo);
224
225   ////////////////////////////////////////////////////////////////////////////////
226   // Retrieve the script runs.
227   ////////////////////////////////////////////////////////////////////////////////
228
229   multilanguageSupport.SetScripts(utf32Characters,
230                                   0u,
231                                   numberOfCharacters,
232                                   scripts);
233
234   // Check if there are emojis.
235   // If there are an RGBA8888 pixel format is needed.
236   for(const auto& run : scripts)
237   {
238     if(run.script == TextAbstraction::Script::EMOJI)
239     {
240       rendererParameters.pixelFormat = TextAbstraction::TextRenderer::Parameters::RGBA8888;
241       break;
242     }
243   }
244
245   ////////////////////////////////////////////////////////////////////////////////
246   // Retrieve the font runs.
247   ////////////////////////////////////////////////////////////////////////////////
248
249   // Set the description font run with the given text parameters.
250   FontDescriptionRun fontDescriptionRun;
251   fontDescriptionRun.characterRun.characterIndex     = 0u;
252   fontDescriptionRun.characterRun.numberOfCharacters = numberOfCharacters;
253
254   fontDescriptionRun.familyLength  = 0u;
255   fontDescriptionRun.familyName    = nullptr;
256   fontDescriptionRun.familyDefined = !textParameters.fontFamily.empty();
257   if(fontDescriptionRun.familyDefined)
258   {
259     // The allocated memory will be freed when the logical model is destroyed.
260     fontDescriptionRun.familyLength = textParameters.fontFamily.size();
261     fontDescriptionRun.familyName   = new char[fontDescriptionRun.familyLength];
262     memcpy(fontDescriptionRun.familyName, textParameters.fontFamily.c_str(), fontDescriptionRun.familyLength);
263   }
264
265   fontDescriptionRun.weightDefined = !textParameters.fontWeight.empty();
266   if(fontDescriptionRun.weightDefined)
267   {
268     fontDescriptionRun.weight = StringToWeight(textParameters.fontWeight.c_str());
269   }
270
271   fontDescriptionRun.widthDefined = !textParameters.fontWidth.empty();
272   if(fontDescriptionRun.widthDefined)
273   {
274     fontDescriptionRun.width = StringToWidth(textParameters.fontWidth.c_str());
275   }
276
277   fontDescriptionRun.slantDefined = !textParameters.fontSlant.empty();
278   if(fontDescriptionRun.slantDefined)
279   {
280     fontDescriptionRun.slant = StringToSlant(textParameters.fontSlant.c_str());
281   }
282
283   fontDescriptionRun.sizeDefined = !EqualsZero(textParameters.fontSize);
284   if(fontDescriptionRun.sizeDefined)
285   {
286     fontDescriptionRun.size = static_cast<unsigned int>(textParameters.fontSize * TO_POINT_26_DOT_6);
287   }
288
289   fontDescriptionRuns.PushBack(fontDescriptionRun);
290
291   // Validates the fonts. If there is a character with no assigned font it sets a default one.
292   // After this call, fonts are validated.
293   multilanguageSupport.ValidateFonts(utf32Characters,
294                                      scripts,
295                                      fontDescriptionRuns,
296                                      defaultFontDescription,
297                                      defaultPointSize,
298                                      1.0f,
299                                      0u,
300                                      numberOfCharacters,
301                                      validFonts);
302
303   ////////////////////////////////////////////////////////////////////////////////
304   // Retrieve the Bidirectional info.
305   ////////////////////////////////////////////////////////////////////////////////
306
307   bidirectionalInfo.Reserve(1u);
308
309   SetBidirectionalInfo(utf32Characters,
310                        scripts,
311                        lineBreakInfo,
312                        0u,
313                        numberOfCharacters,
314                        bidirectionalInfo);
315
316   const bool hasBidirectionalText = 0u != bidirectionalInfo.Count();
317   if(hasBidirectionalText)
318   {
319     // Only set the character directions if there is right to left characters.
320     GetCharactersDirection(bidirectionalInfo,
321                            numberOfCharacters,
322                            0u,
323                            numberOfCharacters,
324                            directions);
325
326     // This paragraph has right to left text. Some characters may need to be mirrored.
327     // TODO: consider if the mirrored string can be stored as well.
328
329     internalDataModel.isTextMirrored = GetMirroredText(utf32Characters,
330                                                        directions,
331                                                        bidirectionalInfo,
332                                                        0u,
333                                                        numberOfCharacters,
334                                                        mirroredUtf32Characters);
335   }
336 }
337
338 void ShapeText(TextAbstraction::TextRenderer::Parameters& rendererParameters, Vector<EmbeddedItemInfo>& embeddedItemLayout, InternalDataModel& internalDataModel)
339 {
340   Vector<GlyphIndex>           newParagraphGlyphs; // Glyphs for the new paragraph characters.
341   const Length                 numberOfCharacters      = internalDataModel.numberOfCharacters;
342   const bool                   isTextMirrored          = internalDataModel.isTextMirrored;
343   Text::ModelPtr&              textModel               = internalDataModel.textModel;
344   const Vector<Character>&     mirroredUtf32Characters = internalDataModel.mirroredUtf32Characters;
345   FontClient&                  fontClient              = internalDataModel.fontClient;
346   const Vector<Character>&     utf32Characters         = textModel->mLogicalModel->mText;          // Characters encoded in utf32.
347   const Vector<LineBreakInfo>& lineBreakInfo           = textModel->mLogicalModel->mLineBreakInfo; // The line break info.
348   const Vector<ScriptRun>&     scripts                 = textModel->mLogicalModel->mScriptRuns;    // Charactes's script.
349   const Vector<FontRun>&       validFonts              = textModel->mLogicalModel->mFontRuns;      // Validated fonts.
350
351   Vector<CharacterIndex>& glyphsToCharacters = textModel->mVisualModel->mGlyphsToCharacters; // Glyphs to character map.
352   Vector<Length>&         charactersPerGlyph = textModel->mVisualModel->mCharactersPerGlyph; // Number of characters per glyph.
353
354   ////////////////////////////////////////////////////////////////////////////////
355   // Retrieve the glyphs. Text shaping
356   ////////////////////////////////////////////////////////////////////////////////
357
358   const Vector<Character>& textToShape = isTextMirrored ? mirroredUtf32Characters : utf32Characters;
359
360   newParagraphGlyphs.Reserve(1u);
361
362   // Shapes the text.
363   ShapeText(textToShape,
364             lineBreakInfo,
365             scripts,
366             validFonts,
367             0u,
368             0u,
369             numberOfCharacters,
370             rendererParameters.glyphs,
371             glyphsToCharacters,
372             charactersPerGlyph,
373             newParagraphGlyphs);
374
375   // Create the 'number of glyphs' per character and the glyph to character conversion tables.
376   textModel->mVisualModel->CreateGlyphsPerCharacterTable(0u, 0u, numberOfCharacters);
377   textModel->mVisualModel->CreateCharacterToGlyphTable(0u, 0u, numberOfCharacters);
378
379   internalDataModel.numberOfGlyphs = rendererParameters.glyphs.Count();
380
381   // Once the text has been shaped and the glyphs created it's possible to replace the font id of those glyphs
382   // that represent an image or an item and create the embedded item layout info.
383   // Note: the position of the embedded item can't be set until the text is laid-out.
384   embeddedItemLayout.Reserve(textModel->mLogicalModel->mEmbeddedItems.Count());
385   for(const auto& item : textModel->mLogicalModel->mEmbeddedItems)
386   {
387     // Get the glyph that matches with the character index.
388     const GlyphIndex glyphIndex = textModel->mVisualModel->mCharactersToGlyph[item.characterIndex];
389     GlyphInfo&       glyph      = rendererParameters.glyphs[glyphIndex];
390
391     glyph.fontId                                                     = 0u;
392     Pixel::Format                                        pixelFormat = Pixel::A8;
393     TextAbstraction::FontClient::EmbeddedItemDescription description = {std::string(item.url, item.urlLength), item.width, item.height, item.colorBlendingMode};
394     glyph.index                                                      = fontClient.CreateEmbeddedItem(description, pixelFormat); // Set here an index to an item.
395
396     if((Pixel::RGBA8888 == pixelFormat) || (Pixel::BGRA8888 == pixelFormat))
397     {
398       rendererParameters.pixelFormat = TextAbstraction::TextRenderer::Parameters::RGBA8888;
399     }
400
401     // If the url is empty the item is going to be added after the text is rendered. It's needed to store the layout here.
402     if(description.url.empty())
403     {
404       EmbeddedItemInfo embeddedInfo =
405         {
406           item.characterIndex,
407           glyphIndex,
408           Vector2::ZERO,
409           Size(static_cast<float>(item.width), static_cast<float>(item.height)),
410           Size(static_cast<float>(item.width), static_cast<float>(item.height)),
411           Degree(0.f),
412           item.colorBlendingMode};
413
414       embeddedItemLayout.PushBack(embeddedInfo);
415     }
416   }
417 }
418
419 void SetColorSegmentation(const RendererParameters& textParameters, InternalDataModel& internalDataModel)
420 {
421   Text::ModelPtr&            textModel    = internalDataModel.textModel;
422   Vector<ColorBlendingMode>& blendingMode = internalDataModel.blendingMode;
423
424   Vector<ColorRun>& colorRuns = textModel->mLogicalModel->mColorRuns; // colors of the text.
425
426   Vector<GlyphIndex>& charactersToGlyph  = textModel->mVisualModel->mCharactersToGlyph;  // Characters to glyphs map.
427   Vector<Length>&     glyphsPerCharacter = textModel->mVisualModel->mGlyphsPerCharacter; // The number of glyphs that are shaped.
428
429   ////////////////////////////////////////////////////////////////////////////////
430   // Set the color runs in glyphs.
431   ////////////////////////////////////////////////////////////////////////////////
432
433   SetColorSegmentationInfo(colorRuns,
434                            charactersToGlyph,
435                            glyphsPerCharacter,
436                            0u,
437                            0u,
438                            internalDataModel.numberOfCharacters,
439                            textModel->mVisualModel->mColors,
440                            textModel->mVisualModel->mColorIndices);
441
442   // Insert the default color at the beginning of the vector.
443   textModel->mVisualModel->mColors.Insert(textModel->mVisualModel->mColors.Begin(), textParameters.textColor);
444
445   // Set how the embedded items are blended with text color.
446   blendingMode.Resize(internalDataModel.numberOfGlyphs, textParameters.isTextColorSet ? ColorBlendingMode::MULTIPLY : ColorBlendingMode::NONE);
447
448   if(!textParameters.isTextColorSet)
449   {
450     // Traverse the color runs.
451     for(const auto& run : colorRuns)
452     {
453       const GlyphIndex     firstGlyph       = textModel->mVisualModel->mCharactersToGlyph[run.characterRun.characterIndex];
454       const CharacterIndex lastCharacter    = run.characterRun.characterIndex + run.characterRun.numberOfCharacters - 1u;
455       const GlyphIndex     lastGlyphPlusOne = textModel->mVisualModel->mCharactersToGlyph[lastCharacter] + textModel->mVisualModel->mGlyphsPerCharacter[lastCharacter];
456
457       for(GlyphIndex index = firstGlyph; index < lastGlyphPlusOne; ++index)
458       {
459         blendingMode[index] = ColorBlendingMode::MULTIPLY;
460       }
461     }
462   }
463
464   // Traverse the embedded items and update the blending mode vector.
465   for(const auto& item : textModel->mLogicalModel->mEmbeddedItems)
466   {
467     const GlyphIndex glyphIndex = textModel->mVisualModel->mCharactersToGlyph[item.characterIndex];
468     blendingMode[glyphIndex]    = item.colorBlendingMode;
469   }
470 }
471
472 void SetEmojiVector(InternalDataModel& internalDataModel)
473 {
474   Vector<bool>&   isEmoji        = internalDataModel.isEmoji;
475   Text::ModelPtr& textModel      = internalDataModel.textModel;
476   const Length    numberOfGlyphs = internalDataModel.numberOfGlyphs;
477
478   const Vector<ScriptRun>& scripts = textModel->mLogicalModel->mScriptRuns; // Charactes's script.
479   ////////////////////////////////////////////////////////////////////////////////
480   // Set the isEmoji Vector
481   ////////////////////////////////////////////////////////////////////////////////
482
483   isEmoji.Resize(numberOfGlyphs, false);
484
485   for(const auto& run : scripts)
486   {
487     if(run.script == TextAbstraction::Script::EMOJI)
488     {
489       const GlyphIndex     firstGlyph       = textModel->mVisualModel->mCharactersToGlyph[run.characterRun.characterIndex];
490       const CharacterIndex lastCharacter    = run.characterRun.characterIndex + run.characterRun.numberOfCharacters - 1u;
491       const GlyphIndex     lastGlyphPlusOne = textModel->mVisualModel->mCharactersToGlyph[lastCharacter] + textModel->mVisualModel->mGlyphsPerCharacter[lastCharacter];
492
493       for(GlyphIndex index = firstGlyph; index < lastGlyphPlusOne; ++index)
494       {
495         isEmoji[index] = true;
496       }
497     }
498   }
499 }
500
501 void Align(const RendererParameters& textParameters, TextAbstraction::TextRenderer::Parameters& rendererParameters, Vector<EmbeddedItemInfo>& embeddedItemLayout, InternalDataModel& internalDataModel, const Size& newLayoutSize)
502 {
503   Text::Layout::Engine& layoutEngine       = internalDataModel.layoutEngine;
504   Text::ModelPtr&       textModel          = internalDataModel.textModel;
505   const Length          numberOfCharacters = internalDataModel.numberOfCharacters;
506   Size&                 textLayoutArea     = internalDataModel.textLayoutArea;
507
508   Vector<LineRun>& lines = textModel->mVisualModel->mLines; // The laid out lines.
509
510   ////////////////////////////////////////////////////////////////////////////////
511   // Align the text.
512   ////////////////////////////////////////////////////////////////////////////////
513
514   HorizontalAlignment::Type horizontalAlignment         = Toolkit::HorizontalAlignment::CENTER;
515   HorizontalAlignment::Type horizontalCircularAlignment = Toolkit::HorizontalAlignment::CENTER;
516   VerticalAlignment::Type   verticalAlignment           = VerticalAlignment::CENTER;
517   CircularAlignment::Type   circularAlignment           = CircularAlignment::BEGIN;
518
519   Layout::Type layout = Layout::SINGLELINE;
520
521   // Sets the alignment
522   Property::Value horizontalAlignmentStr(textParameters.horizontalAlignment);
523   GetHorizontalAlignmentEnumeration(horizontalAlignmentStr, horizontalAlignment);
524   horizontalCircularAlignment = horizontalAlignment;
525
526   Property::Value verticalAlignmentStr(textParameters.verticalAlignment);
527   GetVerticalAlignmentEnumeration(verticalAlignmentStr, verticalAlignment);
528
529   Property::Value circularAlignmentStr(textParameters.circularAlignment);
530   GetCircularAlignmentEnumeration(circularAlignmentStr, circularAlignment);
531
532   Property::Value layoutStr(textParameters.layout);
533   GetLayoutEnumeration(layoutStr, layout);
534
535   // Whether the layout is circular.
536   const bool isCircularTextLayout = (Layout::CIRCULAR == layout);
537   const bool isClockwise          = isCircularTextLayout && (0.f < textParameters.incrementAngle);
538
539   // Convert CircularAlignment to HorizontalAlignment.
540   if(isCircularTextLayout)
541   {
542     switch(circularAlignment)
543     {
544       case CircularAlignment::BEGIN:
545       {
546         horizontalCircularAlignment = Toolkit::HorizontalAlignment::BEGIN;
547         break;
548       }
549       case CircularAlignment::CENTER:
550       {
551         horizontalCircularAlignment = Toolkit::HorizontalAlignment::CENTER;
552         break;
553       }
554       case CircularAlignment::END:
555       {
556         horizontalCircularAlignment = Toolkit::HorizontalAlignment::END;
557         break;
558       }
559     }
560   }
561   textModel->mHorizontalAlignment = isCircularTextLayout ? horizontalCircularAlignment : horizontalAlignment;
562
563   // Retrieve the line of text to know the direction and the width. @todo multi-line
564   const LineRun& line = lines[0u];
565
566   if(isCircularTextLayout)
567   {
568     // Set the circular alignment.
569     rendererParameters.circularLayout = isClockwise ? TextRenderer::Parameters::CLOCKWISE : TextRenderer::Parameters::COUNTER_CLOCKWISE;
570
571     // Update the text's height to be used by the ellipsis code.
572     textLayoutArea.height = newLayoutSize.height;
573
574     // Set the size of the text laid out on a straight horizontal line.
575     rendererParameters.circularWidth  = static_cast<unsigned int>(newLayoutSize.width);
576     rendererParameters.circularHeight = static_cast<unsigned int>(newLayoutSize.height);
577
578     // Calculate the center of the circular text according the horizontal and vertical alingments and the radius.
579     switch(horizontalAlignment)
580     {
581       case HorizontalAlignment::BEGIN:
582       {
583         rendererParameters.centerX = static_cast<int>(textParameters.radius);
584         break;
585       }
586       case HorizontalAlignment::CENTER:
587       {
588         rendererParameters.centerX = static_cast<int>(textParameters.textWidth / 2u);
589         break;
590       }
591       case HorizontalAlignment::END:
592       {
593         rendererParameters.centerX = static_cast<int>(textParameters.textWidth) - static_cast<int>(textParameters.radius);
594         break;
595       }
596     }
597
598     switch(verticalAlignment)
599     {
600       case VerticalAlignment::TOP:
601       {
602         rendererParameters.centerY = static_cast<int>(textParameters.radius);
603         break;
604       }
605       case VerticalAlignment::CENTER:
606       {
607         rendererParameters.centerY = static_cast<int>(textParameters.textHeight / 2u);
608         break;
609       }
610       case VerticalAlignment::BOTTOM:
611       {
612         rendererParameters.centerY = static_cast<int>(textParameters.textHeight) - static_cast<int>(textParameters.radius);
613         break;
614       }
615     }
616
617     // Calculate the beginning angle according with the given horizontal alignment.
618     const bool isRTL = RTL == line.direction;
619
620     CircularAlignment::Type alignment = circularAlignment;
621     if(isRTL)
622     {
623       // Swap the alignment type if the line is right to left.
624       switch(alignment)
625       {
626         case CircularAlignment::BEGIN:
627         {
628           alignment = CircularAlignment::END;
629           break;
630         }
631         case CircularAlignment::CENTER:
632         {
633           // Nothing to do.
634           break;
635         }
636         case CircularAlignment::END:
637         {
638           alignment = CircularAlignment::BEGIN;
639           break;
640         }
641       }
642     }
643
644     float angleOffset = 0.f;
645
646     switch(alignment)
647     {
648       case CircularAlignment::BEGIN:
649       {
650         angleOffset = 0.f;
651         break;
652       }
653       case CircularAlignment::CENTER:
654       {
655         const bool  isNeg     = textParameters.incrementAngle < 0.f;
656         const float textWidth = static_cast<float>(rendererParameters.circularWidth);
657         angleOffset           = (isNeg ? -0.5f : 0.5f) * (textLayoutArea.width - textWidth) / static_cast<float>(rendererParameters.radius);
658         break;
659       }
660       case CircularAlignment::END:
661       {
662         const bool  isNeg     = textParameters.incrementAngle < 0.f;
663         const float textWidth = static_cast<float>(rendererParameters.circularWidth);
664         angleOffset           = (isNeg ? -1.f : 1.f) * (textLayoutArea.width - textWidth) / static_cast<float>(rendererParameters.radius);
665         break;
666       }
667     }
668
669     // Update the beginning angle with the calculated offset.
670     rendererParameters.beginAngle = Radian(Degree(textParameters.beginAngle)) + angleOffset;
671
672     // Set the vertical position of the glyphs.
673     for(auto& position : rendererParameters.positions)
674     {
675       position.y = 0.f;
676     }
677   }
678   else
679   {
680     // Calculate the vertical offset according with the given alignment.
681     float penY = 0.f;
682
683     switch(verticalAlignment)
684     {
685       case VerticalAlignment::TOP:
686       {
687         penY = line.ascender;
688         break;
689       }
690       case VerticalAlignment::CENTER:
691       {
692         penY = line.ascender + 0.5f * (textLayoutArea.height - (line.ascender - line.descender));
693         break;
694       }
695       case VerticalAlignment::BOTTOM:
696       {
697         penY = textLayoutArea.height;
698         break;
699       }
700     }
701
702     // Calculate the horizontal offset according with the given alignment.
703     float alignmentOffset = 0.f;
704     layoutEngine.Align(textLayoutArea,
705                        0u,
706                        numberOfCharacters,
707                        horizontalAlignment,
708                        lines,
709                        alignmentOffset,
710                        Dali::LayoutDirection::LEFT_TO_RIGHT,
711                        false);
712
713     // Update the position of the glyphs with the calculated offsets.
714     for(auto& position : rendererParameters.positions)
715     {
716       position.x += line.alignmentOffset;
717       position.y = penY;
718     }
719   }
720
721   // Cairo adds the bearing to the position of the glyph
722   // that has already been added by the DALi's layout engine,
723   // so it's needed to be removed here.
724   for(unsigned int index = 0u; index < rendererParameters.glyphs.Count(); ++index)
725   {
726     const GlyphInfo& glyph    = rendererParameters.glyphs[index];
727     Vector2&         position = rendererParameters.positions[index];
728
729     position.x -= glyph.xBearing;
730   }
731
732   // Set the position of the embedded items (if there is any).
733   EmbeddedItemInfo* embeddedItemLayoutBuffer = embeddedItemLayout.Begin();
734
735   for(Length index = 0u, endIndex = embeddedItemLayout.Count(); index < endIndex; ++index)
736   {
737     EmbeddedItemInfo& embeddedItem = *(embeddedItemLayoutBuffer + index);
738
739     embeddedItem.position = rendererParameters.positions[embeddedItem.glyphIndex];
740
741     if(isCircularTextLayout)
742     {
743       // Calculate the new position of the embedded item in the circular path.
744
745       // Center of the bitmap.
746       const float halfWidth  = 0.5f * embeddedItem.size.width;
747       const float halfHeight = 0.5f * embeddedItem.size.height;
748       double      centerX    = static_cast<double>(embeddedItem.position.x + halfWidth);
749       double      centerY    = static_cast<double>(embeddedItem.position.y - halfHeight);
750
751       Dali::TextAbstraction::CircularTextParameters circularTextParameters;
752
753       circularTextParameters.radius     = static_cast<double>(rendererParameters.radius);
754       circularTextParameters.invRadius  = 1.0 / circularTextParameters.radius;
755       circularTextParameters.beginAngle = static_cast<double>(-rendererParameters.beginAngle + Dali::Math::PI_2);
756       circularTextParameters.centerX    = 0.5f * static_cast<double>(textParameters.textWidth);
757       circularTextParameters.centerY    = 0.5f * static_cast<double>(textParameters.textHeight);
758
759       // Calculate the rotation angle.
760       float radians = rendererParameters.beginAngle;
761       if(isClockwise)
762       {
763         radians += static_cast<float>(circularTextParameters.invRadius * centerX);
764         radians = -radians;
765       }
766       else
767       {
768         radians -= static_cast<float>(circularTextParameters.invRadius * centerX);
769         radians = -radians + Dali::Math::PI;
770       }
771       embeddedItem.angle = Degree(Radian(radians));
772
773       Dali::TextAbstraction::TransformToArc(circularTextParameters, centerX, centerY);
774
775       // Recalculate the size of the embedded item after the rotation to position it correctly.
776       float width  = embeddedItem.size.width;
777       float height = embeddedItem.size.height;
778
779       // Transform the input angle into the range [0..2PI]
780       radians = fmod(radians, TWO_PI);
781       radians += (radians < 0.f) ? TWO_PI : 0.f;
782
783       // Does the same operations than rotate by shear.
784       if((radians > Math::PI_4) && (radians <= RAD_135))
785       {
786         std::swap(width, height);
787         radians -= Math::PI_2;
788       }
789       else if((radians > RAD_135) && (radians <= RAD_225))
790       {
791         radians -= Math::PI;
792       }
793       else if((radians > RAD_225) && (radians <= RAD_315))
794       {
795         std::swap(width, height);
796         radians -= RAD_270;
797       }
798
799       if(fabs(radians) > Dali::Math::MACHINE_EPSILON_10)
800       {
801         const float angleSinus   = fabs(sin(radians));
802         const float angleCosinus = cos(radians);
803
804         // Calculate the rotated image dimensions.
805         embeddedItem.rotatedSize.height = width * angleSinus + height * angleCosinus;
806         embeddedItem.rotatedSize.width  = height * angleSinus + width * angleCosinus + 1.f;
807       }
808
809       embeddedItem.position.x = floor(static_cast<float>(centerX) - 0.5f * embeddedItem.rotatedSize.width);
810       embeddedItem.position.y = floor(static_cast<float>(centerY) - 0.5f * embeddedItem.rotatedSize.height);
811     }
812     else
813     {
814       embeddedItem.position.y -= embeddedItem.size.height;
815     }
816   }
817 }
818
819 void Ellipsis(const RendererParameters& textParameters, TextAbstraction::TextRenderer::Parameters& rendererParameters, Vector<EmbeddedItemInfo>& embeddedItemLayout, InternalDataModel& internalDataModel)
820 {
821   Text::ModelPtr& textModel  = internalDataModel.textModel;
822   FontClient&     fontClient = internalDataModel.fontClient;
823
824   Vector<LineRun>&        lines                     = textModel->mVisualModel->mLines; // The laid out lines.
825   Vector<bool>&           isEmoji                   = internalDataModel.isEmoji;
826   const Size              textLayoutArea            = internalDataModel.textLayoutArea;
827   const float             modelCharacterSpacing     = textModel->mVisualModel->GetCharacterSpacing();
828   float                   calculatedAdvance         = 0.f;
829   Vector<CharacterIndex>& glyphToCharacterMap       = textModel->mVisualModel->mGlyphsToCharacters;
830   const CharacterIndex*   glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
831
832   // Get the character-spacing runs.
833   const Vector<CharacterSpacingGlyphRun>& characterSpacingGlyphRuns = textModel->mVisualModel->GetCharacterSpacingGlyphRuns();
834
835   ////////////////////////////////////////////////////////////////////////////////
836   // Ellipsis the text.
837   ////////////////////////////////////////////////////////////////////////////////
838
839   if(textParameters.ellipsisEnabled)
840   {
841     const LineRun& line = lines[0u]; // TODO: multi-line
842
843     if(line.ellipsis)
844     {
845       Length finalNumberOfGlyphs = 0u;
846
847       if((GetLineHeight(line, (lines.Size() == 1))) > textLayoutArea.height)
848       {
849         // The height of the line is bigger than the height of the text area.
850         // Show the ellipsis glyph even if it doesn't fit in the text area.
851         // If no text is rendered then issues are rised and it may be a while
852         // until is find out that the text area is too small.
853
854         // Get the first glyph which is going to be replaced and the ellipsis glyph.
855         GlyphInfo&       glyphInfo     = rendererParameters.glyphs[0u];
856         const GlyphInfo& ellipsisGlyph = fontClient.GetEllipsisGlyph(fontClient.GetPointSize(glyphInfo.fontId));
857
858         // Change the 'x' and 'y' position of the ellipsis glyph.
859         Vector2& position = rendererParameters.positions[0u];
860         position.x        = ellipsisGlyph.xBearing;
861         position.y        = textLayoutArea.height - ellipsisGlyph.yBearing;
862
863         // Replace the glyph by the ellipsis glyph.
864         glyphInfo = ellipsisGlyph;
865
866         // Set the final number of glyphs
867         finalNumberOfGlyphs = 1u;
868       }
869       else
870       {
871         // firstPenX, penY and firstPenSet are used to position the ellipsis glyph if needed.
872         float firstPenX   = 0.f; // Used if rtl text is elided.
873         bool  firstPenSet = false;
874
875         // Add the ellipsis glyph.
876         bool   inserted              = false;
877         float  removedGlypsWidth     = 0.f;
878         Length numberOfRemovedGlyphs = 0u;
879         if(line.glyphRun.numberOfGlyphs > 0u)
880         {
881           GlyphIndex index = line.glyphRun.numberOfGlyphs - 1u;
882
883           GlyphInfo* glyphs         = rendererParameters.glyphs.Begin();
884           Vector2*   glyphPositions = rendererParameters.positions.Begin();
885
886           float penY = 0.f;
887
888           // The ellipsis glyph has to fit in the place where the last glyph(s) is(are) removed.
889           while(!inserted)
890           {
891             const GlyphInfo& glyphToRemove = *(glyphs + index);
892
893             if(0u != glyphToRemove.fontId)
894             {
895               // i.e. The font id of the glyph shaped from the '\n' character is zero.
896
897               // Need to reshape the glyph as the font may be different in size.
898               const GlyphInfo& ellipsisGlyph = fontClient.GetEllipsisGlyph(fontClient.GetPointSize(glyphToRemove.fontId));
899
900               if(!firstPenSet)
901               {
902                 const Vector2& position = *(glyphPositions + index);
903
904                 // Calculates the penY of the current line. It will be used to position the ellipsis glyph.
905                 penY = position.y;
906
907                 // Calculates the first penX which will be used if rtl text is elided.
908                 firstPenX = position.x - glyphToRemove.xBearing;
909                 if(firstPenX < -ellipsisGlyph.xBearing)
910                 {
911                   // Avoids to exceed the bounding box when rtl text is elided.
912                   firstPenX = -ellipsisGlyph.xBearing;
913                 }
914
915                 removedGlypsWidth = -ellipsisGlyph.xBearing;
916
917                 firstPenSet = true;
918               }
919
920               const float characterSpacing = GetGlyphCharacterSpacing(index, characterSpacingGlyphRuns, modelCharacterSpacing);
921               calculatedAdvance            = GetCalculatedAdvance(*(textModel->mLogicalModel->mText.Begin() + (*(glyphToCharacterMapBuffer + index))), characterSpacing, glyphToRemove.advance);
922               removedGlypsWidth += std::min(calculatedAdvance, (glyphToRemove.xBearing + glyphToRemove.width));
923
924               // Calculate the width of the ellipsis glyph and check if it fits.
925               const float ellipsisGlyphWidth = ellipsisGlyph.width + ellipsisGlyph.xBearing;
926               if(ellipsisGlyphWidth < removedGlypsWidth)
927               {
928                 GlyphInfo& glyphInfo = *(glyphs + index);
929                 Vector2&   position  = *(glyphPositions + index);
930                 position.x -= (0.f > glyphInfo.xBearing) ? glyphInfo.xBearing : 0.f;
931
932                 // Replace the glyph by the ellipsis glyph.
933                 glyphInfo = ellipsisGlyph;
934
935                 // Update the isEmoji vector
936                 isEmoji[index] = false;
937
938                 // Change the 'x' and 'y' position of the ellipsis glyph.
939
940                 if(position.x > firstPenX)
941                 {
942                   position.x = firstPenX + removedGlypsWidth - ellipsisGlyphWidth;
943                 }
944
945                 position.x += ellipsisGlyph.xBearing;
946                 position.y = penY;
947
948                 inserted = true;
949               }
950             }
951
952             if(!inserted)
953             {
954               if(index > 0u)
955               {
956                 --index;
957               }
958               else
959               {
960                 // No space for the ellipsis.
961                 inserted = true;
962               }
963               ++numberOfRemovedGlyphs;
964             }
965
966             // Set the final number of glyphs
967             finalNumberOfGlyphs = line.glyphRun.numberOfGlyphs - numberOfRemovedGlyphs;
968           }
969         }
970
971         // Resize the number of glyphs/positions
972         rendererParameters.glyphs.Resize(finalNumberOfGlyphs);
973         rendererParameters.positions.Resize(finalNumberOfGlyphs);
974
975         // Remove from the embedded items those exceding the last laid out glyph.
976         embeddedItemLayout.Erase(std::remove_if(embeddedItemLayout.Begin(),
977                                                 embeddedItemLayout.End(),
978                                                 [finalNumberOfGlyphs](const EmbeddedItemInfo& item) {
979                                                   return item.glyphIndex >= finalNumberOfGlyphs;
980                                                 }),
981                                  embeddedItemLayout.End());
982       }
983     }
984   }
985 }
986
987 Size LayoutText(const RendererParameters& textParameters, TextAbstraction::TextRenderer::Parameters& rendererParameters, Vector<EmbeddedItemInfo>& embeddedItemLayout, InternalDataModel& internalDataModel)
988 {
989   ////////////////////////////////////////////////////////////////////////////////
990   // Layout the text.
991   ////////////////////////////////////////////////////////////////////////////////
992   Text::ModelPtr&          textModel               = internalDataModel.textModel;
993   Text::Layout::Engine&    layoutEngine            = internalDataModel.layoutEngine;
994   FontClient&              fontClient              = internalDataModel.fontClient;
995   const Length             numberOfGlyphs          = internalDataModel.numberOfGlyphs;
996   const bool               isTextMirrored          = internalDataModel.isTextMirrored;
997   const Vector<Character>& mirroredUtf32Characters = internalDataModel.mirroredUtf32Characters;
998   const Length             numberOfCharacters      = internalDataModel.numberOfCharacters;
999   const auto               ellipsisPosition        = textModel->mEllipsisPosition;
1000
1001   Layout::Type layout = Layout::SINGLELINE;
1002
1003   Property::Value layoutStr(textParameters.layout);
1004   GetLayoutEnumeration(layoutStr, layout);
1005
1006   // Whether the layout is multi-line.
1007   const Text::Layout::Engine::Type horizontalLayout = (Layout::MULTILINE == layout) ? Text::Layout::Engine::MULTI_LINE_BOX : Text::Layout::Engine::SINGLE_LINE_BOX;
1008   layoutEngine.SetLayout(horizontalLayout); // TODO: multi-line.
1009
1010   // Set minimun line size
1011   layoutEngine.SetDefaultLineSize(textParameters.minLineSize);
1012
1013   // Whether the layout is circular.
1014   const bool isCircularTextLayout = (Layout::CIRCULAR == layout);
1015   const bool isClockwise          = isCircularTextLayout && (0.f < textParameters.incrementAngle);
1016
1017   // Calculates the max ascender or the max descender.
1018   // Is used to calculate the radius of the base line of the text.
1019   float maxAscenderDescender = 0.f;
1020   if(isCircularTextLayout)
1021   {
1022     FontId currentFontId = 0u;
1023     for(const auto& glyph : rendererParameters.glyphs)
1024     {
1025       if(currentFontId != glyph.fontId)
1026       {
1027         currentFontId = glyph.fontId;
1028         FontMetrics metrics;
1029         fontClient.GetFontMetrics(currentFontId, metrics);
1030         maxAscenderDescender = std::max(maxAscenderDescender, isClockwise ? metrics.ascender : metrics.descender);
1031       }
1032     }
1033   }
1034   const unsigned int radius = textParameters.radius - static_cast<unsigned int>(maxAscenderDescender);
1035
1036   // Set the layout parameters.
1037   Size textLayoutArea = Size(static_cast<float>(textParameters.textWidth),
1038                              static_cast<float>(textParameters.textHeight));
1039
1040   // padding
1041   Extents padding                  = textParameters.padding;
1042   internalDataModel.textLayoutArea = Size(textLayoutArea.x - (padding.start + padding.end), textLayoutArea.y - (padding.top + padding.bottom));
1043
1044   if(isCircularTextLayout)
1045   {
1046     // In a circular layout, the length of the text area depends on the radius.
1047     rendererParameters.radius              = radius;
1048     internalDataModel.textLayoutArea.width = fabs(Radian(Degree(textParameters.incrementAngle)) * static_cast<float>(rendererParameters.radius));
1049   }
1050   // Resize the vector of positions to have the same size than the vector of glyphs.
1051   rendererParameters.positions.Resize(numberOfGlyphs);
1052
1053   textModel->mLineWrapMode          = Text::LineWrap::WORD;
1054   textModel->mIgnoreSpacesAfterText = false;
1055   Text::Layout::Parameters layoutParameters(internalDataModel.textLayoutArea,
1056                                             textModel);
1057
1058   // Whether the last character is a new paragraph character.
1059   const Vector<Character>& textToShape = isTextMirrored ? mirroredUtf32Characters : textModel->mLogicalModel->mText;
1060   layoutParameters.isLastNewParagraph  = TextAbstraction::IsNewParagraph(textToShape[numberOfCharacters - 1u]);
1061
1062   // The initial glyph and the number of glyphs to layout.
1063   layoutParameters.startGlyphIndex        = 0u;
1064   layoutParameters.numberOfGlyphs         = numberOfGlyphs;
1065   layoutParameters.startLineIndex         = 0u;
1066   layoutParameters.estimatedNumberOfLines = 1u;
1067   layoutParameters.interGlyphExtraAdvance = 0.f;
1068
1069   // Update the visual model.
1070   Size newLayoutSize;
1071   bool isAutoScrollEnabled            = false;
1072   bool isAutoScrollMaxTextureExceeded = false;
1073   bool isHiddenInputEnabled           = false;
1074
1075   layoutEngine.LayoutText(layoutParameters,
1076                           newLayoutSize,
1077                           textParameters.ellipsisEnabled,
1078                           isAutoScrollEnabled,
1079                           isAutoScrollMaxTextureExceeded,
1080                           isHiddenInputEnabled,
1081                           ellipsisPosition);
1082
1083   return newLayoutSize;
1084 }
1085
1086 Devel::PixelBuffer RenderText(const RendererParameters& textParameters, TextAbstraction::TextRenderer::Parameters& rendererParameters)
1087 {
1088   ////////////////////////////////////////////////////////////////////////////////
1089   // Render the text.
1090   ////////////////////////////////////////////////////////////////////////////////
1091
1092   rendererParameters.width  = textParameters.textWidth;
1093   rendererParameters.height = textParameters.textHeight;
1094
1095   TextAbstraction::TextRenderer renderer = TextAbstraction::TextRenderer::Get();
1096   return renderer.Render(rendererParameters);
1097 }
1098
1099 Devel::PixelBuffer Render(const RendererParameters& textParameters, Vector<EmbeddedItemInfo>& embeddedItemLayout)
1100 {
1101   if(textParameters.text.empty())
1102   {
1103     Dali::Devel::PixelBuffer pixelBuffer = Dali::Devel::PixelBuffer::New(textParameters.textWidth,
1104                                                                          textParameters.textHeight,
1105                                                                          Dali::Pixel::RGBA8888);
1106
1107     const unsigned int bufferSize = textParameters.textWidth * textParameters.textHeight * Dali::Pixel::GetBytesPerPixel(Dali::Pixel::RGBA8888);
1108     unsigned char*     buffer     = pixelBuffer.GetBuffer();
1109     memset(buffer, 0, bufferSize);
1110
1111     return pixelBuffer;
1112   }
1113
1114   FontClient fontClient = FontClient::Get();
1115   MetricsPtr metrics;
1116   // Use this to access FontClient i.e. to get down-scaled Emoji metrics.
1117   metrics = Metrics::New(fontClient);
1118
1119   Text::ModelPtr    textModel = Text::Model::New();
1120   InternalDataModel internalData(fontClient, metrics, textModel);
1121
1122   TextAbstraction::TextRenderer::Parameters rendererParameters(internalData.textModel->mVisualModel->mGlyphs,
1123                                                                internalData.textModel->mVisualModel->mGlyphPositions,
1124                                                                internalData.textModel->mVisualModel->mColors,
1125                                                                internalData.textModel->mVisualModel->mColorIndices,
1126                                                                internalData.blendingMode,
1127                                                                internalData.isEmoji);
1128
1129   rendererParameters.width       = textParameters.textWidth;
1130   rendererParameters.height      = textParameters.textHeight;
1131   rendererParameters.pixelFormat = TextAbstraction::TextRenderer::Parameters::RGBA8888; // @note: At the moment all textures are generated RGBA8888
1132
1133   ////////////////////////////////////////////////////////////////////////////////
1134   // Process the markup string if the mark-up processor is enabled.
1135   ////////////////////////////////////////////////////////////////////////////////
1136   ShapeTextPreprocess(textParameters, rendererParameters, internalData);
1137
1138   ////////////////////////////////////////////////////////////////////////////////
1139   // Retrieve the glyphs. Text shaping
1140   ////////////////////////////////////////////////////////////////////////////////
1141   ShapeText(rendererParameters, embeddedItemLayout, internalData);
1142
1143   ////////////////////////////////////////////////////////////////////////////////
1144   // Retrieve the glyph's metrics.
1145   ////////////////////////////////////////////////////////////////////////////////
1146
1147   metrics->GetGlyphMetrics(rendererParameters.glyphs.Begin(), internalData.numberOfGlyphs);
1148
1149   ////////////////////////////////////////////////////////////////////////////////
1150   // Set the color runs in glyphs.
1151   ////////////////////////////////////////////////////////////////////////////////
1152   SetColorSegmentation(textParameters, internalData);
1153
1154   ////////////////////////////////////////////////////////////////////////////////
1155   // Set the isEmoji Vector
1156   ////////////////////////////////////////////////////////////////////////////////
1157   SetEmojiVector(internalData);
1158
1159   ////////////////////////////////////////////////////////////////////////////////
1160   // Layout the text
1161   ////////////////////////////////////////////////////////////////////////////////
1162   Size newLayoutSize = LayoutText(textParameters, rendererParameters, embeddedItemLayout, internalData);
1163
1164   ////////////////////////////////////////////////////////////////////////////////
1165   // Align the text.
1166   ////////////////////////////////////////////////////////////////////////////////
1167   Align(textParameters, rendererParameters, embeddedItemLayout, internalData, newLayoutSize);
1168
1169   ////////////////////////////////////////////////////////////////////////////////
1170   // Ellipsis the text.
1171   ////////////////////////////////////////////////////////////////////////////////
1172   Ellipsis(textParameters, rendererParameters, embeddedItemLayout, internalData);
1173
1174   ////////////////////////////////////////////////////////////////////////////////
1175   // Render the text.
1176   ////////////////////////////////////////////////////////////////////////////////
1177   return RenderText(textParameters, rendererParameters);
1178 }
1179
1180 Devel::PixelBuffer CreateShadow(const ShadowParameters& shadowParameters)
1181 {
1182   // The size of the pixel data.
1183   const int width  = static_cast<int>(shadowParameters.input.GetWidth());
1184   const int height = static_cast<int>(shadowParameters.input.GetHeight());
1185
1186   // The shadow's offset.
1187   const int xOffset = static_cast<int>(shadowParameters.offset.x);
1188   const int yOffset = static_cast<int>(shadowParameters.offset.y);
1189
1190   // The size in bytes of the pixel of the input's buffer.
1191   const Pixel::Format inputFormat    = shadowParameters.input.GetPixelFormat();
1192   const unsigned int  inputPixelSize = Pixel::GetBytesPerPixel(inputFormat);
1193   const bool          isA8           = Pixel::A8 == inputFormat;
1194
1195   // Creates the output pixel buffer.
1196   Devel::PixelBuffer outputPixelBuffer = Devel::PixelBuffer::New(width, height, Pixel::RGBA8888);
1197
1198   // Clear the output buffer
1199   unsigned char* outputPixelBufferPtr = outputPixelBuffer.GetBuffer();
1200   memset(outputPixelBufferPtr, 0, width * height * Pixel::GetBytesPerPixel(Pixel::RGBA8888));
1201
1202   // Gets the buffer of the input pixel buffer.
1203   const unsigned char* const inputPixelBuffer = shadowParameters.input.GetBuffer();
1204
1205   float textColor[4u];
1206   if(isA8)
1207   {
1208     memcpy(textColor, shadowParameters.textColor.AsFloat(), 4u * sizeof(float));
1209   }
1210   const float* const shadowColor = shadowParameters.color.AsFloat();
1211
1212   // Traverse the input pixel buffer and write the text on the foreground and the shadow on the background.
1213   for(int rowIndex = 0; rowIndex < height; ++rowIndex)
1214   {
1215     // Calculates the rowIndex to the input pixel buffer for the shadow and whether it's within the boundaries.
1216     const int  yOffsetIndex    = rowIndex - yOffset;
1217     const bool isValidRowIndex = ((yOffsetIndex >= 0) && (yOffsetIndex < height));
1218
1219     const int rows       = rowIndex * width;
1220     const int offsetRows = yOffsetIndex * width;
1221     for(int columnIndex = 0; columnIndex < width; ++columnIndex)
1222     {
1223       // Index to the input buffer to retrieve the alpha value of the foreground text.
1224       const unsigned int index = inputPixelSize * static_cast<unsigned int>(rows + columnIndex);
1225
1226       // Build the index to the input buffer to retrieve the alpha value of the background shadow.
1227       unsigned int shadowIndex        = 0u;
1228       bool         isValidShadowIndex = false;
1229       if(isValidRowIndex)
1230       {
1231         const int xOffsetIndex = columnIndex - xOffset;
1232         isValidShadowIndex     = ((xOffsetIndex >= 0) && (xOffsetIndex < width));
1233
1234         if(isValidShadowIndex)
1235         {
1236           shadowIndex = inputPixelSize * static_cast<unsigned int>(offsetRows + xOffsetIndex);
1237         }
1238       }
1239
1240       // If the input buffer is an alpha mask, retrieve the values for the foreground text and the background shadow.
1241       // If not retrieve the color.
1242       float inputShadowOffsetAlphaValue = 1.f;
1243       float inputAlphaValue             = 1.f;
1244       if(isA8)
1245       {
1246         // Retrieve the alpha value for the shadow.
1247         inputShadowOffsetAlphaValue = isValidShadowIndex ? (static_cast<float>(*(inputPixelBuffer + shadowIndex)) / 255.f) : 0.f;
1248
1249         // Retrieve the alpha value for the text.
1250         inputAlphaValue = static_cast<float>(*(inputPixelBuffer + index)) / 255.f;
1251       }
1252       else
1253       {
1254         // The input buffer is not an alpha mask. Retrieve the color.
1255         textColor[0u]               = TO_FLOAT * static_cast<float>(*(inputPixelBuffer + index + 0u));
1256         textColor[1u]               = TO_FLOAT * static_cast<float>(*(inputPixelBuffer + index + 1u));
1257         textColor[2u]               = TO_FLOAT * static_cast<float>(*(inputPixelBuffer + index + 2u));
1258         textColor[3u]               = TO_FLOAT * static_cast<float>(*(inputPixelBuffer + index + 3u));
1259         inputAlphaValue             = textColor[3u];
1260         inputShadowOffsetAlphaValue = isValidShadowIndex ? TO_FLOAT * static_cast<float>(*(inputPixelBuffer + shadowIndex + 3u)) : 0.f;
1261       }
1262
1263       // Build the output color.
1264       float outputColor[4u];
1265
1266       if(shadowParameters.blendShadow)
1267       {
1268         // Blend the shadow's color with the text's color on top
1269         const float textAlpha   = textColor[3u] * inputAlphaValue;
1270         const float shadowAlpha = shadowColor[3u] * inputShadowOffsetAlphaValue;
1271
1272         // Blends the alpha.
1273         outputColor[3u]              = 1.f - ((1.f - textAlpha) * (1.f - shadowAlpha));
1274         const bool isOutputAlphaZero = outputColor[3u] < Dali::Math::MACHINE_EPSILON_1000;
1275         if(isOutputAlphaZero)
1276         {
1277           std::fill(outputColor, outputColor + 4u, 0.f);
1278         }
1279         else
1280         {
1281           // Blends the RGB components.
1282           float shadowComponent = 0.f;
1283           float textComponent   = 0.f;
1284
1285           shadowComponent = shadowColor[0u] * inputShadowOffsetAlphaValue;
1286           textComponent   = textColor[0u] * inputAlphaValue;
1287           outputColor[0u] = (textComponent * textAlpha / outputColor[3u]) + (shadowComponent * shadowAlpha * (1.f - textAlpha) / outputColor[3u]);
1288
1289           shadowComponent = shadowColor[1u] * inputShadowOffsetAlphaValue;
1290           textComponent   = textColor[1u] * inputAlphaValue;
1291           outputColor[1u] = (textComponent * textAlpha / outputColor[3u]) + (shadowComponent * shadowAlpha * (1.f - textAlpha) / outputColor[3u]);
1292
1293           shadowComponent = shadowColor[2u] * inputShadowOffsetAlphaValue;
1294           textComponent   = textColor[2u] * inputAlphaValue;
1295           outputColor[2u] = (textComponent * textAlpha / outputColor[3u]) + (shadowComponent * shadowAlpha * (1.f - textAlpha) / outputColor[3u]);
1296         }
1297       }
1298       else
1299       {
1300         // No blending!!!
1301         std::fill(outputColor, outputColor + 4u, 0.f);
1302
1303         const float textAlpha   = textColor[3u];
1304         const float shadowAlpha = shadowColor[3u] * inputShadowOffsetAlphaValue;
1305
1306         // Write shadow first.
1307         if(shadowAlpha > Dali::Math::MACHINE_EPSILON_1000)
1308         {
1309           outputColor[0u] = shadowColor[0u] * inputShadowOffsetAlphaValue;
1310           outputColor[1u] = shadowColor[1u] * inputShadowOffsetAlphaValue;
1311           outputColor[2u] = shadowColor[2u] * inputShadowOffsetAlphaValue;
1312           outputColor[3u] = shadowAlpha;
1313         }
1314
1315         // Write character on top.
1316         if(textAlpha > Dali::Math::MACHINE_EPSILON_1000)
1317         {
1318           outputColor[0u] = textColor[0u];
1319           outputColor[1u] = textColor[1u];
1320           outputColor[2u] = textColor[2u];
1321           outputColor[3u] = textAlpha;
1322         }
1323       }
1324
1325       // Write the color into the output pixel buffer.
1326       const unsigned int outputIndex             = 4u * (rows + columnIndex);
1327       *(outputPixelBufferPtr + outputIndex + 0u) = static_cast<unsigned char>(TO_UCHAR * outputColor[0u]);
1328       *(outputPixelBufferPtr + outputIndex + 1u) = static_cast<unsigned char>(TO_UCHAR * outputColor[1u]);
1329       *(outputPixelBufferPtr + outputIndex + 2u) = static_cast<unsigned char>(TO_UCHAR * outputColor[2u]);
1330       *(outputPixelBufferPtr + outputIndex + 3u) = static_cast<unsigned char>(TO_UCHAR * outputColor[3u]);
1331     }
1332   }
1333
1334   // Returns the pixel buffer.
1335   return outputPixelBuffer;
1336 }
1337
1338 Devel::PixelBuffer ConvertToRgba8888(Devel::PixelBuffer pixelBuffer, const Vector4& color, bool multiplyByAlpha)
1339 {
1340   if(Dali::Pixel::A8 != pixelBuffer.GetPixelFormat())
1341   {
1342     // Does nothing.
1343     return pixelBuffer;
1344   }
1345
1346   const unsigned int width          = pixelBuffer.GetWidth();
1347   const unsigned int height         = pixelBuffer.GetHeight();
1348   Devel::PixelBuffer newPixelBuffer = Devel::PixelBuffer::New(width, height, Dali::Pixel::RGBA8888);
1349
1350   unsigned char*             dstBuffer = newPixelBuffer.GetBuffer();
1351   const unsigned char* const srcBuffer = pixelBuffer.GetBuffer();
1352
1353   const unsigned char r = static_cast<unsigned char>(TO_UCHAR * color.r);
1354   const unsigned char g = static_cast<unsigned char>(TO_UCHAR * color.g);
1355   const unsigned char b = static_cast<unsigned char>(TO_UCHAR * color.b);
1356
1357   unsigned char dstColor[4];
1358   for(unsigned int j = 0u; j < height; ++j)
1359   {
1360     const unsigned int lineIndex = j * width;
1361     for(unsigned int i = 0u; i < width; ++i)
1362     {
1363       const unsigned int srcIndex = lineIndex + i;
1364
1365       const float srcAlpha = static_cast<float>(*(srcBuffer + srcIndex));
1366
1367       if(multiplyByAlpha)
1368       {
1369         dstColor[0u] = static_cast<unsigned char>(srcAlpha * color.r);
1370         dstColor[1u] = static_cast<unsigned char>(srcAlpha * color.g);
1371         dstColor[2u] = static_cast<unsigned char>(srcAlpha * color.b);
1372         dstColor[3u] = static_cast<unsigned char>(srcAlpha * color.a);
1373       }
1374       else
1375       {
1376         dstColor[0u] = r;
1377         dstColor[1u] = g;
1378         dstColor[2u] = b;
1379         dstColor[3u] = static_cast<unsigned char>(srcAlpha);
1380       }
1381
1382       const unsigned int dstIndex = srcIndex * 4u;
1383       memcpy(dstBuffer + dstIndex, dstColor, 4u);
1384     }
1385   }
1386
1387   return newPixelBuffer;
1388 }
1389
1390 void UpdateBuffer(Devel::PixelBuffer src, Devel::PixelBuffer dst, unsigned int x, unsigned int y, bool blend)
1391 {
1392   const Dali::Pixel::Format pixelFormat = dst.GetPixelFormat();
1393   if(src.GetPixelFormat() != pixelFormat)
1394   {
1395     DALI_LOG_ERROR("PixelBuffer::SetBuffer. The pixel format of the new data must be the same of the current pixel buffer.");
1396     return;
1397   }
1398
1399   const unsigned int srcWidth  = src.GetWidth();
1400   const unsigned int srcHeight = src.GetHeight();
1401   const unsigned int dstWidth  = dst.GetWidth();
1402   const unsigned int dstHeight = dst.GetHeight();
1403
1404   if((x > dstWidth) ||
1405      (y > dstHeight) ||
1406      (x + srcWidth > dstWidth) ||
1407      (y + srcHeight > dstHeight))
1408   {
1409     DALI_LOG_ERROR("PixelBuffer::SetBuffer. The source pixel buffer is out of the boundaries of the destination pixel buffer.");
1410     return;
1411   }
1412
1413   const unsigned int bytesPerPixel = Dali::Pixel::GetBytesPerPixel(pixelFormat);
1414   // Ignore when pixelFormat is invalid or contain float
1415   if(bytesPerPixel == 0u || bytesPerPixel == 6u || bytesPerPixel == 12u)
1416   {
1417     return;
1418   }
1419   const unsigned int alphaIndex = bytesPerPixel - 1u;
1420
1421   const unsigned char* const srcBuffer = src.GetBuffer();
1422   unsigned char*             dstBuffer = dst.GetBuffer();
1423
1424   if(!blend)
1425   {
1426     const unsigned int currentLineSize = dstWidth * bytesPerPixel;
1427     const unsigned int newLineSize     = srcWidth * bytesPerPixel;
1428     unsigned char*     currentBuffer   = dstBuffer + (y * dstWidth + x) * bytesPerPixel;
1429     for(unsigned int j = 0u; j < srcHeight; ++j)
1430     {
1431       memcpy(currentBuffer + j * currentLineSize, srcBuffer + j * newLineSize, newLineSize);
1432     }
1433   }
1434   else
1435   {
1436     float outputColor[4u];
1437
1438     // Blend the src pixel buffer with the dst pixel buffer as background.
1439     //
1440     //  fgColor, fgAlpha, bgColor, bgAlpha
1441     //
1442     //  alpha = 1 - ( 1 - fgAlpha ) * ( 1 - bgAlpha )
1443     //  color = ( fgColor * fgAlpha / alpha ) + ( bgColor * bgAlpha * ( 1 - fgAlpha ) / alpha )
1444
1445     // Jump till the 'x,y' position
1446     const unsigned int dstWidthBytes = dstWidth * bytesPerPixel;
1447     dstBuffer += (y * dstWidthBytes + x * bytesPerPixel);
1448
1449     for(unsigned int j = 0u; j < srcHeight; ++j)
1450     {
1451       const unsigned int srcLineIndex = j * srcWidth;
1452       for(unsigned int i = 0u; i < srcWidth; ++i)
1453       {
1454         const float srcAlpha = TO_FLOAT * static_cast<float>(*(srcBuffer + bytesPerPixel * (srcLineIndex + i) + alphaIndex));
1455         const float dstAlpha = TO_FLOAT * static_cast<float>(*(dstBuffer + i * bytesPerPixel + alphaIndex));
1456
1457         // Blends the alpha channel.
1458         const float oneMinusSrcAlpha = 1.f - srcAlpha;
1459         outputColor[alphaIndex]      = 1.f - (oneMinusSrcAlpha * (1.f - dstAlpha));
1460
1461         // Blends the RGB channels.
1462         const bool isOutputAlphaZero = outputColor[alphaIndex] < Dali::Math::MACHINE_EPSILON_1000;
1463         if(isOutputAlphaZero)
1464         {
1465           std::fill(outputColor, outputColor + bytesPerPixel, 0.f);
1466         }
1467         else
1468         {
1469           const float srcAlphaOverOutputAlpha                 = srcAlpha / outputColor[alphaIndex];                    // fgAlpha / alpha
1470           const float dstAlphaOneMinusSrcAlphaOverOutputAlpha = dstAlpha * oneMinusSrcAlpha / outputColor[alphaIndex]; // bgAlpha * ( 1 - fgAlpha ) / alpha
1471           for(unsigned int index = 0u; index < alphaIndex; ++index)
1472           {
1473             const float dstComponent = TO_FLOAT * static_cast<float>(*(dstBuffer + i * bytesPerPixel + index)) * dstAlpha;
1474             const float srcComponent = TO_FLOAT * static_cast<float>(*(srcBuffer + bytesPerPixel * (srcLineIndex + i) + index)) * srcAlpha;
1475             outputColor[index]       = (srcComponent * srcAlphaOverOutputAlpha) + (dstComponent * dstAlphaOneMinusSrcAlphaOverOutputAlpha);
1476           }
1477         }
1478
1479         for(unsigned int index = 0u; index < bytesPerPixel; ++index)
1480         {
1481           *(dstBuffer + i * bytesPerPixel + index) = static_cast<unsigned char>(TO_UCHAR * outputColor[index]);
1482         }
1483       }
1484
1485       dstBuffer += dstWidthBytes;
1486     }
1487   }
1488 }
1489
1490 Dali::Property::Array RenderForLastIndex(RendererParameters& textParameters)
1491 {
1492   Property::Array offsetValues;
1493   if(textParameters.text.empty())
1494   {
1495     return offsetValues;
1496   }
1497   FontClient fontClient = FontClient::Get();
1498   MetricsPtr metrics;
1499   metrics = Metrics::New(fontClient);
1500
1501   Text::ModelPtr    textModel = Text::Model::New();
1502   InternalDataModel internalData(fontClient, metrics, textModel);
1503
1504   TextAbstraction::TextRenderer::Parameters rendererParameters(textModel->mVisualModel->mGlyphs,
1505                                                                textModel->mVisualModel->mGlyphPositions,
1506                                                                textModel->mVisualModel->mColors,
1507                                                                textModel->mVisualModel->mColorIndices,
1508                                                                internalData.blendingMode,
1509                                                                internalData.isEmoji);
1510
1511   rendererParameters.width  = textParameters.textWidth;
1512   rendererParameters.height = textParameters.textHeight;
1513
1514   ////////////////////////////////////////////////////////////////////////////////
1515   // Process the markup string if the mark-up processor is enabled.
1516   ////////////////////////////////////////////////////////////////////////////////
1517   ShapeTextPreprocess(textParameters, rendererParameters, internalData);
1518
1519   ////////////////////////////////////////////////////////////////////////////////
1520   // Retrieve the glyphs. Text shaping
1521   ////////////////////////////////////////////////////////////////////////////////
1522   Dali::Vector<Dali::Toolkit::DevelText::EmbeddedItemInfo> embeddedItemLayout;
1523   ShapeText(rendererParameters, embeddedItemLayout, internalData);
1524
1525   ////////////////////////////////////////////////////////////////////////////////
1526   // Retrieve the glyph's metrics.
1527   ////////////////////////////////////////////////////////////////////////////////
1528   metrics->GetGlyphMetrics(rendererParameters.glyphs.Begin(), internalData.numberOfGlyphs);
1529
1530   ////////////////////////////////////////////////////////////////////////////////
1531   // Layout the text
1532   ////////////////////////////////////////////////////////////////////////////////
1533   int boundingBox           = textParameters.textHeight - (textParameters.padding.top + textParameters.padding.bottom);
1534   textParameters.textHeight = MAX_INT; // layout for the entire area.
1535   LayoutText(textParameters, rendererParameters, embeddedItemLayout, internalData);
1536
1537   ////////////////////////////////////////////////////////////////////////////////
1538   // Calculation last character index
1539   ////////////////////////////////////////////////////////////////////////////////
1540   Vector<LineRun>& lines              = internalData.textModel->mVisualModel->mLines;
1541   unsigned int     numberOfLines      = lines.Count();
1542   int              numberOfCharacters = 0;
1543   float            penY               = 0.f;
1544   float            lineSize           = internalData.layoutEngine.GetDefaultLineSize();
1545   float            lineOffset         = 0.f;
1546   bool             isLastLine;
1547   for(unsigned int index = 0u; index < numberOfLines; ++index)
1548   {
1549     const LineRun& line = *(lines.Begin() + index);
1550     numberOfCharacters += line.characterRun.numberOfCharacters;
1551     isLastLine = (index == numberOfLines - 1);
1552
1553     lineOffset = lineSize > 0.f ? lineSize : GetLineHeight(line, isLastLine);
1554     penY += lineOffset;
1555     if((penY + lineOffset) > boundingBox)
1556     {
1557       offsetValues.PushBack(numberOfCharacters);
1558       penY = 0.f;
1559     }
1560   }
1561   if(penY > 0.f)
1562   {
1563     // add remain character index
1564     offsetValues.PushBack(numberOfCharacters);
1565   }
1566
1567   return offsetValues;
1568 }
1569
1570 Dali::Property::Array GetLastCharacterIndex(RendererParameters& textParameters)
1571 {
1572   Dali::Property::Array offsetValues = Toolkit::DevelText::RenderForLastIndex(textParameters);
1573   return offsetValues;
1574 }
1575
1576 } // namespace DevelText
1577
1578 } // namespace Toolkit
1579
1580 } // namespace Dali