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