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