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