2 * Copyright (c) 2019 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali-toolkit/devel-api/text/text-utils-devel.h>
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>
31 #include <dali-toolkit/internal/text/bidirectional-support.h>
32 #include <dali-toolkit/internal/text/character-set-conversion.h>
33 #include <dali-toolkit/internal/text/color-segmentation.h>
34 #include <dali-toolkit/internal/text/layouts/layout-engine.h>
35 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
36 #include <dali-toolkit/internal/text/markup-processor.h>
37 #include <dali-toolkit/internal/text/multi-language-support.h>
38 #include <dali-toolkit/internal/text/segmentation.h>
39 #include <dali-toolkit/internal/text/shaper.h>
40 #include <dali-toolkit/internal/text/text-enumerations-impl.h>
41 #include <dali-toolkit/internal/text/text-font-style.h>
42 #include <dali-toolkit/internal/text/text-model.h>
47 using namespace TextAbstraction;
60 * @brief The text's layout.
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.
71 namespace CircularAlignment
75 * @brief The enumerations for the circular alignment.
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).
84 } // namespace CircularAlignment
86 const float TO_POINT_26_DOT_6 = 64.f;
87 const float TO_FLOAT = 1.f / 255.f;
88 const float TO_UCHAR = 255.f;
89 const bool RTL = true;
90 const float TWO_PI = 2.f * Dali::Math::PI; ///< 360 degrees in radians
91 const float RAD_135 = Math::PI_2 + Math::PI_4; ///< 135 degrees in radians;
92 const float RAD_225 = RAD_135 + Math::PI_2; ///< 225 degrees in radians;
93 const float RAD_270 = 3.f * Math::PI_2; ///< 270 degrees in radians;
94 const float RAD_315 = RAD_225 + Math::PI_2; ///< 315 degrees in radians;
95 const float MAX_INT = std::numeric_limits<int>::max();
97 DALI_ENUM_TO_STRING_TABLE_BEGIN( LAYOUT_TYPE )
98 DALI_ENUM_TO_STRING_WITH_SCOPE( DevelText::Layout, SINGLELINE )
99 DALI_ENUM_TO_STRING_WITH_SCOPE( DevelText::Layout, MULTILINE )
100 DALI_ENUM_TO_STRING_WITH_SCOPE( DevelText::Layout, CIRCULAR )
101 DALI_ENUM_TO_STRING_TABLE_END( LAYOUT_TYPE )
103 DALI_ENUM_TO_STRING_TABLE_BEGIN( CIRCULAR_ALIGNMENT_TYPE )
104 DALI_ENUM_TO_STRING_WITH_SCOPE( DevelText::CircularAlignment, BEGIN )
105 DALI_ENUM_TO_STRING_WITH_SCOPE( DevelText::CircularAlignment, CENTER )
106 DALI_ENUM_TO_STRING_WITH_SCOPE( DevelText::CircularAlignment, END )
107 DALI_ENUM_TO_STRING_TABLE_END( CIRCULAR_ALIGNMENT_TYPE )
109 struct InternalDataModel
111 InternalDataModel( FontClient& fontClient,
113 Text::ModelPtr textModel )
114 : fontClient( fontClient ),
116 textModel( textModel ),
117 numberOfCharacters{ 0u },
118 isTextMirrored{ false },
121 layoutEngine.SetMetrics( metrics );
124 FontClient& fontClient;
126 Text::Layout::Engine layoutEngine; ///< The layout engine.
127 Text::ModelPtr textModel; ///< Pointer to the text's model.
128 Vector<ColorBlendingMode> blendingMode; ///< How embedded items and bitmap font glyphs are blended with color text.
129 Vector<bool> isEmoji; ///< Whether the glyph is an emoji.
131 Vector<Character> mirroredUtf32Characters; // The utf32Characters Characters but mirrored if there are RTL text.
133 Length numberOfCharacters; // The number of characters (not glyphs!).
134 bool isTextMirrored ; // Whether the text has been mirrored.
136 Length numberOfGlyphs;
140 bool GetLayoutEnumeration(const Property::Value& propertyValue, DevelText::Layout::Type& layout)
142 return Scripting::GetEnumerationProperty(propertyValue, LAYOUT_TYPE_TABLE, LAYOUT_TYPE_TABLE_COUNT, layout);
145 bool GetCircularAlignmentEnumeration(const Property::Value& propertyValue, DevelText::CircularAlignment::Type& circularAlignment)
147 return Scripting::GetEnumerationProperty(propertyValue, CIRCULAR_ALIGNMENT_TYPE_TABLE, CIRCULAR_ALIGNMENT_TYPE_TABLE_COUNT, circularAlignment);
150 void ShapeTextPreprocess( const RendererParameters& textParameters, TextAbstraction::TextRenderer::Parameters& rendererParameters, InternalDataModel& internalDataModel )
153 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
154 const uint8_t* utf8 = NULL; // pointer to the first character of the text (encoded in utf8)
155 Length textSize = 0u; // The length of the utf8 string.
157 Length& numberOfCharacters = internalDataModel.numberOfCharacters;
158 Vector<Character>& mirroredUtf32Characters = internalDataModel.mirroredUtf32Characters;
159 Text::ModelPtr& textModel = internalDataModel.textModel;
161 Vector<Character>& utf32Characters = textModel->mLogicalModel->mText; // Characters encoded in utf32.
162 Vector<LineBreakInfo>& lineBreakInfo = textModel->mLogicalModel->mLineBreakInfo; // The line break info.
163 Vector<ScriptRun>& scripts = textModel->mLogicalModel->mScriptRuns; // Charactes's script.
164 Vector<FontDescriptionRun>& fontDescriptionRuns = textModel->mLogicalModel->mFontDescriptionRuns; // Desired font descriptions.
165 Vector<FontRun>& validFonts = textModel->mLogicalModel->mFontRuns; // Validated fonts.
166 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = textModel->mLogicalModel->mBidirectionalParagraphInfo; // The bidirectional info per paragraph.
167 Vector<CharacterDirection>& directions = textModel->mLogicalModel->mCharacterDirections; // Character's directions.
168 Vector<ColorRun>& colorRuns = textModel->mLogicalModel->mColorRuns; // colors of the text.
170 // the default font's description.
171 FontDescription defaultFontDescription;
172 PointSize26Dot6 defaultPointSize = FontClient::DEFAULT_POINT_SIZE;
174 ////////////////////////////////////////////////////////////////////////////////
175 // Process the markup string if the mark-up processor is enabled.
176 ////////////////////////////////////////////////////////////////////////////////
178 MarkupProcessData markupProcessData( colorRuns,
180 textModel->mLogicalModel->mEmbeddedItems );
182 if (textParameters.markupEnabled)
184 ProcessMarkupString(textParameters.text, markupProcessData);
185 textSize = markupProcessData.markupProcessedText.size();
187 // This is a bit horrible but std::string returns a (signed) char*
188 utf8 = reinterpret_cast<const uint8_t*>(markupProcessData.markupProcessedText.c_str());
192 textSize = textParameters.text.size();
194 // This is a bit horrible but std::string returns a (signed) char*
195 utf8 = reinterpret_cast<const uint8_t*>(textParameters.text.c_str());
198 ////////////////////////////////////////////////////////////////////////////////
199 // Convert from utf8 to utf32
200 ////////////////////////////////////////////////////////////////////////////////
202 utf32Characters.Resize(textSize);
204 // Transform a text array encoded in utf8 into an array encoded in utf32.
205 // It returns the actual number of characters.
206 numberOfCharacters = Utf8ToUtf32( utf8, textSize, utf32Characters.Begin() );
207 utf32Characters.Resize( numberOfCharacters );
209 ////////////////////////////////////////////////////////////////////////////////
210 // Retrieve the Line and Word Break Info.
211 ////////////////////////////////////////////////////////////////////////////////
213 lineBreakInfo.Resize( numberOfCharacters, LINE_NO_BREAK );
215 SetLineBreakInfo( utf32Characters,
220 ////////////////////////////////////////////////////////////////////////////////
221 // Retrieve the script runs.
222 ////////////////////////////////////////////////////////////////////////////////
224 multilanguageSupport.SetScripts( utf32Characters,
229 // Check if there are emojis.
230 // If there are an RGBA8888 pixel format is needed.
231 for( const auto& run : scripts )
233 if( run.script == TextAbstraction::Script::EMOJI )
235 rendererParameters.pixelFormat = TextAbstraction::TextRenderer::Parameters::RGBA8888;
240 ////////////////////////////////////////////////////////////////////////////////
241 // Retrieve the font runs.
242 ////////////////////////////////////////////////////////////////////////////////
244 // Set the description font run with the given text parameters.
245 FontDescriptionRun fontDescriptionRun;
246 fontDescriptionRun.characterRun.characterIndex = 0u;
247 fontDescriptionRun.characterRun.numberOfCharacters = numberOfCharacters;
249 fontDescriptionRun.familyLength = 0u;
250 fontDescriptionRun.familyName = nullptr;
251 fontDescriptionRun.familyDefined = !textParameters.fontFamily.empty();
252 if( fontDescriptionRun.familyDefined )
254 // The allocated memory will be freed when the logical model is destroyed.
255 fontDescriptionRun.familyLength = textParameters.fontFamily.size();
256 fontDescriptionRun.familyName = new char[fontDescriptionRun.familyLength];
257 memcpy( fontDescriptionRun.familyName, textParameters.fontFamily.c_str(), fontDescriptionRun.familyLength );
260 fontDescriptionRun.weightDefined = !textParameters.fontWeight.empty();
261 if( fontDescriptionRun.weightDefined )
263 fontDescriptionRun.weight = StringToWeight( textParameters.fontWeight.c_str() );
266 fontDescriptionRun.widthDefined = !textParameters.fontWidth.empty();
267 if( fontDescriptionRun.widthDefined )
269 fontDescriptionRun.width = StringToWidth( textParameters.fontWidth.c_str() );
272 fontDescriptionRun.slantDefined = !textParameters.fontSlant.empty();
273 if( fontDescriptionRun.slantDefined )
275 fontDescriptionRun.slant = StringToSlant( textParameters.fontSlant.c_str() );
278 fontDescriptionRun.sizeDefined = !EqualsZero( textParameters.fontSize );
279 if( fontDescriptionRun.sizeDefined )
281 fontDescriptionRun.size = static_cast<unsigned int>( textParameters.fontSize * TO_POINT_26_DOT_6 );
284 fontDescriptionRuns.PushBack( fontDescriptionRun );
286 // Validates the fonts. If there is a character with no assigned font it sets a default one.
287 // After this call, fonts are validated.
288 multilanguageSupport.ValidateFonts( utf32Characters,
291 defaultFontDescription,
297 ////////////////////////////////////////////////////////////////////////////////
298 // Retrieve the Bidirectional info.
299 ////////////////////////////////////////////////////////////////////////////////
301 bidirectionalInfo.Reserve( 1u );
303 SetBidirectionalInfo( utf32Characters,
310 const bool hasBidirectionalText = 0u != bidirectionalInfo.Count();
311 if( hasBidirectionalText )
313 // Only set the character directions if there is right to left characters.
314 GetCharactersDirection( bidirectionalInfo,
320 // This paragraph has right to left text. Some characters may need to be mirrored.
321 // TODO: consider if the mirrored string can be stored as well.
323 internalDataModel.isTextMirrored = GetMirroredText( utf32Characters,
328 mirroredUtf32Characters );
332 void ShapeText( TextAbstraction::TextRenderer::Parameters& rendererParameters, Vector<EmbeddedItemInfo>& embeddedItemLayout, InternalDataModel& internalDataModel )
334 Vector<GlyphIndex> newParagraphGlyphs; // Glyphs for the new paragraph characters.
335 const Length numberOfCharacters = internalDataModel.numberOfCharacters;
336 const bool isTextMirrored = internalDataModel.isTextMirrored;
337 Text::ModelPtr& textModel = internalDataModel.textModel;
338 const Vector<Character>& mirroredUtf32Characters = internalDataModel.mirroredUtf32Characters;
339 FontClient& fontClient = internalDataModel.fontClient;
340 const Vector<Character>& utf32Characters = textModel->mLogicalModel->mText; // Characters encoded in utf32.
341 const Vector<LineBreakInfo>& lineBreakInfo = textModel->mLogicalModel->mLineBreakInfo; // The line break info.
342 const Vector<ScriptRun>& scripts = textModel->mLogicalModel->mScriptRuns; // Charactes's script.
343 const Vector<FontRun>& validFonts = textModel->mLogicalModel->mFontRuns; // Validated fonts.
345 Vector<CharacterIndex>& glyphsToCharacters = textModel->mVisualModel->mGlyphsToCharacters; // Glyphs to character map.
346 Vector<Length>& charactersPerGlyph = textModel->mVisualModel->mCharactersPerGlyph; // Number of characters per glyph.
348 ////////////////////////////////////////////////////////////////////////////////
349 // Retrieve the glyphs. Text shaping
350 ////////////////////////////////////////////////////////////////////////////////
352 const Vector<Character>& textToShape = isTextMirrored ? mirroredUtf32Characters : utf32Characters;
354 newParagraphGlyphs.Reserve( 1u );
357 ShapeText( textToShape,
364 rendererParameters.glyphs,
367 newParagraphGlyphs );
369 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
370 textModel->mVisualModel->CreateGlyphsPerCharacterTable( 0u, 0u, numberOfCharacters );
371 textModel->mVisualModel->CreateCharacterToGlyphTable( 0u, 0u, numberOfCharacters );
373 internalDataModel.numberOfGlyphs = rendererParameters.glyphs.Count();
375 // Once the text has been shaped and the glyphs created it's possible to replace the font id of those glyphs
376 // that represent an image or an item and create the embedded item layout info.
377 // Note: the position of the embedded item can't be set until the text is laid-out.
378 embeddedItemLayout.Reserve( textModel->mLogicalModel->mEmbeddedItems.Count() );
379 for( const auto& item : textModel->mLogicalModel->mEmbeddedItems )
381 // Get the glyph that matches with the character index.
382 const GlyphIndex glyphIndex = textModel->mVisualModel->mCharactersToGlyph[item.characterIndex];
383 GlyphInfo& glyph = rendererParameters.glyphs[glyphIndex];
386 Pixel::Format pixelFormat = Pixel::A8;
387 TextAbstraction::FontClient::EmbeddedItemDescription description = { std::string( item.url, item.urlLength ), item.width, item.height, item.colorBlendingMode };
388 glyph.index = fontClient.CreateEmbeddedItem( description, pixelFormat ); // Set here an index to an item.
390 if( ( Pixel::RGBA8888 == pixelFormat ) || ( Pixel::BGRA8888 == pixelFormat ) )
392 rendererParameters.pixelFormat = TextAbstraction::TextRenderer::Parameters::RGBA8888;
395 // If the url is empty the item is going to be added after the text is rendered. It's needed to store the layout here.
396 if( description.url.empty() )
398 EmbeddedItemInfo embeddedInfo =
403 Size( static_cast<float>( item.width ), static_cast<float>( item.height ) ),
404 Size( static_cast<float>( item.width ), static_cast<float>( item.height ) ),
406 item.colorBlendingMode
409 embeddedItemLayout.PushBack( embeddedInfo );
414 void SetColorSegmentation( const RendererParameters& textParameters, InternalDataModel& internalDataModel )
417 Text::ModelPtr& textModel = internalDataModel.textModel;
418 Vector<ColorBlendingMode>& blendingMode = internalDataModel.blendingMode;
420 Vector<ColorRun>& colorRuns = textModel->mLogicalModel->mColorRuns; // colors of the text.
422 Vector<GlyphIndex>& charactersToGlyph = textModel->mVisualModel->mCharactersToGlyph; // Characters to glyphs map.
423 Vector<Length>& glyphsPerCharacter = textModel->mVisualModel->mGlyphsPerCharacter; // The number of glyphs that are shaped.
425 ////////////////////////////////////////////////////////////////////////////////
426 // Set the color runs in glyphs.
427 ////////////////////////////////////////////////////////////////////////////////
429 SetColorSegmentationInfo( colorRuns,
434 internalDataModel.numberOfCharacters,
435 textModel->mVisualModel->mColors,
436 textModel->mVisualModel->mColorIndices );
438 // Insert the default color at the beginning of the vector.
439 textModel->mVisualModel->mColors.Insert( textModel->mVisualModel->mColors.Begin(),textParameters.textColor );
441 // Set how the embedded items are blended with text color.
442 blendingMode.Resize( internalDataModel.numberOfGlyphs, textParameters.isTextColorSet ? ColorBlendingMode::MULTIPLY : ColorBlendingMode::NONE );
444 if( !textParameters.isTextColorSet )
446 // Traverse the color runs.
447 for( const auto& run : colorRuns )
449 const GlyphIndex firstGlyph = textModel->mVisualModel->mCharactersToGlyph[run.characterRun.characterIndex];
450 const CharacterIndex lastCharacter = run.characterRun.characterIndex + run.characterRun.numberOfCharacters - 1u;
451 const GlyphIndex lastGlyphPlusOne = textModel->mVisualModel->mCharactersToGlyph[lastCharacter] + textModel->mVisualModel->mGlyphsPerCharacter[lastCharacter];
453 for( GlyphIndex index = firstGlyph; index < lastGlyphPlusOne; ++index )
455 blendingMode[index] = ColorBlendingMode::MULTIPLY;
460 // Traverse the embedded items and update the blending mode vector.
461 for( const auto& item : textModel->mLogicalModel->mEmbeddedItems )
463 const GlyphIndex glyphIndex = textModel->mVisualModel->mCharactersToGlyph[item.characterIndex];
464 blendingMode[glyphIndex] = item.colorBlendingMode;
468 void SetEmojiVector( InternalDataModel& internalDataModel )
470 Vector<bool>& isEmoji = internalDataModel.isEmoji;
471 Text::ModelPtr& textModel = internalDataModel.textModel;
472 const Length numberOfGlyphs = internalDataModel.numberOfGlyphs;
474 const Vector<ScriptRun>& scripts = textModel->mLogicalModel->mScriptRuns; // Charactes's script.
475 ////////////////////////////////////////////////////////////////////////////////
476 // Set the isEmoji Vector
477 ////////////////////////////////////////////////////////////////////////////////
479 isEmoji.Resize( numberOfGlyphs, false );
481 for( const auto& run : scripts )
483 if( run.script == TextAbstraction::Script::EMOJI )
485 const GlyphIndex firstGlyph = textModel->mVisualModel->mCharactersToGlyph[run.characterRun.characterIndex];
486 const CharacterIndex lastCharacter = run.characterRun.characterIndex + run.characterRun.numberOfCharacters - 1u;
487 const GlyphIndex lastGlyphPlusOne = textModel->mVisualModel->mCharactersToGlyph[lastCharacter] + textModel->mVisualModel->mGlyphsPerCharacter[lastCharacter];
489 for( GlyphIndex index = firstGlyph; index < lastGlyphPlusOne; ++index )
491 isEmoji[index] = true;
498 void Align( const RendererParameters& textParameters, TextAbstraction::TextRenderer::Parameters& rendererParameters,
499 Vector<EmbeddedItemInfo>& embeddedItemLayout, InternalDataModel& internalDataModel,
500 const Size& newLayoutSize )
502 Text::Layout::Engine& layoutEngine = internalDataModel.layoutEngine;
503 Text::ModelPtr& textModel = internalDataModel.textModel;
504 const Length numberOfCharacters = internalDataModel.numberOfCharacters;
505 Size& textLayoutArea = internalDataModel.textLayoutArea;
507 Vector<LineRun>& lines = textModel->mVisualModel->mLines; // The laid out lines.
509 ////////////////////////////////////////////////////////////////////////////////
511 ////////////////////////////////////////////////////////////////////////////////
513 HorizontalAlignment::Type horizontalAlignment = Toolkit::HorizontalAlignment::CENTER;
514 HorizontalAlignment::Type horizontalCircularAlignment = Toolkit::HorizontalAlignment::CENTER;
515 VerticalAlignment::Type verticalAlignment = VerticalAlignment::CENTER;
516 CircularAlignment::Type circularAlignment = CircularAlignment::BEGIN;
518 Layout::Type layout = Layout::SINGLELINE;
520 // Sets the alignment
521 Property::Value horizontalAlignmentStr( textParameters.horizontalAlignment );
522 GetHorizontalAlignmentEnumeration( horizontalAlignmentStr, horizontalAlignment );
523 horizontalCircularAlignment = horizontalAlignment;
525 Property::Value verticalAlignmentStr( textParameters.verticalAlignment );
526 GetVerticalAlignmentEnumeration( verticalAlignmentStr, verticalAlignment );
528 Property::Value circularAlignmentStr( textParameters.circularAlignment );
529 GetCircularAlignmentEnumeration( circularAlignmentStr, circularAlignment );
531 Property::Value layoutStr( textParameters.layout );
532 GetLayoutEnumeration( layoutStr, layout );
534 // Whether the layout is circular.
535 const bool isCircularTextLayout = (Layout::CIRCULAR == layout);
536 const bool isClockwise = isCircularTextLayout && ( 0.f < textParameters.incrementAngle );
538 // Convert CircularAlignment to HorizontalAlignment.
539 if( isCircularTextLayout )
541 switch( circularAlignment )
543 case CircularAlignment::BEGIN:
545 horizontalCircularAlignment = Toolkit::HorizontalAlignment::BEGIN;
548 case CircularAlignment::CENTER:
550 horizontalCircularAlignment = Toolkit::HorizontalAlignment::CENTER;
553 case CircularAlignment::END:
555 horizontalCircularAlignment = Toolkit::HorizontalAlignment::END;
560 textModel->mHorizontalAlignment = isCircularTextLayout ? horizontalCircularAlignment : horizontalAlignment;
562 // Retrieve the line of text to know the direction and the width. @todo multi-line
563 const LineRun& line = lines[0u];
565 if( isCircularTextLayout )
567 // Set the circular alignment.
568 rendererParameters.circularLayout = isClockwise ? TextRenderer::Parameters::CLOCKWISE : TextRenderer::Parameters::COUNTER_CLOCKWISE;
570 // Update the text's height to be used by the ellipsis code.
571 textLayoutArea.height = newLayoutSize.height;
573 // Set the size of the text laid out on a straight horizontal line.
574 rendererParameters.circularWidth = static_cast<unsigned int>( newLayoutSize.width );
575 rendererParameters.circularHeight = static_cast<unsigned int>( newLayoutSize.height );
577 // Calculate the center of the circular text according the horizontal and vertical alingments and the radius.
578 switch( horizontalAlignment )
580 case HorizontalAlignment::BEGIN:
582 rendererParameters.centerX = static_cast<int>(textParameters.radius);
585 case HorizontalAlignment::CENTER:
587 rendererParameters.centerX = static_cast<int>( textParameters.textWidth / 2u );
590 case HorizontalAlignment::END:
592 rendererParameters.centerX = static_cast<int>( textParameters.textWidth ) - static_cast<int>(textParameters.radius);
597 switch( verticalAlignment )
599 case VerticalAlignment::TOP:
601 rendererParameters.centerY = static_cast<int>(textParameters.radius);
604 case VerticalAlignment::CENTER:
606 rendererParameters.centerY = static_cast<int>( textParameters.textHeight / 2u );
609 case VerticalAlignment::BOTTOM:
611 rendererParameters.centerY = static_cast<int>( textParameters.textHeight ) - static_cast<int>(textParameters.radius);
616 // Calculate the beginning angle according with the given horizontal alignment.
617 const bool isRTL = RTL == line.direction;
619 CircularAlignment::Type alignment = circularAlignment;
622 // Swap the alignment type if the line is right to left.
625 case CircularAlignment::BEGIN:
627 alignment = CircularAlignment::END;
630 case CircularAlignment::CENTER:
635 case CircularAlignment::END:
637 alignment = CircularAlignment::BEGIN;
643 float angleOffset = 0.f;
647 case CircularAlignment::BEGIN:
652 case CircularAlignment::CENTER:
654 const bool isNeg = textParameters.incrementAngle < 0.f;
655 const float textWidth = static_cast<float>( rendererParameters.circularWidth );
656 angleOffset = ( isNeg ? -0.5f : 0.5f ) * ( textLayoutArea.width - textWidth ) / static_cast<float>( rendererParameters.radius );
659 case CircularAlignment::END:
661 const bool isNeg = textParameters.incrementAngle < 0.f;
662 const float textWidth = static_cast<float>( rendererParameters.circularWidth );
663 angleOffset = ( isNeg ? -1.f : 1.f ) * ( textLayoutArea.width - textWidth ) / static_cast<float>( rendererParameters.radius );
668 // Update the beginning angle with the calculated offset.
669 rendererParameters.beginAngle = Radian( Degree( textParameters.beginAngle ) ) + angleOffset;
671 // Set the vertical position of the glyphs.
672 for( auto& position : rendererParameters.positions )
679 // Calculate the vertical offset according with the given alignment.
682 switch( verticalAlignment )
684 case VerticalAlignment::TOP:
686 penY = line.ascender;
689 case VerticalAlignment::CENTER:
691 penY = line.ascender + 0.5f * ( textLayoutArea.height - ( line.ascender - line.descender ) );
694 case VerticalAlignment::BOTTOM:
696 penY = textLayoutArea.height;
701 // Calculate the horizontal offset according with the given alignment.
702 float alignmentOffset = 0.f;
703 layoutEngine.Align( textLayoutArea,
709 Dali::LayoutDirection::LEFT_TO_RIGHT,
712 // Update the position of the glyphs with the calculated offsets.
713 for( auto& position : rendererParameters.positions )
715 position.x += line.alignmentOffset;
720 // Cairo adds the bearing to the position of the glyph
721 // that has already been added by the DALi's layout engine,
722 // so it's needed to be removed here.
723 for( unsigned int index = 0u; index < rendererParameters.glyphs.Count(); ++index )
725 const GlyphInfo& glyph = rendererParameters.glyphs[index];
726 Vector2& position = rendererParameters.positions[index];
728 position.x -= glyph.xBearing;
731 // Set the position of the embedded items (if there is any).
732 EmbeddedItemInfo* embeddedItemLayoutBuffer = embeddedItemLayout.Begin();
734 for( Length index = 0u, endIndex = embeddedItemLayout.Count(); index < endIndex; ++index )
736 EmbeddedItemInfo& embeddedItem = *( embeddedItemLayoutBuffer + index );
738 embeddedItem.position = rendererParameters.positions[embeddedItem.glyphIndex];
740 if( isCircularTextLayout )
742 // Calculate the new position of the embedded item in the circular path.
744 // Center of the bitmap.
745 const float halfWidth = 0.5f * embeddedItem.size.width;
746 const float halfHeight = 0.5f * embeddedItem.size.height;
747 double centerX = static_cast<double>( embeddedItem.position.x + halfWidth );
748 double centerY = static_cast<double>(embeddedItem.position.y - halfHeight);
750 Dali::TextAbstraction::CircularTextParameters circularTextParameters;
752 circularTextParameters.radius = static_cast<double>( rendererParameters.radius );
753 circularTextParameters.invRadius = 1.0 / circularTextParameters.radius;
754 circularTextParameters.beginAngle = static_cast<double>( -rendererParameters.beginAngle + Dali::Math::PI_2 );
755 circularTextParameters.centerX = 0.5f * static_cast<double>( textParameters.textWidth );
756 circularTextParameters.centerY = 0.5f * static_cast<double>( textParameters.textHeight );
758 // Calculate the rotation angle.
759 float radians = rendererParameters.beginAngle;
762 radians += static_cast<float>( circularTextParameters.invRadius * centerX );
767 radians -= static_cast<float>( circularTextParameters.invRadius * centerX );
768 radians = -radians + Dali::Math::PI;
770 embeddedItem.angle = Degree( Radian( radians ) );
772 Dali::TextAbstraction::TransformToArc( circularTextParameters, centerX, centerY );
774 // Recalculate the size of the embedded item after the rotation to position it correctly.
775 float width = embeddedItem.size.width;
776 float height = embeddedItem.size.height;
778 // Transform the input angle into the range [0..2PI]
779 radians = fmod( radians, TWO_PI );
780 radians += ( radians < 0.f ) ? TWO_PI : 0.f;
782 // Does the same operations than rotate by shear.
783 if( ( radians > Math::PI_4 ) && ( radians <= RAD_135 ) )
785 std::swap( width, height );
786 radians -= Math::PI_2;
788 else if( ( radians > RAD_135 ) && ( radians <= RAD_225 ) )
792 else if( ( radians > RAD_225 ) && ( radians <= RAD_315 ) )
794 std::swap( width, height );
798 if( fabs( radians ) > Dali::Math::MACHINE_EPSILON_10 )
800 const float angleSinus = fabs( sin( radians ) );
801 const float angleCosinus = cos( radians );
803 // Calculate the rotated image dimensions.
804 embeddedItem.rotatedSize.height = width * angleSinus + height * angleCosinus;
805 embeddedItem.rotatedSize.width = height * angleSinus + width * angleCosinus + 1.f;
808 embeddedItem.position.x = floor( static_cast<float>( centerX ) - 0.5f * embeddedItem.rotatedSize.width );
809 embeddedItem.position.y = floor( static_cast<float>( centerY ) - 0.5f * embeddedItem.rotatedSize.height );
813 embeddedItem.position.y -= embeddedItem.size.height;
819 void Ellipsis( const RendererParameters& textParameters, TextAbstraction::TextRenderer::Parameters& rendererParameters,
820 Vector<EmbeddedItemInfo>& embeddedItemLayout, InternalDataModel& internalDataModel )
822 Text::ModelPtr& textModel = internalDataModel.textModel;
823 FontClient& fontClient = internalDataModel.fontClient;
825 Vector<LineRun>& lines = textModel->mVisualModel->mLines; // The laid out lines.
826 Vector<bool>& isEmoji = internalDataModel.isEmoji;
827 const Size textLayoutArea = internalDataModel.textLayoutArea;
828 ////////////////////////////////////////////////////////////////////////////////
829 // Ellipsis the text.
830 ////////////////////////////////////////////////////////////////////////////////
832 if( textParameters.ellipsisEnabled )
834 const LineRun& line = lines[0u]; // TODO: multi-line
838 Length finalNumberOfGlyphs = 0u;
840 if( ( line.ascender - line.descender ) > textLayoutArea.height )
842 // The height of the line is bigger than the height of the text area.
843 // Show the ellipsis glyph even if it doesn't fit in the text area.
844 // If no text is rendered then issues are rised and it may be a while
845 // until is find out that the text area is too small.
847 // Get the first glyph which is going to be replaced and the ellipsis glyph.
848 GlyphInfo& glyphInfo = rendererParameters.glyphs[0u];
849 const GlyphInfo& ellipsisGlyph = fontClient.GetEllipsisGlyph( fontClient.GetPointSize( glyphInfo.fontId ) );
851 // Change the 'x' and 'y' position of the ellipsis glyph.
852 Vector2& position = rendererParameters.positions[0u];
853 position.x = ellipsisGlyph.xBearing;
854 position.y = textLayoutArea.height - ellipsisGlyph.yBearing;
856 // Replace the glyph by the ellipsis glyph.
857 glyphInfo = ellipsisGlyph;
859 // Set the final number of glyphs
860 finalNumberOfGlyphs = 1u;
865 // firstPenX, penY and firstPenSet are used to position the ellipsis glyph if needed.
866 float firstPenX = 0.f; // Used if rtl text is elided.
867 bool firstPenSet = false;
869 // Add the ellipsis glyph.
870 bool inserted = false;
871 float removedGlypsWidth = 0.f;
872 Length numberOfRemovedGlyphs = 0u;
873 if (line.glyphRun.numberOfGlyphs > 0u)
875 GlyphIndex index = line.glyphRun.numberOfGlyphs - 1u;
877 GlyphInfo* glyphs = rendererParameters.glyphs.Begin();
878 Vector2* glyphPositions = rendererParameters.positions.Begin();
882 // The ellipsis glyph has to fit in the place where the last glyph(s) is(are) removed.
885 const GlyphInfo& glyphToRemove = *( glyphs + index );
887 if( 0u != glyphToRemove.fontId )
889 // i.e. The font id of the glyph shaped from the '\n' character is zero.
891 // Need to reshape the glyph as the font may be different in size.
892 const GlyphInfo& ellipsisGlyph = fontClient.GetEllipsisGlyph( fontClient.GetPointSize( glyphToRemove.fontId ) );
896 const Vector2& position = *( glyphPositions + index );
898 // Calculates the penY of the current line. It will be used to position the ellipsis glyph.
901 // Calculates the first penX which will be used if rtl text is elided.
902 firstPenX = position.x - glyphToRemove.xBearing;
903 if( firstPenX < -ellipsisGlyph.xBearing )
905 // Avoids to exceed the bounding box when rtl text is elided.
906 firstPenX = -ellipsisGlyph.xBearing;
909 removedGlypsWidth = -ellipsisGlyph.xBearing;
914 removedGlypsWidth += std::min( glyphToRemove.advance, ( glyphToRemove.xBearing + glyphToRemove.width ) );
916 // Calculate the width of the ellipsis glyph and check if it fits.
917 const float ellipsisGlyphWidth = ellipsisGlyph.width + ellipsisGlyph.xBearing;
918 if( ellipsisGlyphWidth < removedGlypsWidth )
920 GlyphInfo& glyphInfo = *( glyphs + index );
921 Vector2& position = *( glyphPositions + index );
922 position.x -= ( 0.f > glyphInfo.xBearing ) ? glyphInfo.xBearing : 0.f;
924 // Replace the glyph by the ellipsis glyph.
925 glyphInfo = ellipsisGlyph;
927 // Update the isEmoji vector
928 isEmoji[index] = false;
930 // Change the 'x' and 'y' position of the ellipsis glyph.
932 if( position.x > firstPenX )
934 position.x = firstPenX + removedGlypsWidth - ellipsisGlyphWidth;
937 position.x += ellipsisGlyph.xBearing;
952 // No space for the ellipsis.
955 ++numberOfRemovedGlyphs;
958 // Set the final number of glyphs
959 finalNumberOfGlyphs = line.glyphRun.numberOfGlyphs - numberOfRemovedGlyphs;
963 // Resize the number of glyphs/positions
964 rendererParameters.glyphs.Resize( finalNumberOfGlyphs );
965 rendererParameters.positions.Resize( finalNumberOfGlyphs );
967 // Remove from the embedded items those exceding the last laid out glyph.
968 embeddedItemLayout.Erase( std::remove_if( embeddedItemLayout.Begin(),
969 embeddedItemLayout.End(),
970 [finalNumberOfGlyphs]( const EmbeddedItemInfo& item )
972 return item.glyphIndex >= finalNumberOfGlyphs;
974 embeddedItemLayout.End() );
980 Size LayoutText( const RendererParameters& textParameters, TextAbstraction::TextRenderer::Parameters& rendererParameters,
981 Vector<EmbeddedItemInfo>& embeddedItemLayout, InternalDataModel& internalDataModel )
983 ////////////////////////////////////////////////////////////////////////////////
985 ////////////////////////////////////////////////////////////////////////////////
986 Text::ModelPtr& textModel = internalDataModel.textModel;
987 Text::Layout::Engine& layoutEngine = internalDataModel.layoutEngine;
988 FontClient& fontClient = internalDataModel.fontClient;
989 const Length numberOfGlyphs = internalDataModel.numberOfGlyphs;
990 const bool isTextMirrored = internalDataModel.isTextMirrored;
991 const Vector<Character>& mirroredUtf32Characters = internalDataModel.mirroredUtf32Characters;
992 const Length numberOfCharacters = internalDataModel.numberOfCharacters;
994 Layout::Type layout = Layout::SINGLELINE;
996 Property::Value layoutStr( textParameters.layout );
997 GetLayoutEnumeration( layoutStr, layout );
999 // Whether the layout is multi-line.
1000 const Text::Layout::Engine::Type horizontalLayout = ( Layout::MULTILINE == layout ) ? Text::Layout::Engine::MULTI_LINE_BOX : Text::Layout::Engine::SINGLE_LINE_BOX;
1001 layoutEngine.SetLayout( horizontalLayout ); // TODO: multi-line.
1003 // Set minimun line size
1004 layoutEngine.SetDefaultLineSize( textParameters.minLineSize );
1006 // Whether the layout is circular.
1007 const bool isCircularTextLayout = (Layout::CIRCULAR == layout);
1008 const bool isClockwise = isCircularTextLayout && ( 0.f < textParameters.incrementAngle );
1010 // Calculates the max ascender or the max descender.
1011 // Is used to calculate the radius of the base line of the text.
1012 float maxAscenderDescender = 0.f;
1013 if( isCircularTextLayout )
1015 FontId currentFontId = 0u;
1016 for( const auto& glyph : rendererParameters.glyphs )
1018 if( currentFontId != glyph.fontId )
1020 currentFontId = glyph.fontId;
1021 FontMetrics metrics;
1022 fontClient.GetFontMetrics(currentFontId, metrics);
1023 maxAscenderDescender = std::max( maxAscenderDescender, isClockwise ? metrics.ascender : metrics.descender );
1027 const unsigned int radius = textParameters.radius - static_cast<unsigned int>( maxAscenderDescender );
1029 // Set the layout parameters.
1030 internalDataModel.textLayoutArea = Size( static_cast<float>( textParameters.textWidth ),
1031 static_cast<float>( textParameters.textHeight ) );
1033 if( isCircularTextLayout )
1035 // In a circular layout, the length of the text area depends on the radius.
1036 rendererParameters.radius = radius;
1037 internalDataModel.textLayoutArea.width = fabs( Radian( Degree( textParameters.incrementAngle ) ) * static_cast<float>( rendererParameters.radius ) );
1039 // Resize the vector of positions to have the same size than the vector of glyphs.
1040 rendererParameters.positions.Resize( numberOfGlyphs );
1042 textModel->mLineWrapMode = LineWrap::WORD;
1043 textModel->mIgnoreSpacesAfterText = false;
1044 textModel->mMatchSystemLanguageDirection = false;
1045 Text::Layout::Parameters layoutParameters( internalDataModel.textLayoutArea,
1049 // Whether the last character is a new paragraph character.
1050 const Vector<Character>& textToShape = isTextMirrored ? mirroredUtf32Characters : textModel->mLogicalModel->mText;
1051 layoutParameters.isLastNewParagraph = TextAbstraction::IsNewParagraph( textToShape[numberOfCharacters - 1u] );
1053 // The initial glyph and the number of glyphs to layout.
1054 layoutParameters.startGlyphIndex = 0u;
1055 layoutParameters.numberOfGlyphs = numberOfGlyphs;
1056 layoutParameters.startLineIndex = 0u;
1057 layoutParameters.estimatedNumberOfLines = 1u;
1058 layoutParameters.interGlyphExtraAdvance = 0.f;
1060 // Update the visual model.
1062 bool isAutoScrollEnabled = false;
1063 layoutEngine.LayoutText( layoutParameters,
1065 textParameters.ellipsisEnabled,
1066 isAutoScrollEnabled );
1068 return newLayoutSize;
1073 Devel::PixelBuffer RenderText( const RendererParameters& textParameters, TextAbstraction::TextRenderer::Parameters& rendererParameters )
1075 ////////////////////////////////////////////////////////////////////////////////
1077 ////////////////////////////////////////////////////////////////////////////////
1079 rendererParameters.width = textParameters.textWidth;
1080 rendererParameters.height = textParameters.textHeight;
1082 TextAbstraction::TextRenderer renderer = TextAbstraction::TextRenderer::Get();
1083 return renderer.Render( rendererParameters );
1087 Devel::PixelBuffer Render( const RendererParameters& textParameters, Vector<EmbeddedItemInfo>& embeddedItemLayout )
1089 if( textParameters.text.empty() )
1091 Dali::Devel::PixelBuffer pixelBuffer = Dali::Devel::PixelBuffer::New( textParameters.textWidth,
1092 textParameters.textHeight,
1093 Dali::Pixel::RGBA8888 );
1095 const unsigned int bufferSize = textParameters.textWidth * textParameters.textHeight * Dali::Pixel::GetBytesPerPixel(Dali::Pixel::RGBA8888);
1096 unsigned char* buffer = pixelBuffer.GetBuffer();
1097 memset(buffer, 0, bufferSize);
1102 FontClient fontClient = FontClient::Get();
1104 // Use this to access FontClient i.e. to get down-scaled Emoji metrics.
1105 metrics = Metrics::New( fontClient );
1107 Text::ModelPtr textModel = Text::Model::New();
1108 InternalDataModel internalData( fontClient, metrics, textModel );
1110 TextAbstraction::TextRenderer::Parameters rendererParameters( internalData.textModel->mVisualModel->mGlyphs,
1111 internalData.textModel->mVisualModel->mGlyphPositions,
1112 internalData.textModel->mVisualModel->mColors,
1113 internalData.textModel->mVisualModel->mColorIndices,
1114 internalData.blendingMode,
1115 internalData.isEmoji );
1118 rendererParameters.width = textParameters.textWidth;
1119 rendererParameters.height = textParameters.textHeight;
1120 rendererParameters.pixelFormat = TextAbstraction::TextRenderer::Parameters::RGBA8888; // @note: At the moment all textures are generated RGBA8888
1123 ////////////////////////////////////////////////////////////////////////////////
1124 // Process the markup string if the mark-up processor is enabled.
1125 ////////////////////////////////////////////////////////////////////////////////
1126 ShapeTextPreprocess( textParameters, rendererParameters, internalData );
1128 ////////////////////////////////////////////////////////////////////////////////
1129 // Retrieve the glyphs. Text shaping
1130 ////////////////////////////////////////////////////////////////////////////////
1131 ShapeText( rendererParameters, embeddedItemLayout, internalData );
1134 ////////////////////////////////////////////////////////////////////////////////
1135 // Retrieve the glyph's metrics.
1136 ////////////////////////////////////////////////////////////////////////////////
1138 metrics->GetGlyphMetrics( rendererParameters.glyphs.Begin(), internalData.numberOfGlyphs );
1140 ////////////////////////////////////////////////////////////////////////////////
1141 // Set the color runs in glyphs.
1142 ////////////////////////////////////////////////////////////////////////////////
1143 SetColorSegmentation( textParameters, internalData );
1146 ////////////////////////////////////////////////////////////////////////////////
1147 // Set the isEmoji Vector
1148 ////////////////////////////////////////////////////////////////////////////////
1149 SetEmojiVector( internalData );
1151 ////////////////////////////////////////////////////////////////////////////////
1153 ////////////////////////////////////////////////////////////////////////////////
1154 Size newLayoutSize = LayoutText( textParameters, rendererParameters, embeddedItemLayout, internalData );
1156 ////////////////////////////////////////////////////////////////////////////////
1158 ////////////////////////////////////////////////////////////////////////////////
1159 Align( textParameters, rendererParameters, embeddedItemLayout, internalData, newLayoutSize );
1162 ////////////////////////////////////////////////////////////////////////////////
1163 // Ellipsis the text.
1164 ////////////////////////////////////////////////////////////////////////////////
1165 Ellipsis( textParameters, rendererParameters, embeddedItemLayout, internalData );
1167 ////////////////////////////////////////////////////////////////////////////////
1169 ////////////////////////////////////////////////////////////////////////////////
1170 return RenderText( textParameters, rendererParameters );
1174 Devel::PixelBuffer CreateShadow( const ShadowParameters& shadowParameters )
1176 // The size of the pixel data.
1177 const int width = static_cast<int>(shadowParameters.input.GetWidth());
1178 const int height = static_cast<int>(shadowParameters.input.GetHeight());
1180 // The shadow's offset.
1181 const int xOffset = static_cast<int>( shadowParameters.offset.x );
1182 const int yOffset = static_cast<int>( shadowParameters.offset.y );
1184 // The size in bytes of the pixel of the input's buffer.
1185 const Pixel::Format inputFormat = shadowParameters.input.GetPixelFormat();
1186 const unsigned int inputPixelSize = Pixel::GetBytesPerPixel( inputFormat );
1187 const bool isA8 = Pixel::A8 == inputFormat;
1189 // Creates the output pixel buffer.
1190 Devel::PixelBuffer outputPixelBuffer = Devel::PixelBuffer::New(width, height, Pixel::RGBA8888);
1192 // Clear the output buffer
1193 unsigned char* outputPixelBufferPtr = outputPixelBuffer.GetBuffer();
1194 memset(outputPixelBufferPtr, 0, width * height * Pixel::GetBytesPerPixel(Pixel::RGBA8888));
1196 // Gets the buffer of the input pixel buffer.
1197 const unsigned char* const inputPixelBuffer = shadowParameters.input.GetBuffer();
1199 float textColor[4u];
1202 memcpy(textColor, shadowParameters.textColor.AsFloat(), 4u * sizeof(float));
1204 const float* const shadowColor = shadowParameters.color.AsFloat();
1206 // Traverse the input pixel buffer and write the text on the foreground and the shadow on the background.
1207 for (int rowIndex = 0; rowIndex < height; ++rowIndex)
1209 // Calculates the rowIndex to the input pixel buffer for the shadow and whether it's within the boundaries.
1210 const int yOffsetIndex = rowIndex - yOffset;
1211 const bool isValidRowIndex = ((yOffsetIndex >= 0) && (yOffsetIndex < height));
1213 const int rows = rowIndex * width;
1214 const int offsetRows = yOffsetIndex * width;
1215 for (int columnIndex = 0; columnIndex < width; ++columnIndex)
1217 // Index to the input buffer to retrieve the alpha value of the foreground text.
1218 const unsigned int index = inputPixelSize * static_cast<unsigned int>(rows + columnIndex);
1220 // Build the index to the input buffer to retrieve the alpha value of the background shadow.
1221 unsigned int shadowIndex = 0u;
1222 bool isValidShadowIndex = false;
1223 if (isValidRowIndex)
1225 const int xOffsetIndex = columnIndex - xOffset;
1226 isValidShadowIndex = ((xOffsetIndex >= 0) && (xOffsetIndex < width));
1228 if (isValidShadowIndex)
1230 shadowIndex = inputPixelSize * static_cast<unsigned int>(offsetRows + xOffsetIndex);
1234 // If the input buffer is an alpha mask, retrieve the values for the foreground text and the background shadow.
1235 // If not retrieve the color.
1236 float inputShadowOffsetAlphaValue = 1.f;
1237 float inputAlphaValue = 1.f;
1240 // Retrieve the alpha value for the shadow.
1241 inputShadowOffsetAlphaValue = isValidShadowIndex ? (static_cast<float>(*(inputPixelBuffer + shadowIndex)) / 255.f) : 0.f;
1243 // Retrieve the alpha value for the text.
1244 inputAlphaValue = static_cast<float>(*(inputPixelBuffer + index)) / 255.f;
1248 // The input buffer is not an alpha mask. Retrieve the color.
1249 textColor[0u] = TO_FLOAT * static_cast<float>( *(inputPixelBuffer + index + 0u) );
1250 textColor[1u] = TO_FLOAT * static_cast<float>( *(inputPixelBuffer + index + 1u) );
1251 textColor[2u] = TO_FLOAT * static_cast<float>( *(inputPixelBuffer + index + 2u) );
1252 textColor[3u] = TO_FLOAT * static_cast<float>( *(inputPixelBuffer + index + 3u) );
1253 inputAlphaValue = textColor[3u];
1254 inputShadowOffsetAlphaValue = isValidShadowIndex ? TO_FLOAT * static_cast<float>( *(inputPixelBuffer + shadowIndex + 3u) ) : 0.f;
1257 // Build the output color.
1258 float outputColor[4u];
1260 if( shadowParameters.blendShadow )
1262 // Blend the shadow's color with the text's color on top
1263 const float textAlpha = textColor[3u] * inputAlphaValue;
1264 const float shadowAlpha = shadowColor[3u] * inputShadowOffsetAlphaValue;
1266 // Blends the alpha.
1267 outputColor[3u] = 1.f - ((1.f - textAlpha) * (1.f - shadowAlpha));
1268 const bool isOutputAlphaZero = outputColor[3u] < Dali::Math::MACHINE_EPSILON_1000;
1269 if( isOutputAlphaZero )
1271 std::fill( outputColor, outputColor + 4u, 0.f );
1275 // Blends the RGB components.
1276 float shadowComponent = 0.f;
1277 float textComponent = 0.f;
1279 shadowComponent = shadowColor[0u] * inputShadowOffsetAlphaValue;
1280 textComponent = textColor[0u] * inputAlphaValue;
1281 outputColor[0u] = (textComponent * textAlpha / outputColor[3u]) + (shadowComponent * shadowAlpha * (1.f - textAlpha) / outputColor[3u]);
1283 shadowComponent = shadowColor[1u] * inputShadowOffsetAlphaValue;
1284 textComponent = textColor[1u] * inputAlphaValue;
1285 outputColor[1u] = (textComponent * textAlpha / outputColor[3u]) + (shadowComponent * shadowAlpha * (1.f - textAlpha) / outputColor[3u]);
1287 shadowComponent = shadowColor[2u] * inputShadowOffsetAlphaValue;
1288 textComponent = textColor[2u] * inputAlphaValue;
1289 outputColor[2u] = (textComponent * textAlpha / outputColor[3u]) + (shadowComponent * shadowAlpha * (1.f - textAlpha) / outputColor[3u]);
1295 std::fill( outputColor, outputColor + 4u, 0.f );
1297 const float textAlpha = textColor[3u];
1298 const float shadowAlpha = shadowColor[3u] * inputShadowOffsetAlphaValue;
1300 // Write shadow first.
1301 if( shadowAlpha > Dali::Math::MACHINE_EPSILON_1000 )
1303 outputColor[0u] = shadowColor[0u] * inputShadowOffsetAlphaValue;
1304 outputColor[1u] = shadowColor[1u] * inputShadowOffsetAlphaValue;
1305 outputColor[2u] = shadowColor[2u] * inputShadowOffsetAlphaValue;
1306 outputColor[3u] = shadowAlpha;
1309 // Write character on top.
1310 if( textAlpha > Dali::Math::MACHINE_EPSILON_1000 )
1312 outputColor[0u] = textColor[0u];
1313 outputColor[1u] = textColor[1u];
1314 outputColor[2u] = textColor[2u];
1315 outputColor[3u] = textAlpha;
1319 // Write the color into the output pixel buffer.
1320 const unsigned int outputIndex = 4u * (rows + columnIndex);
1321 *(outputPixelBufferPtr + outputIndex + 0u) = static_cast<unsigned char>( TO_UCHAR * outputColor[0u] );
1322 *(outputPixelBufferPtr + outputIndex + 1u) = static_cast<unsigned char>( TO_UCHAR * outputColor[1u] );
1323 *(outputPixelBufferPtr + outputIndex + 2u) = static_cast<unsigned char>( TO_UCHAR * outputColor[2u] );
1324 *(outputPixelBufferPtr + outputIndex + 3u) = static_cast<unsigned char>( TO_UCHAR * outputColor[3u] );
1328 // Returns the pixel buffer.
1329 return outputPixelBuffer;
1332 Devel::PixelBuffer ConvertToRgba8888(Devel::PixelBuffer pixelBuffer, const Vector4& color, bool multiplyByAlpha)
1334 if (Dali::Pixel::A8 != pixelBuffer.GetPixelFormat())
1340 const unsigned int width = pixelBuffer.GetWidth();
1341 const unsigned int height = pixelBuffer.GetHeight();
1342 Devel::PixelBuffer newPixelBuffer = Devel::PixelBuffer::New( width, height, Dali::Pixel::RGBA8888 );
1344 unsigned char* dstBuffer = newPixelBuffer.GetBuffer();
1345 const unsigned char* const srcBuffer = pixelBuffer.GetBuffer();
1347 const unsigned char r = static_cast<unsigned char>( TO_UCHAR * color.r );
1348 const unsigned char g = static_cast<unsigned char>( TO_UCHAR * color.g );
1349 const unsigned char b = static_cast<unsigned char>( TO_UCHAR * color.b );
1351 unsigned char dstColor[4];
1352 for( unsigned int j = 0u; j < height; ++j )
1354 const unsigned int lineIndex = j * width;
1355 for( unsigned int i=0u; i < width; ++i )
1357 const unsigned int srcIndex = lineIndex + i;
1359 const float srcAlpha = static_cast<float>( *( srcBuffer + srcIndex ) );
1361 if( multiplyByAlpha )
1363 dstColor[0u] = static_cast<unsigned char>( srcAlpha * color.r );
1364 dstColor[1u] = static_cast<unsigned char>( srcAlpha * color.g );
1365 dstColor[2u] = static_cast<unsigned char>( srcAlpha * color.b );
1366 dstColor[3u] = static_cast<unsigned char>( srcAlpha * color.a );
1373 dstColor[3u] = static_cast<unsigned char>( srcAlpha );
1376 const unsigned int dstIndex = srcIndex * 4u;
1377 memcpy( dstBuffer + dstIndex, dstColor, 4u );
1381 return newPixelBuffer;
1384 void UpdateBuffer(Devel::PixelBuffer src, Devel::PixelBuffer dst, unsigned int x, unsigned int y, bool blend)
1386 const Dali::Pixel::Format pixelFormat = dst.GetPixelFormat();
1387 if( src.GetPixelFormat() != pixelFormat )
1389 DALI_LOG_ERROR("PixelBuffer::SetBuffer. The pixel format of the new data must be the same of the current pixel buffer.");
1393 const unsigned int srcWidth = src.GetWidth();
1394 const unsigned int srcHeight = src.GetHeight();
1395 const unsigned int dstWidth = dst.GetWidth();
1396 const unsigned int dstHeight = dst.GetHeight();
1398 if( ( x > dstWidth ) ||
1399 ( y > dstHeight ) ||
1400 ( x + srcWidth > dstWidth ) ||
1401 ( y + srcHeight > dstHeight ) )
1403 DALI_LOG_ERROR("PixelBuffer::SetBuffer. The source pixel buffer is out of the boundaries of the destination pixel buffer.");
1407 const unsigned int bytesPerPixel = Dali::Pixel::GetBytesPerPixel(pixelFormat);
1408 if( bytesPerPixel == 0u || bytesPerPixel == 12u || bytesPerPixel == 24u )
1412 const unsigned int alphaIndex = bytesPerPixel - 1u;
1414 const unsigned char* const srcBuffer = src.GetBuffer();
1415 unsigned char* dstBuffer = dst.GetBuffer();
1419 const unsigned int currentLineSize = dstWidth * bytesPerPixel;
1420 const unsigned int newLineSize = srcWidth * bytesPerPixel;
1421 unsigned char* currentBuffer = dstBuffer + (y * dstWidth + x) * bytesPerPixel;
1422 for (unsigned int j = 0u; j < srcHeight; ++j)
1424 memcpy(currentBuffer + j * currentLineSize, srcBuffer + j * newLineSize, newLineSize);
1429 float outputColor[4u];
1431 // Blend the src pixel buffer with the dst pixel buffer as background.
1433 // fgColor, fgAlpha, bgColor, bgAlpha
1435 // alpha = 1 - ( 1 - fgAlpha ) * ( 1 - bgAlpha )
1436 // color = ( fgColor * fgAlpha / alpha ) + ( bgColor * bgAlpha * ( 1 - fgAlpha ) / alpha )
1438 // Jump till the 'x,y' position
1439 const unsigned int dstWidthBytes = dstWidth * bytesPerPixel;
1440 dstBuffer += ( y * dstWidthBytes + x * bytesPerPixel );
1442 for (unsigned int j = 0u; j < srcHeight; ++j)
1444 const unsigned int srcLineIndex = j * srcWidth;
1445 for (unsigned int i = 0u; i < srcWidth; ++i)
1447 const float srcAlpha = TO_FLOAT * static_cast<float>( *( srcBuffer + bytesPerPixel * ( srcLineIndex + i ) + alphaIndex ) );
1448 const float dstAlpha = TO_FLOAT * static_cast<float>( *(dstBuffer + i*bytesPerPixel + alphaIndex) );
1450 // Blends the alpha channel.
1451 const float oneMinusSrcAlpha = 1.f - srcAlpha;
1452 outputColor[alphaIndex] = 1.f - (oneMinusSrcAlpha * (1.f - dstAlpha));
1454 // Blends the RGB channels.
1455 const bool isOutputAlphaZero = outputColor[alphaIndex] < Dali::Math::MACHINE_EPSILON_1000;
1456 if( isOutputAlphaZero )
1458 std::fill( outputColor, outputColor + bytesPerPixel, 0.f );
1462 const float srcAlphaOverOutputAlpha = srcAlpha / outputColor[alphaIndex]; // fgAlpha / alpha
1463 const float dstAlphaOneMinusSrcAlphaOverOutputAlpha = dstAlpha * oneMinusSrcAlpha / outputColor[alphaIndex]; // bgAlpha * ( 1 - fgAlpha ) / alpha
1464 for (unsigned int index = 0u; index < alphaIndex; ++index)
1466 const float dstComponent = TO_FLOAT * static_cast<float>( *( dstBuffer + i * bytesPerPixel + index ) ) * dstAlpha;
1467 const float srcComponent = TO_FLOAT * static_cast<float>(*(srcBuffer + bytesPerPixel * (srcLineIndex + i) + index) ) * srcAlpha;
1468 outputColor[index] = ( srcComponent * srcAlphaOverOutputAlpha ) + ( dstComponent * dstAlphaOneMinusSrcAlphaOverOutputAlpha );
1472 for (unsigned int index = 0u; index < bytesPerPixel; ++index)
1474 *(dstBuffer + i * bytesPerPixel + index) = static_cast<unsigned char>( TO_UCHAR * outputColor[index] );
1478 dstBuffer += dstWidthBytes;
1484 Dali::Property::Array RenderForLastIndex( RendererParameters& textParameters )
1486 Property::Array offsetValues;
1487 if( textParameters.text.empty() )
1489 return offsetValues;
1491 FontClient fontClient = FontClient::Get();
1493 metrics = Metrics::New( fontClient );
1495 Text::ModelPtr textModel = Text::Model::New();
1496 InternalDataModel internalData( fontClient, metrics, textModel );
1498 TextAbstraction::TextRenderer::Parameters rendererParameters( textModel->mVisualModel->mGlyphs,
1499 textModel->mVisualModel->mGlyphPositions,
1500 textModel->mVisualModel->mColors,
1501 textModel->mVisualModel->mColorIndices,
1502 internalData.blendingMode,
1503 internalData.isEmoji );
1506 rendererParameters.width = textParameters.textWidth;
1507 rendererParameters.height = textParameters.textHeight;
1509 ////////////////////////////////////////////////////////////////////////////////
1510 // Process the markup string if the mark-up processor is enabled.
1511 ////////////////////////////////////////////////////////////////////////////////
1512 ShapeTextPreprocess( textParameters, rendererParameters, internalData );
1514 ////////////////////////////////////////////////////////////////////////////////
1515 // Retrieve the glyphs. Text shaping
1516 ////////////////////////////////////////////////////////////////////////////////
1517 Dali::Vector<Dali::Toolkit::DevelText::EmbeddedItemInfo> embeddedItemLayout;
1518 ShapeText( rendererParameters, embeddedItemLayout, internalData );
1521 ////////////////////////////////////////////////////////////////////////////////
1522 // Retrieve the glyph's metrics.
1523 ////////////////////////////////////////////////////////////////////////////////
1524 metrics->GetGlyphMetrics( rendererParameters.glyphs.Begin(), internalData.numberOfGlyphs );
1527 ////////////////////////////////////////////////////////////////////////////////
1529 ////////////////////////////////////////////////////////////////////////////////
1530 int boundingBox = textParameters.textHeight;
1531 textParameters.textHeight = MAX_INT; // layout for the entire area.
1532 LayoutText( textParameters, rendererParameters, embeddedItemLayout, internalData );
1534 ////////////////////////////////////////////////////////////////////////////////
1535 // Calculation last character index
1536 ////////////////////////////////////////////////////////////////////////////////
1537 Vector<LineRun>& lines = internalData.textModel->mVisualModel->mLines;
1538 unsigned int numberOfLines = lines.Count();
1539 int numberOfCharacters = 0;
1541 float lineSize = internalData.layoutEngine.GetDefaultLineSize();
1542 float lineOffset = 0.f;
1543 for( unsigned int index = 0u; index < numberOfLines; ++index )
1545 const LineRun& line = *( lines.Begin() + index );
1546 numberOfCharacters += line.characterRun.numberOfCharacters;
1548 lineOffset = lineSize > 0.f ? lineSize : ( line.ascender + -line.descender );
1550 if( ( penY + lineOffset ) > boundingBox )
1552 offsetValues.PushBack( numberOfCharacters );
1558 // add remain character index
1559 offsetValues.PushBack( numberOfCharacters );
1562 return offsetValues;
1566 Dali::Property::Array GetLastCharacterIndex( RendererParameters& textParameters )
1568 Dali::Property::Array offsetValues = Toolkit::DevelText::RenderForLastIndex( textParameters );
1569 return offsetValues;
1573 } // namespace DevelText
1575 } // namespace Toolkit