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