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