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