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