2 * Copyright (c) 2022 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/scripting/enum-helper.h>
23 #include <dali/devel-api/text-abstraction/font-client.h>
24 #include <dali/devel-api/text-abstraction/text-renderer-layout-helper.h>
25 #include <dali/devel-api/text-abstraction/text-renderer.h>
26 #include <dali/integration-api/debug.h>
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/glyph-metrics-helper.h>
35 #include <dali-toolkit/internal/text/layouts/layout-engine.h>
36 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
37 #include <dali-toolkit/internal/text/markup-processor/markup-processor.h>
38 #include <dali-toolkit/internal/text/multi-language-support.h>
39 #include <dali-toolkit/internal/text/rendering/styles/character-spacing-helper-functions.h>
40 #include <dali-toolkit/internal/text/segmentation.h>
41 #include <dali-toolkit/internal/text/shaper.h>
42 #include <dali-toolkit/internal/text/text-enumerations-impl.h>
43 #include <dali-toolkit/internal/text/text-font-style.h>
44 #include <dali-toolkit/internal/text/text-model.h>
48 using namespace TextAbstraction;
59 * @brief The text's layout.
63 SINGLELINE, ///< The text is laid out on a single line.
64 MULTILINE, ///< The text is laid out in multiple lines.
65 CIRCULAR, ///< The text is laid out on a single line following a circular path.
70 namespace CircularAlignment
73 * @brief The enumerations for the circular alignment.
77 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).
78 CENTER, ///< The text is centered within the arc.
79 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 } // namespace CircularAlignment
84 const float TO_POINT_26_DOT_6 = 64.f;
85 const float TO_FLOAT = 1.f / 255.f;
86 const float TO_UCHAR = 255.f;
87 const bool RTL = true;
88 const float TWO_PI = 2.f * Dali::Math::PI; ///< 360 degrees in radians
89 const float RAD_135 = Math::PI_2 + Math::PI_4; ///< 135 degrees in radians;
90 const float RAD_225 = RAD_135 + Math::PI_2; ///< 225 degrees in radians;
91 const float RAD_270 = 3.f * Math::PI_2; ///< 270 degrees in radians;
92 const float RAD_315 = RAD_225 + Math::PI_2; ///< 315 degrees in radians;
93 const float MAX_INT = std::numeric_limits<int>::max();
95 DALI_ENUM_TO_STRING_TABLE_BEGIN(LAYOUT_TYPE)
96 DALI_ENUM_TO_STRING_WITH_SCOPE(DevelText::Layout, SINGLELINE)
97 DALI_ENUM_TO_STRING_WITH_SCOPE(DevelText::Layout, MULTILINE)
98 DALI_ENUM_TO_STRING_WITH_SCOPE(DevelText::Layout, CIRCULAR)
99 DALI_ENUM_TO_STRING_TABLE_END(LAYOUT_TYPE)
101 DALI_ENUM_TO_STRING_TABLE_BEGIN(CIRCULAR_ALIGNMENT_TYPE)
102 DALI_ENUM_TO_STRING_WITH_SCOPE(DevelText::CircularAlignment, BEGIN)
103 DALI_ENUM_TO_STRING_WITH_SCOPE(DevelText::CircularAlignment, CENTER)
104 DALI_ENUM_TO_STRING_WITH_SCOPE(DevelText::CircularAlignment, END)
105 DALI_ENUM_TO_STRING_TABLE_END(CIRCULAR_ALIGNMENT_TYPE)
107 struct InternalDataModel
109 InternalDataModel(FontClient& fontClient,
111 Text::ModelPtr textModel)
112 : fontClient(fontClient),
114 textModel(textModel),
115 numberOfCharacters{0u},
116 isTextMirrored{false},
119 layoutEngine.SetMetrics(metrics);
122 FontClient& fontClient;
124 Text::Layout::Engine layoutEngine; ///< The layout engine.
125 Text::ModelPtr textModel; ///< Pointer to the text's model.
126 Vector<ColorBlendingMode> blendingMode; ///< How embedded items and bitmap font glyphs are blended with color text.
127 Vector<bool> isEmoji; ///< Whether the glyph is an emoji.
129 Vector<Character> mirroredUtf32Characters; // The utf32Characters Characters but mirrored if there are RTL text.
131 Length numberOfCharacters; // The number of characters (not glyphs!).
132 bool isTextMirrored; // Whether the text has been mirrored.
134 Length numberOfGlyphs;
138 bool GetLayoutEnumeration(const Property::Value& propertyValue, DevelText::Layout::Type& layout)
140 return Scripting::GetEnumerationProperty(propertyValue, LAYOUT_TYPE_TABLE, LAYOUT_TYPE_TABLE_COUNT, layout);
143 bool GetCircularAlignmentEnumeration(const Property::Value& propertyValue, DevelText::CircularAlignment::Type& circularAlignment)
145 return Scripting::GetEnumerationProperty(propertyValue, CIRCULAR_ALIGNMENT_TYPE_TABLE, CIRCULAR_ALIGNMENT_TYPE_TABLE_COUNT, circularAlignment);
148 void ShapeTextPreprocess(const RendererParameters& textParameters, TextAbstraction::TextRenderer::Parameters& rendererParameters, InternalDataModel& internalDataModel)
150 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
151 const uint8_t* utf8 = NULL; // pointer to the first character of the text (encoded in utf8)
152 Length textSize = 0u; // The length of the utf8 string.
154 Length& numberOfCharacters = internalDataModel.numberOfCharacters;
155 Vector<Character>& mirroredUtf32Characters = internalDataModel.mirroredUtf32Characters;
156 Text::ModelPtr& textModel = internalDataModel.textModel;
158 Vector<Character>& utf32Characters = textModel->mLogicalModel->mText; // Characters encoded in utf32.
159 Vector<LineBreakInfo>& lineBreakInfo = textModel->mLogicalModel->mLineBreakInfo; // The line break info.
160 Vector<ScriptRun>& scripts = textModel->mLogicalModel->mScriptRuns; // Charactes's script.
161 Vector<FontDescriptionRun>& fontDescriptionRuns = textModel->mLogicalModel->mFontDescriptionRuns; // Desired font descriptions.
162 Vector<FontRun>& validFonts = textModel->mLogicalModel->mFontRuns; // Validated fonts.
163 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = textModel->mLogicalModel->mBidirectionalParagraphInfo; // The bidirectional info per paragraph.
164 Vector<CharacterDirection>& directions = textModel->mLogicalModel->mCharacterDirections; // Character's directions.
165 Vector<ColorRun>& colorRuns = textModel->mLogicalModel->mColorRuns; // colors of the text.
167 // the default font's description.
168 FontDescription defaultFontDescription;
169 PointSize26Dot6 defaultPointSize = FontClient::DEFAULT_POINT_SIZE;
171 ////////////////////////////////////////////////////////////////////////////////
172 // Process the markup string if the mark-up processor is enabled.
173 ////////////////////////////////////////////////////////////////////////////////
175 MarkupProcessData markupProcessData(colorRuns,
177 textModel->mLogicalModel->mEmbeddedItems,
178 textModel->mLogicalModel->mAnchors,
179 textModel->mLogicalModel->mUnderlinedCharacterRuns,
180 textModel->mLogicalModel->mBackgroundColorRuns,
181 textModel->mLogicalModel->mStrikethroughCharacterRuns,
182 textModel->mLogicalModel->mBoundedParagraphRuns,
183 textModel->mLogicalModel->mCharacterSpacingCharacterRuns);
185 if(textParameters.markupEnabled)
187 MarkupPropertyData markupPropertyData(Color::MEDIUM_BLUE, Color::DARK_MAGENTA);
189 ProcessMarkupString(textParameters.text, markupPropertyData, markupProcessData);
190 textSize = markupProcessData.markupProcessedText.size();
192 // This is a bit horrible but std::string returns a (signed) char*
193 utf8 = reinterpret_cast<const uint8_t*>(markupProcessData.markupProcessedText.c_str());
197 textSize = textParameters.text.size();
199 // This is a bit horrible but std::string returns a (signed) char*
200 utf8 = reinterpret_cast<const uint8_t*>(textParameters.text.c_str());
203 ////////////////////////////////////////////////////////////////////////////////
204 // Convert from utf8 to utf32
205 ////////////////////////////////////////////////////////////////////////////////
207 utf32Characters.Resize(textSize);
209 // Transform a text array encoded in utf8 into an array encoded in utf32.
210 // It returns the actual number of characters.
211 numberOfCharacters = Utf8ToUtf32(utf8, textSize, utf32Characters.Begin());
212 utf32Characters.Resize(numberOfCharacters);
214 ////////////////////////////////////////////////////////////////////////////////
215 // Retrieve the Line and Word Break Info.
216 ////////////////////////////////////////////////////////////////////////////////
218 lineBreakInfo.Resize(numberOfCharacters, LINE_NO_BREAK);
220 SetLineBreakInfo(utf32Characters,
225 ////////////////////////////////////////////////////////////////////////////////
226 // Retrieve the script runs.
227 ////////////////////////////////////////////////////////////////////////////////
229 multilanguageSupport.SetScripts(utf32Characters,
234 // Check if there are emojis.
235 // If there are an RGBA8888 pixel format is needed.
236 for(const auto& run : scripts)
238 if(run.script == TextAbstraction::Script::EMOJI)
240 rendererParameters.pixelFormat = TextAbstraction::TextRenderer::Parameters::RGBA8888;
245 ////////////////////////////////////////////////////////////////////////////////
246 // Retrieve the font runs.
247 ////////////////////////////////////////////////////////////////////////////////
249 // Set the description font run with the given text parameters.
250 FontDescriptionRun fontDescriptionRun;
251 fontDescriptionRun.characterRun.characterIndex = 0u;
252 fontDescriptionRun.characterRun.numberOfCharacters = numberOfCharacters;
254 fontDescriptionRun.familyLength = 0u;
255 fontDescriptionRun.familyName = nullptr;
256 fontDescriptionRun.familyDefined = !textParameters.fontFamily.empty();
257 if(fontDescriptionRun.familyDefined)
259 // The allocated memory will be freed when the logical model is destroyed.
260 fontDescriptionRun.familyLength = textParameters.fontFamily.size();
261 fontDescriptionRun.familyName = new char[fontDescriptionRun.familyLength];
262 memcpy(fontDescriptionRun.familyName, textParameters.fontFamily.c_str(), fontDescriptionRun.familyLength);
265 fontDescriptionRun.weightDefined = !textParameters.fontWeight.empty();
266 if(fontDescriptionRun.weightDefined)
268 fontDescriptionRun.weight = StringToWeight(textParameters.fontWeight.c_str());
271 fontDescriptionRun.widthDefined = !textParameters.fontWidth.empty();
272 if(fontDescriptionRun.widthDefined)
274 fontDescriptionRun.width = StringToWidth(textParameters.fontWidth.c_str());
277 fontDescriptionRun.slantDefined = !textParameters.fontSlant.empty();
278 if(fontDescriptionRun.slantDefined)
280 fontDescriptionRun.slant = StringToSlant(textParameters.fontSlant.c_str());
283 fontDescriptionRun.sizeDefined = !EqualsZero(textParameters.fontSize);
284 if(fontDescriptionRun.sizeDefined)
286 fontDescriptionRun.size = static_cast<unsigned int>(textParameters.fontSize * TO_POINT_26_DOT_6);
289 fontDescriptionRuns.PushBack(fontDescriptionRun);
291 // Validates the fonts. If there is a character with no assigned font it sets a default one.
292 // After this call, fonts are validated.
293 multilanguageSupport.ValidateFonts(utf32Characters,
296 defaultFontDescription,
303 ////////////////////////////////////////////////////////////////////////////////
304 // Retrieve the Bidirectional info.
305 ////////////////////////////////////////////////////////////////////////////////
307 bidirectionalInfo.Reserve(1u);
309 SetBidirectionalInfo(utf32Characters,
316 const bool hasBidirectionalText = 0u != bidirectionalInfo.Count();
317 if(hasBidirectionalText)
319 // Only set the character directions if there is right to left characters.
320 GetCharactersDirection(bidirectionalInfo,
326 // This paragraph has right to left text. Some characters may need to be mirrored.
327 // TODO: consider if the mirrored string can be stored as well.
329 internalDataModel.isTextMirrored = GetMirroredText(utf32Characters,
334 mirroredUtf32Characters);
338 void ShapeText(TextAbstraction::TextRenderer::Parameters& rendererParameters, Vector<EmbeddedItemInfo>& embeddedItemLayout, InternalDataModel& internalDataModel)
340 Vector<GlyphIndex> newParagraphGlyphs; // Glyphs for the new paragraph characters.
341 const Length numberOfCharacters = internalDataModel.numberOfCharacters;
342 const bool isTextMirrored = internalDataModel.isTextMirrored;
343 Text::ModelPtr& textModel = internalDataModel.textModel;
344 const Vector<Character>& mirroredUtf32Characters = internalDataModel.mirroredUtf32Characters;
345 FontClient& fontClient = internalDataModel.fontClient;
346 const Vector<Character>& utf32Characters = textModel->mLogicalModel->mText; // Characters encoded in utf32.
347 const Vector<LineBreakInfo>& lineBreakInfo = textModel->mLogicalModel->mLineBreakInfo; // The line break info.
348 const Vector<ScriptRun>& scripts = textModel->mLogicalModel->mScriptRuns; // Charactes's script.
349 const Vector<FontRun>& validFonts = textModel->mLogicalModel->mFontRuns; // Validated fonts.
351 Vector<CharacterIndex>& glyphsToCharacters = textModel->mVisualModel->mGlyphsToCharacters; // Glyphs to character map.
352 Vector<Length>& charactersPerGlyph = textModel->mVisualModel->mCharactersPerGlyph; // Number of characters per glyph.
354 ////////////////////////////////////////////////////////////////////////////////
355 // Retrieve the glyphs. Text shaping
356 ////////////////////////////////////////////////////////////////////////////////
358 const Vector<Character>& textToShape = isTextMirrored ? mirroredUtf32Characters : utf32Characters;
360 newParagraphGlyphs.Reserve(1u);
363 ShapeText(textToShape,
370 rendererParameters.glyphs,
375 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
376 textModel->mVisualModel->CreateGlyphsPerCharacterTable(0u, 0u, numberOfCharacters);
377 textModel->mVisualModel->CreateCharacterToGlyphTable(0u, 0u, numberOfCharacters);
379 internalDataModel.numberOfGlyphs = rendererParameters.glyphs.Count();
381 // Once the text has been shaped and the glyphs created it's possible to replace the font id of those glyphs
382 // that represent an image or an item and create the embedded item layout info.
383 // Note: the position of the embedded item can't be set until the text is laid-out.
384 embeddedItemLayout.Reserve(textModel->mLogicalModel->mEmbeddedItems.Count());
385 for(const auto& item : textModel->mLogicalModel->mEmbeddedItems)
387 // Get the glyph that matches with the character index.
388 const GlyphIndex glyphIndex = textModel->mVisualModel->mCharactersToGlyph[item.characterIndex];
389 GlyphInfo& glyph = rendererParameters.glyphs[glyphIndex];
392 Pixel::Format pixelFormat = Pixel::A8;
393 TextAbstraction::FontClient::EmbeddedItemDescription description = {std::string(item.url, item.urlLength), item.width, item.height, item.colorBlendingMode};
394 glyph.index = fontClient.CreateEmbeddedItem(description, pixelFormat); // Set here an index to an item.
396 if((Pixel::RGBA8888 == pixelFormat) || (Pixel::BGRA8888 == pixelFormat))
398 rendererParameters.pixelFormat = TextAbstraction::TextRenderer::Parameters::RGBA8888;
401 // 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.
402 if(description.url.empty())
404 EmbeddedItemInfo embeddedInfo =
409 Size(static_cast<float>(item.width), static_cast<float>(item.height)),
410 Size(static_cast<float>(item.width), static_cast<float>(item.height)),
412 item.colorBlendingMode};
414 embeddedItemLayout.PushBack(embeddedInfo);
419 void SetColorSegmentation(const RendererParameters& textParameters, InternalDataModel& internalDataModel)
421 Text::ModelPtr& textModel = internalDataModel.textModel;
422 Vector<ColorBlendingMode>& blendingMode = internalDataModel.blendingMode;
424 Vector<ColorRun>& colorRuns = textModel->mLogicalModel->mColorRuns; // colors of the text.
426 Vector<GlyphIndex>& charactersToGlyph = textModel->mVisualModel->mCharactersToGlyph; // Characters to glyphs map.
427 Vector<Length>& glyphsPerCharacter = textModel->mVisualModel->mGlyphsPerCharacter; // The number of glyphs that are shaped.
429 ////////////////////////////////////////////////////////////////////////////////
430 // Set the color runs in glyphs.
431 ////////////////////////////////////////////////////////////////////////////////
433 SetColorSegmentationInfo(colorRuns,
438 internalDataModel.numberOfCharacters,
439 textModel->mVisualModel->mColors,
440 textModel->mVisualModel->mColorIndices);
442 // Insert the default color at the beginning of the vector.
443 textModel->mVisualModel->mColors.Insert(textModel->mVisualModel->mColors.Begin(), textParameters.textColor);
445 // Set how the embedded items are blended with text color.
446 blendingMode.Resize(internalDataModel.numberOfGlyphs, textParameters.isTextColorSet ? ColorBlendingMode::MULTIPLY : ColorBlendingMode::NONE);
448 if(!textParameters.isTextColorSet)
450 // Traverse the color runs.
451 for(const auto& run : colorRuns)
453 const GlyphIndex firstGlyph = textModel->mVisualModel->mCharactersToGlyph[run.characterRun.characterIndex];
454 const CharacterIndex lastCharacter = run.characterRun.characterIndex + run.characterRun.numberOfCharacters - 1u;
455 const GlyphIndex lastGlyphPlusOne = textModel->mVisualModel->mCharactersToGlyph[lastCharacter] + textModel->mVisualModel->mGlyphsPerCharacter[lastCharacter];
457 for(GlyphIndex index = firstGlyph; index < lastGlyphPlusOne; ++index)
459 blendingMode[index] = ColorBlendingMode::MULTIPLY;
464 // Traverse the embedded items and update the blending mode vector.
465 for(const auto& item : textModel->mLogicalModel->mEmbeddedItems)
467 const GlyphIndex glyphIndex = textModel->mVisualModel->mCharactersToGlyph[item.characterIndex];
468 blendingMode[glyphIndex] = item.colorBlendingMode;
472 void SetEmojiVector(InternalDataModel& internalDataModel)
474 Vector<bool>& isEmoji = internalDataModel.isEmoji;
475 Text::ModelPtr& textModel = internalDataModel.textModel;
476 const Length numberOfGlyphs = internalDataModel.numberOfGlyphs;
478 const Vector<ScriptRun>& scripts = textModel->mLogicalModel->mScriptRuns; // Charactes's script.
479 ////////////////////////////////////////////////////////////////////////////////
480 // Set the isEmoji Vector
481 ////////////////////////////////////////////////////////////////////////////////
483 isEmoji.Resize(numberOfGlyphs, false);
485 for(const auto& run : scripts)
487 if(run.script == TextAbstraction::Script::EMOJI)
489 const GlyphIndex firstGlyph = textModel->mVisualModel->mCharactersToGlyph[run.characterRun.characterIndex];
490 const CharacterIndex lastCharacter = run.characterRun.characterIndex + run.characterRun.numberOfCharacters - 1u;
491 const GlyphIndex lastGlyphPlusOne = textModel->mVisualModel->mCharactersToGlyph[lastCharacter] + textModel->mVisualModel->mGlyphsPerCharacter[lastCharacter];
493 for(GlyphIndex index = firstGlyph; index < lastGlyphPlusOne; ++index)
495 isEmoji[index] = true;
501 void Align(const RendererParameters& textParameters, TextAbstraction::TextRenderer::Parameters& rendererParameters, Vector<EmbeddedItemInfo>& embeddedItemLayout, InternalDataModel& internalDataModel, const Size& newLayoutSize)
503 Text::Layout::Engine& layoutEngine = internalDataModel.layoutEngine;
504 Text::ModelPtr& textModel = internalDataModel.textModel;
505 const Length numberOfCharacters = internalDataModel.numberOfCharacters;
506 Size& textLayoutArea = internalDataModel.textLayoutArea;
508 Vector<LineRun>& lines = textModel->mVisualModel->mLines; // The laid out lines.
510 ////////////////////////////////////////////////////////////////////////////////
512 ////////////////////////////////////////////////////////////////////////////////
514 HorizontalAlignment::Type horizontalAlignment = Toolkit::HorizontalAlignment::CENTER;
515 HorizontalAlignment::Type horizontalCircularAlignment = Toolkit::HorizontalAlignment::CENTER;
516 VerticalAlignment::Type verticalAlignment = VerticalAlignment::CENTER;
517 CircularAlignment::Type circularAlignment = CircularAlignment::BEGIN;
519 Layout::Type layout = Layout::SINGLELINE;
521 // Sets the alignment
522 Property::Value horizontalAlignmentStr(textParameters.horizontalAlignment);
523 GetHorizontalAlignmentEnumeration(horizontalAlignmentStr, horizontalAlignment);
524 horizontalCircularAlignment = horizontalAlignment;
526 Property::Value verticalAlignmentStr(textParameters.verticalAlignment);
527 GetVerticalAlignmentEnumeration(verticalAlignmentStr, verticalAlignment);
529 Property::Value circularAlignmentStr(textParameters.circularAlignment);
530 GetCircularAlignmentEnumeration(circularAlignmentStr, circularAlignment);
532 Property::Value layoutStr(textParameters.layout);
533 GetLayoutEnumeration(layoutStr, layout);
535 // Whether the layout is circular.
536 const bool isCircularTextLayout = (Layout::CIRCULAR == layout);
537 const bool isClockwise = isCircularTextLayout && (0.f < textParameters.incrementAngle);
539 // Convert CircularAlignment to HorizontalAlignment.
540 if(isCircularTextLayout)
542 switch(circularAlignment)
544 case CircularAlignment::BEGIN:
546 horizontalCircularAlignment = Toolkit::HorizontalAlignment::BEGIN;
549 case CircularAlignment::CENTER:
551 horizontalCircularAlignment = Toolkit::HorizontalAlignment::CENTER;
554 case CircularAlignment::END:
556 horizontalCircularAlignment = Toolkit::HorizontalAlignment::END;
561 textModel->mHorizontalAlignment = isCircularTextLayout ? horizontalCircularAlignment : horizontalAlignment;
563 // Retrieve the line of text to know the direction and the width. @todo multi-line
564 const LineRun& line = lines[0u];
566 if(isCircularTextLayout)
568 // Set the circular alignment.
569 rendererParameters.circularLayout = isClockwise ? TextRenderer::Parameters::CLOCKWISE : TextRenderer::Parameters::COUNTER_CLOCKWISE;
571 // Update the text's height to be used by the ellipsis code.
572 textLayoutArea.height = newLayoutSize.height;
574 // Set the size of the text laid out on a straight horizontal line.
575 rendererParameters.circularWidth = static_cast<unsigned int>(newLayoutSize.width);
576 rendererParameters.circularHeight = static_cast<unsigned int>(newLayoutSize.height);
578 // Calculate the center of the circular text according the horizontal and vertical alingments and the radius.
579 switch(horizontalAlignment)
581 case HorizontalAlignment::BEGIN:
583 rendererParameters.centerX = static_cast<int>(textParameters.radius);
586 case HorizontalAlignment::CENTER:
588 rendererParameters.centerX = static_cast<int>(textParameters.textWidth / 2u);
591 case HorizontalAlignment::END:
593 rendererParameters.centerX = static_cast<int>(textParameters.textWidth) - static_cast<int>(textParameters.radius);
598 switch(verticalAlignment)
600 case VerticalAlignment::TOP:
602 rendererParameters.centerY = static_cast<int>(textParameters.radius);
605 case VerticalAlignment::CENTER:
607 rendererParameters.centerY = static_cast<int>(textParameters.textHeight / 2u);
610 case VerticalAlignment::BOTTOM:
612 rendererParameters.centerY = static_cast<int>(textParameters.textHeight) - static_cast<int>(textParameters.radius);
617 // Calculate the beginning angle according with the given horizontal alignment.
618 const bool isRTL = RTL == line.direction;
620 CircularAlignment::Type alignment = circularAlignment;
623 // Swap the alignment type if the line is right to left.
626 case CircularAlignment::BEGIN:
628 alignment = CircularAlignment::END;
631 case CircularAlignment::CENTER:
636 case CircularAlignment::END:
638 alignment = CircularAlignment::BEGIN;
644 float angleOffset = 0.f;
648 case CircularAlignment::BEGIN:
653 case CircularAlignment::CENTER:
655 const bool isNeg = textParameters.incrementAngle < 0.f;
656 const float textWidth = static_cast<float>(rendererParameters.circularWidth);
657 angleOffset = (isNeg ? -0.5f : 0.5f) * (textLayoutArea.width - textWidth) / static_cast<float>(rendererParameters.radius);
660 case CircularAlignment::END:
662 const bool isNeg = textParameters.incrementAngle < 0.f;
663 const float textWidth = static_cast<float>(rendererParameters.circularWidth);
664 angleOffset = (isNeg ? -1.f : 1.f) * (textLayoutArea.width - textWidth) / static_cast<float>(rendererParameters.radius);
669 // Update the beginning angle with the calculated offset.
670 rendererParameters.beginAngle = Radian(Degree(textParameters.beginAngle)) + angleOffset;
672 // Set the vertical position of the glyphs.
673 for(auto& position : rendererParameters.positions)
680 // Calculate the vertical offset according with the given alignment.
683 switch(verticalAlignment)
685 case VerticalAlignment::TOP:
687 penY = line.ascender;
690 case VerticalAlignment::CENTER:
692 penY = line.ascender + 0.5f * (textLayoutArea.height - (line.ascender - line.descender));
695 case VerticalAlignment::BOTTOM:
697 penY = textLayoutArea.height;
702 // Calculate the horizontal offset according with the given alignment.
703 float alignmentOffset = 0.f;
704 layoutEngine.Align(textLayoutArea,
710 Dali::LayoutDirection::LEFT_TO_RIGHT,
713 // Update the position of the glyphs with the calculated offsets.
714 for(auto& position : rendererParameters.positions)
716 position.x += line.alignmentOffset;
721 // Cairo adds the bearing to the position of the glyph
722 // that has already been added by the DALi's layout engine,
723 // so it's needed to be removed here.
724 for(unsigned int index = 0u; index < rendererParameters.glyphs.Count(); ++index)
726 const GlyphInfo& glyph = rendererParameters.glyphs[index];
727 Vector2& position = rendererParameters.positions[index];
729 position.x -= glyph.xBearing;
732 // Set the position of the embedded items (if there is any).
733 EmbeddedItemInfo* embeddedItemLayoutBuffer = embeddedItemLayout.Begin();
735 for(Length index = 0u, endIndex = embeddedItemLayout.Count(); index < endIndex; ++index)
737 EmbeddedItemInfo& embeddedItem = *(embeddedItemLayoutBuffer + index);
739 embeddedItem.position = rendererParameters.positions[embeddedItem.glyphIndex];
741 if(isCircularTextLayout)
743 // Calculate the new position of the embedded item in the circular path.
745 // Center of the bitmap.
746 const float halfWidth = 0.5f * embeddedItem.size.width;
747 const float halfHeight = 0.5f * embeddedItem.size.height;
748 double centerX = static_cast<double>(embeddedItem.position.x + halfWidth);
749 double centerY = static_cast<double>(embeddedItem.position.y - halfHeight);
751 Dali::TextAbstraction::CircularTextParameters circularTextParameters;
753 circularTextParameters.radius = static_cast<double>(rendererParameters.radius);
754 circularTextParameters.invRadius = 1.0 / circularTextParameters.radius;
755 circularTextParameters.beginAngle = static_cast<double>(-rendererParameters.beginAngle + Dali::Math::PI_2);
756 circularTextParameters.centerX = 0.5f * static_cast<double>(textParameters.textWidth);
757 circularTextParameters.centerY = 0.5f * static_cast<double>(textParameters.textHeight);
759 // Calculate the rotation angle.
760 float radians = rendererParameters.beginAngle;
763 radians += static_cast<float>(circularTextParameters.invRadius * centerX);
768 radians -= static_cast<float>(circularTextParameters.invRadius * centerX);
769 radians = -radians + Dali::Math::PI;
771 embeddedItem.angle = Degree(Radian(radians));
773 Dali::TextAbstraction::TransformToArc(circularTextParameters, centerX, centerY);
775 // Recalculate the size of the embedded item after the rotation to position it correctly.
776 float width = embeddedItem.size.width;
777 float height = embeddedItem.size.height;
779 // Transform the input angle into the range [0..2PI]
780 radians = fmod(radians, TWO_PI);
781 radians += (radians < 0.f) ? TWO_PI : 0.f;
783 // Does the same operations than rotate by shear.
784 if((radians > Math::PI_4) && (radians <= RAD_135))
786 std::swap(width, height);
787 radians -= Math::PI_2;
789 else if((radians > RAD_135) && (radians <= RAD_225))
793 else if((radians > RAD_225) && (radians <= RAD_315))
795 std::swap(width, height);
799 if(fabs(radians) > Dali::Math::MACHINE_EPSILON_10)
801 const float angleSinus = fabs(sin(radians));
802 const float angleCosinus = cos(radians);
804 // Calculate the rotated image dimensions.
805 embeddedItem.rotatedSize.height = width * angleSinus + height * angleCosinus;
806 embeddedItem.rotatedSize.width = height * angleSinus + width * angleCosinus + 1.f;
809 embeddedItem.position.x = floor(static_cast<float>(centerX) - 0.5f * embeddedItem.rotatedSize.width);
810 embeddedItem.position.y = floor(static_cast<float>(centerY) - 0.5f * embeddedItem.rotatedSize.height);
814 embeddedItem.position.y -= embeddedItem.size.height;
819 void Ellipsis(const RendererParameters& textParameters, TextAbstraction::TextRenderer::Parameters& rendererParameters, Vector<EmbeddedItemInfo>& embeddedItemLayout, InternalDataModel& internalDataModel)
821 Text::ModelPtr& textModel = internalDataModel.textModel;
822 FontClient& fontClient = internalDataModel.fontClient;
824 Vector<LineRun>& lines = textModel->mVisualModel->mLines; // The laid out lines.
825 Vector<bool>& isEmoji = internalDataModel.isEmoji;
826 const Size textLayoutArea = internalDataModel.textLayoutArea;
827 const float modelCharacterSpacing = textModel->mVisualModel->GetCharacterSpacing();
828 float calculatedAdvance = 0.f;
829 Vector<CharacterIndex>& glyphToCharacterMap = textModel->mVisualModel->mGlyphsToCharacters;
830 const CharacterIndex* glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
832 // Get the character-spacing runs.
833 const Vector<CharacterSpacingGlyphRun>& characterSpacingGlyphRuns = textModel->mVisualModel->GetCharacterSpacingGlyphRuns();
835 ////////////////////////////////////////////////////////////////////////////////
836 // Ellipsis the text.
837 ////////////////////////////////////////////////////////////////////////////////
839 if(textParameters.ellipsisEnabled)
841 const LineRun& line = lines[0u]; // TODO: multi-line
845 Length finalNumberOfGlyphs = 0u;
847 if((GetLineHeight(line, (lines.Size() == 1))) > textLayoutArea.height)
849 // The height of the line is bigger than the height of the text area.
850 // Show the ellipsis glyph even if it doesn't fit in the text area.
851 // If no text is rendered then issues are rised and it may be a while
852 // until is find out that the text area is too small.
854 // Get the first glyph which is going to be replaced and the ellipsis glyph.
855 GlyphInfo& glyphInfo = rendererParameters.glyphs[0u];
856 const GlyphInfo& ellipsisGlyph = fontClient.GetEllipsisGlyph(fontClient.GetPointSize(glyphInfo.fontId));
858 // Change the 'x' and 'y' position of the ellipsis glyph.
859 Vector2& position = rendererParameters.positions[0u];
860 position.x = ellipsisGlyph.xBearing;
861 position.y = textLayoutArea.height - ellipsisGlyph.yBearing;
863 // Replace the glyph by the ellipsis glyph.
864 glyphInfo = ellipsisGlyph;
866 // Set the final number of glyphs
867 finalNumberOfGlyphs = 1u;
871 // firstPenX, penY and firstPenSet are used to position the ellipsis glyph if needed.
872 float firstPenX = 0.f; // Used if rtl text is elided.
873 bool firstPenSet = false;
875 // Add the ellipsis glyph.
876 bool inserted = false;
877 float removedGlypsWidth = 0.f;
878 Length numberOfRemovedGlyphs = 0u;
879 if(line.glyphRun.numberOfGlyphs > 0u)
881 GlyphIndex index = line.glyphRun.numberOfGlyphs - 1u;
883 GlyphInfo* glyphs = rendererParameters.glyphs.Begin();
884 Vector2* glyphPositions = rendererParameters.positions.Begin();
888 // The ellipsis glyph has to fit in the place where the last glyph(s) is(are) removed.
891 const GlyphInfo& glyphToRemove = *(glyphs + index);
893 if(0u != glyphToRemove.fontId)
895 // i.e. The font id of the glyph shaped from the '\n' character is zero.
897 // Need to reshape the glyph as the font may be different in size.
898 const GlyphInfo& ellipsisGlyph = fontClient.GetEllipsisGlyph(fontClient.GetPointSize(glyphToRemove.fontId));
902 const Vector2& position = *(glyphPositions + index);
904 // Calculates the penY of the current line. It will be used to position the ellipsis glyph.
907 // Calculates the first penX which will be used if rtl text is elided.
908 firstPenX = position.x - glyphToRemove.xBearing;
909 if(firstPenX < -ellipsisGlyph.xBearing)
911 // Avoids to exceed the bounding box when rtl text is elided.
912 firstPenX = -ellipsisGlyph.xBearing;
915 removedGlypsWidth = -ellipsisGlyph.xBearing;
920 const float characterSpacing = GetGlyphCharacterSpacing(index, characterSpacingGlyphRuns, modelCharacterSpacing);
921 calculatedAdvance = GetCalculatedAdvance(*(textModel->mLogicalModel->mText.Begin() + (*(glyphToCharacterMapBuffer + index))), characterSpacing, glyphToRemove.advance);
922 removedGlypsWidth += std::min(calculatedAdvance, (glyphToRemove.xBearing + glyphToRemove.width));
924 // Calculate the width of the ellipsis glyph and check if it fits.
925 const float ellipsisGlyphWidth = ellipsisGlyph.width + ellipsisGlyph.xBearing;
926 if(ellipsisGlyphWidth < removedGlypsWidth)
928 GlyphInfo& glyphInfo = *(glyphs + index);
929 Vector2& position = *(glyphPositions + index);
930 position.x -= (0.f > glyphInfo.xBearing) ? glyphInfo.xBearing : 0.f;
932 // Replace the glyph by the ellipsis glyph.
933 glyphInfo = ellipsisGlyph;
935 // Update the isEmoji vector
936 isEmoji[index] = false;
938 // Change the 'x' and 'y' position of the ellipsis glyph.
940 if(position.x > firstPenX)
942 position.x = firstPenX + removedGlypsWidth - ellipsisGlyphWidth;
945 position.x += ellipsisGlyph.xBearing;
960 // No space for the ellipsis.
963 ++numberOfRemovedGlyphs;
966 // Set the final number of glyphs
967 finalNumberOfGlyphs = line.glyphRun.numberOfGlyphs - numberOfRemovedGlyphs;
971 // Resize the number of glyphs/positions
972 rendererParameters.glyphs.Resize(finalNumberOfGlyphs);
973 rendererParameters.positions.Resize(finalNumberOfGlyphs);
975 // Remove from the embedded items those exceding the last laid out glyph.
976 embeddedItemLayout.Erase(std::remove_if(embeddedItemLayout.Begin(),
977 embeddedItemLayout.End(),
978 [finalNumberOfGlyphs](const EmbeddedItemInfo& item) {
979 return item.glyphIndex >= finalNumberOfGlyphs;
981 embeddedItemLayout.End());
987 Size LayoutText(const RendererParameters& textParameters, TextAbstraction::TextRenderer::Parameters& rendererParameters, Vector<EmbeddedItemInfo>& embeddedItemLayout, InternalDataModel& internalDataModel)
989 ////////////////////////////////////////////////////////////////////////////////
991 ////////////////////////////////////////////////////////////////////////////////
992 Text::ModelPtr& textModel = internalDataModel.textModel;
993 Text::Layout::Engine& layoutEngine = internalDataModel.layoutEngine;
994 FontClient& fontClient = internalDataModel.fontClient;
995 const Length numberOfGlyphs = internalDataModel.numberOfGlyphs;
996 const bool isTextMirrored = internalDataModel.isTextMirrored;
997 const Vector<Character>& mirroredUtf32Characters = internalDataModel.mirroredUtf32Characters;
998 const Length numberOfCharacters = internalDataModel.numberOfCharacters;
999 const auto ellipsisPosition = textModel->mEllipsisPosition;
1001 Layout::Type layout = Layout::SINGLELINE;
1003 Property::Value layoutStr(textParameters.layout);
1004 GetLayoutEnumeration(layoutStr, layout);
1006 // Whether the layout is multi-line.
1007 const Text::Layout::Engine::Type horizontalLayout = (Layout::MULTILINE == layout) ? Text::Layout::Engine::MULTI_LINE_BOX : Text::Layout::Engine::SINGLE_LINE_BOX;
1008 layoutEngine.SetLayout(horizontalLayout); // TODO: multi-line.
1010 // Set minimun line size
1011 layoutEngine.SetDefaultLineSize(textParameters.minLineSize);
1013 // Whether the layout is circular.
1014 const bool isCircularTextLayout = (Layout::CIRCULAR == layout);
1015 const bool isClockwise = isCircularTextLayout && (0.f < textParameters.incrementAngle);
1017 // Calculates the max ascender or the max descender.
1018 // Is used to calculate the radius of the base line of the text.
1019 float maxAscenderDescender = 0.f;
1020 if(isCircularTextLayout)
1022 FontId currentFontId = 0u;
1023 for(const auto& glyph : rendererParameters.glyphs)
1025 if(currentFontId != glyph.fontId)
1027 currentFontId = glyph.fontId;
1028 FontMetrics metrics;
1029 fontClient.GetFontMetrics(currentFontId, metrics);
1030 maxAscenderDescender = std::max(maxAscenderDescender, isClockwise ? metrics.ascender : metrics.descender);
1034 const unsigned int radius = textParameters.radius - static_cast<unsigned int>(maxAscenderDescender);
1036 // Set the layout parameters.
1037 Size textLayoutArea = Size(static_cast<float>(textParameters.textWidth),
1038 static_cast<float>(textParameters.textHeight));
1041 Extents padding = textParameters.padding;
1042 internalDataModel.textLayoutArea = Size(textLayoutArea.x - (padding.start + padding.end), textLayoutArea.y - (padding.top + padding.bottom));
1044 if(isCircularTextLayout)
1046 // In a circular layout, the length of the text area depends on the radius.
1047 rendererParameters.radius = radius;
1048 internalDataModel.textLayoutArea.width = fabs(Radian(Degree(textParameters.incrementAngle)) * static_cast<float>(rendererParameters.radius));
1050 // Resize the vector of positions to have the same size than the vector of glyphs.
1051 rendererParameters.positions.Resize(numberOfGlyphs);
1053 textModel->mLineWrapMode = Text::LineWrap::WORD;
1054 textModel->mIgnoreSpacesAfterText = false;
1055 Text::Layout::Parameters layoutParameters(internalDataModel.textLayoutArea,
1058 // Whether the last character is a new paragraph character.
1059 const Vector<Character>& textToShape = isTextMirrored ? mirroredUtf32Characters : textModel->mLogicalModel->mText;
1060 layoutParameters.isLastNewParagraph = TextAbstraction::IsNewParagraph(textToShape[numberOfCharacters - 1u]);
1062 // The initial glyph and the number of glyphs to layout.
1063 layoutParameters.startGlyphIndex = 0u;
1064 layoutParameters.numberOfGlyphs = numberOfGlyphs;
1065 layoutParameters.startLineIndex = 0u;
1066 layoutParameters.estimatedNumberOfLines = 1u;
1067 layoutParameters.interGlyphExtraAdvance = 0.f;
1069 // Update the visual model.
1071 bool isAutoScrollEnabled = false;
1072 bool isAutoScrollMaxTextureExceeded = false;
1073 bool isHiddenInputEnabled = false;
1075 layoutEngine.LayoutText(layoutParameters,
1077 textParameters.ellipsisEnabled,
1078 isAutoScrollEnabled,
1079 isAutoScrollMaxTextureExceeded,
1080 isHiddenInputEnabled,
1083 return newLayoutSize;
1086 Devel::PixelBuffer RenderText(const RendererParameters& textParameters, TextAbstraction::TextRenderer::Parameters& rendererParameters)
1088 ////////////////////////////////////////////////////////////////////////////////
1090 ////////////////////////////////////////////////////////////////////////////////
1092 rendererParameters.width = textParameters.textWidth;
1093 rendererParameters.height = textParameters.textHeight;
1095 TextAbstraction::TextRenderer renderer = TextAbstraction::TextRenderer::Get();
1096 return renderer.Render(rendererParameters);
1099 Devel::PixelBuffer Render(const RendererParameters& textParameters, Vector<EmbeddedItemInfo>& embeddedItemLayout)
1101 if(textParameters.text.empty())
1103 Dali::Devel::PixelBuffer pixelBuffer = Dali::Devel::PixelBuffer::New(textParameters.textWidth,
1104 textParameters.textHeight,
1105 Dali::Pixel::RGBA8888);
1107 const unsigned int bufferSize = textParameters.textWidth * textParameters.textHeight * Dali::Pixel::GetBytesPerPixel(Dali::Pixel::RGBA8888);
1108 unsigned char* buffer = pixelBuffer.GetBuffer();
1109 memset(buffer, 0, bufferSize);
1114 FontClient fontClient = FontClient::Get();
1116 // Use this to access FontClient i.e. to get down-scaled Emoji metrics.
1117 metrics = Metrics::New(fontClient);
1119 Text::ModelPtr textModel = Text::Model::New();
1120 InternalDataModel internalData(fontClient, metrics, textModel);
1122 TextAbstraction::TextRenderer::Parameters rendererParameters(internalData.textModel->mVisualModel->mGlyphs,
1123 internalData.textModel->mVisualModel->mGlyphPositions,
1124 internalData.textModel->mVisualModel->mColors,
1125 internalData.textModel->mVisualModel->mColorIndices,
1126 internalData.blendingMode,
1127 internalData.isEmoji);
1129 rendererParameters.width = textParameters.textWidth;
1130 rendererParameters.height = textParameters.textHeight;
1131 rendererParameters.pixelFormat = TextAbstraction::TextRenderer::Parameters::RGBA8888; // @note: At the moment all textures are generated RGBA8888
1133 ////////////////////////////////////////////////////////////////////////////////
1134 // Process the markup string if the mark-up processor is enabled.
1135 ////////////////////////////////////////////////////////////////////////////////
1136 ShapeTextPreprocess(textParameters, rendererParameters, internalData);
1138 ////////////////////////////////////////////////////////////////////////////////
1139 // Retrieve the glyphs. Text shaping
1140 ////////////////////////////////////////////////////////////////////////////////
1141 ShapeText(rendererParameters, embeddedItemLayout, internalData);
1143 ////////////////////////////////////////////////////////////////////////////////
1144 // Retrieve the glyph's metrics.
1145 ////////////////////////////////////////////////////////////////////////////////
1147 metrics->GetGlyphMetrics(rendererParameters.glyphs.Begin(), internalData.numberOfGlyphs);
1149 ////////////////////////////////////////////////////////////////////////////////
1150 // Set the color runs in glyphs.
1151 ////////////////////////////////////////////////////////////////////////////////
1152 SetColorSegmentation(textParameters, internalData);
1154 ////////////////////////////////////////////////////////////////////////////////
1155 // Set the isEmoji Vector
1156 ////////////////////////////////////////////////////////////////////////////////
1157 SetEmojiVector(internalData);
1159 ////////////////////////////////////////////////////////////////////////////////
1161 ////////////////////////////////////////////////////////////////////////////////
1162 Size newLayoutSize = LayoutText(textParameters, rendererParameters, embeddedItemLayout, internalData);
1164 ////////////////////////////////////////////////////////////////////////////////
1166 ////////////////////////////////////////////////////////////////////////////////
1167 Align(textParameters, rendererParameters, embeddedItemLayout, internalData, newLayoutSize);
1169 ////////////////////////////////////////////////////////////////////////////////
1170 // Ellipsis the text.
1171 ////////////////////////////////////////////////////////////////////////////////
1172 Ellipsis(textParameters, rendererParameters, embeddedItemLayout, internalData);
1174 ////////////////////////////////////////////////////////////////////////////////
1176 ////////////////////////////////////////////////////////////////////////////////
1177 return RenderText(textParameters, rendererParameters);
1180 Devel::PixelBuffer CreateShadow(const ShadowParameters& shadowParameters)
1182 // The size of the pixel data.
1183 const int width = static_cast<int>(shadowParameters.input.GetWidth());
1184 const int height = static_cast<int>(shadowParameters.input.GetHeight());
1186 // The shadow's offset.
1187 const int xOffset = static_cast<int>(shadowParameters.offset.x);
1188 const int yOffset = static_cast<int>(shadowParameters.offset.y);
1190 // The size in bytes of the pixel of the input's buffer.
1191 const Pixel::Format inputFormat = shadowParameters.input.GetPixelFormat();
1192 const unsigned int inputPixelSize = Pixel::GetBytesPerPixel(inputFormat);
1193 const bool isA8 = Pixel::A8 == inputFormat;
1195 // Creates the output pixel buffer.
1196 Devel::PixelBuffer outputPixelBuffer = Devel::PixelBuffer::New(width, height, Pixel::RGBA8888);
1198 // Clear the output buffer
1199 unsigned char* outputPixelBufferPtr = outputPixelBuffer.GetBuffer();
1200 memset(outputPixelBufferPtr, 0, width * height * Pixel::GetBytesPerPixel(Pixel::RGBA8888));
1202 // Gets the buffer of the input pixel buffer.
1203 const unsigned char* const inputPixelBuffer = shadowParameters.input.GetBuffer();
1205 float textColor[4u];
1208 memcpy(textColor, shadowParameters.textColor.AsFloat(), 4u * sizeof(float));
1210 const float* const shadowColor = shadowParameters.color.AsFloat();
1212 // Traverse the input pixel buffer and write the text on the foreground and the shadow on the background.
1213 for(int rowIndex = 0; rowIndex < height; ++rowIndex)
1215 // Calculates the rowIndex to the input pixel buffer for the shadow and whether it's within the boundaries.
1216 const int yOffsetIndex = rowIndex - yOffset;
1217 const bool isValidRowIndex = ((yOffsetIndex >= 0) && (yOffsetIndex < height));
1219 const int rows = rowIndex * width;
1220 const int offsetRows = yOffsetIndex * width;
1221 for(int columnIndex = 0; columnIndex < width; ++columnIndex)
1223 // Index to the input buffer to retrieve the alpha value of the foreground text.
1224 const unsigned int index = inputPixelSize * static_cast<unsigned int>(rows + columnIndex);
1226 // Build the index to the input buffer to retrieve the alpha value of the background shadow.
1227 unsigned int shadowIndex = 0u;
1228 bool isValidShadowIndex = false;
1231 const int xOffsetIndex = columnIndex - xOffset;
1232 isValidShadowIndex = ((xOffsetIndex >= 0) && (xOffsetIndex < width));
1234 if(isValidShadowIndex)
1236 shadowIndex = inputPixelSize * static_cast<unsigned int>(offsetRows + xOffsetIndex);
1240 // If the input buffer is an alpha mask, retrieve the values for the foreground text and the background shadow.
1241 // If not retrieve the color.
1242 float inputShadowOffsetAlphaValue = 1.f;
1243 float inputAlphaValue = 1.f;
1246 // Retrieve the alpha value for the shadow.
1247 inputShadowOffsetAlphaValue = isValidShadowIndex ? (static_cast<float>(*(inputPixelBuffer + shadowIndex)) / 255.f) : 0.f;
1249 // Retrieve the alpha value for the text.
1250 inputAlphaValue = static_cast<float>(*(inputPixelBuffer + index)) / 255.f;
1254 // The input buffer is not an alpha mask. Retrieve the color.
1255 textColor[0u] = TO_FLOAT * static_cast<float>(*(inputPixelBuffer + index + 0u));
1256 textColor[1u] = TO_FLOAT * static_cast<float>(*(inputPixelBuffer + index + 1u));
1257 textColor[2u] = TO_FLOAT * static_cast<float>(*(inputPixelBuffer + index + 2u));
1258 textColor[3u] = TO_FLOAT * static_cast<float>(*(inputPixelBuffer + index + 3u));
1259 inputAlphaValue = textColor[3u];
1260 inputShadowOffsetAlphaValue = isValidShadowIndex ? TO_FLOAT * static_cast<float>(*(inputPixelBuffer + shadowIndex + 3u)) : 0.f;
1263 // Build the output color.
1264 float outputColor[4u];
1266 if(shadowParameters.blendShadow)
1268 // Blend the shadow's color with the text's color on top
1269 const float textAlpha = textColor[3u] * inputAlphaValue;
1270 const float shadowAlpha = shadowColor[3u] * inputShadowOffsetAlphaValue;
1272 // Blends the alpha.
1273 outputColor[3u] = 1.f - ((1.f - textAlpha) * (1.f - shadowAlpha));
1274 const bool isOutputAlphaZero = outputColor[3u] < Dali::Math::MACHINE_EPSILON_1000;
1275 if(isOutputAlphaZero)
1277 std::fill(outputColor, outputColor + 4u, 0.f);
1281 // Blends the RGB components.
1282 float shadowComponent = 0.f;
1283 float textComponent = 0.f;
1285 shadowComponent = shadowColor[0u] * inputShadowOffsetAlphaValue;
1286 textComponent = textColor[0u] * inputAlphaValue;
1287 outputColor[0u] = (textComponent * textAlpha / outputColor[3u]) + (shadowComponent * shadowAlpha * (1.f - textAlpha) / outputColor[3u]);
1289 shadowComponent = shadowColor[1u] * inputShadowOffsetAlphaValue;
1290 textComponent = textColor[1u] * inputAlphaValue;
1291 outputColor[1u] = (textComponent * textAlpha / outputColor[3u]) + (shadowComponent * shadowAlpha * (1.f - textAlpha) / outputColor[3u]);
1293 shadowComponent = shadowColor[2u] * inputShadowOffsetAlphaValue;
1294 textComponent = textColor[2u] * inputAlphaValue;
1295 outputColor[2u] = (textComponent * textAlpha / outputColor[3u]) + (shadowComponent * shadowAlpha * (1.f - textAlpha) / outputColor[3u]);
1301 std::fill(outputColor, outputColor + 4u, 0.f);
1303 const float textAlpha = textColor[3u];
1304 const float shadowAlpha = shadowColor[3u] * inputShadowOffsetAlphaValue;
1306 // Write shadow first.
1307 if(shadowAlpha > Dali::Math::MACHINE_EPSILON_1000)
1309 outputColor[0u] = shadowColor[0u] * inputShadowOffsetAlphaValue;
1310 outputColor[1u] = shadowColor[1u] * inputShadowOffsetAlphaValue;
1311 outputColor[2u] = shadowColor[2u] * inputShadowOffsetAlphaValue;
1312 outputColor[3u] = shadowAlpha;
1315 // Write character on top.
1316 if(textAlpha > Dali::Math::MACHINE_EPSILON_1000)
1318 outputColor[0u] = textColor[0u];
1319 outputColor[1u] = textColor[1u];
1320 outputColor[2u] = textColor[2u];
1321 outputColor[3u] = textAlpha;
1325 // Write the color into the output pixel buffer.
1326 const unsigned int outputIndex = 4u * (rows + columnIndex);
1327 *(outputPixelBufferPtr + outputIndex + 0u) = static_cast<unsigned char>(TO_UCHAR * outputColor[0u]);
1328 *(outputPixelBufferPtr + outputIndex + 1u) = static_cast<unsigned char>(TO_UCHAR * outputColor[1u]);
1329 *(outputPixelBufferPtr + outputIndex + 2u) = static_cast<unsigned char>(TO_UCHAR * outputColor[2u]);
1330 *(outputPixelBufferPtr + outputIndex + 3u) = static_cast<unsigned char>(TO_UCHAR * outputColor[3u]);
1334 // Returns the pixel buffer.
1335 return outputPixelBuffer;
1338 Devel::PixelBuffer ConvertToRgba8888(Devel::PixelBuffer pixelBuffer, const Vector4& color, bool multiplyByAlpha)
1340 if(Dali::Pixel::A8 != pixelBuffer.GetPixelFormat())
1346 const unsigned int width = pixelBuffer.GetWidth();
1347 const unsigned int height = pixelBuffer.GetHeight();
1348 Devel::PixelBuffer newPixelBuffer = Devel::PixelBuffer::New(width, height, Dali::Pixel::RGBA8888);
1350 unsigned char* dstBuffer = newPixelBuffer.GetBuffer();
1351 const unsigned char* const srcBuffer = pixelBuffer.GetBuffer();
1353 const unsigned char r = static_cast<unsigned char>(TO_UCHAR * color.r);
1354 const unsigned char g = static_cast<unsigned char>(TO_UCHAR * color.g);
1355 const unsigned char b = static_cast<unsigned char>(TO_UCHAR * color.b);
1357 unsigned char dstColor[4];
1358 for(unsigned int j = 0u; j < height; ++j)
1360 const unsigned int lineIndex = j * width;
1361 for(unsigned int i = 0u; i < width; ++i)
1363 const unsigned int srcIndex = lineIndex + i;
1365 const float srcAlpha = static_cast<float>(*(srcBuffer + srcIndex));
1369 dstColor[0u] = static_cast<unsigned char>(srcAlpha * color.r);
1370 dstColor[1u] = static_cast<unsigned char>(srcAlpha * color.g);
1371 dstColor[2u] = static_cast<unsigned char>(srcAlpha * color.b);
1372 dstColor[3u] = static_cast<unsigned char>(srcAlpha * color.a);
1379 dstColor[3u] = static_cast<unsigned char>(srcAlpha);
1382 const unsigned int dstIndex = srcIndex * 4u;
1383 memcpy(dstBuffer + dstIndex, dstColor, 4u);
1387 return newPixelBuffer;
1390 void UpdateBuffer(Devel::PixelBuffer src, Devel::PixelBuffer dst, unsigned int x, unsigned int y, bool blend)
1392 const Dali::Pixel::Format pixelFormat = dst.GetPixelFormat();
1393 if(src.GetPixelFormat() != pixelFormat)
1395 DALI_LOG_ERROR("PixelBuffer::SetBuffer. The pixel format of the new data must be the same of the current pixel buffer.");
1399 const unsigned int srcWidth = src.GetWidth();
1400 const unsigned int srcHeight = src.GetHeight();
1401 const unsigned int dstWidth = dst.GetWidth();
1402 const unsigned int dstHeight = dst.GetHeight();
1404 if((x > dstWidth) ||
1406 (x + srcWidth > dstWidth) ||
1407 (y + srcHeight > dstHeight))
1409 DALI_LOG_ERROR("PixelBuffer::SetBuffer. The source pixel buffer is out of the boundaries of the destination pixel buffer.");
1413 const unsigned int bytesPerPixel = Dali::Pixel::GetBytesPerPixel(pixelFormat);
1414 // Ignore when pixelFormat is invalid or contain float
1415 if(bytesPerPixel == 0u || bytesPerPixel == 6u || bytesPerPixel == 12u)
1419 const unsigned int alphaIndex = bytesPerPixel - 1u;
1421 const unsigned char* const srcBuffer = src.GetBuffer();
1422 unsigned char* dstBuffer = dst.GetBuffer();
1426 const unsigned int currentLineSize = dstWidth * bytesPerPixel;
1427 const unsigned int newLineSize = srcWidth * bytesPerPixel;
1428 unsigned char* currentBuffer = dstBuffer + (y * dstWidth + x) * bytesPerPixel;
1429 for(unsigned int j = 0u; j < srcHeight; ++j)
1431 memcpy(currentBuffer + j * currentLineSize, srcBuffer + j * newLineSize, newLineSize);
1436 float outputColor[4u];
1438 // Blend the src pixel buffer with the dst pixel buffer as background.
1440 // fgColor, fgAlpha, bgColor, bgAlpha
1442 // alpha = 1 - ( 1 - fgAlpha ) * ( 1 - bgAlpha )
1443 // color = ( fgColor * fgAlpha / alpha ) + ( bgColor * bgAlpha * ( 1 - fgAlpha ) / alpha )
1445 // Jump till the 'x,y' position
1446 const unsigned int dstWidthBytes = dstWidth * bytesPerPixel;
1447 dstBuffer += (y * dstWidthBytes + x * bytesPerPixel);
1449 for(unsigned int j = 0u; j < srcHeight; ++j)
1451 const unsigned int srcLineIndex = j * srcWidth;
1452 for(unsigned int i = 0u; i < srcWidth; ++i)
1454 const float srcAlpha = TO_FLOAT * static_cast<float>(*(srcBuffer + bytesPerPixel * (srcLineIndex + i) + alphaIndex));
1455 const float dstAlpha = TO_FLOAT * static_cast<float>(*(dstBuffer + i * bytesPerPixel + alphaIndex));
1457 // Blends the alpha channel.
1458 const float oneMinusSrcAlpha = 1.f - srcAlpha;
1459 outputColor[alphaIndex] = 1.f - (oneMinusSrcAlpha * (1.f - dstAlpha));
1461 // Blends the RGB channels.
1462 const bool isOutputAlphaZero = outputColor[alphaIndex] < Dali::Math::MACHINE_EPSILON_1000;
1463 if(isOutputAlphaZero)
1465 std::fill(outputColor, outputColor + bytesPerPixel, 0.f);
1469 const float srcAlphaOverOutputAlpha = srcAlpha / outputColor[alphaIndex]; // fgAlpha / alpha
1470 const float dstAlphaOneMinusSrcAlphaOverOutputAlpha = dstAlpha * oneMinusSrcAlpha / outputColor[alphaIndex]; // bgAlpha * ( 1 - fgAlpha ) / alpha
1471 for(unsigned int index = 0u; index < alphaIndex; ++index)
1473 const float dstComponent = TO_FLOAT * static_cast<float>(*(dstBuffer + i * bytesPerPixel + index)) * dstAlpha;
1474 const float srcComponent = TO_FLOAT * static_cast<float>(*(srcBuffer + bytesPerPixel * (srcLineIndex + i) + index)) * srcAlpha;
1475 outputColor[index] = (srcComponent * srcAlphaOverOutputAlpha) + (dstComponent * dstAlphaOneMinusSrcAlphaOverOutputAlpha);
1479 for(unsigned int index = 0u; index < bytesPerPixel; ++index)
1481 *(dstBuffer + i * bytesPerPixel + index) = static_cast<unsigned char>(TO_UCHAR * outputColor[index]);
1485 dstBuffer += dstWidthBytes;
1490 Dali::Property::Array RenderForLastIndex(RendererParameters& textParameters)
1492 Property::Array offsetValues;
1493 if(textParameters.text.empty())
1495 return offsetValues;
1497 FontClient fontClient = FontClient::Get();
1499 metrics = Metrics::New(fontClient);
1501 Text::ModelPtr textModel = Text::Model::New();
1502 InternalDataModel internalData(fontClient, metrics, textModel);
1504 TextAbstraction::TextRenderer::Parameters rendererParameters(textModel->mVisualModel->mGlyphs,
1505 textModel->mVisualModel->mGlyphPositions,
1506 textModel->mVisualModel->mColors,
1507 textModel->mVisualModel->mColorIndices,
1508 internalData.blendingMode,
1509 internalData.isEmoji);
1511 rendererParameters.width = textParameters.textWidth;
1512 rendererParameters.height = textParameters.textHeight;
1514 ////////////////////////////////////////////////////////////////////////////////
1515 // Process the markup string if the mark-up processor is enabled.
1516 ////////////////////////////////////////////////////////////////////////////////
1517 ShapeTextPreprocess(textParameters, rendererParameters, internalData);
1519 ////////////////////////////////////////////////////////////////////////////////
1520 // Retrieve the glyphs. Text shaping
1521 ////////////////////////////////////////////////////////////////////////////////
1522 Dali::Vector<Dali::Toolkit::DevelText::EmbeddedItemInfo> embeddedItemLayout;
1523 ShapeText(rendererParameters, embeddedItemLayout, internalData);
1525 ////////////////////////////////////////////////////////////////////////////////
1526 // Retrieve the glyph's metrics.
1527 ////////////////////////////////////////////////////////////////////////////////
1528 metrics->GetGlyphMetrics(rendererParameters.glyphs.Begin(), internalData.numberOfGlyphs);
1530 ////////////////////////////////////////////////////////////////////////////////
1532 ////////////////////////////////////////////////////////////////////////////////
1533 int boundingBox = textParameters.textHeight - (textParameters.padding.top + textParameters.padding.bottom);
1534 textParameters.textHeight = MAX_INT; // layout for the entire area.
1535 LayoutText(textParameters, rendererParameters, embeddedItemLayout, internalData);
1537 ////////////////////////////////////////////////////////////////////////////////
1538 // Calculation last character index
1539 ////////////////////////////////////////////////////////////////////////////////
1540 Vector<LineRun>& lines = internalData.textModel->mVisualModel->mLines;
1541 unsigned int numberOfLines = lines.Count();
1542 int numberOfCharacters = 0;
1544 float lineSize = internalData.layoutEngine.GetDefaultLineSize();
1545 float lineOffset = 0.f;
1547 for(unsigned int index = 0u; index < numberOfLines; ++index)
1549 const LineRun& line = *(lines.Begin() + index);
1550 numberOfCharacters += line.characterRun.numberOfCharacters;
1551 isLastLine = (index == numberOfLines - 1);
1553 lineOffset = lineSize > 0.f ? lineSize : GetLineHeight(line, isLastLine);
1555 if((penY + lineOffset) > boundingBox)
1557 offsetValues.PushBack(numberOfCharacters);
1563 // add remain character index
1564 offsetValues.PushBack(numberOfCharacters);
1567 return offsetValues;
1570 Dali::Property::Array GetLastCharacterIndex(RendererParameters& textParameters)
1572 Dali::Property::Array offsetValues = Toolkit::DevelText::RenderForLastIndex(textParameters);
1573 return offsetValues;
1576 } // namespace DevelText
1578 } // namespace Toolkit