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