[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / text / text-visual.cpp
1 /*
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/visuals/text/text-visual.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/adaptor-framework/image-loading.h>
23 #include <dali/devel-api/rendering/renderer-devel.h>
24 #include <dali/devel-api/rendering/texture-devel.h>
25 #include <dali/devel-api/text-abstraction/text-abstraction-definitions.h>
26 #include <dali/integration-api/debug.h>
27 #include <dali/integration-api/trace.h>
28 #include <string.h>
29
30 // INTERNAL HEADER
31 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
32 #include <dali-toolkit/devel-api/text/text-enumerations-devel.h>
33 #include <dali-toolkit/devel-api/visuals/text-visual-properties-devel.h>
34 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
35 #include <dali-toolkit/internal/text/script-run.h>
36 #include <dali-toolkit/internal/text/text-effects-style.h>
37 #include <dali-toolkit/internal/text/text-enumerations-impl.h>
38 #include <dali-toolkit/internal/text/text-font-style.h>
39 #include <dali-toolkit/internal/visuals/image-atlas-manager.h>
40 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
41 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
42 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
43 #include <dali-toolkit/public-api/visuals/text-visual-properties.h>
44 #include <dali-toolkit/public-api/visuals/visual-properties.h>
45
46 namespace Dali
47 {
48 namespace Toolkit
49 {
50 namespace Internal
51 {
52 namespace
53 {
54 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_TEXT_PERFORMANCE_MARKER, false);
55
56 const int CUSTOM_PROPERTY_COUNT(2); // uTextColorAnimatable, uHasMultipleTextColors
57
58 /**
59  * Return Property index for the given string key
60  * param[in] stringKey the string index key
61  * return the key as an index
62  */
63
64 Dali::Property::Index StringKeyToIndexKey(const std::string& stringKey)
65 {
66   Dali::Property::Index result = Property::INVALID_KEY;
67
68   if(stringKey == VISUAL_TYPE)
69   {
70     result = Toolkit::Visual::Property::TYPE;
71   }
72   else if(stringKey == TEXT_PROPERTY)
73   {
74     result = Toolkit::TextVisual::Property::TEXT;
75   }
76   else if(stringKey == FONT_FAMILY_PROPERTY)
77   {
78     result = Toolkit::TextVisual::Property::FONT_FAMILY;
79   }
80   else if(stringKey == FONT_STYLE_PROPERTY)
81   {
82     result = Toolkit::TextVisual::Property::FONT_STYLE;
83   }
84   else if(stringKey == POINT_SIZE_PROPERTY)
85   {
86     result = Toolkit::TextVisual::Property::POINT_SIZE;
87   }
88   else if(stringKey == MULTI_LINE_PROPERTY)
89   {
90     result = Toolkit::TextVisual::Property::MULTI_LINE;
91   }
92   else if(stringKey == HORIZONTAL_ALIGNMENT_PROPERTY)
93   {
94     result = Toolkit::TextVisual::Property::HORIZONTAL_ALIGNMENT;
95   }
96   else if(stringKey == VERTICAL_ALIGNMENT_PROPERTY)
97   {
98     result = Toolkit::TextVisual::Property::VERTICAL_ALIGNMENT;
99   }
100   else if(stringKey == TEXT_COLOR_PROPERTY)
101   {
102     result = Toolkit::TextVisual::Property::TEXT_COLOR;
103   }
104   else if(stringKey == ENABLE_MARKUP_PROPERTY)
105   {
106     result = Toolkit::TextVisual::Property::ENABLE_MARKUP;
107   }
108   else if(stringKey == SHADOW_PROPERTY)
109   {
110     result = Toolkit::TextVisual::Property::SHADOW;
111   }
112   else if(stringKey == UNDERLINE_PROPERTY)
113   {
114     result = Toolkit::TextVisual::Property::UNDERLINE;
115   }
116   else if(stringKey == OUTLINE_PROPERTY)
117   {
118     result = Toolkit::DevelTextVisual::Property::OUTLINE;
119   }
120   else if(stringKey == BACKGROUND_PROPERTY)
121   {
122     result = Toolkit::DevelTextVisual::Property::BACKGROUND;
123   }
124
125   return result;
126 }
127
128 void TextColorConstraint(Vector4& current, const PropertyInputContainer& inputs)
129 {
130   Vector4 color = inputs[0]->GetVector4();
131   current.r     = color.r * color.a;
132   current.g     = color.g * color.a;
133   current.b     = color.b * color.a;
134   current.a     = color.a;
135 }
136
137 void OpacityConstraint(float& current, const PropertyInputContainer& inputs)
138 {
139   // Make zero if the alpha value of text color is zero to skip rendering text
140   if(EqualsZero(inputs[0]->GetVector4().a))
141   {
142     current = 0.0f;
143   }
144   else
145   {
146     current = 1.0f;
147   }
148 }
149
150 } // unnamed namespace
151
152 TextVisualPtr TextVisual::New(VisualFactoryCache& factoryCache, TextVisualShaderFactory& shaderFactory, const Property::Map& properties)
153 {
154   TextVisualPtr textVisualPtr(new TextVisual(factoryCache, shaderFactory));
155   textVisualPtr->SetProperties(properties);
156   textVisualPtr->Initialize();
157   return textVisualPtr;
158 }
159
160 Property::Map TextVisual::ConvertStringKeysToIndexKeys(const Property::Map& propertyMap)
161 {
162   Property::Map outMap;
163
164   for(Property::Map::SizeType index = 0u, count = propertyMap.Count(); index < count; ++index)
165   {
166     const KeyValuePair& keyValue = propertyMap.GetKeyValue(index);
167
168     Property::Index indexKey = keyValue.first.indexKey;
169
170     if(keyValue.first.type == Property::Key::STRING)
171     {
172       indexKey = StringKeyToIndexKey(keyValue.first.stringKey);
173     }
174
175     outMap.Insert(indexKey, keyValue.second);
176   }
177
178   return outMap;
179 }
180
181 float TextVisual::GetHeightForWidth(float width)
182 {
183   return mController->GetHeightForWidth(width);
184 }
185
186 void TextVisual::GetNaturalSize(Vector2& naturalSize)
187 {
188   naturalSize = mController->GetNaturalSize().GetVectorXY();
189 }
190
191 void TextVisual::DoCreatePropertyMap(Property::Map& map) const
192 {
193   Property::Value value;
194
195   map.Clear();
196   map.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::TEXT);
197
198   std::string text;
199   mController->GetText(text);
200   map.Insert(Toolkit::TextVisual::Property::TEXT, text);
201
202   map.Insert(Toolkit::TextVisual::Property::FONT_FAMILY, mController->GetDefaultFontFamily());
203
204   GetFontStyleProperty(mController, value, Text::FontStyle::DEFAULT);
205   map.Insert(Toolkit::TextVisual::Property::FONT_STYLE, value);
206
207   map.Insert(Toolkit::TextVisual::Property::POINT_SIZE, mController->GetDefaultFontSize(Text::Controller::POINT_SIZE));
208
209   map.Insert(Toolkit::TextVisual::Property::MULTI_LINE, mController->IsMultiLineEnabled());
210
211   map.Insert(Toolkit::TextVisual::Property::HORIZONTAL_ALIGNMENT, mController->GetHorizontalAlignment());
212
213   map.Insert(Toolkit::TextVisual::Property::VERTICAL_ALIGNMENT, mController->GetVerticalAlignment());
214
215   map.Insert(Toolkit::TextVisual::Property::TEXT_COLOR, mController->GetDefaultColor());
216
217   map.Insert(Toolkit::TextVisual::Property::ENABLE_MARKUP, mController->IsMarkupProcessorEnabled());
218
219   GetShadowProperties(mController, value, Text::EffectStyle::DEFAULT);
220   map.Insert(Toolkit::TextVisual::Property::SHADOW, value);
221
222   GetUnderlineProperties(mController, value, Text::EffectStyle::DEFAULT);
223   map.Insert(Toolkit::TextVisual::Property::UNDERLINE, value);
224
225   GetOutlineProperties(mController, value, Text::EffectStyle::DEFAULT);
226   map.Insert(Toolkit::DevelTextVisual::Property::OUTLINE, value);
227
228   GetBackgroundProperties(mController, value, Text::EffectStyle::DEFAULT);
229   map.Insert(Toolkit::DevelTextVisual::Property::BACKGROUND, value);
230
231   GetStrikethroughProperties(mController, value, Text::EffectStyle::DEFAULT);
232   map.Insert(Toolkit::DevelTextVisual::Property::STRIKETHROUGH, value);
233 }
234
235 void TextVisual::DoCreateInstancePropertyMap(Property::Map& map) const
236 {
237   map.Clear();
238   map.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::TEXT);
239   std::string text;
240   mController->GetText(text);
241   map.Insert(Toolkit::TextVisual::Property::TEXT, text);
242 }
243
244 void TextVisual::EnablePreMultipliedAlpha(bool preMultiplied)
245 {
246   // Make always enable pre multiplied alpha whether preMultiplied value is false.
247   if(!preMultiplied)
248   {
249     DALI_LOG_WARNING("Note : TextVisual cannot disable PreMultipliedAlpha\n");
250   }
251 }
252
253 TextVisual::TextVisual(VisualFactoryCache& factoryCache, TextVisualShaderFactory& shaderFactory)
254 : Visual::Base(factoryCache, Visual::FittingMode::FIT_KEEP_ASPECT_RATIO, Toolkit::Visual::TEXT),
255   mController(Text::Controller::New()),
256   mTypesetter(Text::Typesetter::New(mController->GetTextModel())),
257   mTextVisualShaderFactory(shaderFactory),
258   mTextShaderFeatureCache(),
259   mHasMultipleTextColorsIndex(Property::INVALID_INDEX),
260   mAnimatableTextColorPropertyIndex(Property::INVALID_INDEX),
261   mTextColorAnimatableIndex(Property::INVALID_INDEX),
262   mRendererUpdateNeeded(false)
263 {
264   // Enable the pre-multiplied alpha to improve the text quality
265   mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
266 }
267
268 TextVisual::~TextVisual()
269 {
270 }
271
272 void TextVisual::OnInitialize()
273 {
274   Geometry geometry = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY);
275   Shader   shader   = GetTextShader(mFactoryCache, TextVisualShaderFeature::FeatureBuilder());
276
277   mImpl->mRenderer = VisualRenderer::New(geometry, shader);
278   mImpl->mRenderer.ReserveCustomProperties(CUSTOM_PROPERTY_COUNT);
279   mHasMultipleTextColorsIndex = mImpl->mRenderer.RegisterUniqueProperty("uHasMultipleTextColors", static_cast<float>(false));
280 }
281
282 void TextVisual::DoSetProperties(const Property::Map& propertyMap)
283 {
284   for(Property::Map::SizeType index = 0u, count = propertyMap.Count(); index < count; ++index)
285   {
286     const KeyValuePair& keyValue = propertyMap.GetKeyValue(index);
287
288     Property::Index indexKey = keyValue.first.indexKey;
289
290     if(keyValue.first.type == Property::Key::STRING)
291     {
292       indexKey = StringKeyToIndexKey(keyValue.first.stringKey);
293     }
294
295     DoSetProperty(indexKey, keyValue.second);
296   }
297
298   // Elide the text if it exceeds the boundaries.
299   mController->SetTextElideEnabled(true);
300
301   // Retrieve the layout engine to set the cursor's width.
302   Text::Layout::Engine& engine = mController->GetLayoutEngine();
303
304   // Sets 0 as cursor's width.
305   engine.SetCursorWidth(0u); // Do not layout space for the cursor.
306 }
307
308 void TextVisual::DoSetOnScene(Actor& actor)
309 {
310   mControl = actor;
311
312   mImpl->mRenderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, Toolkit::DepthIndex::CONTENT);
313
314   const Vector4& defaultColor = mController->GetTextModel()->GetDefaultColor();
315   if(mTextColorAnimatableIndex == Property::INVALID_INDEX)
316   {
317     mTextColorAnimatableIndex = mImpl->mRenderer.RegisterUniqueProperty("uTextColorAnimatable", defaultColor);
318   }
319   else
320   {
321     mImpl->mRenderer.SetProperty(mTextColorAnimatableIndex, defaultColor);
322   }
323
324   if(mAnimatableTextColorPropertyIndex != Property::INVALID_INDEX)
325   {
326     // Create constraint for the animatable text's color Property with uTextColorAnimatable in the renderer.
327     if(mTextColorAnimatableIndex != Property::INVALID_INDEX)
328     {
329       if(!mColorConstraint)
330       {
331         mColorConstraint = Constraint::New<Vector4>(mImpl->mRenderer, mTextColorAnimatableIndex, TextColorConstraint);
332         mColorConstraint.AddSource(Source(actor, mAnimatableTextColorPropertyIndex));
333       }
334       mColorConstraint.Apply();
335     }
336
337     // Make zero if the alpha value of text color is zero to skip rendering text
338     if(!mOpacityConstraint)
339     {
340       // VisualRenderer::Property::OPACITY uses same animatable property internally.
341       mOpacityConstraint = Constraint::New<float>(mImpl->mRenderer, Dali::DevelRenderer::Property::OPACITY, OpacityConstraint);
342       mOpacityConstraint.AddSource(Source(actor, mAnimatableTextColorPropertyIndex));
343     }
344     mOpacityConstraint.Apply();
345   }
346
347   // Renderer needs textures and to be added to control
348   mRendererUpdateNeeded = true;
349
350   UpdateRenderer();
351 }
352
353 void TextVisual::RemoveRenderer(Actor& actor)
354 {
355   for(RendererContainer::iterator iter = mRendererList.begin(); iter != mRendererList.end(); ++iter)
356   {
357     Renderer renderer = (*iter);
358     if(renderer)
359     {
360       // Removes the renderer from the actor.
361       actor.RemoveRenderer(renderer);
362     }
363   }
364   // Clear the renderer list
365   mRendererList.clear();
366 }
367
368 void TextVisual::DoSetOffScene(Actor& actor)
369 {
370   if(mColorConstraint)
371   {
372     mColorConstraint.Remove();
373   }
374   if(mOpacityConstraint)
375   {
376     mOpacityConstraint.Remove();
377   }
378
379   RemoveRenderer(actor);
380
381   // Resets the control handle.
382   mControl.Reset();
383 }
384
385 void TextVisual::OnSetTransform()
386 {
387   UpdateRenderer();
388 }
389
390 void TextVisual::DoSetProperty(Dali::Property::Index index, const Dali::Property::Value& propertyValue)
391 {
392   switch(index)
393   {
394     case Toolkit::TextVisual::Property::ENABLE_MARKUP:
395     {
396       const bool enableMarkup = propertyValue.Get<bool>();
397       mController->SetMarkupProcessorEnabled(enableMarkup);
398       break;
399     }
400     case Toolkit::TextVisual::Property::TEXT:
401     {
402       mController->SetText(propertyValue.Get<std::string>());
403       break;
404     }
405     case Toolkit::TextVisual::Property::FONT_FAMILY:
406     {
407       SetFontFamilyProperty(mController, propertyValue);
408       break;
409     }
410     case Toolkit::TextVisual::Property::FONT_STYLE:
411     {
412       SetFontStyleProperty(mController, propertyValue, Text::FontStyle::DEFAULT);
413       break;
414     }
415     case Toolkit::TextVisual::Property::POINT_SIZE:
416     {
417       const float pointSize = propertyValue.Get<float>();
418       if(!Equals(mController->GetDefaultFontSize(Text::Controller::POINT_SIZE), pointSize))
419       {
420         mController->SetDefaultFontSize(pointSize, Text::Controller::POINT_SIZE);
421       }
422       break;
423     }
424     case Toolkit::TextVisual::Property::MULTI_LINE:
425     {
426       mController->SetMultiLineEnabled(propertyValue.Get<bool>());
427       break;
428     }
429     case Toolkit::TextVisual::Property::HORIZONTAL_ALIGNMENT:
430     {
431       if(mController)
432       {
433         Text::HorizontalAlignment::Type alignment(static_cast<Text::HorizontalAlignment::Type>(-1)); // Set to invalid value to ensure a valid mode does get set
434         if(Toolkit::Text::GetHorizontalAlignmentEnumeration(propertyValue, alignment))
435         {
436           mController->SetHorizontalAlignment(alignment);
437         }
438       }
439       break;
440     }
441     case Toolkit::TextVisual::Property::VERTICAL_ALIGNMENT:
442     {
443       if(mController)
444       {
445         Toolkit::Text::VerticalAlignment::Type alignment(static_cast<Text::VerticalAlignment::Type>(-1)); // Set to invalid value to ensure a valid mode does get set
446         if(Toolkit::Text::GetVerticalAlignmentEnumeration(propertyValue, alignment))
447         {
448           mController->SetVerticalAlignment(alignment);
449         }
450       }
451       break;
452     }
453     case Toolkit::TextVisual::Property::TEXT_COLOR:
454     {
455       const Vector4& textColor = propertyValue.Get<Vector4>();
456       if(mController->GetDefaultColor() != textColor)
457       {
458         mController->SetDefaultColor(textColor);
459       }
460       break;
461     }
462     case Toolkit::TextVisual::Property::SHADOW:
463     {
464       SetShadowProperties(mController, propertyValue, Text::EffectStyle::DEFAULT);
465       break;
466     }
467     case Toolkit::TextVisual::Property::UNDERLINE:
468     {
469       SetUnderlineProperties(mController, propertyValue, Text::EffectStyle::DEFAULT);
470       break;
471     }
472     case Toolkit::DevelTextVisual::Property::OUTLINE:
473     {
474       SetOutlineProperties(mController, propertyValue, Text::EffectStyle::DEFAULT);
475       break;
476     }
477     case Toolkit::DevelTextVisual::Property::BACKGROUND:
478     {
479       SetBackgroundProperties(mController, propertyValue, Text::EffectStyle::DEFAULT);
480       break;
481     }
482     case Toolkit::DevelTextVisual::Property::STRIKETHROUGH:
483     {
484       SetStrikethroughProperties(mController, propertyValue, Text::EffectStyle::DEFAULT);
485       break;
486     }
487   }
488 }
489
490 void TextVisual::UpdateRenderer()
491 {
492   Actor control = mControl.GetHandle();
493   if(!control)
494   {
495     // Nothing to do.
496     return;
497   }
498
499   // Calculates the size to be used to relayout.
500   Vector2 relayoutSize;
501
502   const bool isWidthRelative  = fabsf(mImpl->mTransform.mOffsetSizeMode.z) < Math::MACHINE_EPSILON_1000;
503   const bool isHeightRelative = fabsf(mImpl->mTransform.mOffsetSizeMode.w) < Math::MACHINE_EPSILON_1000;
504
505   // Round the size and offset to avoid pixel alignement issues.
506   relayoutSize.width  = floorf(0.5f + (isWidthRelative ? mImpl->mControlSize.width * mImpl->mTransform.mSize.x : mImpl->mTransform.mSize.width));
507   relayoutSize.height = floorf(0.5f + (isHeightRelative ? mImpl->mControlSize.height * mImpl->mTransform.mSize.y : mImpl->mTransform.mSize.height));
508
509   auto textLengthUtf32 = mController->GetNumberOfCharacters();
510
511   if((fabsf(relayoutSize.width) < Math::MACHINE_EPSILON_1000) || (fabsf(relayoutSize.height) < Math::MACHINE_EPSILON_1000) || textLengthUtf32 == 0u)
512   {
513     // Remove the texture set and any renderer previously set.
514     RemoveRenderer(control);
515
516     // Nothing else to do if the relayout size is zero.
517     ResourceReady(Toolkit::Visual::ResourceStatus::READY);
518     return;
519   }
520
521   Dali::LayoutDirection::Type layoutDirection = mController->GetLayoutDirection(control);
522
523   const Text::Controller::UpdateTextType updateTextType = mController->Relayout(relayoutSize, layoutDirection);
524
525   if(Text::Controller::NONE_UPDATED != (Text::Controller::MODEL_UPDATED & updateTextType) || mRendererUpdateNeeded)
526   {
527     mRendererUpdateNeeded = false;
528
529     // Remove the texture set and any renderer previously set.
530     RemoveRenderer(control);
531
532     if((relayoutSize.width > Math::MACHINE_EPSILON_1000) &&
533        (relayoutSize.height > Math::MACHINE_EPSILON_1000))
534     {
535       // Check whether it is a markup text with multiple text colors
536       const Vector4* const colorsBuffer          = mController->GetTextModel()->GetColors();
537       bool                 hasMultipleTextColors = (NULL != colorsBuffer);
538
539       // Check whether the text contains any color glyph
540       bool containsColorGlyph = false;
541
542       TextAbstraction::FontClient  fontClient     = TextAbstraction::FontClient::Get();
543       const Text::GlyphInfo* const glyphsBuffer   = mController->GetTextModel()->GetGlyphs();
544       const Text::Length           numberOfGlyphs = mController->GetTextModel()->GetNumberOfGlyphs();
545       for(Text::Length glyphIndex = 0; glyphIndex < numberOfGlyphs; glyphIndex++)
546       {
547         // Retrieve the glyph's info.
548         const Text::GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
549
550         // Whether the current glyph is a color one.
551         if(fontClient.IsColorGlyph(glyphInfo->fontId, glyphInfo->index))
552         {
553           containsColorGlyph = true;
554           break;
555         }
556       }
557
558       // Check whether the text contains any style colors (e.g. underline color, shadow color, etc.)
559
560       bool           shadowEnabled = false;
561       const Vector2& shadowOffset  = mController->GetTextModel()->GetShadowOffset();
562       if(fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1)
563       {
564         shadowEnabled = true;
565       }
566
567       const bool outlineEnabled             = (mController->GetTextModel()->GetOutlineWidth() > Math::MACHINE_EPSILON_1);
568       const bool backgroundEnabled          = mController->GetTextModel()->IsBackgroundEnabled();
569       const bool markupOrSpannedText        = mController->IsMarkupProcessorEnabled() || mController->GetTextModel()->IsSpannedTextPlaced();
570       const bool markupUnderlineEnabled     = markupOrSpannedText && mController->GetTextModel()->IsMarkupUnderlineSet();
571       const bool markupStrikethroughEnabled = markupOrSpannedText && mController->GetTextModel()->IsMarkupStrikethroughSet();
572       const bool underlineEnabled           = mController->GetTextModel()->IsUnderlineEnabled() || markupUnderlineEnabled;
573       const bool strikethroughEnabled       = mController->GetTextModel()->IsStrikethroughEnabled() || markupStrikethroughEnabled;
574       const bool backgroundMarkupSet        = mController->GetTextModel()->IsMarkupBackgroundColorSet();
575       const bool styleEnabled               = (shadowEnabled || outlineEnabled || backgroundEnabled || markupOrSpannedText || backgroundMarkupSet);
576       const bool isOverlayStyle             = underlineEnabled || strikethroughEnabled;
577
578       AddRenderer(control, relayoutSize, hasMultipleTextColors, containsColorGlyph, styleEnabled, isOverlayStyle);
579
580       // Text rendered and ready to display
581       ResourceReady(Toolkit::Visual::ResourceStatus::READY);
582     }
583   }
584 }
585
586 void TextVisual::AddTexture(TextureSet& textureSet, PixelData& data, Sampler& sampler, unsigned int textureSetIndex)
587 {
588   Texture texture = Texture::New(Dali::TextureType::TEXTURE_2D,
589                                  data.GetPixelFormat(),
590                                  data.GetWidth(),
591                                  data.GetHeight());
592   texture.Upload(data);
593
594   textureSet.SetTexture(textureSetIndex, texture);
595   textureSet.SetSampler(textureSetIndex, sampler);
596 }
597
598 void TextVisual::AddTilingTexture(TextureSet& textureSet, TilingInfo& tilingInfo, PixelData& data, Sampler& sampler, unsigned int textureSetIndex)
599 {
600   Texture texture = Texture::New(Dali::TextureType::TEXTURE_2D,
601                                  tilingInfo.textPixelFormat,
602                                  tilingInfo.width,
603                                  tilingInfo.height);
604   DevelTexture::UploadSubPixelData(texture, data, 0u, tilingInfo.offsetHeight, tilingInfo.width, tilingInfo.height);
605
606   textureSet.SetTexture(textureSetIndex, texture);
607   textureSet.SetSampler(textureSetIndex, sampler);
608 }
609
610 void TextVisual::CreateTextureSet(TilingInfo& info, VisualRenderer& renderer, Sampler& sampler)
611 {
612   TextureSet textureSet      = TextureSet::New();
613   uint32_t   textureSetIndex = 0u;
614
615   // Convert the buffer to pixel data to make it a texture.
616
617   if(info.textPixelData)
618   {
619     AddTilingTexture(textureSet, info, info.textPixelData, sampler, textureSetIndex);
620     ++textureSetIndex;
621   }
622
623   if(mTextShaderFeatureCache.IsEnabledStyle() && info.stylePixelData)
624   {
625     AddTilingTexture(textureSet, info, info.stylePixelData, sampler, textureSetIndex);
626     ++textureSetIndex;
627   }
628
629   if(mTextShaderFeatureCache.IsEnabledOverlay() && info.overlayStylePixelData)
630   {
631     AddTilingTexture(textureSet, info, info.overlayStylePixelData, sampler, textureSetIndex);
632     ++textureSetIndex;
633   }
634
635   if(mTextShaderFeatureCache.IsEnabledEmoji() && !mTextShaderFeatureCache.IsEnabledMultiColor() && info.maskPixelData)
636   {
637     AddTilingTexture(textureSet, info, info.maskPixelData, sampler, textureSetIndex);
638     ++textureSetIndex;
639   }
640
641   renderer.SetTextures(textureSet);
642
643   //Register transform properties
644   mImpl->mTransform.SetUniforms(renderer, Direction::LEFT_TO_RIGHT);
645
646   // Enable the pre-multiplied alpha to improve the text quality
647   renderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, true);
648   renderer.SetProperty(VisualRenderer::Property::VISUAL_PRE_MULTIPLIED_ALPHA, true);
649
650   // Set size and offset for the tiling.
651   renderer.SetProperty(VisualRenderer::Property::TRANSFORM_SIZE, Vector2(info.width, info.height));
652   renderer.SetProperty(VisualRenderer::Property::TRANSFORM_OFFSET, info.transformOffset);
653   renderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
654   renderer.RegisterProperty("uHasMultipleTextColors", static_cast<float>(mTextShaderFeatureCache.IsEnabledMultiColor()));
655
656   mRendererList.push_back(renderer);
657 }
658
659 void TextVisual::AddRenderer(Actor& actor, const Vector2& size, bool hasMultipleTextColors, bool containsColorGlyph, bool styleEnabled, bool isOverlayStyle)
660 {
661   Shader shader = GetTextShader(mFactoryCache, TextVisualShaderFeature::FeatureBuilder().EnableMultiColor(hasMultipleTextColors).EnableEmoji(containsColorGlyph).EnableStyle(styleEnabled).EnableOverlay(isOverlayStyle));
662   mImpl->mRenderer.SetShader(shader);
663
664   DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_VISUAL_UPDATE_RENDERER");
665
666   // Get the maximum size.
667   const int maxTextureSize = Dali::GetMaxTextureSize();
668
669   // No tiling required. Use the default renderer.
670   if(size.height < maxTextureSize)
671   {
672     TextureSet textureSet = GetTextTexture(size);
673
674     mImpl->mRenderer.SetTextures(textureSet);
675     //Register transform properties
676     mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
677     mImpl->mRenderer.SetProperty(mHasMultipleTextColorsIndex, static_cast<float>(hasMultipleTextColors));
678     mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
679
680     mRendererList.push_back(mImpl->mRenderer);
681   }
682   // If the pixel data exceeds the maximum size, tiling is required.
683   else
684   {
685     // Filter mode needs to be set to linear to produce better quality while scaling.
686     Sampler sampler = Sampler::New();
687     sampler.SetFilterMode(FilterMode::LINEAR, FilterMode::LINEAR);
688
689     // Create RGBA texture if the text contains emojis or multiple text colors, otherwise L8 texture
690     Pixel::Format textPixelFormat = (containsColorGlyph || hasMultipleTextColors) ? Pixel::RGBA8888 : Pixel::L8;
691
692     // Check the text direction
693     Toolkit::DevelText::TextDirection::Type textDirection = mController->GetTextDirection();
694
695     // Create a texture for the text without any styles
696     PixelData data = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_NO_STYLES, false, textPixelFormat);
697
698     int verifiedWidth  = data.GetWidth();
699     int verifiedHeight = data.GetHeight();
700
701     // Set information for creating textures.
702     TilingInfo info(verifiedWidth, maxTextureSize, textPixelFormat);
703
704     // Get the pixel data of text.
705     info.textPixelData = data;
706
707     if(mTextShaderFeatureCache.IsEnabledStyle())
708     {
709       // Create RGBA texture for all the text styles (without the text itself)
710       info.stylePixelData = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_NO_TEXT, false, Pixel::RGBA8888);
711     }
712
713     if(mTextShaderFeatureCache.IsEnabledOverlay())
714     {
715       // Create RGBA texture for all the overlay styles
716       info.overlayStylePixelData = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_OVERLAY_STYLE, false, Pixel::RGBA8888);
717     }
718
719     if(mTextShaderFeatureCache.IsEnabledEmoji() && !mTextShaderFeatureCache.IsEnabledMultiColor())
720     {
721       // Create a L8 texture as a mask to avoid color glyphs (e.g. emojis) to be affected by text color animation
722       info.maskPixelData = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_MASK, false, Pixel::L8);
723     }
724
725     // Get the current offset for recalculate the offset when tiling.
726     Property::Map retMap;
727     mImpl->mTransform.GetPropertyMap(retMap);
728     Property::Value* offsetValue = retMap.Find(Dali::Toolkit::Visual::Transform::Property::OFFSET);
729     if(offsetValue)
730     {
731       offsetValue->Get(info.transformOffset);
732     }
733
734     // Create a textureset in the default renderer.
735     CreateTextureSet(info, mImpl->mRenderer, sampler);
736
737     verifiedHeight -= maxTextureSize;
738
739     Geometry geometry = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY);
740
741     // Create a renderer by cutting maxTextureSize.
742     while(verifiedHeight > 0)
743     {
744       VisualRenderer tilingRenderer = VisualRenderer::New(geometry, shader);
745       tilingRenderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, Toolkit::DepthIndex::CONTENT);
746       // New offset position of buffer for tiling.
747       info.offsetHeight += maxTextureSize;
748       // New height for tiling.
749       info.height = (verifiedHeight - maxTextureSize) > 0 ? maxTextureSize : verifiedHeight;
750       // New offset for tiling.
751       info.transformOffset.y += maxTextureSize;
752       // Create a textureset int the new tiling renderer.
753       CreateTextureSet(info, tilingRenderer, sampler);
754
755       verifiedHeight -= maxTextureSize;
756     }
757   }
758
759   mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
760
761   const Vector4& defaultColor = mController->GetTextModel()->GetDefaultColor();
762
763   for(RendererContainer::iterator iter = mRendererList.begin(); iter != mRendererList.end(); ++iter)
764   {
765     Renderer renderer = (*iter);
766     if(renderer)
767     {
768       actor.AddRenderer(renderer);
769
770       if(renderer != mImpl->mRenderer)
771       {
772         // Set constraint for text label's color for non-default renderers.
773         if(mAnimatableTextColorPropertyIndex != Property::INVALID_INDEX)
774         {
775           // Register unique property, or get property for default renderer.
776           Property::Index index = renderer.RegisterUniqueProperty("uTextColorAnimatable", defaultColor);
777
778           // Create constraint for the animatable text's color Property with uTextColorAnimatable in the renderer.
779           if(index != Property::INVALID_INDEX)
780           {
781             Constraint colorConstraint = Constraint::New<Vector4>(renderer, index, TextColorConstraint);
782             colorConstraint.AddSource(Source(actor, mAnimatableTextColorPropertyIndex));
783             colorConstraint.Apply();
784           }
785
786           // Make zero if the alpha value of text color is zero to skip rendering text
787           // VisualRenderer::Property::OPACITY uses same animatable property internally.
788           Constraint opacityConstraint = Constraint::New<float>(renderer, Dali::DevelRenderer::Property::OPACITY, OpacityConstraint);
789           opacityConstraint.AddSource(Source(actor, mAnimatableTextColorPropertyIndex));
790           opacityConstraint.Apply();
791         }
792       }
793     }
794   }
795 }
796
797 TextureSet TextVisual::GetTextTexture(const Vector2& size)
798 {
799   // Filter mode needs to be set to linear to produce better quality while scaling.
800   Sampler sampler = Sampler::New();
801   sampler.SetFilterMode(FilterMode::LINEAR, FilterMode::LINEAR);
802
803   TextureSet textureSet = TextureSet::New();
804
805   // Create RGBA texture if the text contains emojis or multiple text colors, otherwise L8 texture
806   Pixel::Format textPixelFormat = (mTextShaderFeatureCache.IsEnabledEmoji() || mTextShaderFeatureCache.IsEnabledMultiColor()) ? Pixel::RGBA8888 : Pixel::L8;
807
808   // Check the text direction
809   Toolkit::DevelText::TextDirection::Type textDirection = mController->GetTextDirection();
810
811   // Create a texture for the text without any styles
812   PixelData data = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_NO_STYLES, false, textPixelFormat);
813
814   uint32_t textureSetIndex = 0u;
815   AddTexture(textureSet, data, sampler, textureSetIndex);
816   ++textureSetIndex;
817
818   if(mTextShaderFeatureCache.IsEnabledStyle())
819   {
820     // Create RGBA texture for all the text styles that render in the background (without the text itself)
821     PixelData styleData = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_NO_TEXT, false, Pixel::RGBA8888);
822     AddTexture(textureSet, styleData, sampler, textureSetIndex);
823     ++textureSetIndex;
824   }
825
826   if(mTextShaderFeatureCache.IsEnabledOverlay())
827   {
828     // Create RGBA texture for overlay styles such as underline and strikethrough (without the text itself)
829     PixelData overlayStyleData = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_OVERLAY_STYLE, false, Pixel::RGBA8888);
830     AddTexture(textureSet, overlayStyleData, sampler, textureSetIndex);
831     ++textureSetIndex;
832   }
833
834   if(mTextShaderFeatureCache.IsEnabledEmoji() && !mTextShaderFeatureCache.IsEnabledMultiColor())
835   {
836     // Create a L8 texture as a mask to avoid color glyphs (e.g. emojis) to be affected by text color animation
837     PixelData maskData = mTypesetter->Render(size, textDirection, Text::Typesetter::RENDER_MASK, false, Pixel::L8);
838
839     AddTexture(textureSet, maskData, sampler, textureSetIndex);
840   }
841
842   return textureSet;
843 }
844
845 Shader TextVisual::GetTextShader(VisualFactoryCache& factoryCache, const TextVisualShaderFeature::FeatureBuilder& featureBuilder)
846 {
847   // Cache feature builder informations.
848   mTextShaderFeatureCache = featureBuilder;
849
850   Shader shader = mTextVisualShaderFactory.GetShader(factoryCache, mTextShaderFeatureCache);
851   return shader;
852 }
853
854 } // namespace Internal
855
856 } // namespace Toolkit
857
858 } // namespace Dali