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