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