Merge "If the size of the text is larger than the size of the control, setting it...
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / text / text-visual.cpp
1 /*
2  * Copyright (c) 2020 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/public-api/animation/constraints.h>
23 #include <dali/devel-api/rendering/renderer-devel.h>
24 #include <dali/devel-api/text-abstraction/text-abstraction-definitions.h>
25 #include <dali/devel-api/adaptor-framework/image-loading.h>
26 #include <dali/devel-api/images/pixel-data-devel.h>
27 #include <string.h>
28
29 // INTERNAL HEADER
30 #include <dali-toolkit/public-api/visuals/text-visual-properties.h>
31 #include <dali-toolkit/devel-api/visuals/text-visual-properties-devel.h>
32 #include <dali-toolkit/public-api/visuals/visual-properties.h>
33 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
34 #include <dali-toolkit/internal/visuals/image-atlas-manager.h>
35 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
36 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
37 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
38 #include <dali-toolkit/internal/text/text-font-style.h>
39 #include <dali-toolkit/internal/text/text-effects-style.h>
40 #include <dali-toolkit/internal/text/script-run.h>
41 #include <dali-toolkit/internal/text/text-enumerations-impl.h>
42 #include <dali-toolkit/devel-api/text/text-enumerations-devel.h>
43
44 namespace Dali
45 {
46
47 namespace Toolkit
48 {
49
50 namespace Internal
51 {
52
53 namespace
54 {
55 const Vector4 FULL_TEXTURE_RECT( 0.f, 0.f, 1.f, 1.f );
56
57 const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
58   attribute mediump vec2 aPosition;\n
59   uniform highp   mat4 uMvpMatrix;\n
60   uniform mediump vec3 uSize;\n
61   uniform mediump vec4 pixelArea;\n
62
63   varying mediump vec2 vTexCoord;\n
64
65   //Visual size and offset
66   uniform mediump vec2 offset;\n
67   uniform mediump vec2 size;\n
68   uniform mediump vec4 offsetSizeMode;\n
69   uniform mediump vec2 origin;\n
70   uniform mediump vec2 anchorPoint;\n
71
72   vec4 ComputeVertexPosition()\n
73   {\n
74     vec2 visualSize = mix(uSize.xy*size, size, offsetSizeMode.zw );\n
75     vec2 visualOffset = mix( offset, offset/uSize.xy, offsetSizeMode.xy);\n
76     return vec4( (aPosition + anchorPoint)*visualSize + (visualOffset + origin)*uSize.xy, 0.0, 1.0 );\n
77   }\n
78
79   void main()\n
80   {\n
81     gl_Position = uMvpMatrix * ComputeVertexPosition();\n
82     vTexCoord = pixelArea.xy+pixelArea.zw*(aPosition + vec2(0.5) );\n
83   }\n
84 );
85
86 const char* FRAGMENT_SHADER_SINGLE_COLOR_TEXT = DALI_COMPOSE_SHADER(
87   varying mediump vec2 vTexCoord;\n
88   uniform sampler2D sTexture;\n
89   uniform lowp vec4 uTextColorAnimatable;\n
90   uniform lowp vec4 uColor;\n
91   uniform lowp vec3 mixColor;\n
92   \n
93   void main()\n
94   {\n
95     mediump float textTexture = texture2D( sTexture, vTexCoord ).r;\n
96
97     // Set the color of the text to what it is animated to.
98     gl_FragColor = uTextColorAnimatable * textTexture * uColor * vec4( mixColor, 1.0 );
99   }\n
100 );
101
102 const char* FRAGMENT_SHADER_MULTI_COLOR_TEXT = DALI_COMPOSE_SHADER(
103   varying mediump vec2 vTexCoord;\n
104   uniform sampler2D sTexture;\n
105   uniform lowp vec4 uColor;\n
106   uniform lowp vec3 mixColor;\n
107   \n
108   void main()\n
109   {\n
110     mediump vec4 textTexture = texture2D( sTexture, vTexCoord );\n
111
112     gl_FragColor = textTexture * uColor * vec4( mixColor, 1.0 );
113   }\n
114 );
115
116 const char* FRAGMENT_SHADER_SINGLE_COLOR_TEXT_WITH_STYLE = DALI_COMPOSE_SHADER(
117   varying mediump vec2 vTexCoord;\n
118   uniform sampler2D sTexture;\n
119   uniform sampler2D sStyle;\n
120   uniform lowp vec4 uTextColorAnimatable;\n
121   uniform lowp vec4 uColor;\n
122   uniform lowp vec3 mixColor;\n
123   \n
124   void main()\n
125   {\n
126     mediump float textTexture = texture2D( sTexture, vTexCoord ).r;\n
127     mediump vec4 styleTexture = texture2D( sStyle, vTexCoord );\n
128
129     // Draw the text as overlay above the style
130     gl_FragColor = ( uTextColorAnimatable * textTexture + styleTexture * ( 1.0 - uTextColorAnimatable.a * textTexture ) ) * uColor * vec4( mixColor, 1.0 );\n
131   }\n
132 );
133
134 const char* FRAGMENT_SHADER_MULTI_COLOR_TEXT_WITH_STYLE = DALI_COMPOSE_SHADER(
135   varying mediump vec2 vTexCoord;\n
136   uniform sampler2D sTexture;\n
137   uniform sampler2D sStyle;\n
138   uniform lowp vec4 uColor;\n
139   uniform lowp vec3 mixColor;\n
140   \n
141   void main()\n
142   {\n
143     mediump vec4 textTexture = texture2D( sTexture, vTexCoord );\n
144     mediump vec4 styleTexture = texture2D( sStyle, vTexCoord );\n
145
146     // Draw the text as overlay above the style
147     gl_FragColor = ( textTexture + styleTexture * ( 1.0 - textTexture.a ) ) * uColor * vec4( mixColor, 1.0 );\n
148   }\n
149 );
150
151 const char* FRAGMENT_SHADER_SINGLE_COLOR_TEXT_WITH_EMOJI = DALI_COMPOSE_SHADER(
152   varying mediump vec2 vTexCoord;\n
153   uniform sampler2D sTexture;\n
154   uniform sampler2D sMask;\n
155   uniform lowp vec4 uTextColorAnimatable;\n
156   uniform lowp vec4 uColor;\n
157   uniform lowp vec3 mixColor;\n
158   \n
159   void main()\n
160   {\n
161     mediump vec4 textTexture = texture2D( sTexture, vTexCoord );\n
162     mediump float maskTexture = texture2D( sMask, vTexCoord ).r;\n
163
164     // Set the color of non-transparent pixel in text to what it is animated to.
165     // Markup text with multiple text colors are not animated (but can be supported later on if required).
166     // Emoji color are not animated.
167     mediump float vstep = step( 0.0001, textTexture.a );\n
168     textTexture.rgb = mix( textTexture.rgb, uTextColorAnimatable.rgb, vstep * maskTexture );\n
169
170     // Draw the text as overlay above the style
171     gl_FragColor = textTexture * uColor * vec4( mixColor, 1.0 );\n
172   }\n
173 );
174
175 const char* FRAGMENT_SHADER_SINGLE_COLOR_TEXT_WITH_STYLE_AND_EMOJI = DALI_COMPOSE_SHADER(
176   varying mediump vec2 vTexCoord;\n
177   uniform sampler2D sTexture;\n
178   uniform sampler2D sStyle;\n
179   uniform sampler2D sMask;\n
180   uniform lowp float uHasMultipleTextColors;\n
181   uniform lowp vec4 uTextColorAnimatable;\n
182   uniform lowp vec4 uColor;\n
183   uniform lowp vec3 mixColor;\n
184   \n
185   void main()\n
186   {\n
187     mediump vec4 textTexture = texture2D( sTexture, vTexCoord );\n
188     mediump vec4 styleTexture = texture2D( sStyle, vTexCoord );\n
189     mediump float maskTexture = texture2D( sMask, vTexCoord ).r;\n
190
191     // Set the color of non-transparent pixel in text to what it is animated to.
192     // Markup text with multiple text colors are not animated (but can be supported later on if required).
193     // Emoji color are not animated.
194     mediump float vstep = step( 0.0001, textTexture.a );\n
195     textTexture.rgb = mix( textTexture.rgb, uTextColorAnimatable.rgb, vstep * maskTexture * ( 1.0 - uHasMultipleTextColors ) );\n
196
197     // Draw the text as overlay above the style
198     gl_FragColor = ( textTexture + styleTexture * ( 1.0 - textTexture.a ) ) * uColor * vec4( mixColor, 1.0 );\n
199   }\n
200 );
201
202 /**
203  * Return Property index for the given string key
204  * param[in] stringKey the string index key
205  * return the key as an index
206  */
207
208 Dali::Property::Index StringKeyToIndexKey( const std::string& stringKey )
209 {
210   Dali::Property::Index result = Property::INVALID_KEY;
211
212   if( stringKey == VISUAL_TYPE )
213   {
214     result = Toolkit::Visual::Property::TYPE;
215   }
216   else if( stringKey == TEXT_PROPERTY )
217   {
218     result = Toolkit::TextVisual::Property::TEXT;
219   }
220   else if( stringKey == FONT_FAMILY_PROPERTY )
221   {
222     result = Toolkit::TextVisual::Property::FONT_FAMILY;
223   }
224   else if( stringKey == FONT_STYLE_PROPERTY )
225   {
226     result = Toolkit::TextVisual::Property::FONT_STYLE;
227   }
228   else if( stringKey == POINT_SIZE_PROPERTY )
229   {
230     result = Toolkit::TextVisual::Property::POINT_SIZE;
231   }
232   else if( stringKey == MULTI_LINE_PROPERTY )
233   {
234     result = Toolkit::TextVisual::Property::MULTI_LINE;
235   }
236   else if( stringKey == HORIZONTAL_ALIGNMENT_PROPERTY )
237   {
238     result = Toolkit::TextVisual::Property::HORIZONTAL_ALIGNMENT;
239   }
240   else if( stringKey == VERTICAL_ALIGNMENT_PROPERTY )
241   {
242     result = Toolkit::TextVisual::Property::VERTICAL_ALIGNMENT;
243   }
244   else if( stringKey == TEXT_COLOR_PROPERTY )
245   {
246     result = Toolkit::TextVisual::Property::TEXT_COLOR;
247   }
248   else if( stringKey == ENABLE_MARKUP_PROPERTY )
249   {
250     result = Toolkit::TextVisual::Property::ENABLE_MARKUP;
251   }
252   else if( stringKey == SHADOW_PROPERTY )
253   {
254     result = Toolkit::TextVisual::Property::SHADOW;
255   }
256   else if( stringKey == UNDERLINE_PROPERTY )
257   {
258     result = Toolkit::TextVisual::Property::UNDERLINE;
259   }
260   else if( stringKey == OUTLINE_PROPERTY )
261   {
262     result = Toolkit::DevelTextVisual::Property::OUTLINE;
263   }
264   else if( stringKey == BACKGROUND_PROPERTY )
265   {
266     result = Toolkit::DevelTextVisual::Property::BACKGROUND;
267   }
268
269   return result;
270 }
271
272 void TextColorConstraint( Vector4& current, const PropertyInputContainer& inputs )
273 {
274   Vector4 color = inputs[0]->GetVector4();
275   current.r = color.r * color.a;
276   current.g = color.g * color.a;
277   current.b = color.b * color.a;
278   current.a = color.a;
279 }
280
281 void OpacityConstraint( float& current, const PropertyInputContainer& inputs )
282 {
283   // Make zero if the alpha value of text color is zero to skip rendering text
284   if( EqualsZero( inputs[0]->GetVector4().a ) )
285   {
286     current = 0.0f;
287   }
288   else
289   {
290     current = 1.0f;
291   }
292 }
293
294 } // unnamed namespace
295
296 TextVisualPtr TextVisual::New( VisualFactoryCache& factoryCache, const Property::Map& properties )
297 {
298   TextVisualPtr TextVisualPtr( new TextVisual( factoryCache ) );
299   TextVisualPtr->SetProperties( properties );
300   return TextVisualPtr;
301 }
302
303 void TextVisual::ConvertStringKeysToIndexKeys( Property::Map& propertyMap )
304 {
305   Property::Map outMap;
306
307   for( Property::Map::SizeType index = 0u, count = propertyMap.Count(); index < count; ++index )
308   {
309     const KeyValuePair& keyValue = propertyMap.GetKeyValue( index );
310
311     Property::Index indexKey = keyValue.first.indexKey;
312
313     if ( keyValue.first.type == Property::Key::STRING )
314     {
315       indexKey = StringKeyToIndexKey( keyValue.first.stringKey );
316     }
317
318     outMap.Insert( indexKey, keyValue.second );
319   }
320
321   propertyMap = outMap;
322 }
323
324 float TextVisual::GetHeightForWidth( float width )
325 {
326   return mController->GetHeightForWidth( width );
327 }
328
329 void TextVisual::GetNaturalSize( Vector2& naturalSize )
330 {
331   naturalSize = mController->GetNaturalSize().GetVectorXY();
332 }
333
334 void TextVisual::DoCreatePropertyMap( Property::Map& map ) const
335 {
336   Property::Value value;
337
338   map.Clear();
339   map.Insert( Toolkit::Visual::Property::TYPE, Toolkit::Visual::TEXT );
340
341   std::string text;
342   mController->GetText( text );
343   map.Insert( Toolkit::TextVisual::Property::TEXT, text );
344
345   map.Insert( Toolkit::TextVisual::Property::FONT_FAMILY, mController->GetDefaultFontFamily() );
346
347   GetFontStyleProperty( mController, value, Text::FontStyle::DEFAULT );
348   map.Insert( Toolkit::TextVisual::Property::FONT_STYLE, value );
349
350   map.Insert( Toolkit::TextVisual::Property::POINT_SIZE, mController->GetDefaultFontSize( Text::Controller::POINT_SIZE ) );
351
352   map.Insert( Toolkit::TextVisual::Property::MULTI_LINE, mController->IsMultiLineEnabled() );
353
354   map.Insert( Toolkit::TextVisual::Property::HORIZONTAL_ALIGNMENT, mController->GetHorizontalAlignment() );
355
356   map.Insert( Toolkit::TextVisual::Property::VERTICAL_ALIGNMENT, mController->GetVerticalAlignment() );
357
358   map.Insert( Toolkit::TextVisual::Property::TEXT_COLOR, mController->GetDefaultColor() );
359
360   map.Insert( Toolkit::TextVisual::Property::ENABLE_MARKUP, mController->IsMarkupProcessorEnabled() );
361
362   GetShadowProperties( mController, value, Text::EffectStyle::DEFAULT );
363   map.Insert( Toolkit::TextVisual::Property::SHADOW, value );
364
365   GetUnderlineProperties( mController, value, Text::EffectStyle::DEFAULT );
366   map.Insert( Toolkit::TextVisual::Property::UNDERLINE, value );
367
368   GetOutlineProperties( mController, value, Text::EffectStyle::DEFAULT );
369   map.Insert( Toolkit::DevelTextVisual::Property::OUTLINE, value );
370
371   GetBackgroundProperties( mController, value, Text::EffectStyle::DEFAULT );
372   map.Insert( Toolkit::DevelTextVisual::Property::BACKGROUND, value );
373 }
374
375 void TextVisual::DoCreateInstancePropertyMap( Property::Map& map ) const
376 {
377   map.Clear();
378   map.Insert( Toolkit::Visual::Property::TYPE, Toolkit::Visual::TEXT );
379   std::string text;
380   mController->GetText( text );
381   map.Insert( Toolkit::TextVisual::Property::TEXT, text );
382 }
383
384
385 TextVisual::TextVisual( VisualFactoryCache& factoryCache )
386 : Visual::Base( factoryCache, Visual::FittingMode::FIT_KEEP_ASPECT_RATIO, Toolkit::Visual::TEXT ),
387   mController( Text::Controller::New() ),
388   mTypesetter( Text::Typesetter::New( mController->GetTextModel() ) ),
389   mAnimatableTextColorPropertyIndex( Property::INVALID_INDEX ),
390   mRendererUpdateNeeded( false )
391 {
392 }
393
394 TextVisual::~TextVisual()
395 {
396 }
397
398 void TextVisual::DoSetProperties( const Property::Map& propertyMap )
399 {
400   for( Property::Map::SizeType index = 0u, count = propertyMap.Count(); index < count; ++index )
401   {
402     const KeyValuePair& keyValue = propertyMap.GetKeyValue( index );
403
404     Property::Index indexKey = keyValue.first.indexKey;
405
406     if( keyValue.first.type == Property::Key::STRING )
407     {
408       indexKey = StringKeyToIndexKey( keyValue.first.stringKey );
409     }
410
411     DoSetProperty( indexKey, keyValue.second );
412   }
413
414   // Elide the text if it exceeds the boundaries.
415   mController->SetTextElideEnabled( true );
416
417   // Retrieve the layout engine to set the cursor's width.
418   Text::Layout::Engine& engine = mController->GetLayoutEngine();
419
420   // Sets 0 as cursor's width.
421   engine.SetCursorWidth( 0u ); // Do not layout space for the cursor.
422 }
423
424 void TextVisual::DoSetOnScene( Actor& actor )
425 {
426   mControl = actor;
427
428   Geometry geometry = mFactoryCache.GetGeometry( VisualFactoryCache::QUAD_GEOMETRY );
429   Shader shader = GetTextShader(mFactoryCache, TextType::SINGLE_COLOR_TEXT, TextType::NO_EMOJI, TextType::NO_STYLES);
430
431   mImpl->mRenderer = Renderer::New( geometry, shader );
432   mImpl->mRenderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, Toolkit::DepthIndex::CONTENT );
433
434   // Enable the pre-multiplied alpha to improve the text quality
435   EnablePreMultipliedAlpha(true);
436
437   const Vector4& defaultColor = mController->GetTextModel()->GetDefaultColor();
438   Dali::Property::Index shaderTextColorIndex = mImpl->mRenderer.RegisterProperty( "uTextColorAnimatable", defaultColor );
439
440   if ( mAnimatableTextColorPropertyIndex != Property::INVALID_INDEX )
441   {
442     // Create constraint for the animatable text's color Property with uTextColorAnimatable in the renderer.
443     if( shaderTextColorIndex != Property::INVALID_INDEX )
444     {
445       Constraint colorConstraint = Constraint::New<Vector4>( mImpl->mRenderer, shaderTextColorIndex, TextColorConstraint );
446       colorConstraint.AddSource( Source( actor, mAnimatableTextColorPropertyIndex ) );
447       colorConstraint.Apply();
448
449       // Make zero if the alpha value of text color is zero to skip rendering text
450       Constraint opacityConstraint = Constraint::New< float >( mImpl->mRenderer, Dali::DevelRenderer::Property::OPACITY, OpacityConstraint );
451       opacityConstraint.AddSource( Source( actor, mAnimatableTextColorPropertyIndex ) );
452       opacityConstraint.Apply();
453     }
454   }
455
456   // Renderer needs textures and to be added to control
457   mRendererUpdateNeeded = true;
458
459   mRendererList.push_back( mImpl->mRenderer );
460
461   UpdateRenderer();
462 }
463
464 void TextVisual::RemoveRenderer( Actor& actor )
465 {
466   for( RendererContainer::iterator iter = mRendererList.begin(); iter != mRendererList.end(); ++iter)
467   {
468     Renderer renderer = (*iter);
469     if( renderer )
470     {
471       // Removes the renderer from the actor.
472       actor.RemoveRenderer( renderer );
473     }
474   }
475   // Clear the renderer list
476   mRendererList.clear();
477 }
478
479 void TextVisual::DoSetOffScene( Actor& actor )
480 {
481   RemoveRenderer( actor );
482
483   // Resets the renderer.
484   mImpl->mRenderer.Reset();
485
486   // Resets the control handle.
487   mControl.Reset();
488 }
489
490 void TextVisual::OnSetTransform()
491 {
492   UpdateRenderer();
493 }
494
495 void TextVisual::DoSetProperty( Dali::Property::Index index, const Dali::Property::Value& propertyValue )
496 {
497   switch( index )
498   {
499     case Toolkit::TextVisual::Property::ENABLE_MARKUP:
500     {
501       const bool enableMarkup = propertyValue.Get<bool>();
502       mController->SetMarkupProcessorEnabled( enableMarkup );
503       break;
504     }
505     case Toolkit::TextVisual::Property::TEXT:
506     {
507       mController->SetText( propertyValue.Get<std::string>() );
508       break;
509     }
510     case Toolkit::TextVisual::Property::FONT_FAMILY:
511     {
512       SetFontFamilyProperty( mController, propertyValue );
513       break;
514     }
515     case Toolkit::TextVisual::Property::FONT_STYLE:
516     {
517       SetFontStyleProperty( mController, propertyValue, Text::FontStyle::DEFAULT );
518       break;
519     }
520     case Toolkit::TextVisual::Property::POINT_SIZE:
521     {
522       const float pointSize = propertyValue.Get<float>();
523       if( !Equals( mController->GetDefaultFontSize( Text::Controller::POINT_SIZE ), pointSize ) )
524       {
525         mController->SetDefaultFontSize( pointSize, Text::Controller::POINT_SIZE );
526       }
527       break;
528     }
529     case Toolkit::TextVisual::Property::MULTI_LINE:
530     {
531       mController->SetMultiLineEnabled( propertyValue.Get<bool>() );
532       break;
533     }
534     case Toolkit::TextVisual::Property::HORIZONTAL_ALIGNMENT:
535     {
536       if( mController )
537       {
538         Text::HorizontalAlignment::Type alignment( static_cast< Text::HorizontalAlignment::Type >( -1 ) ); // Set to invalid value to ensure a valid mode does get set
539         if( Toolkit::Text::GetHorizontalAlignmentEnumeration( propertyValue, alignment ) )
540         {
541           mController->SetHorizontalAlignment( alignment );
542         }
543       }
544       break;
545     }
546     case Toolkit::TextVisual::Property::VERTICAL_ALIGNMENT:
547     {
548       if( mController )
549       {
550         Toolkit::Text::VerticalAlignment::Type alignment( static_cast< Text::VerticalAlignment::Type >( -1 ) ); // Set to invalid value to ensure a valid mode does get set
551         if( Toolkit::Text::GetVerticalAlignmentEnumeration( propertyValue, alignment) )
552         {
553           mController->SetVerticalAlignment( alignment );
554         }
555       }
556       break;
557     }
558     case Toolkit::TextVisual::Property::TEXT_COLOR:
559     {
560       const Vector4& textColor = propertyValue.Get< Vector4 >();
561       if( mController->GetDefaultColor() != textColor )
562       {
563         mController->SetDefaultColor( textColor );
564       }
565       break;
566     }
567     case Toolkit::TextVisual::Property::SHADOW:
568     {
569       SetShadowProperties( mController, propertyValue, Text::EffectStyle::DEFAULT );
570       break;
571     }
572     case Toolkit::TextVisual::Property::UNDERLINE:
573     {
574       SetUnderlineProperties( mController, propertyValue, Text::EffectStyle::DEFAULT );
575       break;
576     }
577     case Toolkit::DevelTextVisual::Property::OUTLINE:
578     {
579       SetOutlineProperties( mController, propertyValue, Text::EffectStyle::DEFAULT );
580       break;
581     }
582     case Toolkit::DevelTextVisual::Property::BACKGROUND:
583     {
584       SetBackgroundProperties( mController, propertyValue, Text::EffectStyle::DEFAULT );
585       break;
586     }
587   }
588 }
589
590 void TextVisual::UpdateRenderer()
591 {
592   Actor control = mControl.GetHandle();
593   if( !control )
594   {
595     // Nothing to do.
596     return;
597   }
598
599   // Calculates the size to be used to relayout.
600   Vector2 relayoutSize;
601
602   const bool isWidthRelative = fabsf( mImpl->mTransform.mOffsetSizeMode.z ) < Math::MACHINE_EPSILON_1000;
603   const bool isHeightRelative = fabsf( mImpl->mTransform.mOffsetSizeMode.w ) < Math::MACHINE_EPSILON_1000;
604
605   // Round the size and offset to avoid pixel alignement issues.
606   relayoutSize.width = floorf( 0.5f + ( isWidthRelative ? mImpl->mControlSize.width * mImpl->mTransform.mSize.x : mImpl->mTransform.mSize.width ) );
607   relayoutSize.height = floorf( 0.5f + ( isHeightRelative ? mImpl->mControlSize.height * mImpl->mTransform.mSize.y : mImpl->mTransform.mSize.height ) );
608
609   std::string text;
610   mController->GetText( text );
611
612   if( ( fabsf( relayoutSize.width ) < Math::MACHINE_EPSILON_1000 ) || ( fabsf( relayoutSize.height ) < Math::MACHINE_EPSILON_1000 ) || text.empty() )
613   {
614     // Remove the texture set and any renderer previously set.
615     RemoveRenderer( control );
616
617     // Nothing else to do if the relayout size is zero.
618     ResourceReady( Toolkit::Visual::ResourceStatus::READY );
619     return;
620   }
621
622   Dali::LayoutDirection::Type layoutDirection = static_cast<Dali::LayoutDirection::Type>( control.GetProperty( Dali::Actor::Property::LAYOUT_DIRECTION ).Get<int>() );
623
624   const Text::Controller::UpdateTextType updateTextType = mController->Relayout( relayoutSize, layoutDirection );
625
626   if( Text::Controller::NONE_UPDATED != ( Text::Controller::MODEL_UPDATED & updateTextType )
627    || mRendererUpdateNeeded )
628   {
629     mRendererUpdateNeeded = false;
630
631     // Remove the texture set and any renderer previously set.
632     RemoveRenderer( control );
633
634     if( ( relayoutSize.width > Math::MACHINE_EPSILON_1000 ) &&
635         ( relayoutSize.height > Math::MACHINE_EPSILON_1000 ) )
636     {
637       // Check whether it is a markup text with multiple text colors
638       const Vector4* const colorsBuffer = mController->GetTextModel()->GetColors();
639       bool hasMultipleTextColors = ( NULL != colorsBuffer );
640
641       // Check whether the text contains any color glyph
642       bool containsColorGlyph = false;
643
644       TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
645       const Text::GlyphInfo* const glyphsBuffer = mController->GetTextModel()->GetGlyphs();
646       const Text::Length numberOfGlyphs = mController->GetTextModel()->GetNumberOfGlyphs();
647       for ( Text::Length glyphIndex = 0; glyphIndex < numberOfGlyphs; glyphIndex++ )
648       {
649         // Retrieve the glyph's info.
650         const Text::GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
651
652         // Whether the current glyph is a color one.
653         if( fontClient.IsColorGlyph( glyphInfo->fontId, glyphInfo->index ) )
654         {
655           containsColorGlyph = true;
656           break;
657         }
658       }
659
660       // Check whether the text contains any style colors (e.g. underline color, shadow color, etc.)
661
662       bool shadowEnabled = false;
663       const Vector2& shadowOffset = mController->GetTextModel()->GetShadowOffset();
664       if ( fabsf( shadowOffset.x ) > Math::MACHINE_EPSILON_1 || fabsf( shadowOffset.y ) > Math::MACHINE_EPSILON_1 )
665       {
666         shadowEnabled = true;
667       }
668
669       const bool underlineEnabled = mController->GetTextModel()->IsUnderlineEnabled();
670       const bool outlineEnabled = ( mController->GetTextModel()->GetOutlineWidth() > Math::MACHINE_EPSILON_1 );
671       const bool backgroundEnabled = mController->GetTextModel()->IsBackgroundEnabled();;
672
673       const bool styleEnabled = ( shadowEnabled || underlineEnabled || outlineEnabled || backgroundEnabled );
674
675
676       AddRenderer( control, relayoutSize, hasMultipleTextColors, containsColorGlyph, styleEnabled );
677
678       // Text rendered and ready to display
679       ResourceReady( Toolkit::Visual::ResourceStatus::READY );
680     }
681   }
682 }
683
684 void TextVisual::AddTexture( TextureSet& textureSet, PixelData& data, Sampler& sampler, unsigned int textureSetIndex )
685 {
686   Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D,
687                                   data.GetPixelFormat(),
688                                   data.GetWidth(),
689                                   data.GetHeight() );
690   texture.Upload( data );
691
692   textureSet.SetTexture( textureSetIndex, texture );
693   textureSet.SetSampler( textureSetIndex, sampler );
694 }
695
696 PixelData TextVisual::ConvertToPixelData( unsigned char* buffer, int width, int height, int offsetPosition, const Pixel::Format textPixelFormat )
697 {
698   int bpp = Pixel::GetBytesPerPixel( textPixelFormat );
699   unsigned int bufferSize = width * height * bpp;
700   unsigned char* dstBuffer = static_cast<unsigned char*>( malloc ( bufferSize ) );
701   memcpy( dstBuffer, buffer + offsetPosition * bpp, bufferSize );
702   PixelData pixelData = Dali::PixelData::New( dstBuffer,
703                                               bufferSize,
704                                               width,
705                                               height,
706                                               textPixelFormat,
707                                               Dali::PixelData::FREE );
708   return pixelData;
709 }
710
711 void TextVisual::CreateTextureSet( TilingInfo& info, Renderer& renderer, Sampler& sampler, bool hasMultipleTextColors, bool containsColorGlyph, bool styleEnabled )
712 {
713
714   TextureSet textureSet = TextureSet::New();
715   unsigned int textureSetIndex = 0u;
716
717   // Convert the buffer to pixel data to make it a texture.
718   if( info.textBuffer )
719   {
720     PixelData data = ConvertToPixelData( info.textBuffer, info.width, info.height, info.offsetPosition, info.textPixelFormat );
721     AddTexture( textureSet, data, sampler, textureSetIndex );
722     ++textureSetIndex;
723   }
724
725   if( styleEnabled && info.styleBuffer )
726   {
727     PixelData styleData = ConvertToPixelData( info.styleBuffer, info.width, info.height, info.offsetPosition, Pixel::RGBA8888 );
728     AddTexture( textureSet, styleData, sampler, textureSetIndex );
729     ++textureSetIndex;
730   }
731
732   if( containsColorGlyph && !hasMultipleTextColors && info.maskBuffer )
733   {
734     PixelData maskData = ConvertToPixelData( info.maskBuffer, info.width, info.height, info.offsetPosition, Pixel::L8 );
735     AddTexture( textureSet, maskData, sampler, textureSetIndex );
736   }
737
738   renderer.SetTextures( textureSet );
739
740   //Register transform properties
741   mImpl->mTransform.RegisterUniforms( renderer, Direction::LEFT_TO_RIGHT );
742
743   // Enable the pre-multiplied alpha to improve the text quality
744   renderer.SetProperty( Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, true );
745   renderer.RegisterProperty( PREMULTIPLIED_ALPHA, 1.0f );
746
747   // Set size and offset for the tiling.
748   renderer.RegisterProperty( SIZE, Vector2( info.width, info.height ) );
749   renderer.RegisterProperty( OFFSET, Vector2( info.offSet.x, info.offSet.y ) );
750   renderer.RegisterProperty( "uHasMultipleTextColors", static_cast<float>( hasMultipleTextColors ) );
751   renderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON);
752
753   mRendererList.push_back( renderer );
754 }
755
756
757 void TextVisual::AddRenderer( Actor& actor, const Vector2& size, bool hasMultipleTextColors, bool containsColorGlyph, bool styleEnabled )
758 {
759   Shader shader = GetTextShader( mFactoryCache, hasMultipleTextColors, containsColorGlyph, styleEnabled );
760   mImpl->mRenderer.SetShader( shader );
761
762   // Get the maximum size.
763   const int maxTextureSize = Dali::GetMaxTextureSize();
764
765   // No tiling required. Use the default renderer.
766   if( size.height < maxTextureSize )
767   {
768     TextureSet textureSet = GetTextTexture( size, hasMultipleTextColors, containsColorGlyph, styleEnabled );
769
770     mImpl->mRenderer.SetTextures( textureSet );
771     //Register transform properties
772     mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
773     mImpl->mRenderer.RegisterProperty( "uHasMultipleTextColors", static_cast<float>( hasMultipleTextColors ) );
774     mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON);
775
776     mRendererList.push_back( mImpl->mRenderer );
777   }
778   // If the pixel data exceeds the maximum size, tiling is required.
779   else
780   {
781     // Filter mode needs to be set to linear to produce better quality while scaling.
782     Sampler sampler = Sampler::New();
783     sampler.SetFilterMode( FilterMode::LINEAR, FilterMode::LINEAR );
784
785     // Create RGBA texture if the text contains emojis or multiple text colors, otherwise L8 texture
786     Pixel::Format textPixelFormat = ( containsColorGlyph || hasMultipleTextColors ) ? Pixel::RGBA8888 : Pixel::L8;
787
788     // Check the text direction
789     Toolkit::DevelText::TextDirection::Type textDirection = mController->GetTextDirection();
790
791     // Create a texture for the text without any styles
792     PixelData data = mTypesetter->Render( size, textDirection, Text::Typesetter::RENDER_NO_STYLES, false, textPixelFormat );
793
794     int verifiedWidth = data.GetWidth();
795     int verifiedHeight = data.GetHeight();
796
797     // Set information for creating textures.
798     TilingInfo info( verifiedWidth, maxTextureSize, textPixelFormat );
799
800     // Get the buffer of text.
801     Dali::DevelPixelData::PixelDataBuffer textPixelData = Dali::DevelPixelData::ReleasePixelDataBuffer( data );
802     info.textBuffer = textPixelData.buffer;
803
804     if( styleEnabled )
805     {
806       // Create RGBA texture for all the text styles (without the text itself)
807       PixelData styleData = mTypesetter->Render( size, textDirection, Text::Typesetter::RENDER_NO_TEXT, false, Pixel::RGBA8888 );
808       Dali::DevelPixelData::PixelDataBuffer stylePixelData = Dali::DevelPixelData::ReleasePixelDataBuffer( styleData );
809       info.styleBuffer = stylePixelData.buffer;
810     }
811
812     if ( containsColorGlyph && !hasMultipleTextColors )
813     {
814       // Create a L8 texture as a mask to avoid color glyphs (e.g. emojis) to be affected by text color animation
815       PixelData maskData = mTypesetter->Render( size, textDirection, Text::Typesetter::RENDER_MASK, false, Pixel::L8 );
816       Dali::DevelPixelData::PixelDataBuffer maskPixelData = Dali::DevelPixelData::ReleasePixelDataBuffer( maskData );
817       info.maskBuffer = maskPixelData.buffer;
818     }
819
820     // Get the current offset for recalculate the offset when tiling.
821     Property::Map retMap;
822     mImpl->mTransform.GetPropertyMap( retMap );
823     Property::Value* offsetValue = retMap.Find( Dali::Toolkit::Visual::Transform::Property::OFFSET );
824     if( offsetValue )
825     {
826       offsetValue->Get( info.offSet );
827     }
828
829     // Create a textureset in the default renderer.
830     CreateTextureSet( info, mImpl->mRenderer, sampler, hasMultipleTextColors, containsColorGlyph, styleEnabled );
831
832     verifiedHeight -= maxTextureSize;
833
834     Geometry geometry = mFactoryCache.GetGeometry( VisualFactoryCache::QUAD_GEOMETRY );
835
836     int offsetPosition = verifiedWidth * maxTextureSize;
837     // Create a renderer by cutting maxTextureSize.
838     while( verifiedHeight > 0 )
839     {
840       Renderer tilingRenderer = Renderer::New( geometry, shader );
841       tilingRenderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, Toolkit::DepthIndex::CONTENT );
842       // New offset position of buffer for tiling.
843       info.offsetPosition += offsetPosition;
844       // New height for tiling.
845       info.height = ( verifiedHeight - maxTextureSize ) > 0 ? maxTextureSize : verifiedHeight;
846       // New offset for tiling.
847       info.offSet.y += maxTextureSize;
848       // Create a textureset int the new tiling renderer.
849       CreateTextureSet( info, tilingRenderer, sampler, hasMultipleTextColors, containsColorGlyph, styleEnabled );
850
851       verifiedHeight -= maxTextureSize;
852     }
853   }
854
855   mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
856
857   for( RendererContainer::iterator iter = mRendererList.begin(); iter != mRendererList.end(); ++iter)
858   {
859     Renderer renderer = (*iter);
860     if( renderer )
861     {
862       actor.AddRenderer( renderer );
863     }
864   }
865 }
866
867
868 TextureSet TextVisual::GetTextTexture( const Vector2& size, bool hasMultipleTextColors, bool containsColorGlyph, bool styleEnabled )
869 {
870   // Filter mode needs to be set to linear to produce better quality while scaling.
871   Sampler sampler = Sampler::New();
872   sampler.SetFilterMode( FilterMode::LINEAR, FilterMode::LINEAR );
873
874   TextureSet textureSet = TextureSet::New();
875
876   // Create RGBA texture if the text contains emojis or multiple text colors, otherwise L8 texture
877   Pixel::Format textPixelFormat = ( containsColorGlyph || hasMultipleTextColors ) ? Pixel::RGBA8888 : Pixel::L8;
878
879   // Check the text direction
880   Toolkit::DevelText::TextDirection::Type textDirection = mController->GetTextDirection();
881
882   // Create a texture for the text without any styles
883   PixelData data = mTypesetter->Render( size, textDirection, Text::Typesetter::RENDER_NO_STYLES, false, textPixelFormat );
884
885   // It may happen the image atlas can't handle a pixel data it exceeds the maximum size.
886   // In that case, create a texture. TODO: should tile the text.
887   unsigned int textureSetIndex = 0u;
888
889   AddTexture( textureSet, data, sampler, textureSetIndex );
890   ++textureSetIndex;
891
892   if ( styleEnabled )
893   {
894     // Create RGBA texture for all the text styles (without the text itself)
895     PixelData styleData = mTypesetter->Render( size, textDirection, Text::Typesetter::RENDER_NO_TEXT, false, Pixel::RGBA8888 );
896
897     AddTexture( textureSet, styleData, sampler, textureSetIndex );
898     ++textureSetIndex;
899   }
900
901   if ( containsColorGlyph && !hasMultipleTextColors )
902   {
903     // Create a L8 texture as a mask to avoid color glyphs (e.g. emojis) to be affected by text color animation
904     PixelData maskData = mTypesetter->Render( size, textDirection, Text::Typesetter::RENDER_MASK, false, Pixel::L8 );
905
906     AddTexture( textureSet, maskData, sampler, textureSetIndex );
907   }
908
909   return textureSet;
910 }
911
912 Shader TextVisual::GetTextShader( VisualFactoryCache& factoryCache, bool hasMultipleTextColors, bool containsColorGlyph, bool styleEnabled )
913 {
914   Shader shader;
915
916   if( hasMultipleTextColors && !styleEnabled )
917   {
918     // We don't animate text color if the text contains multiple colors
919     shader = factoryCache.GetShader( VisualFactoryCache::TEXT_SHADER_MULTI_COLOR_TEXT );
920     if( !shader )
921     {
922       shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_MULTI_COLOR_TEXT );
923       shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
924       factoryCache.SaveShader( VisualFactoryCache::TEXT_SHADER_MULTI_COLOR_TEXT, shader );
925     }
926   }
927   else if( hasMultipleTextColors && styleEnabled )
928   {
929     // We don't animate text color if the text contains multiple colors
930     shader = factoryCache.GetShader( VisualFactoryCache::TEXT_SHADER_MULTI_COLOR_TEXT_WITH_STYLE );
931     if( !shader )
932     {
933       shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_MULTI_COLOR_TEXT_WITH_STYLE );
934       shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
935       factoryCache.SaveShader( VisualFactoryCache::TEXT_SHADER_MULTI_COLOR_TEXT_WITH_STYLE, shader );
936     }
937   }
938   else if( !hasMultipleTextColors && !containsColorGlyph && !styleEnabled )
939   {
940     shader = factoryCache.GetShader( VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT );
941     if( !shader )
942     {
943       shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_SINGLE_COLOR_TEXT );
944       shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
945       factoryCache.SaveShader( VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT, shader );
946     }
947   }
948   else if( !hasMultipleTextColors && !containsColorGlyph && styleEnabled )
949   {
950     shader = factoryCache.GetShader( VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_STYLE );
951     if( !shader )
952     {
953       shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_SINGLE_COLOR_TEXT_WITH_STYLE );
954       shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
955       factoryCache.SaveShader( VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_STYLE, shader );
956     }
957   }
958   else if( !hasMultipleTextColors && containsColorGlyph && !styleEnabled )
959   {
960     shader = factoryCache.GetShader( VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_EMOJI );
961     if( !shader )
962     {
963       shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_SINGLE_COLOR_TEXT_WITH_EMOJI );
964       shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
965       factoryCache.SaveShader( VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_EMOJI, shader );
966     }
967   }
968   else // if( !hasMultipleTextColors && containsColorGlyph && styleEnabled )
969   {
970     shader = factoryCache.GetShader( VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_STYLE_AND_EMOJI );
971     if( !shader )
972     {
973       shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_SINGLE_COLOR_TEXT_WITH_STYLE_AND_EMOJI );
974       shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
975       factoryCache.SaveShader( VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_STYLE_AND_EMOJI, shader );
976     }
977   }
978
979   return shader;
980 }
981
982 } // namespace Internal
983
984 } // namespace Toolkit
985
986 } // namespace Dali